@intlify/shared
@intlify/shared is the internal utility layer published by the Vue I18n and Intlify project. It contains small type guards, placeholder formatting, display conversion, source code frames, cache-key helpers, a typed event emitter, warning helpers, HTML escaping and translation sanitization, and an object-copy routine. It has no Vue peer dependency and can be imported on its own, but its README describes it as shared infrastructure for Intlify rather than a standalone application utility toolkit.
Keep @intlify/shared as a transitive dependency unless you are extending Intlify itself or need byte-for-byte behavioral parity with Vue I18n. Its helpers work, but the Node 22 floor and monorepo-internal support posture make focused public packages safer for application code.
Use it if
- You are building or debugging an Intlify package and need the exact helpers used by Vue I18n 11
- You publish an adapter that already depends on the Intlify 11 package family and want consistent formatting, warning, or emitter behavior
- You need to reproduce Vue I18n's message sanitization or cache-key behavior exactly rather than approximately
- You have inspected the v11.4.8 exports and accept coupling your code to a monorepo support package
- You just need common JavaScript helpers: the package README offers no public API reference and explicitly calls this the shared utility package for the Intlify project, so @vue/shared or a focused utility has a clearer support contract
- Your Node deployment is below 22: version 11.4.8 declares engines.node as >= 22 even though older Intlify lines and many ordinary utilities support earlier runtimes
- You need a complete HTML sanitizer: sanitizeTranslatedHtml rewrites dangerous attribute values and event handlers, but the v11.4.8 source does not parse or remove script elements, while escapeHtml is for treating an entire string as text
- You expect a conventional deep clone: deepCopy mutates the destination, skips __proto__, recursively merges plain objects, and assigns arrays by reference according to its source implementation
- You want independent release and documentation cadence: this package is versioned and published from the vue-i18n monorepo, whose main branch already contains 12.0.0-alpha package metadata while npm latest remains 11.4.8
Setup reality
The install has no dependencies, peer dependencies, native compilation, credentials, or configuration file, but it is less frictionless than its size suggests. Version 11.4.8 declares Node >= 22, which package managers may enforce or warn about. It ships ESM, CommonJS, browser, production, development, and TypeScript declaration targets through an exports map; use the package root and let the resolver select a build rather than importing unpublished source paths. The dist/* subpath is exposed, but binding application code to a particular generated file makes upgrades more brittle. Most downloads are likely transitive through vue-i18n, so seeing the package in a lockfile is not a reason to add it to package.json. If you do import it directly, keep its major and minor line aligned with the rest of your @intlify packages because all are released from one monorepo. Several names are easy to overread: format only replaces simple alphanumeric {tokens}; sanitizeTranslatedHtml is targeted translation markup handling, not a general HTML parser; deepCopy writes into an existing object and does not independently clone arrays; createEmitter calls handlers synchronously and provides no once method or async error isolation. The package's own README lists provenance and licensing but no API examples, so the TypeScript declarations and exact tagged source are the practical reference. Test bundler output in production mode because the exports map selects different CommonJS files for production and development.
Patterns
Replace named placeholdersformat-named-placeholders
import { format } from '@intlify/shared';
const message = format('Hello {name}, you have {count} items', {
name: 'Ada',
count: 3,
});Only alphanumeric tokens match. Missing keys become an empty string, and this helper does not implement ICU message syntax or locale-aware plural rules.
Replace positional placeholdersformat-positional-placeholders
import { format } from '@intlify/shared';
const message = format('{0} of {1}', 3, 10);Positional indexes are looked up on the arguments array. Use vue-i18n message formatting when translation grammar, pluralization, or locale rules matter.
Create an Intlify format cache keycreate-cache-key
import { generateFormatCacheKey } from '@intlify/shared';
const key = generateFormatCacheKey(
'en-US',
'invoice.total',
'{amount, number, currency}'
);The function serializes fixed l, k, and s properties. It is meant to match Intlify's cache identity, not to create a cryptographic or collision-resistant key.
Escape line separators and apostrophes in JSONserialize-script-safe-json
import { friendlyJSONstringify } from '@intlify/shared';
const json = friendlyJSONstringify({ text: "it's line\u2028two" });This wraps JSON.stringify and escapes U+2028, U+2029, and apostrophes. It is not a general HTML or JavaScript sanitizer.
Use the bundled type guardsnarrow-runtime-values
import { isArray, isDate, isPlainObject, isPromise } from '@intlify/shared';
if (isDate(value)) console.log(value.toISOString());
if (isArray(value)) console.log(value.length);
if (isPlainObject(value)) console.log(Object.keys(value));
if (isPromise(value)) await value;isPromise checks for an object with then and catch functions; it recognizes promise-like values and does not require a native Promise instance.
Convert a value to display textrender-display-value
import { toDisplayString } from '@intlify/shared';
console.log(toDisplayString(null)); // ''
console.log(toDisplayString({ a: 1 })); // pretty JSON
console.log(toDisplayString(['a', 'b'])); // pretty JSONPlain objects and arrays use two-space JSON formatting. Circular data throws through JSON.stringify, and custom object toString methods are preserved.
Show a source error with nearby linesgenerate-source-frame
import { generateCodeFrame } from '@intlify/shared';
const source = 'first line
hello {name
last line';
const start = source.indexOf('{name');
console.error(generateCodeFrame(source, start, start + 5));Offsets are JavaScript string indexes, not UTF-8 byte offsets. The helper displays a two-line range around the selected location.
Create a typed synchronous event emittercreate-typed-emitter
import { createEmitter } from '@intlify/shared';
type Events = { saved: { id: string }; failed: Error };
const emitter = createEmitter<Events>();
const onSaved = (payload?: Events['saved']) => console.log(payload?.id);
emitter.on('saved', onSaved);
emitter.emit('saved', { id: '42' });
emitter.off('saved', onSaved);Handlers run synchronously. Keep the original function reference for off; the emitter has no once helper and does not catch handler exceptions.
Listen to every emitter eventobserve-all-events
const logEvent = (type, payload) => {
console.log(type, payload);
};
emitter.on('*', logEvent);
emitter.emit('saved', { id: '42' });
emitter.off('*', logEvent);Wildcard handlers receive event type first and payload second, after handlers registered for that specific event have run.
Escape a string for HTML text outputescape-untrusted-text
import { escapeHtml } from '@intlify/shared';
const safeText = escapeHtml(userSuppliedText);
container.innerHTML = `<p>${safeText}</p>`;escapeHtml also escapes slash and equals characters. Prefer textContent when possible because it avoids constructing HTML at all.
Neutralize dangerous translation attributessanitize-translation-markup
import { sanitizeTranslatedHtml } from '@intlify/shared';
const html = sanitizeTranslatedHtml(
'<a href="javascript:alert(1)" onclick="steal()">Help</a>'
);This targets attribute values, event handlers, and javascript URLs. It does not parse and remove arbitrary elements, so do not use it as a full untrusted-HTML sanitizer.
Copy nested message properties into a destinationmerge-message-objects
import { deepCopy } from '@intlify/shared';
const target = { nav: { home: 'Home' } };
deepCopy({ nav: { account: 'Account' }, tags: ['new'] }, target);deepCopy mutates target, skips __proto__, and merges nested objects. Arrays are assigned rather than recursively cloned, so source and destination can share an array reference.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @vue/shared | npm | You are already in Vue internals and want the upstream utility collection that this package says some helpers were forked from |
| lodash-es | npm | Application code needs documented, independently versioned collection and object utilities with per-function imports |
| mitt | npm | You only need the tiny event emitter whose implementation Intlify credits as the basis for createEmitter |