Frameworks

Nuxt i18n : Guide Complet de l'Internationalisation

Mark Toledo

Mark Toledo

12 janvier 2025

Nuxt i18n : Guide Complet de l'Internationalisation

Nuxt i18n est le module officiel pour internationaliser vos applications Nuxt.js. Il permet de gérer plusieurs langues avec un routing automatique, des traductions dynamiques et une optimisation SEO intégrée.

Dans ce guide, vous apprendrez à configurer et utiliser @nuxtjs/i18n pour créer un site multilingue professionnel.

Installation de Nuxt i18n

Installer le module

# Avec npm
npm install @nuxtjs/i18n

# Avec pnpm
pnpm add @nuxtjs/i18n

Configuration de base

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/i18n'],

  i18n: {
    locales: [
      { code: 'fr', iso: 'fr-FR', name: 'Français', file: 'fr.json' },
      { code: 'en', iso: 'en-US', name: 'English', file: 'en.json' }
    ],
    defaultLocale: 'fr',
    langDir: 'locales/',
    strategy: 'prefix_except_default'
  }
})

Structure des fichiers de traduction

locales/
├── fr.json
└── en.json
// locales/fr.json
{
  "welcome": "Bienvenue",
  "home": {
    "title": "Accueil",
    "description": "Découvrez notre site"
  },
  "nav": {
    "about": "À propos",
    "contact": "Contact",
    "blog": "Blog"
  },
  "buttons": {
    "submit": "Envoyer",
    "cancel": "Annuler"
  }
}
// locales/en.json
{
  "welcome": "Welcome",
  "home": {
    "title": "Home",
    "description": "Discover our website"
  },
  "nav": {
    "about": "About",
    "contact": "Contact",
    "blog": "Blog"
  },
  "buttons": {
    "submit": "Submit",
    "cancel": "Cancel"
  }
}

Utilisation dans les composants

Fonction $t() dans les templates

<template>
  <div>
    <h1>{{ $t('welcome') }}</h1>
    <p>{{ $t('home.description') }}</p>

    <nav>
      <NuxtLink :to="localePath('/')">{{ $t('nav.about') }}</NuxtLink>
      <NuxtLink :to="localePath('/contact')">{{ $t('nav.contact') }}</NuxtLink>
    </nav>

    <button>{{ $t('buttons.submit') }}</button>
  </div>
</template>

useI18n() dans le script setup

<script setup>
const { t, locale, locales, setLocale } = useI18n()

// Utiliser les traductions
const titre = computed(() => t('home.title'))

// Changer de langue
function changerLangue(code) {
  setLocale(code)
}

// Obtenir la locale actuelle
console.log(locale.value) // 'fr'

// Lister les locales disponibles
console.log(locales.value) // [{ code: 'fr', ... }, { code: 'en', ... }]
</script>

Sélecteur de langue

Composant LanguageSwitcher

<!-- components/LanguageSwitcher.vue -->
<template>
  <div class="language-switcher">
    <button
      v-for="loc in availableLocales"
      :key="loc.code"
      :class="{ active: loc.code === locale }"
      @click="setLocale(loc.code)"
    >
      {{ loc.name }}
    </button>
  </div>
</template>

<script setup>
const { locale, locales, setLocale } = useI18n()

const availableLocales = computed(() => {
  return locales.value.filter(l => l.code !== locale.value)
})
</script>

<style scoped>
.language-switcher button {
  padding: 0.5rem 1rem;
  margin: 0 0.25rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  cursor: pointer;
}

.language-switcher button.active {
  background: #333;
  color: white;
}
</style>

Avec drapeaux

<template>
  <div class="lang-selector">
    <button
      v-for="loc in locales"
      :key="loc.code"
      @click="switchLocalePath(loc.code)"
    >
      <img :src="`/flags/${loc.code}.svg`" :alt="loc.name" />
      <span>{{ loc.name }}</span>
    </button>
  </div>
</template>

<script setup>
const switchLocalePath = useSwitchLocalePath()
const { locales } = useI18n()
</script>

Stratégies de routing

Nuxt i18n offre plusieurs stratégies de routing :

prefix_except_default (recommandé)

// nuxt.config.ts
i18n: {
  strategy: 'prefix_except_default',
  defaultLocale: 'fr'
}

URLs générées :

  • / → Français (langue par défaut)
  • /en → English
  • /about → Français
  • /en/about → English

prefix

i18n: {
  strategy: 'prefix'
}

URLs générées :

  • /fr → Français
  • /en → English
  • /fr/about → Français
  • /en/about → English

prefix_and_default

i18n: {
  strategy: 'prefix_and_default'
}

URLs générées :

  • / et /fr → Français (les deux fonctionnent)
  • /en → English

no_prefix

i18n: {
  strategy: 'no_prefix'
}

URLs générées :

  • / → Langue détectée automatiquement
  • /about → Langue détectée automatiquement

Navigation multilingue

localePath()

Génère un chemin localisé :

<template>
  <nav>
    <!-- Liens internes -->
    <NuxtLink :to="localePath('/')">Accueil</NuxtLink>
    <NuxtLink :to="localePath('/about')">À propos</NuxtLink>

    <!-- Avec paramètres -->
    <NuxtLink :to="localePath({ name: 'blog-slug', params: { slug: 'mon-article' } })">
      Mon article
    </NuxtLink>
  </nav>
</template>

<script setup>
const localePath = useLocalePath()
</script>

switchLocalePath()

Change de langue en conservant la page actuelle :

<template>
  <div>
    <a :href="switchLocalePath('en')">English</a>
    <a :href="switchLocalePath('fr')">Français</a>
  </div>
</template>

<script setup>
const switchLocalePath = useSwitchLocalePath()
</script>

Traductions dynamiques

Avec variables

// locales/fr.json
{
  "greeting": "Bonjour {name} !",
  "items": "Vous avez {count} article | Vous avez {count} articles",
  "date": "Publié le {date}"
}
<template>
  <p>{{ $t('greeting', { name: 'Marie' }) }}</p>
  <!-- "Bonjour Marie !" -->

  <p>{{ $t('items', { count: 5 }) }}</p>
  <!-- "Vous avez 5 articles" -->

  <p>{{ $t('date', { date: formattedDate }) }}</p>
</template>

<script setup>
const formattedDate = new Date().toLocaleDateString('fr-FR')
</script>

Pluralisation

// locales/fr.json
{
  "car": "pas de voiture | {n} voiture | {n} voitures",
  "apple": "aucune pomme | une pomme | {count} pommes"
}
<template>
  <p>{{ $t('car', 0) }}</p>  <!-- "pas de voiture" -->
  <p>{{ $t('car', 1) }}</p>  <!-- "1 voiture" -->
  <p>{{ $t('car', 5) }}</p>  <!-- "5 voitures" -->
</template>

SEO multilingue

Configuration SEO

// nuxt.config.ts
export default defineNuxtConfig({
  i18n: {
    locales: [
      { code: 'fr', iso: 'fr-FR', name: 'Français' },
      { code: 'en', iso: 'en-US', name: 'English' }
    ],
    baseUrl: 'https://monsite.fr',
    // Ajoute automatiquement les balises hreflang
    detectBrowserLanguage: {
      useCookie: true,
      cookieKey: 'i18n_redirected',
      redirectOn: 'root'
    }
  }
})

Meta tags localisés

<script setup>
const { t, locale } = useI18n()
const localePath = useLocalePath()

// SEO dynamique par langue
useSeoMeta({
  title: () => t('home.title'),
  description: () => t('home.description'),
  ogTitle: () => t('home.title'),
  ogDescription: () => t('home.description'),
  ogLocale: () => locale.value
})

// Liens canoniques et alternates
useHead({
  link: [
    {
      rel: 'canonical',
      href: `https://monsite.fr${localePath('/')}`
    }
  ]
})
</script>

Balises hreflang automatiques

Nuxt i18n génère automatiquement :

<link rel="alternate" hreflang="fr" href="https://monsite.fr/" />
<link rel="alternate" hreflang="en" href="https://monsite.fr/en/" />
<link rel="alternate" hreflang="x-default" href="https://monsite.fr/" />

Traductions dans les API Routes

Serveur multilingue

// server/api/messages.get.ts
export default defineEventHandler(async (event) => {
  const locale = getHeader(event, 'accept-language')?.split(',')[0] || 'fr'

  const messages = {
    fr: { welcome: 'Bienvenue', success: 'Opération réussie' },
    en: { welcome: 'Welcome', success: 'Operation successful' }
  }

  return messages[locale] || messages.fr
})

Configuration avancée

Lazy loading des traductions

// nuxt.config.ts
i18n: {
  lazy: true,
  langDir: 'locales/',
  locales: [
    { code: 'fr', file: 'fr.json' },
    { code: 'en', file: 'en.json' },
    { code: 'de', file: 'de.json' }
  ]
}

Les fichiers de traduction sont chargés uniquement quand nécessaire.

Détection automatique de la langue

i18n: {
  detectBrowserLanguage: {
    useCookie: true,
    cookieKey: 'i18n_lang',
    redirectOn: 'root', // Redirige uniquement sur la page d'accueil
    alwaysRedirect: false,
    cookieCrossOrigin: false,
    cookieSecure: true
  }
}

Personnalisation des routes

i18n: {
  customRoutes: 'config',
  pages: {
    about: {
      fr: '/a-propos',
      en: '/about'
    },
    'blog/[slug]': {
      fr: '/articles/[slug]',
      en: '/blog/[slug]'
    }
  }
}

Bonnes pratiques

1. Organiser les traductions

// locales/fr.json
{
  "common": {
    "loading": "Chargement...",
    "error": "Une erreur est survenue",
    "save": "Enregistrer"
  },
  "pages": {
    "home": { "title": "Accueil" },
    "about": { "title": "À propos" }
  },
  "components": {
    "header": { "logo_alt": "Logo du site" },
    "footer": { "copyright": "© 2025" }
  }
}

2. Typage TypeScript

// types/i18n.d.ts
declare module 'vue-i18n' {
  export interface DefineLocaleMessage {
    welcome: string
    home: {
      title: string
      description: string
    }
  }
}

3. Fallback intelligent

i18n: {
  fallbackLocale: 'fr',
  // Si une traduction manque en anglais, utilise le français
}

Conclusion

Nuxt i18n simplifie considérablement l'internationalisation de vos applications Nuxt.js. Avec son routing automatique, ses balises SEO générées et ses nombreuses options de configuration, vous pouvez créer des sites multilingues professionnels en quelques étapes.

Ressources complémentaires :