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ées
Organisation 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_default
Cette 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/account
Navigation 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 about 2 months ago