Internationalisierung (i18n) Routing

Beispiele

Next.js bietet seit v10.0.0 integrierte Unterstützung für internationalisiertes (i18n) Routing. Sie können eine Liste von Locales, die Standard-Locale und domainspezifische Locales angeben, und Next.js übernimmt automatisch das Routing.

Die i18n-Routing-Unterstützung soll derzeit bestehende i18n-Bibliotheken wie react-intl, react-i18next, lingui, rosetta, next-intl, next-translate, next-multilingual, tolgee und andere ergänzen, indem es die Routen und Locale-Parsing vereinfacht.

Erste Schritte

Um zu beginnen, fügen Sie die i18n-Konfiguration zu Ihrer next.config.js-Datei hinzu.

Locales sind UTS Locale Identifiers, ein standardisiertes Format zur Definition von Locales.

Ein Locale-Identifier besteht im Allgemeinen aus einer Sprache, Region und Schrift, getrennt durch einen Bindestrich: Sprache-Region-Schrift. Region und Schrift sind optional. Ein Beispiel:

  • en-US - Englisch, wie in den USA gesprochen
  • nl-NL - Niederländisch, wie in den Niederlanden gesprochen
  • nl - Niederländisch, ohne spezifische Region

Wenn die Benutzer-Locale nl-BE ist und nicht in Ihrer Konfiguration aufgeführt ist, werden Benutzer zu nl weitergeleitet, falls verfügbar, oder andernfalls zur Standard-Locale. Wenn Sie nicht alle Regionen eines Landes unterstützen möchten, ist es daher eine gute Praxis, Landes-Locales als Fallback einzubeziehen.

next.config.js
module.exports = {
  i18n: {
    // Dies sind alle Locales, die Sie in Ihrer Anwendung unterstützen möchten
    locales: ['en-US', 'fr', 'nl-NL'],
    // Dies ist die Standard-Locale, die verwendet werden soll, wenn ein Pfad ohne Locale-Präfix besucht wird, z.B. `/hello`
    defaultLocale: 'en-US',
    // Dies ist eine Liste von Locale-Domains und der Standard-Locale, die sie verarbeiten sollen (nur erforderlich bei Domain-Routing)
    // Hinweis: Subdomains müssen im Domain-Wert enthalten sein, um erkannt zu werden, z.B. "fr.example.com".
    domains: [
      {
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
        // Ein optionales http-Feld kann auch verwendet werden, um Locale-Domains lokal mit http statt https zu testen
        http: true,
      },
    ],
  },
}

Locale-Strategien

Es gibt zwei Strategien zur Locale-Behandlung: Sub-path Routing und Domain Routing.

Sub-path Routing

Sub-path Routing platziert die Locale im URL-Pfad.

next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL'],
    defaultLocale: 'en-US',
  },
}

Mit der obigen Konfiguration sind en-US, fr und nl-NL für das Routing verfügbar, und en-US ist die Standard-Locale. Wenn Sie eine pages/blog.js haben, wären folgende URLs verfügbar:

  • /blog
  • /fr/blog
  • /nl-nl/blog

Die Standard-Locale hat kein Präfix.

Domain Routing

Mit Domain Routing können Sie Locales so konfigurieren, dass sie von verschiedenen Domains aus bereitgestellt werden:

next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'],
    defaultLocale: 'en-US',

    domains: [
      {
        // Hinweis: Subdomains müssen im Domain-Wert enthalten sein, um erkannt zu werden
        // z.B. www.example.com sollte verwendet werden, wenn dies der erwartete Hostname ist
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
        // andere Locales angeben, die zu dieser Domain weitergeleitet werden sollen
        locales: ['nl-BE'],
      },
    ],
  },
}

Wenn Sie beispielsweise pages/blog.js haben, wären folgende URLs verfügbar:

  • example.com/blog
  • www.example.com/blog
  • example.fr/blog
  • example.nl/blog
  • example.nl/nl-BE/blog

Automatische Locale-Erkennung

Wenn ein Benutzer den Anwendungsstamm (normalerweise /) besucht, versucht Next.js automatisch zu erkennen, welche Locale der Benutzer bevorzugt, basierend auf dem Accept-Language-Header und der aktuellen Domain.

Wenn eine andere Locale als die Standard-Locale erkannt wird, wird der Benutzer weitergeleitet zu:

  • Bei Sub-path Routing: Dem mit der Locale präfixierten Pfad
  • Bei Domain Routing: Der Domain, die diese Locale als Standard festgelegt hat

Bei Domain Routing wird ein Benutzer mit dem Accept-Language-Header fr;q=0.9, der example.com besucht, zu example.fr weitergeleitet, da diese Domain standardmäßig die fr-Locale behandelt.

Bei Sub-path Routing würde der Benutzer zu /fr weitergeleitet werden.

Präfix für die Standard-Locale

Mit Next.js 12 und Middleware können wir der Standard-Locale mit einem Workaround ein Präfix hinzufügen.

Hier ist beispielsweise eine next.config.js-Datei mit Unterstützung für einige Sprachen. Beachten Sie, dass die "default"-Locale absichtlich hinzugefügt wurde.

next.config.js
module.exports = {
  i18n: {
    locales: ['default', 'en', 'de', 'fr'],
    defaultLocale: 'default',
    localeDetection: false,
  },
  trailingSlash: true,
}

Als Nächstes können wir Middleware verwenden, um benutzerdefinierte Routing-Regeln hinzuzufügen:

middleware.ts
import { NextRequest, NextResponse } from 'next/server'

const PUBLIC_FILE = /\.(.*)$/

export async function middleware(req: NextRequest) {
  if (
    req.nextUrl.pathname.startsWith('/_next') ||
    req.nextUrl.pathname.includes('/api/') ||
    PUBLIC_FILE.test(req.nextUrl.pathname)
  ) {
    return
  }

  if (req.nextUrl.locale === 'default') {
    const locale = req.cookies.get('NEXT_LOCALE')?.value || 'en'

    return NextResponse.redirect(
      new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url)
    )
  }
}

Diese Middleware überspringt das Hinzufügen des Standard-Präfix zu API-Routen und öffentlichen Dateien wie Schriftarten oder Bildern. Wenn eine Anfrage an die Standard-Locale gestellt wird, leiten wir zu unserem Präfix /en weiter.

Deaktivierung der automatischen Locale-Erkennung

Die automatische Locale-Erkennung kann deaktiviert werden mit:

next.config.js
module.exports = {
  i18n: {
    localeDetection: false,
  },
}

Wenn localeDetection auf false gesetzt ist, leitet Next.js nicht mehr automatisch basierend auf der bevorzugten Locale des Benutzers weiter und stellt nur Locale-Informationen bereit, die entweder von der Locale-basierten Domain oder dem Locale-Pfad erkannt werden, wie oben beschrieben.

Zugriff auf die Locale-Informationen

Sie können über den Next.js-Router auf die Locale-Informationen zugreifen. Beispielsweise sind mit dem useRouter()-Hook folgende Eigenschaften verfügbar:

  • locale enthält die aktuell aktive Locale.
  • locales enthält alle konfigurierten Locales.
  • defaultLocale enthält die konfigurierte Standard-Locale.

Wenn Seiten mit getStaticProps oder getServerSideProps vorgerendert werden, werden die Locale-Informationen im Kontext bereitgestellt, der der Funktion übergeben wird.

Bei Verwendung von getStaticPaths werden die konfigurierten Locales im Kontextparameter der Funktion unter locales und die konfigurierte defaultLocale unter defaultLocale bereitgestellt.

Wechsel zwischen Locales

Sie können next/link oder next/router verwenden, um zwischen Locales zu wechseln.

Für next/link kann eine locale-Prop bereitgestellt werden, um zu einer anderen Locale als der aktuell aktiven zu wechseln. Wenn keine locale-Prop bereitgestellt wird, wird die aktuell aktive locale während Client-Übergängen verwendet. Beispiel:

import Link from 'next/link'

export default function IndexPage(props) {
  return (
    <Link href="/another" locale="fr">
      Zu /fr/another
    </Link>
  )
}

Bei direkter Verwendung der next/router-Methoden können Sie die zu verwendende locale über die Übergangsoptionen angeben. Beispiel:

import { useRouter } from 'next/router'

export default function IndexPage(props) {
  const router = useRouter()

  return (
    <div
      onClick={() => {
        router.push('/another', '/another', { locale: 'fr' })
      }}
    >
      zu /fr/another
    </div>
  )
}

Hinweis: Um nur die locale zu ändern und gleichzeitig alle Routing-Informationen wie dynamische Routen-Query-Werte oder versteckte href-Query-Werte beizubehalten, können Sie den href-Parameter als Objekt bereitstellen:

import { useRouter } from 'next/router'
const router = useRouter()
const { pathname, asPath, query } = router
// Nur die Locale ändern und alle anderen Routing-Informationen einschließlich der href-Query beibehalten
router.push({ pathname, query }, asPath, { locale: nextLocale })

Weitere Informationen zur Objektstruktur für router.push finden Sie hier.

Wenn Sie einen href haben, der bereits die Locale enthält, können Sie die automatische Handhabung des Locale-Präfixes deaktivieren:

import Link from 'next/link'

export default function IndexPage(props) {
  return (
    <Link href="/fr/another" locale={false}>
      Zu /fr/another
    </Link>
  )
}

Nutzung des NEXT_LOCALE-Cookies

Next.js unterstützt das Überschreiben des Accept-Language-Headers mit einem NEXT_LOCALE=the-locale-Cookie. Dieser Cookie kann mit einem Sprachumschalter gesetzt werden, und wenn ein Benutzer zurück zur Seite kommt, wird die im Cookie angegebene Locale verwendet, wenn von / zum korrekten Locale-Ort weitergeleitet wird.

Wenn ein Benutzer beispielsweise die Locale fr in seinem Accept-Language-Header bevorzugt, aber ein NEXT_LOCALE=en-Cookie gesetzt ist, wird der Benutzer beim Besuch von / zur en-Locale weitergeleitet, bis der Cookie entfernt oder abgelaufen ist.

Suchmaschinenoptimierung

Da Next.js weiß, welche Sprache der Benutzer besucht, wird automatisch das lang-Attribut zum <html>-Tag hinzugefügt.

Next.js kennt keine Varianten einer Seite, daher liegt es an Ihnen, die hreflang-Meta-Tags mit next/head hinzuzufügen. Weitere Informationen zu hreflang finden Sie in der Google Webmasters-Dokumentation.

Wie funktioniert dies mit statischer Generierung?

Hinweis: Internationalisiertes Routing ist nicht mit output: 'export' integriert, da es nicht auf der Next.js-Routing-Ebene basiert. Hybride Next.js-Anwendungen, die output: 'export' nicht verwenden, werden vollständig unterstützt.

Dynamische Routen und getStaticProps-Seiten

Für Seiten, die getStaticProps mit dynamischen Routen verwenden, müssen alle gewünschten Locale-Varianten der Seite aus getStaticPaths zurückgegeben werden. Neben dem params-Objekt, das für paths zurückgegeben wird, können Sie auch ein locale-Feld zurückgeben, das angibt, welche Locale gerendert werden soll. Beispiel:

pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
  return {
    paths: [
      // Wenn keine `locale` angegeben ist, wird nur die defaultLocale generiert
      { params: { slug: 'post-1' }, locale: 'en-US' },
      { params: { slug: 'post-1' }, locale: 'fr' },
    ],
    fallback: true,
  }
}

Für automatisch statisch optimierte und nicht-dynamische getStaticProps-Seiten wird eine Version der Seite für jede Locale generiert. Dies ist wichtig zu beachten, da es die Build-Zeiten erhöhen kann, abhängig davon, wie viele Locales in getStaticProps konfiguriert sind.

Wenn Sie beispielsweise 50 Locales mit 10 nicht-dynamischen Seiten konfiguriert haben, die getStaticProps verwenden, bedeutet dies, dass getStaticProps 500 Mal aufgerufen wird. Bei jedem Build werden 50 Versionen der 10 Seiten generiert.

Um die Build-Zeit für dynamische Seiten mit getStaticProps zu verringern, verwenden Sie einen fallback-Modus. Dadurch können Sie nur die beliebtesten Pfade und Locales aus getStaticPaths für die Vorrendering während des Builds zurückgeben. Next.js generiert dann die restlichen Seiten zur Laufzeit, wenn sie angefordert werden.

Automatisch statisch optimierte Seiten

Für Seiten, die automatisch statisch optimiert werden, wird eine Version der Seite für jede Locale generiert.

Nicht-dynamische getStaticProps-Seiten

Für nicht-dynamische getStaticProps-Seiten wird wie oben eine Version für jede Locale generiert. getStaticProps wird mit jeder Locale aufgerufen, die gerendert wird. Wenn Sie eine bestimmte Locale vom Pre-Rendering ausschließen möchten, können Sie notFound: true aus getStaticProps zurückgeben, und diese Variante der Seite wird nicht generiert.

export async function getStaticProps({ locale }) {
  // Rufen Sie einen externen API-Endpunkt ab, um Beiträge zu erhalten.
  // Sie können jede Datenabfragebibliothek verwenden
  const res = await fetch(`https://.../posts?locale=${locale}`)
  const posts = await res.json()

  if (posts.length === 0) {
    return {
      notFound: true,
    }
  }

  // Durch die Rückgabe von { props: posts } erhält die Blog-Komponente
  // `posts` als Prop zum Build-Zeitpunkt
  return {
    props: {
      posts,
    },
  }
}

Grenzen der i18n-Konfiguration

  • locales: 100 Locales insgesamt
  • domains: 100 Locale-Domain-Einträge insgesamt

Gut zu wissen: Diese Grenzen wurden zunächst hinzugefügt, um potenzielle Performance-Probleme beim Build zu vermeiden. Sie können diese Grenzen mit benutzerdefiniertem Routing unter Verwendung von Middleware in Next.js 12 umgehen.