Le Problème
Vous avez construit une belle application Vue. Maintenant, votre équipe produit dit "nous devons supporter l'espagnol, le français et l'allemand."
Par où commencer ? Comment faire pour :
- Extraire toutes ces chaînes codées en dur ?
- Changer de langue sans rafraîchissement de page ?
- Gérer les pluriels différemment dans chaque langue ?
- Charger paresseusement les traductions pour garder votre bundle petit ?
Ce guide parcourt tout, en utilisant vue-i18n, la bibliothèque officielle de localisation Vue.
Ce Que Vous Allez Construire
À la fin de ce tutoriel, vous aurez une application Vue qui :
- ✅ Change de langue instantanément
- ✅ Gère les pluriels correctement dans 40+ langues
- ✅ Formate les dates, nombres et devises par locale
- ✅ Charge paresseusement les traductions pour la performance
- ✅ Fonctionne avec Vue Router pour des URLs optimisées SEO
- ✅ Se synchronise avec un système de gestion de traduction
Stack technique :
- Vue 3 (Composition API)
- vue-i18n v9
- Vite
- TypeScript (optionnel mais recommandé)
Étape 1 : Installer vue-i18n
D'abord, installez la bibliothèque :
Terminalnpm install vue-i18n@9
Pourquoi vue-i18n ? C'est le plugin i18n officiel pour Vue, maintenu par l'équipe Vue. Il a :
- 5M+ téléchargements hebdomadaires
- Support intégré pour la Composition API de Vue 3
- Support SSR pour Nuxt
- Format de message ICU (comme React/Angular)
Étape 2 : Créer des Fichiers de Traduction
Créez un dossier locales avec vos fichiers de traduction :
src/
├── locales/
│ ├── en.json
│ ├── es.json
│ └── fr.json
└── i18n.ts
fr.json :
JSON1{ 2 "nav": { 3 "home": "Accueil", 4 "about": "À propos", 5 "pricing": "Tarifs" 6 }, 7 "hero": { 8 "title": "Construisez des apps plus vite", 9 "subtitle": "Expédiez des fonctionnalités que vos utilisateurs aiment", 10 "cta": "Commencer" 11 }, 12 "product": { 13 "addToCart": "Ajouter au panier", 14 "itemCount": "Aucun article | {n} article | {n} articles", 15 "price": "{amount} par mois" 16 } 17}
Conseils de structure clés :
- Imbriquer par fonctionnalité (
nav,hero,product) pas par page - Utiliser des clés sémantiques (
addToCart) pas des positions UI (button_1) - Formatage cohérent dans toutes les langues
Étape 3 : Configurer vue-i18n
Créez src/i18n.ts :
TypeScript1import { createI18n } from 'vue-i18n'; 2import en from './locales/en.json'; 3import es from './locales/es.json'; 4import fr from './locales/fr.json'; 5 6export const i18n = createI18n({ 7 legacy: false, // Utiliser mode Composition API 8 locale: 'en', // Langue par défaut 9 fallbackLocale: 'en', // Repli si traduction manquante 10 messages: { 11 en, 12 es, 13 fr, 14 }, 15 // Activer les avertissements en dev 16 missingWarn: import.meta.env.DEV, 17 fallbackWarn: import.meta.env.DEV, 18});
Pourquoi legacy: false ? Cela active le support Composition API. Si vous utilisez Vue 3, définissez toujours ceci.
Étape 4 : Ajouter à Votre App Vue
Dans main.ts :
TypeScript1import { createApp } from 'vue'; 2import App from './App.vue'; 3import { i18n } from './i18n'; 4 5const app = createApp(App); 6app.use(i18n); 7app.mount('#app');
C'est tout. Maintenant chaque composant a accès aux traductions.
Étape 5 : Utiliser les Traductions dans les Composants
Usage Basique (Composition API)
VUE1<script setup> 2import { useI18n } from 'vue-i18n'; 3 4const { t } = useI18n(); 5</script> 6 7<template> 8 <nav> 9 <a href="/">{{ $t('nav.home') }}</a> 10 <a href="/about">{{ $t('nav.about') }}</a> 11 <a href="/pricing">{{ $t('nav.pricing') }}</a> 12 </nav> 13 14 <section> 15 <h1>{{ $t('hero.title') }}</h1> 16 <p>{{ $t('hero.subtitle') }}</p> 17 <button>{{ $t('hero.cta') }}</button> 18 </section> 19</template>
Avec Interpolation
Passez des variables en utilisant le deuxième argument :
VUE1<script setup> 2const { t } = useI18n(); 3const price = 29; 4</script> 5 6<template> 7 <p>{{ $t('product.price', { amount: `${price} €` }) }}</p> 8 <!-- Sortie : "29 € par mois" --> 9</template>
Étape 6 : Gérer la Pluralisation
Différentes langues ont différentes règles de pluriel. L'anglais a 2 formes. Le polonais en a 3. L'arabe en a 6 !
vue-i18n gère cela automatiquement en utilisant la syntaxe pipe :
JSON1{ 2 "product": { 3 "itemCount": "Aucun article | {n} article | {n} articles" 4 } 5}
Dans votre composant :
VUE1<script setup> 2const { t } = useI18n(); 3const count = ref(0); 4</script> 5 6<template> 7 <p>{{ $t('product.itemCount', count) }}</p> 8 <!-- count=0: "Aucun article" --> 9 <!-- count=1: "1 article" --> 10 <!-- count=5: "5 articles" --> 11</template>
Étape 7 : Formater Dates, Nombres, Devises
vue-i18n inclut les formateurs Intl :
VUE1<script setup> 2import { useI18n } from 'vue-i18n'; 3 4const { t, n, d } = useI18n(); 5const price = 1299.99; 6const releaseDate = new Date('2026-01-15'); 7</script> 8 9<template> 10 <!-- Formatage nombre --> 11 <p>{{ n(price, 'currency') }}</p> 12 <!-- en: "$1,299.99" --> 13 <!-- fr: "1 299,99 €" --> 14 15 <!-- Formatage date --> 16 <p>{{ d(releaseDate, 'long') }}</p> 17 <!-- en: "January 15, 2026" --> 18 <!-- fr: "15 janvier 2026" --> 19</template>
Configurez les formats dans i18n.ts.
Étape 8 : Changer de Langue
Créez un composant sélecteur de langue :
VUE1<script setup> 2import { useI18n } from 'vue-i18n'; 3 4const { locale, availableLocales } = useI18n(); 5 6const changeLanguage = (lang: string) => { 7 locale.value = lang; 8 // Persister dans localStorage 9 localStorage.setItem('user-locale', lang); 10}; 11</script> 12 13<template> 14 <div class="language-switcher"> 15 <button 16 v-for="lang in availableLocales" 17 :key="lang" 18 :class="{ active: locale === lang }" 19 @click="changeLanguage(lang)" 20 > 21 {{ lang.toUpperCase() }} 22 </button> 23 </div> 24</template>
Étape 9 : Chargement Paresseux des Traductions
Charger toutes les langues au début augmente la taille de votre bundle. Chargez paresseusement les langues à la demande.
TypeScript1// Charger la locale dynamiquement 2export async function loadLocale(locale: string) { 3 // Vérifier si déjà chargé 4 if (i18n.global.availableLocales.includes(locale)) { 5 return; 6 } 7 8 // Chargement paresseux du fichier locale 9 const messages = await import(`./locales/${locale}.json`); 10 i18n.global.setLocaleMessage(locale, messages.default); 11}
Étape 10 : URLs Optimisées SEO avec Vue Router
Pour le SEO, utilisez des URLs basées sur la locale :
example.com/en/productsexample.com/fr/produits
Configurez Vue Router pour gérer le paramètre :locale.
Ajoutez les balises hreflang pour le SEO.
Étape 11 : Support TypeScript
Obtenez l'autocomplétion pour les clés de traduction en définissant un schéma de message basé sur votre fichier locale par défaut.
Étape 12 : Optimisation Production
- Division de Code : Divisez les traductions par route.
- Précharger la Prochaine Langue : Si la plupart des utilisateurs passent de l'Anglais à l'Espagnol, préchargez l'Espagnol.
- Compiler les Messages : vue-i18n peut précompiler les messages pour une exécution plus rapide.
Étape 13 : Connecter à IntlPull
Gérer manuellement les fichiers JSON ne passe pas à l'échelle. Utilisez un Système de Gestion de Traduction (TMS) :
Installer IntlPull CLI :
Terminalnpm install -D @intlpullhq/cli
Configurer :
JSON1// .intlpull.json 2{ 3 "projectId": "votre-fproject-id", 4 "sourceLanguage": "fr", 5 "targetLanguages": ["es", "en", "de"], 6 "format": "json", 7 "outputDir": "src/locales" 8}
Avantages :
- ✅ Les traducteurs travaillent dans une UI, pas en JSON
- ✅ La mémoire de traduction économise des coûts
- ✅ Pré-traduction IA pour les nouvelles clés
- ✅ Flux d'approbation pour la qualité
Pièges Courants
- Oublier d'attendre les Chargements Paresseux
- Chaînes Codées en Dur dans les Composants
- Ne pas Gérer les Clés Manquantes
- Ignorer les Langues RTL
Bonus Nuxt 3
Si vous utilisez Nuxt 3, utilisez @nuxtjs/i18n. Même API que vue-i18n, mais avec SSR, routage automatique, et SEO intégré.
Benchmarks de Performance
vue-i18n est léger et rapide, ajoutant une surcharge minimale à votre application Vue.
