i18n
Système d'Internationalisation (i18n)
Cette documentation présente le système d'internationalisation complet de l'application Nuxt 3, incluant la configuration, l'organisation des traductions, et les bonnes pratiques d'utilisation.
Vue d'Ensemble du Système i18n
L'application utilise le module @nuxtjs/i18n pour gérer la localisation et les traductions. Ce système permet de supporter plusieurs langues avec une navigation automatique, des URLs localisées, et une gestion avancée des traductions.
graph TB
subgraph "Configuration i18n"
Config[nuxt.config.ts]
Locales[Fichiers de Langues]
Strategy[Stratégie d'URL]
end
subgraph "Utilisation"
Components[Composants Vue]
Pages[Pages/Routes]
Server[Routes Serveur]
end
subgraph "APIs i18n"
UseI18n[useI18n()]
T["$t()"]
LocalePath[localePath()]
SwitchPath[switchLocalePath()]
end
Config --> UseI18n
Locales --> T
Strategy --> LocalePath
Components --> UseI18n
Components --> T
Pages --> LocalePath
Server --> Config
UseI18n --> SwitchPath
T --> Components
LocalePath --> Pages
style Config fill:#e1f5fe
style Locales fill:#f3e5f5
style Components fill:#e8f5e8
style UseI18n fill:#fff3e0
Configuration du Système
Configuration Nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
"@nuxtjs/i18n",
// ... autres modules
],
i18n: {
// Chargement paresseux des traductions
lazy: true,
// Dossier contenant les fichiers de langue
langDir: "locales",
// Stratégie d'URL : préfixe sauf pour la langue par défaut
strategy: "prefix_except_default",
// Langue par défaut basée sur la variable d'environnement
defaultLocale: process.env.LOCALE === "en-GB" ? "en-GB" : "fr-FR",
// Langues supportées
locales: [
{
code: "fr-FR",
file: "fr-FR.json",
label: "Français",
icon: "cif:fr",
abbr: "fr",
},
{
code: "en-GB",
file: "en-GB.json",
label: "English",
icon: "cif:gb",
abbr: "en",
},
],
},
});Variables d'Environnement
# .env
LOCALE=fr-FR # Langue par défaut
DEFAULTCURRENCY=EUR # Devise par défaut
CURRENCIES=EUR,USD,GBP # Devises supportéesOrganisation des Fichiers de Traduction
Structure des Dossiers
i18n/
└── locales/
├── fr-FR.json # Traductions françaises (22KB, 639 lignes)
└── en-GB.json # Traductions anglaises (19KB, 637 lignes)
Structure des Clés de Traduction
Les traductions sont organisées par composant pour une meilleure maintenabilité :
{
"DjAddToBuyingListButton": {
"addFavoris": "Ajouter aux favoris",
"selectList": "Sélectionner une liste de favoris existante :",
"placeholder": "Choisir une liste de favoris",
"createList": "Je créé une nouvelle liste de favoris :",
"namePlaceholder": "Nom de votre liste de favoris",
"create": "Crée"
},
"DjCartPage": {
"cart": "Panier",
"details": "Détails de Livraison & Facturation",
"order": "Commande passée"
},
"DjContactUsPage": {
"title": "Nous contacter",
"name": "Nom",
"email": "Email",
"message": "Message",
"mandatory": "*",
"topic": {
"label": "Sujet",
"product": "Question produit",
"order": "Question commande",
"delivery": "Question livraison",
"other": "Autre"
},
"info": {
"one": "Pour toute question,",
"two": "notre équipe vous répond"
},
"submitForm": "Envoyer"
}
}Stratégies d'URL et Routage
Stratégie prefix_except_default
prefix_except_defaultCette stratégie génère des URLs différentes selon la langue :
// Langue par défaut (fr-FR) : pas de préfixe
https://example.com/products
https://example.com/cart
https://example.com/account
// Autres langues (en-GB) : avec préfixe
https://example.com/en-GB/products
https://example.com/en-GB/cart
https://example.com/en-GB/accountNavigation Localisée
<template>
<!-- Lien vers une page dans la langue courante -->
<NuxtLink :to="localePath('/products')">
{{ $t("navigation.products") }}
</NuxtLink>
<!-- Lien vers la même page dans une autre langue -->
<NuxtLink :to="switchLocalePath('en-GB')">
{{ $t("navigation.english") }}
</NuxtLink>
</template>
<script setup>
const localePath = useLocalePath();
const switchLocalePath = useSwitchLocalePath();
</script>Utilisation dans les Composants
Méthodes d'Accès aux Traductions
1. Template avec $t() (Plus Simple)
$t() (Plus Simple)<template>
<div>
<!-- Traduction simple -->
<h1>{{ $t("DjCartPage.cart") }}</h1>
<!-- Traduction avec interpolation -->
<p>{{ $t("DjFaq.phone", { phone: "01 23 45 67 89" }) }}</p>
<!-- Traduction plurielle -->
<span>{{
$t(
"DjFastCartPage.nbProducts",
{ nb: cartLines.length },
cartLines.length
)
}}</span>
</div>
</template>2. Composition API avec useI18n() (Plus Flexible)
useI18n() (Plus Flexible)<script setup lang="ts">
const { t, locale, locales } = useI18n();
// Utilisation dans les variables réactives
const pageTitle = computed(() => t("DjContactUsPage.title"));
// Utilisation dans les tableaux
const topicOptions = [
t("DjContactUsPage.topic.product"),
t("DjContactUsPage.topic.order"),
t("DjContactUsPage.topic.delivery"),
t("DjContactUsPage.topic.other"),
];
// Utilisation dans les schémas de validation
const schema = v.object({
email: v.pipe(
v.string(),
v.email(t("validation.email")),
v.nonEmpty(t("validation.required"))
),
});
</script>Exemples Pratiques d'Utilisation
Formulaires avec Validation
<template>
<UForm :schema="schema" :state="state" @submit="onSubmit">
<UFormGroup :label="$t('DjContactUsPage.name')" name="name">
<UInput v-model="state.name" :placeholder="$t('DjContactUsPage.name')" />
</UFormGroup>
<UButton type="submit">
{{ $t("DjContactUsPage.submitForm") }}
</UButton>
</UForm>
</template>
<script setup lang="ts">
import * as v from "valibot";
const { t } = useI18n();
const schema = v.object({
name: v.pipe(v.string(), v.nonEmpty(t("validation.required"))),
email: v.pipe(v.string(), v.email(t("validation.email"))),
});
const state = reactive({
name: "",
email: "",
});
</script>Tableaux avec Colonnes Traduites
<script setup lang="ts">
const { t } = useI18n();
const columns = [
{
accessorKey: "reference",
header: t("DjOrdersPage.column.reference"),
},
{
accessorKey: "quantity",
header: t("DjOrdersPage.column.quantity"),
},
{
accessorKey: "totalHT",
header: t("DjOrdersPage.column.totalHT"),
},
{
accessorKey: "status",
header: t("DjOrdersPage.column.status"),
},
];
// Status traduit dynamiquement
const statusLabel = computed(() => t(`orderStatus.${order.status}`));
</script>Commutateur de Langue
Composant DjLangSwitcher
<template>
<USelectMenu
v-if="availableLocales"
v-model="selectedLocale"
:items="availableLocales"
>
<UButton variant="ghost" class="flex items-center">
<UIcon :name="selectedLocale.icon" class="w-5 h-5 mr-2" />
<span class="uppercase">{{ selectedLocale?.abbr }}</span>
</UButton>
</USelectMenu>
</template>
<script setup>
const { locale, locales } = useI18n();
const switchLocalePath = useSwitchLocalePath();
const router = useRouter();
const availableLocales = computed(() => locales.value);
const selectedLocale = ref(locales.value.find((i) => i.code === locale.value));
// Changement de langue automatique
watch([selectedLocale], () => {
router.push(switchLocalePath(selectedLocale.value.code));
});
</script>Intégration Côté Serveur
APIs avec Support i18n
// server/api/categories/index.get.ts
export default defineEventHandler(async (event) => {
const query = getQuery(event);
const runtimeConfig = useRuntimeConfig();
// Récupération de la locale depuis la query ou la config
const localeEnv = runtimeConfig.public.locale as string;
const locale =
typeof query.locale === "string" && query.locale.trim().length > 0
? query.locale
: localeEnv;
if (!locale) {
throw createError({
statusCode: 400,
statusMessage: "Locale is required and could not be resolved.",
});
}
// Utilisation de la locale pour les appels SDK
const categories = await sdk.getNavigationCategories({ locale });
return { success: true, categories };
});Middleware avec Détection de Langue
// middleware/i18n.global.ts
export default defineNuxtRouteMiddleware((to) => {
const { locale, defaultLocale } = useI18n();
// Redirection si locale non supportée
if (!["fr-FR", "en-GB"].includes(locale.value)) {
return navigateTo(`/${defaultLocale}/`);
}
});// Schéma de validation avec messages i18n
const { t } = useI18n();
const schema = v.object({
email: v.pipe(
v.string(),
v.email(t("validation.email")),
v.nonEmpty(t("validation.required"))
),
password: v.pipe(
v.string(),
v.minLength(8, t("validation.password.minLength", { min: 8 })),
v.nonEmpty(t("validation.required"))
),
});Bonnes Pratiques
1. Organisation des Clés
✅ Bonne Pratique : Par Composant
{
"DjProductCard": {
"addToCart": "Ajouter au panier",
"viewDetails": "Voir les détails",
"outOfStock": "Rupture de stock"
}
}❌ À Éviter : Clés Plates
{
"addToCart": "Ajouter au panier",
"productViewDetails": "Voir les détails",
"productOutOfStock": "Rupture de stock"
}2. Interpolation et Pluralisation
Variables dans les Traductions
{
"welcome": "Bienvenue {name}",
"itemsCount": "{count} article(s) | {count} article | {count} articles"
}<template>
<!-- Variable simple -->
<p>{{ $t("welcome", { name: user.firstName }) }}</p>
<!-- Pluralisation -->
<p>{{ $t("itemsCount", { count: items.length }, items.length) }}</p>
</template>Configuration Optimisée
// nuxt.config.ts
i18n: {
// Chargement paresseux des traductions
lazy: true,
// Préchargement de certaines langues
precompile: {
strictMessage: false,
},
// Compilation des messages pour la performance
compilation: {
strictMessage: false,
}
}Génération Automatique des Types
// types/i18n.ts
export interface I18nMessages {
DjCartPage: {
cart: string;
details: string;
order: string;
};
DjContactUsPage: {
title: string;
name: string;
email: string;
topic: {
label: string;
product: string;
order: string;
};
};
}
// Utilisation avec autocomplétion
const { t } = useI18n<{ message: I18nMessages }>();Flux Complet d'Internationalisation
Exemple : Ajout d'une Nouvelle Fonctionnalité
sequenceDiagram
participant Dev as Développeur
participant FR as fr-FR.json
participant EN as en-GB.json
participant Comp as Composant Vue
participant User as Utilisateur
Dev->>FR: Ajouter clés françaises
Dev->>EN: Ajouter clés anglaises
Dev->>Comp: Utiliser $t() ou useI18n()
Comp->>FR: Charger traductions (locale=fr)
Comp->>EN: Charger traductions (locale=en)
Comp->>User: Afficher interface traduite
User->>Comp: Changer de langue
Comp->>Comp: Recharger avec nouvelle locale
Comp->>User: Interface mise à jour
1. Ajout des Traductions
// i18n/locales/fr-FR.json
{
"DjNewFeature": {
"title": "Nouvelle Fonctionnalité",
"description": "Description de la fonctionnalité",
"action": "Action principale",
"success": "Opération réussie",
"error": "Une erreur s'est produite"
}
}// i18n/locales/en-GB.json
{
"DjNewFeature": {
"title": "New Feature",
"description": "Feature description",
"action": "Main action",
"success": "Operation successful",
"error": "An error occurred"
}
}2. Utilisation dans le Composant
<template>
<div class="dj-new-feature">
<h2>{{ $t("DjNewFeature.title") }}</h2>
<p>{{ $t("DjNewFeature.description") }}</p>
<UButton @click="handleAction">
{{ $t("DjNewFeature.action") }}
</UButton>
</div>
</template>
<script setup lang="ts">
const { t } = useI18n();
const toast = useToast();
const handleAction = async () => {
try {
await performAction();
toast.add({
title: t("DjNewFeature.success"),
color: "green",
});
} catch (error) {
toast.add({
title: t("DjNewFeature.error"),
color: "red",
});
}
};
</script>Updated 3 months ago
