Multi-language support
- Install the Internationalization plugin for Vue
ps
npm install vue-i18n
npm install @intlify/unplugin-vue-i18n
- Add plugin to
./vite.config.ts
ts
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import path from 'node:path'
// ...
export default defineConfig({
plugins: [
// ... other plugins
VueI18nPlugin({
include: path.resolve(__dirname, './src/i18n/**')
}),
],
})
- 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",
"Number is required": "Number is required",
"Text 3 is required": "Text 3 is required",
"validate": "Validate",
"cancel": "Cancel",
"reset": "Reset",
"custom": "Custom",
"Text 1": "Text 1",
"Enter some text": "Enter some text",
"Text 2": "Text 2",
"Text 3": "Text 3",
"Email": "Email",
"Enter valid e-mail address": "Enter valid e-mail address",
"Number": "Number",
"Enter some number": "Enter some number",
"Enter password, at least 12 characters": "Enter password, at least 12 characters",
"Textarea": "Textarea",
"Enter some larger text here": "Enter some larger text here",
"Accept Terms and Conditions": "Accept Terms and Conditions",
"Rating": "Rating",
"Checkbox": "Checkbox",
"Select": "Select",
"Combo": "Combo",
"Autocomplete": "Autocomplete",
"File!": "File!",
"Date": "Date",
"Select date": "Select date",
"Time": "Time",
"Select time": "Select time",
"Date and Time": "Date and Time",
"Select date and time": "Select date and time",
"Error from outside": "Error from outside",
"Text 2 is required": "Text 2 is required",
"Text 2 must be a valid JSON": "Text 2 must be a valid JSON",
"Text 3 must contain \"custom\"": "Text 3 must contain \"custom\"",
"Number must be at least 10": "Number must be at least 10"
}
@/i18n/sandbox/fr.json
- /sandbox FR file
json
{
"sandbox.title": "\"Titre du bac à sable\"",
"sandbox.content": "\"Contenu du bac à sable\"",
"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": "Non autorisé",
"validate": "Valider",
"cancel": "Annuler",
"reset": "Réinitialiser",
"custom": "Personnalisé",
"Enter some text": "Saisissez du texte",
"Text 1": "Texte 1",
"Text 2": "Texte 2",
"Text 3": "Texte 3",
"Email": "E-mail",
"Enter valid e-mail address": "Entrez une adresse e-mail valide",
"Number": "Nombre",
"Enter some number": "Saisissez un nombre",
"Enter password, at least 12 characters": "Entrez un mot de passe d'au moins 12 caractères",
"Textarea": "Zone de texte",
"Enter some larger text here": "Entrez un texte plus long ici",
"Accept Terms and Conditions": "Acceptez les termes et conditions",
"Rating": "Évaluation",
"Checkbox": "Case à cocher",
"Select": "Sélectionner",
"Combo": "Combo",
"Autocomplete": "Autocomplétion",
"File!": "Fichier!",
"Date": "Date",
"Select date": "Sélectionnez une date",
"Time": "Temps",
"Select time": "Sélectionnez une heure",
"Date and Time": "Date et heure",
"Select date and time": "Sélectionnez une date et une heure",
"Error from outside": "Erreur externe",
"Text 2 is required": "Texte 2 est requis",
"Text 3 is required": "Texte 3 est requis",
"Number is required": "Nombre est requis"
}
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.
- Create i18n configuration
@/plugins/i18n.ts
ts
import { createI18n } from 'vue-i18n'
import messages from '@intlify/unplugin-vue-i18n/messages'
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: 'en',
fallbackLocale: 'en',
fallbackWarn: false,
messages,
})
export default i18n
- Initialize i18n in main.ts
ts
// ...
import i18n from './plugins/i18n'
// ...
app.use(i18n)
// ...
- 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>