Internationalisierung

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

Begriffe

  • Locale: Ein Identifikator für eine Gruppe von Sprach- und Formatierungspräferenzen. Dies beinhaltet normalerweise die bevorzugte Sprache des Nutzers und möglicherweise deren 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 Nutzers im Browser zu verwenden, um das zu verwendende Locale auszuwählen. Das Ändern Ihrer bevorzugten Sprache passt den Accept-Language-Header an, der an Ihre Anwendung gesendet wird.

Mit den folgenden Bibliotheken können Sie beispielsweise eine eingehende Request untersuchen, um basierend auf den Headers, den geplanten unterstützten Locales und dem Standard-Locale das 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 Nutzer basierend auf dem Locale in der Middleware umleiten.

middleware.js

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

// Bevorzugtes Locale ermitteln, ähnlich wie oben oder mit einer Bibliothek
function getLocale(request) { ... }

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

  if (pathnameHasLocale) return

  // Umleitung, wenn kein Locale vorhanden ist
  const locale = getLocale(request)
  request.nextUrl.pathname = `/${locale}${pathname}`
  // z.B. eingehende Anfrage ist /products
  // Die neue URL ist nun /en-US/products
  return Response.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. Dies ermöglicht dem Next.js-Router, verschiedene Locales in der Route dynamisch zu verarbeiten und den lang-Parameter an jedes Layout und jede Seite weiterzuleiten. Beispiel:

app/[lang]/page.js
// Sie haben nun Zugriff auf das aktuelle Locale
// z.B. /en-US/products -> `lang` ist "en-US"
export default async function Page({ params: { lang } }) {
  return ...
}

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

Lokalisierung

Die Anpassung von angezeigten Inhalten basierend auf dem bevorzugten Locale des Nutzers, oder Lokalisierung, ist nichts Next.js-spezifisches. 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 das angeforderte Locale zu laden:

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

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

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
}

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 clientseitige JavaScript-Bundle-Größe beeinflussen könnten. Dieser Code wird nur auf dem Server ausgeführt, und nur das resultierende HTML wird an den Browser gesendet.

Statische Generierung

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 sein, beispielsweise im Root-Layout:

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

Ressourcen