Vue Intl (FormatJS)
Vue Intl is the official FormatJS binding for Vue.js applications, providing comprehensive internationalization (i18n) and localization (l10n) capabilities. Currently at version 7.2.3, it is actively maintained as part of the broader FormatJS monorepo, which sees frequent updates across its packages. The library leverages web standards like the JavaScript `Intl` API, ECMA-402, Unicode CLDR, and ICU Message syntax for robust and accurate formatting of messages, dates, numbers, and more. It offers seamless integration with Vue 3's Composition API via `useIntl()` and also supports injection patterns, distinguishing itself by being part of a well-established, standards-compliant internationalization ecosystem with extensive tooling for message extraction and compilation.
Common errors
-
The message format compilation is not supported in this build.
cause ICU message strings (e.g., plural rules, select formats) are being used directly in the browser runtime without prior compilation, which is not supported by the default runtime-only build.fixUtilize `@formatjs/cli`, `babel-plugin-formatjs`, or `@formatjs/ts-transformer` during your build process to pre-compile messages. This converts ICU strings into optimized JavaScript functions, improving performance and enabling advanced formatting. -
Messages are not displaying or falling back to message ID string.
cause The `id` for a message cannot be found in the provided `messages` object for the current locale, or the locale data is missing/incorrect.fixVerify that the message `id` passed to `$formatMessage` or `intl.formatMessage` exists in your registered message dictionaries. Ensure that the correct locale messages are loaded and set for the application.
Warnings
- gotcha Vue's template interpolation (`{{ ... }}`) can conflict with ICU MessageFormat's double curly brace syntax (e.g., `{count, plural, one {#}}`). This can lead to parsing errors in Vue Single File Components (SFCs).
- gotcha By default, `vue-intl` only includes locale data for the 'en' locale. For other languages or specific formatting rules (e.g., pluralization, relative time), you must explicitly load the corresponding locale data to avoid incorrect or default formatting.
- gotcha The underlying FormatJS libraries rely on the browser's native `Intl` API. For older browsers or environments without full `Intl` support, a polyfill (e.g., `@formatjs/intl-pluralrules`, `@formatjs/intl-relativetimeformat`) is required to ensure consistent internationalization behavior.
- breaking While no direct `vue-intl` v6-to-v7 migration guide is provided, the broader FormatJS ecosystem (which `vue-intl` is a part of) transitioned its build outputs to ES6 for CDN bundles and CommonJS files starting from v7 across many packages. This affects projects targeting older environments (e.g., ES5, IE11, older Node.js versions) and may require a transpiler or reconfiguration of build targets.
Install
-
npm install vue-intl -
yarn add vue-intl -
pnpm add vue-intl
Imports
- createIntl
const createIntl = require('vue-intl')import { createIntl } from 'vue-intl' - useIntl
import { useIntl } from 'vue-intl' - IntlConfig
import type { IntlConfig } from 'vue-intl'
Quickstart
import { createApp } from 'vue';
import App from './App.vue';
import { createIntl, useIntl } from 'vue-intl';
// 1. Define your messages for a locale
const messages = {
en: {
greeting: 'Hello, {name}!',
current_date: 'Today is {ts, date, ::yyyyMMdd}',
photo_count: '{count, plural, one {# photo} other {# photos}} taken.'
},
fr: {
greeting: 'Bonjour, {name}!',
current_date: 'Aujourd'hui, c'est le {ts, date, ::yyyyMMdd}',
photo_count: '{count, plural, one {# photo} other {# photos}} pris.'
}
};
// 2. Create the Vue app instance
const app = createApp(App);
// 3. Install the vue-intl plugin
app.use(createIntl({
locale: 'en',
defaultLocale: 'en',
messages: messages.en,
// Load locale data dynamically in a real app
// For production, consider using @formatjs/cli or babel-plugin-formatjs for message compilation
}));
// 4. Example Vue component using useIntl hook
const MyComponent = {
template: `
<div>
<p>{{ formattedGreeting }}</p>
<p>{{ $formatDate(new Date(), { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) }}</p>
<p>{{ $formatMessage({ id: 'photo_count', defaultMessage: 'No photos.', description: 'Number of photos' }, { count: photoCount }) }}</p>
</div>
`,
setup() {
const intl = useIntl();
const photoCount = 5; // Example dynamic value
const formattedGreeting = intl.formatMessage({ id: 'greeting', defaultMessage: 'Hello, world!' }, { name: 'Vue User' });
return {
formattedGreeting,
photoCount
};
},
};
app.component('MyComponent', MyComponent);
app.mount('#app');