Skip to main content

Vue i18n: Complete Internationalization Setup with vue-i18n

From installation to production: configure vue-i18n with Composition API, handle plurals with CLDR rules, use component interpolation, and add smart locale fallback chains.

1

Install vue-i18n

vue-i18n is the official internationalization plugin for Vue.js. It provides reactive translations, ICU-style pluralization, component interpolation, datetime and number formatting, and both Options API and Composition API support.

Terminal
npm install vue-i18n@9
2

Configure vue-i18n

Create an i18n instance with createI18n() and register it as a Vue plugin. Define your locale messages, set the default locale, and configure the fallback locale. vue-i18n supports both legacy (Options API) and composition (Composition API) modes.

src/i18n.ts
// src/i18n.ts
import { createI18n } from 'vue-i18n'
import en from './locales/en.json'
import de from './locales/de.json'

const i18n = createI18n({
  legacy: false,           // Use Composition API mode
  locale: 'en',            // Default locale
  fallbackLocale: 'en',   // Fallback locale
  messages: { en, de },
})

export default i18n
src/main.ts
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import i18n from './i18n'

const app = createApp(App)
app.use(i18n)
app.mount('#app')
If you see 'Not available in legacy mode' errors, you're mixing Composition API i18n calls (useI18n()) with legacy mode configuration. Set legacy: false in createI18n() to use Composition API mode, or use the Options API $t() syntax consistently.
3

Use Translations in Templates

vue-i18n provides the $t() function in templates (Options API) and the t() function from useI18n() (Composition API). Both accept the translation key and optional named/list parameters for interpolation.

src/locales/en.json
// src/locales/en.json
{
  "nav": {
    "home": "Home",
    "about": "About",
    "settings": "Settings"
  },
  "greeting": "Hello, {name}!",
  "cart": {
    "itemCount": "no items | one item | {count} items"
  }
}

// src/locales/de.json
{
  "nav": {
    "home": "Startseite",
    "about": "Über uns",
    "settings": "Einstellungen"
  },
  "greeting": "Hallo, {name}!",
  "cart": {
    "itemCount": "keine Artikel | ein Artikel | {count} Artikel"
  }
}
4

Handle Pluralization

vue-i18n supports pluralization through pipe-separated forms and the $tc() function (legacy) or t() with a count parameter (Composition API). Define plural forms in your locale messages using the pipe separator: 'no items | one item | {count} items'.

MyComponent.vue
<template>
  <div>
    <!-- Simple translation -->
    <h1>{{ $t('nav.home') }}</h1>

    <!-- With variables -->
    <p>{{ $t('greeting', { name: userName }) }}</p>

    <!-- In attributes -->
    <input :placeholder="$t('nav.settings')" />

    <!-- Composition API -->
    <p>{{ greeting }}</p>
  </div>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'

const { t } = useI18n()
const userName = 'Alice'
const greeting = t('greeting', { name: userName })
</script>
The pipe-separated plural syntax only supports cardinal plurals with up to three forms (zero | one | other). For languages with complex CLDR plural rules (Arabic, Russian, Polish), use ICU MessageFormat syntax or the @.plural modifier with explicit CLDR category mappings.
5

Add Locale Fallback Chains

vue-i18n's built-in fallbackLocale only supports a flat list of fallback locales, not per-locale chains. A pt-BR user with a missing key falls back to English instead of pt-PT. vue-i18n-locale-chain adds configurable deep-merge chains so regional users always see the closest available translation.

Plurals.vue
<template>
  <div>
    <!-- Pipe-separated plurals -->
    <p>{{ $t('cart.itemCount', count) }}</p>

    <!-- Named plurals (recommended for complex languages) -->
    <p>{{ $t('orders', { n: orderCount }) }}</p>
  </div>
</template>

<!-- In your locale file: -->
<!-- "orders": "{n} order | {n} orders" -->
vue-i18n-locale-chain deep-merges messages at load time. The merged message set is cached by vue-i18n's reactivity system, so there is no runtime performance penalty for fallback lookups.
6

Automate Translations

With vue-i18n configured, translate your locale JSON files using AI. From your IDE, ask your AI assistant to translate the source file, or integrate translation into your CI/CD pipeline for fully automated localization.

LanguageSwitcher.vue
<template>
  <select v-model="locale">
    <option v-for="lang in availableLocales" :key="lang" :value="lang">
      {{ lang }}
    </option>
  </select>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'

const { locale, availableLocales } = useI18n()
</script>
src/i18n.ts (lazy loading)
// src/i18n.ts
import { createI18n } from 'vue-i18n'

const i18n = createI18n({
  legacy: false,
  locale: 'en',
  fallbackLocale: 'en',
  messages: {},
})

export async function loadLocale(locale: string) {
  const messages = await import(`./locales/${locale}.json`)
  i18n.global.setLocaleMessage(locale, messages.default)
  i18n.global.locale.value = locale
}

export default i18n
7

Locale Fallback Chain Configuration

Configure per-locale fallback chains so regional users see the closest available translation instead of jumping straight to English. vue-i18n supports object-style fallbackLocale for defining chains per locale.

src/i18n.ts
// src/i18n.ts
import { createI18n } from 'vue-i18n'

const i18n = createI18n({
  legacy: false,
  locale: 'pt-BR',
  fallbackLocale: {
    'pt-BR': ['pt', 'en'],
    'zh-Hant-TW': ['zh-Hant', 'zh', 'en'],
    'es-419': ['es', 'en'],
    default: ['en'],
  },
  messages: {
    en: { /* ... */ },
    pt: { /* ... */ },
    'pt-BR': { /* ... */ },
  },
})
The fallbackLocale object lets you define different chains for different locales. A pt-BR user falls back to pt then en, while a zh-Hant-TW user falls back to zh-Hant, zh, then en.

Common Pitfalls

Mixing Legacy and Composition API Modes

Using useI18n() with legacy: true (the default) causes errors. Set legacy: false in createI18n() if you use Composition API, or stick to $t() in templates with Options API. Do not mix both modes in the same application.

Using v-html for Translated Strings

Using v-html to render translations that contain HTML is an XSS risk if any interpolation values come from user input. Use the &lt;i18n-t&gt; component for translations with embedded HTML or Vue components — it is safe by default and supports reactive component embedding.

Regional Users See English Instead of Parent Locale

vue-i18n's fallbackLocale is a flat list, not a per-locale chain. pt-BR falls back to whatever is in the list (usually 'en'), skipping pt-PT entirely. Use vue-i18n-locale-chain to add proper regional fallback with deep-merge.

Try i18n Agent Now

Drop your translation file here

JSON, YAML, PO, XML, CSV, Markdown, Properties

or click to browse

Target languages

No signup requiredInstant estimate

Vue i18n FAQ