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 formatage. 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 de l'utilisateur dans le navigateur pour sélectionner la locale à utiliser. Changer votre langue préférée modifiera l'en-tête Accept-Language entrant dans 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 prendre en charge 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 le sous-chemin (/fr/products), soit par le domaine (my-site.fr/products). Avec ces informations, vous pouvez maintenant rediriger l'utilisateur en fonction de la locale dans le Middleware.

middleware.js

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

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

export function middleware(request) {
  // Vérifiez s'il y a une locale prise en charge dans le chemin
  const { pathname } = request.nextUrl
  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  )

  if (pathnameHasLocale) return

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

export const config = {
  matcher: [
    // Ignore tous les chemins internes (_next)
    '/((?!_next).*)',
    // Optionnel : exécutez uniquement 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 mise en page et page. Par exemple :

app/[lang]/page.js
// Vous avez maintenant accès à la locale actuelle
// Par exemple, /en-US/products -> `lang` est "en-US"
export default async function Page({ params: { lang } }) {
  return ...
}

La mise en page racine peut également être imbriquée 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 à la fois 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 :

app/[lang]/dictionaries.js
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) => dictionaries[locale]()

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

app/[lang]/page.js
import { getDictionary } from './dictionaries'

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

Comme toutes les mises en page 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 s'exécutera uniquement sur le serveur, et seul le HTML résultant sera envoyé au navigateur.

Génération statique

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

app/[lang]/layout.js
export async function generateStaticParams() {
  return [{ lang: 'en-US' }, { lang: 'de' }]
}

export default function Root({ children, params }) {
  return (
    <html lang={params.lang}>
      <body>{children}</body>
    </html>
  )
}

Ressources