Internationalisierung

Next.js ermöglicht es Ihnen, das Routing und das Rendering von Inhalten für die Unterstützung mehrerer Sprachen zu konfigurieren. Die Anpassung Ihrer Website an verschiedene Locales umfasst übersetzte Inhalte (Lokalisierung) und internationalisierte Routen.

Terminologie

  • Locale: Ein Identifikator für eine Gruppe von Sprach- und Formatierungspräferenzen. Dies beinhaltet normalerweise die bevorzugte Sprache des Benutzers und möglicherweise dessen geografische Region.
    • en-US: Englisch, wie in den USA gesprochen
    • nl-NL: Niederländisch, wie in den Niederlanden gesprochen
    • nl: Niederländisch, ohne spezifische Region

Routing-Übersicht

Es wird empfohlen, die Spracheinstellungen des Benutzers im Browser zu nutzen, um die zu verwendende Locale auszuwählen. Das Ändern Ihrer bevorzugten Sprache passt den Accept-Language-Header in den eingehenden Anfragen an Ihre Anwendung an.

Mit den folgenden Bibliotheken können Sie beispielsweise eine eingehende Request analysieren, um basierend auf den Headers, den unterstützten Locales und der Standard-Locale die passende Locale auszuwählen.

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'

Das Routing kann durch Sub-Pfade (/fr/products) oder Domains (my-site.fr/products) internationalisiert werden. Mit diesen Informationen können Sie den Benutzer basierend auf der Locale in der Middleware weiterleiten.

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

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

// Ermitteln der bevorzugten Locale, ähnlich wie oben oder mit einer Bibliothek
function getLocale(request) { ... }

export function middleware(request) {
  // Prüfen, ob ein unterstützter Locale im Pfad vorhanden ist
  const { pathname } = request.nextUrl
  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  )

  if (pathnameHasLocale) return

  // Weiterleitung, wenn keine Locale vorhanden ist
  const locale = getLocale(request)
  request.nextUrl.pathname = `/${locale}${pathname}`
  // Beispiel: Eingehende Anfrage ist /products
  // Die neue URL ist nun /en-US/products
  return NextResponse.redirect(request.nextUrl)
}

export const config = {
  matcher: [
    // Alle internen Pfade überspringen (_next)
    '/((?!_next).*)',
    // Optional: Nur auf der Root-URL (/) ausführen
    // '/'
  ],
}

Stellen Sie sicher, dass alle speziellen Dateien in app/ unter app/[lang] verschachtelt sind. Dadurch kann der Next.js-Router verschiedene Locales in der Route dynamisch verarbeiten und den lang-Parameter an jedes Layout und jede Seite weitergeben. Beispiel:

// Sie haben nun Zugriff auf die aktuelle Locale
// z.B. /en-US/products -> `lang` ist "en-US"
export default async function Page({
  params,
}: {
  params: Promise<{ lang: string }>
}) {
  const { lang } = await params
  return ...
}

Das Root-Layout kann ebenfalls im neuen Ordner verschachtelt werden (z.B. app/[lang]/layout.js).

Lokalisierung

Die Anpassung der angezeigten Inhalte basierend auf der bevorzugten Locale des Benutzers, oder Lokalisierung, ist nichts spezifisches für Next.js. Die unten beschriebenen Muster funktionieren genauso mit jeder Webanwendung.

Angenommen, wir möchten sowohl englische als auch niederländische Inhalte in unserer Anwendung unterstützen. Wir könnten zwei verschiedene "Wörterbücher" pflegen, die eine Zuordnung von einem Schlüssel zu einem lokalisierten String bereitstellen. Beispiel:

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

Wir können dann eine getDictionary-Funktion erstellen, um die Übersetzungen für die angeforderte Locale zu laden:

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]()

Basierend auf der aktuell ausgewählten Sprache können wir das Wörterbuch innerhalb eines Layouts oder einer Seite abrufen.

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
}

Da alle Layouts und Seiten im app/-Verzeichnis standardmäßig Server Components sind, müssen wir uns keine Gedanken über die Größe der Übersetzungsdateien machen, die die Größe unseres clientseitigen JavaScript-Bundles beeinflussen könnte. Dieser Code wird nur auf dem Server ausgeführt, und nur das resultierende HTML wird an den Browser gesendet.

Statisches Rendering

Um statische Routen für eine bestimmte Gruppe von Locales zu generieren, können wir generateStaticParams mit jeder Seite oder jedem Layout verwenden. Dies kann global erfolgen, beispielsweise im Root-Layout:

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>
  )
}

Ressourcen

On this page