Skip to content

Multi-language support

  1. Install the Internationalization plugin for Vue
ps
npm install vue-i18n
npm install @intlify/unplugin-vue-i18n
  1. Add plugin to ./vite.config.ts
ts
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
// ...
export default defineConfig({
  plugins: [
    // ... other plugins
    VueI18nPlugin({}),
  ],
})
  1. Create some initial localization files.
@/i18n/en.json - / EN file
json
{
  "welcome": "Welcome",
  "logout": "Logout",
  "login": "Login",
  "app.actions.login": "App actions login",
  "app.actions.logout": "App actions logout",
  "app.messages.welcome": "App messages welcome",
  "Request failed with status code 403": "Request failed with status code 403",
  "you.are.not.allowed.to.access.this.page": "You are not allowed to access this page",
  "copied.to.clipboard": "Copied to clipboard"
}
@/i18n/en.json - / FR file
json
{
  "welcome": "Bienvenue",
  "logout": "\"Déconnexion\"",
  "login": "Connexion",
  "app.actions.login": "\"Actions d'application connexion\"",
  "app.actions.logout": "\"Actions de l'application déconnexion\"",
  "app.messages.welcome": "\"Messages d'application bienvenus\"",
  "Request failed with status code 403": "La requête a échoué avec le code d'état 403",
  "you.are.not.allowed.to.access.this.page": "Vous n'êtes pas autorisé à accéder à cette page",
  "copied.to.clipboard": "Copié dans le presse-papiers"
}
@/i18n/sandbox/en.json - /sandbox EN file
json
{
  "sandbox.title": "Sandbox title",
  "sandbox.content": "Sandbox content",
  "sandbox.missing": "Sandbox missing",
  "View": "View",
  "": "",
  "Delete": "Delete",
  "This is a snack message": "This is a snack message",
  "close": "Close",
  "unauthorized": "Unauthorized"
}
@/i18n/sandbox/fr.json - /sandbox FR file
json
{
  "sandbox.title": "\"Titre du bac à sable\"",
  "sandbox.content": "\"Content of the sandbox\"",
  "sandbox.missing": "\"Bac à sable manquant\"",
  "View": "Vue",
  "": "You didn't write any text that needs translation. Could you please provide the text?",
  "Delete": "Supprimer",
  "This is a snack message": "\"C'est un message de collation\"",
  "close": "Fermer",
  "unauthorized": "Unauthorized"
}

Translation File Structure:

src/i18n/
├── en.json          # Base English translations
├── fr.json          # Base French translations
└── moduleA/         # Module-specific translations
    ├── en.json
    └── fr.json

⚠️ translation structure can be only one level deep, so everything under moduleA has to be included in either base or module translation files.

  1. Create i18n configuration
@/plugins/i18n.ts
ts
import { createI18n } from 'vue-i18n'

const i18n = createI18n({
  legacy: false,
  globalInjection: true,
  locale: 'en',
  fallbackLocale: 'en',
  messages: {
    en: {},
    fr: {},
  },
  fallbackWarn: false,
  missing: handleMissing,
})

export default i18n

function handleMissing(locale: string, key: string) {
  //eslint-disable-next-line
  //@ts-ignore
  const i18nStore = useI18nStore()
  i18nStore.addTranslation(locale, key)
}
  1. Initialize i18n in main.ts
ts
// ...
import i18n from './plugins/i18n'
// ...
app.use(i18n)
// ...
  1. Test

Modify @/App.vue

vue
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import { useI18n } from 'vue-i18n'
const { t, locale } = useI18n()
function toggleLocale() {
  locale.value = locale.value === 'en' ? 'fr' : 'en'
}
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />

    <div class="wrapper">
      <HelloWorld msg="You did it!" />
      <nav>
        <v-btn @click="toggleLocale()">{{ locale }}</v-btn>
        <RouterLink to="/">{{ t('app.home') }}</RouterLink>
        <RouterLink to="/about">{{ t('app.about') }}</RouterLink>
        <RouterLink to="/sandbox">Sandbox</RouterLink>
      </nav>
    </div>
  </header>
  <RouterView />
</template>

And @/pages/sandbox/index.vue

vue
<template>
  <v-card :style="useCardBackground('#00AA00')">
    <v-card-title><v-icon icon="$mdiHome" />{{ t('sandbox.title') }}</v-card-title>
    <v-card-text>{{ t('sandbox.content') }}</v-card-text>
    <!-- ... -->
  </v-card>
</template>

<script setup lang="ts">
// ...
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
// ...
</script>