Guide de localisation avec Next.js

Localisation avec Next.js pour l'App Router et le Pages Router

Créez des routes tenant compte de la langue, chargez efficacement les messages dans les Server et Client Components, comparez next-intl à next-i18next et publiez des pages localisées explorables avec les bonnes métadonnées.

Les fondations d'une application Next.js localisée

Next.js constitue une base solide pour les sites multilingues, car il peut produire un HTML utile avant que le navigateur n'exécute le JavaScript de l'application. La génération statique convient bien aux pages produit et à la documentation ; le rendu dynamique peut servir du contenu propre à un compte ou fréquemment modifié. Aucun de ces modes de rendu ne définit à lui seul une stratégie de localisation. Vous avez toujours besoin de routes tenant compte de la langue, de catalogues, de règles de formatage et d'un processus de traduction.

Le premier choix architectural concerne le routeur. L'App Router utilise des layouts, des Server Components et des segments de route. Son modèle de localisation officiel place la langue dans un segment dynamique de premier niveau comme app/[lang]. Le Pages Router possède une configuration i18n distincte qui peut ajouter un routage par sous-chemin ou par domaine et fournir les données de langue aux pages. Ces systèmes ne doivent pas être présentés comme interchangeables.

Faites de la langue un concept validé par l'application au lieu de transmettre du texte arbitraire provenant de l'URL aux imports de catalogues. Conservez une seule liste de langues prises en charge avec le code de langue, le nom affiché, le sens du texte et la langue de repli. Utilisez cette liste pour générer les routes, créer le sélecteur de langue, choisir les catalogues et produire les métadonnées. Une requête visant une langue non prise en charge doit aboutir de manière prévisible à une réponse introuvable ou à un repli intentionnel.

  • Une URL stable pour chaque langue et chaque page de contenu
  • Une frontière de chargement des catalogues compatible avec le serveur
  • Une stratégie explicite de détection, de repli et de changement de langue
  • Des métadonnées localisées et des signaux de pages alternatives pour les robots d'exploration

Choisir une stratégie de routage par langue

Le routage par sous-chemin donne à chaque langue une URL visible, comme /en/pricing et /fr/pricing. Il fonctionne sur un seul domaine, se teste facilement et transforme le changement de langue en modification de chemin. Le routage par domaine, comme example.fr, peut répondre à des besoins de marque ou à des exigences juridiques propres à un marché, mais ajoute du travail en matière de DNS, de déploiement, d'analytique et de canonicalisation. Choisissez des domaines pour des raisons liées au produit, et non parce que la localisation les exige.

Dans l'App Router, placez toutes les pages localisées sous app/[locale]. Un Proxy peut examiner le chemin, le cookie ou l'en-tête Accept-Language au moment de la requête et rediriger une URL d'entrée neutre vers une langue. Next.js 16 a renommé middleware.ts en proxy.ts, bien que les anciennes applications utilisent encore le nom middleware. Considérez la négociation comme une commodité : chaque URL localisée doit rester directement accessible et le sélecteur doit utiliser des liens ordinaires explorables.

Un export entièrement statique ne peut pas exécuter Proxy, lire les cookies de la requête ni effectuer de redirections à la volée. Avec output: 'export', énumérez les segments de langue à l'aide de generateStaticParams et fournissez une page d'entrée statique, par exemple une page d'accueil dans la langue par défaut ou un sélecteur de langue visible. Ne promettez pas de détection automatique de la langue du navigateur dans un environnement dépourvu d'exécution au moment de la requête.

L'option i18n de next.config.js du Pages Router prend en charge les sous-chemins et domaines de langue dans les applications Next.js déployées. Elle n'est pas compatible avec output: 'export'. Si un projet Pages Router doit produire uniquement des fichiers statiques, représentez la langue par un segment dynamique explicite et générez ces routes plutôt que d'activer le champ i18n intégré. La génération statique et l'export statique sont liés, mais ne constituent pas le même mode de déploiement.

Une structure App Router centrée sur la langue
app/
  [locale]/
    layout.tsx
    page.tsx
    pricing/
      page.tsx
messages/
  en.json
  fr.json
  de.json

next-intl ou next-i18next ou dictionnaires personnalisés

Une bibliothèque doit réduire les tâches d'infrastructure répétitives sans masquer le fonctionnement des URL de langue ni du rendu. Comparez la prise en charge de votre routeur, le chargement des messages côté serveur et côté client, la syntaxe ICU ou i18next, la génération statique, les fonctions de routage et les outils d'extraction. Ne choisissez pas uniquement à partir d'un exemple de démarrage : la localisation touche la navigation, les métadonnées, les erreurs, les formulaires et les contenus en arrière-plan dans toute l'application.

next-intl et next-i18next prennent aujourd'hui en charge l'App Router comme le Pages Router, mais conservent des modèles conceptuels différents. next-intl s'articule autour des messages ICU et d'un routage adapté à Next.js. next-i18next apporte l'écosystème i18next, notamment les espaces de noms, les détecteurs, les chargeurs et les API familières de type react-i18next. Une approche par dictionnaire personnalisé offre la couche d'abstraction la plus légère, mais transfère davantage de responsabilités à votre code.

Approche Routage Modèle de messages Server Components Génération statique Idéal pour
next-intl App Router recommandé ; Pages pris en charge Messages ICU et outils de formatage API pour Server et Client Components Pris en charge avec des paramètres de langue statiques Applications pensées pour Next.js et utilisant ICU
next-i18next App Router et Pages Router Clés i18next, espaces de noms, ICU facultatif API getT côté serveur et useT côté client Pris en charge avec une génération explicite des langues Équipes déjà investies dans i18next
Next.js + dictionnaires personnalisés Routes [locale] explicites La structure JSON ou de module de votre choix Chargement naturel dans les Server Components Oui, lorsque les routes peuvent être énumérées Petits sites aux messages simples

Pour une nouvelle application App Router avec des messages riches et une navigation localisée, next-intl est un choix par défaut ciblé. Si votre organisation utilise déjà i18next ou que l'application partage des ressources avec d'autres interfaces React, next-i18next peut préserver les conventions de catalogues et de plugins. Revérifiez la configuration propre à chaque version avant une migration : les deux bibliothèques ont considérablement évolué au fil des changements apportés par Next.js à ses API serveur.

L'option reposant uniquement sur le framework est raisonnable lorsque le catalogue est réduit, les messages sont simples et l'équipe accepte de créer ses propres outils de validation et d'extraction. Le chargement d'un dictionnaire dans un Server Component évite d'en envoyer le contenu au client, mais les primitives de routage n'offrent ni syntaxe de pluriel, ni contexte pour les traducteurs, ni validation des catalogues, ni processus de révision. N'ajoutez ces capacités que lorsque le produit en a besoin, tout en maintenant l'accès aux catalogues derrière une petite interface.

Une configuration App Router prête pour la production

La séquence suivante utilise une application App Router, car elle met en évidence les décisions auxquelles font face la plupart des nouveaux projets. Les imports exacts varient selon la bibliothèque, mais les frontières restent stables : validez la langue au niveau de la route, chargez les messages côté serveur pour cette requête, fournissez uniquement ce dont les Client Components ont besoin et générez les langues connues pendant la compilation lorsque la page peut être statique.

  1. Étape 1

    Ajouter le segment de route de langue

    Créez app/[locale] et placez-y les layouts et pages localisés. Validez les paramètres par rapport à une liste unique de langues prises en charge et renvoyez une réponse introuvable pour les valeurs inconnues.

  2. Étape 2

    Définir la négociation et le repli

    Décidez du comportement à adopter pour une URL sans langue. Un environnement d'exécution des requêtes peut négocier et rediriger ; un export statique exige une page par défaut déterministe ou un sélecteur de langue. Préservez toujours l'accès direct aux URL localisées.

  3. Étape 3

    Créer des catalogues de messages ciblés

    Organisez les messages par domaine produit ou par route au lieu de placer toute l'application dans un seul fichier. Conservez la même structure d'espaces de noms dans toutes les langues, validez les clés manquantes dans la CI et évitez d'importer toutes les langues dans un Client Component.

  4. Étape 4

    Charger les messages sur le serveur

    Résolvez un catalogue validé pour chaque requête ou route générée. Lorsque la bibliothèque stocke l'état de la langue active, limitez les instances serveur à la requête afin d'empêcher les requêtes simultanées de se transmettre leur choix de langue.

  5. Étape 5

    Séparer la traduction côté serveur et côté client

    Traduisez par défaut dans les Server Components. N'ajoutez un provider ou un hook client que sur la plus petite frontière interactive qui en a besoin, et transmettez des libellés traduits comme propriétés lorsque cette solution est plus simple.

  6. Étape 6

    Générer les variantes linguistiques

    Renvoyez chaque langue prise en charge depuis generateStaticParams pour les pages statiques. Le contenu dynamique peut aussi exiger des combinaisons de slug et de langue. Relancez la compilation chaque fois que les catalogues ou la liste des langues changent.

Charger les messages dans un layout de langue
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';

export default async function LocaleLayout({children, params}: Props) {
  const {locale} = await params;
  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
Générer une arborescence de routes par langue
const locales = ['en', 'fr', 'de'] as const;

export function generateStaticParams() {
  return locales.map((locale) => ({locale}));
}

Confiez au layout racine de la langue les responsabilités transversales : validation des paramètres, chargement du catalogue, définition de html lang et dir, et mise en place de tout provider côté client. Les pages doivent demander les messages par espace de noms sans savoir où résident les catalogues. Cette séparation permet de remplacer les imports JSON locaux par un téléchargement à la compilation ou un processus adossé à un CDN sans réécrire chaque composant.

Pour le Pages Router, transposez ces mêmes responsabilités dans le routage de langue de next.config.js, le chargement des messages via getStaticProps ou getServerSideProps, et getStaticPaths pour les pages localisées dynamiques. Les API des routeurs diffèrent, mais les couches de catalogues et de composants peuvent rester cohérentes. Lors d'une migration, prenez délibérément en charge les deux routeurs et vérifiez que la navigation ne supprime jamais le préfixe de langue.

Formatage, pluriels et mises en page de droite à gauche

Le formatage accompagne la traduction, mais il s'agit de deux tâches distinctes. Les messages expliquent ce que signifie une valeur ; les outils de formatage sensibles à la langue déterminent l'affichage d'une date, d'un nombre, d'une devise, d'un temps relatif ou d'une liste. Les intégrations next-intl et i18next fournissent des fonctions de formatage, tandis qu'une couche de dictionnaire personnalisée peut appeler directement les API Intl de la plateforme. Conservez les dates et nombres bruts comme données jusqu'à l'étape du rendu afin de présenter correctement une même valeur source dans chaque langue.

Les règles de pluriel appartiennent elles aussi à la langue cible, et non à une simple condition singulier-pluriel propre à l'anglais. Les messages ICU peuvent sélectionner des catégories comme one, few, many et other. i18next associe les valeurs de comptage via Intl.PluralRules. Dans les deux cas, transmettez le nombre au message et laissez la bibliothèque choisir la forme grammaticale. Ne concaténez pas un nom traduit à un nombre formaté.

Définissez l'attribut html lang à partir de la langue validée de la route, et dir sur rtl pour les systèmes d'écriture de droite à gauche. En HTML, la langue ne détermine pas automatiquement le sens d'écriture : faites-en donc une propriété explicite de votre configuration de langues. Inversez les espacements directionnels et les icônes lorsque leur sens en dépend, et utilisez les propriétés CSS logiques pour que les mises en page s'adaptent sans seconde feuille de styles.

Générer des métadonnées localisées et des liens alternatifs
export async function generateMetadata({params}: Props) {
  const {locale} = await params;
  const t = await getTranslations({locale, namespace: 'Metadata'});

  return {
    title: t('title'),
    description: t('description'),
    alternates: {
      canonical: `https://example.com/${locale}`,
      languages: {en: '/en', fr: '/fr', de: '/de'}
    }
  };
}

SEO multilingue pour Next.js

Le HTML rendu n'est que le point de départ du SEO multilingue. Les moteurs de recherche ont besoin d'une URL distincte et stable pour chaque version linguistique, ainsi que de liens clairs entre les variantes. Dans l'App Router, generateMetadata peut produire des titres et descriptions localisés, ainsi que alternates.canonical et alternates.languages. Un sitemap peut également répertorier les variantes linguistiques. Appliquez une stratégie d'URL cohérente afin que les métadonnées, la navigation et les entrées du sitemap ne se contredisent jamais.

Chaque page localisée doit faire référence à elle-même et à toutes les langues équivalentes au moyen d'annotations hreflang réciproques. Utilisez des URL absolues en production et ajoutez x-default lorsqu'un sélecteur neutre ou une expérience par défaut est utile. En règle générale, l'URL canonique pointe vers la version dans la langue actuelle, et non automatiquement vers l'anglais. Lorsqu'une page n'est pas disponible dans une langue, omettez cette variante non prise en charge au lieu d'envoyer les robots vers un contenu sans rapport.

Ne dépendez pas uniquement d'Accept-Language. Googlebot explore souvent sans préférence linguistique utile, et un contenu adapté à la langue sur une URL unique peut masquer des variantes. Veillez à ce que les pages de chaque langue restent accessibles par des liens ordinaires. Définissez html lang pour les navigateurs et les technologies d'assistance, mais gardez à l'esprit que Google détermine surtout la langue à partir du contenu visible : la traduction elle-même doit donc être substantielle et cohérente.

  • Renvoyez un HTML localisé complet à une URL stable et explorable.
  • Localisez le titre, la description, les titres de section et le corps visible, pas uniquement l'enveloppe de navigation.
  • Émettez une URL canonique pour la page actuelle et des liens hreflang réciproques pour chaque langue disponible.
  • Incluez les URL localisées dans le sitemap et utilisez x-default pour un repli neutre lorsque cela convient.
  • Conservez un sélecteur de langue explorable et évitez les redirections forcées qui enferment les utilisateurs ou les robots dans une seule langue.

Préserver la rapidité des pages localisées

Les Server Components offrent une occasion importante d'améliorer les performances : les catalogues chargés et utilisés exclusivement sur le serveur n'ont pas à devenir du JavaScript client. Conservez les titres de page, les libellés de navigation et les textes principalement statiques sur le serveur. Lorsqu'un Client Component interactif a besoin de messages, fournissez uniquement l'espace de noms pertinent ou des propriétés déjà traduites au lieu de sérialiser l'intégralité du catalogue de langue dans chaque page.

La génération statique remplace le travail au moment de la requête par du travail à la compilation. Chaque langue multiplie le nombre de variantes de pages ; mesurez donc la durée de compilation et la taille de la sortie à mesure que le site grandit. Générez à l'avance les pages à forte valeur, divisez les catalogues par route ou domaine et évitez d'importer toutes les langues depuis un module client partagé. L'objectif est de produire du HTML localisé sans obliger chaque visiteur à télécharger du contenu dans des langues qu'il n'a jamais sélectionnées.

  • Ne chargez que la langue active et les espaces de noms requis par la route.
  • Formatez et traduisez dans les Server Components, sauf si l'interactivité du navigateur exige une frontière côté client.
  • Générez explicitement chaque langue prise en charge et surveillez le coût de compilation à mesure que le nombre de routes augmente.
  • Gardez la configuration des langues déterministe afin qu'une même URL produise toujours la même sortie statique.

La place de Translatable

Une bibliothèque Next.js détermine comment l'application sélectionne et affiche les messages. Une plateforme de localisation prend en charge le cycle de vie qui les entoure : collecte des clés, traduction des valeurs, révision des modifications, alignement des catalogues de langues et diffusion du contenu approuvé. Ces besoins apparaissent dès que les textes changent plus souvent que les déploiements ou que les contributeurs ne se limitent plus à un seul développeur.

Translatable est conçu autour de fichiers de localisation JSON. Les équipes peuvent importer leurs catalogues existants, les modifier ou les traduire avec l'aide de l'IA, les synchroniser via des fournisseurs Git et distribuer les mises à jour par un CDN. L'application conserve la maîtrise de son routage et de sa bibliothèque i18n, ce qui permet de faire évoluer la diffusion des messages sans lier les composants d'interface à un environnement d'exécution propriétaire.

Commencez par des catalogues versionnés avec le code et un processus exécuté à la compilation. N'introduisez la diffusion à distance que lorsque des mises à jour de texte plus rapides apportent une réelle valeur, et conservez une solution de repli intégrée pour les pages critiques. Cette évolution progressive maintient le rendu statique déterministe tout en offrant aux équipes de contenu un chemin plus simple vers la production à mesure que le produit grandit.

Questions fréquentes

Quel routeur convient le mieux à l'i18n : App Router ou Pages Router ?

Pour les nouvelles applications, l'App Router constitue généralement le meilleur point de départ, car les Server Components peuvent charger les messages sans envoyer des catalogues entiers au navigateur. Le Pages Router reste adapté aux applications existantes et inclut le routage de langue via next.config.js. Choisissez d'abord le routeur qui correspond à l'application, puis une bibliothèque i18n qui le prend en charge.

Faut-il utiliser next-intl ou next-i18next ?

Choisissez next-intl si vous souhaitez une API axée sur ICU et un routage de langue étroitement intégré. Choisissez next-i18next si votre équipe préfère l'écosystème i18next, les espaces de noms et les plugins ; les versions actuelles prennent en charge les deux routeurs. Une approche reposant uniquement sur les dictionnaires du framework convient aux petits projets dont les messages sont simples, mais vous devrez fournir vous-même les outils de formatage, d'extraction, de validation et de gestion du processus.

L'App Router de Next.js intègre-t-il le routage i18n ?

L'App Router fournit les primitives de routage et de rendu nécessaires à la localisation, mais pas de système de messages complet. Vous devez créer un segment de langue comme app/[locale], charger des dictionnaires ou utiliser une bibliothèque, puis mettre en œuvre la négociation si nécessaire. Le Pages Router propose séparément un routage de langue intégré grâce au champ i18n de next.config.js.

Un site Next.js localisé peut-il utiliser l'export statique ?

Oui, si toutes les routes localisées peuvent être générées à l'avance. Avec l'App Router, utilisez un segment [locale] explicite et generateStaticParams. Un export entièrement statique ne peut pas dépendre de Proxy, des cookies, des redirections ou de la détection de langue au moment de la requête. Le routage i18n intégré du Pages Router n'est pas compatible avec output: 'export' ; les projets exportés ont donc besoin de routes de langue explicites.

Puis-je ajouter des langues à Next.js sans modifier le code ?

En général, pas entièrement. Une nouvelle langue exige un catalogue, la mise à jour de la configuration des langues prises en charge, des paramètres statiques ou une configuration de routage, des métadonnées localisées et une nouvelle compilation. Une plateforme de traduction peut éliminer une grande partie du travail manuel sur les catalogues, mais l'application doit toujours reconnaître et publier la langue en toute sécurité.

Créez un processus de localisation facile à maintenir pour votre équipe

Regroupez vos catalogues JSON, traduisez-les avec l'aide de l'IA et diffusez les mises à jour via Git ou un CDN mondial.

Commencer gratuitement