Internationalisation

Next.js vous permet de configurer le routage et le rendu du contenu pour prendre en charge plusieurs langues. Adapter votre site à différentes locales inclut du contenu traduit (localisation) et des routes internationalisées.

Terminologie

  • Locale : Un identifiant pour un ensemble de préférences linguistiques et de format. Cela inclut généralement la langue préférée de l'utilisateur et éventuellement sa région géographique.
    • en-US : Anglais tel que parlé aux États-Unis
    • nl-NL : Néerlandais tel que parlé aux Pays-Bas
    • nl : Néerlandais, sans région spécifique

Aperçu du routage

Il est recommandé d'utiliser les préférences linguistiques du navigateur pour sélectionner la locale à utiliser. Changer votre langue préférée modifiera l'en-tête Accept-Language envoyé à votre application.

Par exemple, en utilisant les bibliothèques suivantes, vous pouvez examiner une Request entrante pour déterminer quelle locale sélectionner, en fonction des Headers, des locales que vous prévoyez de supporter et de la locale par défaut.

middleware.js
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'

let headers = { 'accept-language': 'en-US,en;q=0.5' }
let languages = new Negotiator({ headers }).languages()
let locales = ['en-US', 'nl-NL', 'nl']
let defaultLocale = 'en-US'

match(languages, locales, defaultLocale) // -> 'en-US'

Le routage peut être internationalisé soit par sous-chemin (/fr/products), soit par domaine (my-site.fr/products). Avec ces informations, vous pouvez maintenant rediriger l'utilisateur en fonction de la locale dans le Middleware.

middleware.js
import { NextResponse } from "next/server";

let locales = ['en-US', 'nl-NL', 'nl']

// Obtenir la locale préférée, similaire à l'exemple ci-dessus ou en utilisant une bibliothèque
function getLocale(request) { ... }

export function middleware(request) {
  // Vérifier si un chemin contient une locale supportée
  const { pathname } = request.nextUrl
  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  )

  if (pathnameHasLocale) return

  // Rediriger s'il n'y a pas de locale
  const locale = getLocale(request)
  request.nextUrl.pathname = `/${locale}${pathname}`
  // Par exemple, une requête entrante /products
  // La nouvelle URL est maintenant /en-US/products
  return NextResponse.redirect(request.nextUrl)
}

export const config = {
  matcher: [
    // Ignorer tous les chemins internes (_next)
    '/((?!_next).*)',
    // Optionnel : n'exécuter que sur l'URL racine (/)
    // '/'
  ],
}

Enfin, assurez-vous que tous les fichiers spéciaux dans app/ sont imbriqués sous app/[lang]. Cela permet au routeur Next.js de gérer dynamiquement différentes locales dans la route et de transmettre le paramètre lang à chaque layout et page. Par exemple :

// Vous avez maintenant accès à la locale courante
// Par exemple, /en-US/products -> `lang` est "en-US"
export default async function Page({
  params,
}: {
  params: Promise<{ lang: string }>
}) {
  const { lang } = await params
  return ...
}

Le layout racine peut également être imbriqué dans le nouveau dossier (par exemple app/[lang]/layout.js).

Localisation

Changer le contenu affiché en fonction de la locale préférée de l'utilisateur, ou localisation, n'est pas spécifique à Next.js. Les modèles décrits ci-dessous fonctionneraient de la même manière avec n'importe quelle application web.

Supposons que nous voulions prendre en charge du contenu en anglais et en néerlandais dans notre application. Nous pourrions maintenir deux "dictionnaires" différents, qui sont des objets nous donnant une correspondance entre une clé et une chaîne localisée. Par exemple :

dictionaries/en.json
{
  "products": {
    "cart": "Add to Cart"
  }
}
dictionaries/nl.json
{
  "products": {
    "cart": "Toevoegen aan Winkelwagen"
  }
}

Nous pouvons ensuite créer une fonction getDictionary pour charger les traductions pour la locale demandée :

import 'server-only'

const dictionaries = {
  en: () => import('./dictionaries/en.json').then((module) => module.default),
  nl: () => import('./dictionaries/nl.json').then((module) => module.default),
}

export const getDictionary = async (locale: 'en' | 'nl') =>
  dictionaries[locale]()

Étant donné la langue actuellement sélectionnée, nous pouvons récupérer le dictionnaire dans un layout ou une page.

import { getDictionary } from './dictionaries'

export default async function Page({
  params,
}: {
  params: Promise<{ lang: 'en' | 'nl' }>
}) {
  const { lang } = await params
  const dict = await getDictionary(lang) // en
  return <button>{dict.products.cart}</button> // Add to Cart
}

Comme tous les layouts et pages dans le répertoire app/ sont par défaut des Composants Serveur, nous n'avons pas à nous soucier de la taille des fichiers de traduction affectant la taille de notre bundle JavaScript côté client. Ce code ne s'exécutera que sur le serveur, et seul le HTML résultant sera envoyé au navigateur.

Rendu statique

Pour générer des routes statiques pour un ensemble donné de locales, nous pouvons utiliser generateStaticParams avec n'importe quelle page ou layout. Cela peut être global, par exemple dans le layout racine :

export async function generateStaticParams() {
  return [{ lang: 'en-US' }, { lang: 'de' }]
}

export default async function RootLayout({
  children,
  params,
}: Readonly<{
  children: React.ReactNode
  params: Promise<{ lang: 'en-US' | 'de' }>
}>) {
  return (
    <html lang={(await params).lang}>
      <body>{children}</body>
    </html>
  )
}

Ressources

On this page