Stellen Sie Ihre Frage und erhalten Sie einen Resümee des Dokuments, indem Sie diese Seite und den AI-Anbieter Ihrer Wahl referenzieren
Der Inhalt dieser Seite wurde mit einer KI übersetzt.
Den englischen Originaltext ansehenIf you have an idea for improving this documentation, please feel free to contribute by submitting a pull request on GitHub.
GitHub link to the documentationCopy doc Markdown to clipboard
Übersetzen Sie Ihre Next.js 15 Website mit next-i18next unter Verwendung von Intlayer | Internationalisierung (i18n)
Für wen diese Anleitung ist
- Junior: Folgen Sie den genauen Schritten und kopieren Sie die Codeblöcke. Sie erhalten eine funktionierende mehrsprachige App.
- Mid-level: Verwenden Sie die Checklisten und Best-Practice-Hinweise, um häufige Fallstricke zu vermeiden.
- Senior: Überfliegen Sie die Abschnitte zur Gesamtstruktur, SEO und Automatisierung; Sie finden sinnvolle Voreinstellungen und Erweiterungspunkte.
Was Sie bauen werden
- App Router Projekt mit lokalisierten Routen (z.B.
/,/fr/...) - i18n-Konfiguration mit Locales, Standard-Locale, RTL-Unterstützung
- Serverseitige i18n-Initialisierung und ein Client-Provider
- Namespaced Übersetzungen, die bei Bedarf geladen werden
- SEO mit
hreflang, lokalisiertemsitemap,robots - Middleware für Locale-Routing
- Intlayer-Integration zur Automatisierung von Übersetzungs-Workflows (Tests, KI-Ausfüllung, JSON-Synchronisation)
Hinweis: next-i18next basiert auf i18next. Diese Anleitung verwendet die i18next-Primitiven, die mit next-i18next im App Router kompatibel sind, und hält dabei die Architektur einfach und produktionsbereit. Für einen umfassenderen Vergleich siehe next-i18next vs next-intl vs Intlayer.
1) Projektstruktur
Installieren Sie die next-i18next-Abhängigkeiten:
Kopieren Sie den Code in die Zwischenablage
npm install next-i18next i18next react-i18next i18next-resources-to-backendBeginnen Sie mit einer klaren Struktur. Halten Sie die Nachrichten nach Locale und Namespace getrennt.
Kopieren Sie den Code in die Zwischenablage
.├── i18n.config.ts└── src ├── locales │ ├── en │ │ ├── common.json │ │ └── about.json │ └── fr │ ├── common.json │ └── about.json ├── app │ ├── i18n │ │ └── server.ts │ └── [locale] │ ├── layout.tsx │ └── about.tsx └── components ├── I18nProvider.tsx ├── ClientComponent.tsx └── ServerComponent.tsxCheckliste (mittel/erfahren):
- Behalten Sie eine JSON-Datei pro Namespace und Locale bei
- Zentralisieren Sie Nachrichten nicht zu stark; verwenden Sie kleine, seiten-/feature-spezifische Namespaces
- Vermeiden Sie es, alle Locales auf einmal zu importieren; laden Sie nur, was Sie benötigen
2) Abhängigkeiten installieren
Kopieren Sie den Code in die Zwischenablage
pnpm add i18next react-i18next i18next-resources-to-backendWenn Sie vorhaben, next-i18next APIs oder Konfigurations-Interop zu verwenden, dann auch:
Kopieren Sie den Code in die Zwischenablage
pnpm add next-i18next3) Kern-i18n-Konfiguration
Definieren Sie Locales, Standard-Locale, RTL und Hilfsfunktionen für lokalisierte Pfade/URLs.
Kopieren Sie den Code in die Zwischenablage
export const locales = ["en", "fr"] as const;export type Locale = (typeof locales)[number];export const defaultLocale: Locale = "en";export const rtlLocales = ["ar", "he", "fa", "ur"] as const;export const isRtl = (locale: string) => (rtlLocales as readonly string[]).includes(locale);export function localizedPath(locale: string, path: string) { return locale === defaultLocale ? path : "/" + locale + path;}const ORIGIN = "https://example.com";export function abs(locale: string, path: string) { return ORIGIN + localizedPath(locale, path);}Senior-Hinweis: Wenn Sie next-i18next.config.js verwenden, halten Sie es synchron mit i18n.config.ts, um Abweichungen zu vermeiden.
4) Serverseitige i18n-Initialisierung
Initialisieren Sie i18next auf dem Server mit einem dynamischen Backend, das nur die benötigten Locale-/Namespace-JSON-Dateien importiert.
Kopieren Sie den Code in die Zwischenablage
import { createInstance } from "i18next";import { initReactI18next } from "react-i18next/initReactI18next";import resourcesToBackend from "i18next-resources-to-backend";import { defaultLocale } from "@/i18n.config";// Lade JSON-Ressourcen aus src/locales/<locale>/<namespace>.jsonconst backend = resourcesToBackend( (locale: string, namespace: string) => import(`../../locales/${locale}/${namespace}.json`));export async function initI18next( locale: string, namespaces: string[] = ["common"]) { const i18n = createInstance(); await i18n .use(initReactI18next) .use(backend) .init({ lng: locale, fallbackLng: defaultLocale, ns: namespaces, defaultNS: "common", interpolation: { escapeValue: false }, react: { useSuspense: false }, }); return i18n;}Zwischenhinweis: Halten Sie die Namespace-Liste pro Seite kurz, um die Payload zu begrenzen. Vermeiden Sie globale „Catch-all“-Bundles.
5) Client-Provider für React-Komponenten
Umhüllen Sie Client-Komponenten mit einem Provider, der die Server-Konfiguration spiegelt und nur die angeforderten Namespaces lädt.
Kopieren Sie den Code in die Zwischenablage
"use client";import * as React from "react";import { I18nextProvider } from "react-i18next";import { createInstance } from "i18next";import { initReactI18next } from "react-i18next/initReactI18next";import resourcesToBackend from "i18next-resources-to-backend";import { defaultLocale } from "@/i18n.config";const backend = resourcesToBackend( (locale: string, namespace: string) => import(`../../locales/${locale}/${namespace}.json`));type Props = { locale: string; namespaces?: string[]; resources?: Record<string, any>; // { ns: Bundle } children: React.ReactNode;};export default function I18nProvider({ locale, namespaces = ["common"], resources, children,}: Props) { const [i18n] = React.useState(() => { const i = createInstance(); i.use(initReactI18next) .use(backend) .init({ lng: locale, fallbackLng: defaultLocale, ns: namespaces, resources: resources ? { [locale]: resources } : undefined, defaultNS: "common", interpolation: { escapeValue: false }, react: { useSuspense: false }, }); return i; }); return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;}Junior-Tipp: Sie müssen nicht alle Nachrichten an den Client übergeben. Beginnen Sie nur mit den Namespaces der jeweiligen Seite.
6) Lokalisierte Layouts und Routen
Legen Sie Sprache und Schreibrichtung fest und generieren Sie Routen pro Locale vor, um statisches Rendering zu bevorzugen.
Kopieren Sie den Code in die Zwischenablage
import type { ReactNode } from "react";import { locales, defaultLocale, isRtl, type Locale } from "@/i18n.config";export const dynamicParams = false;export function generateStaticParams() { return locales.map((locale) => ({ locale }));}export default function LocaleLayout({ children, params,}: { children: ReactNode; params: { locale: string };}) { const locale: Locale = (locales as readonly string[]).includes(params.locale) ? params.locale : defaultLocale; const dir = isRtl(locale) ? "rtl" : "ltr"; return ( <html lang={locale} dir={dir}> <body>{children}</body> </html> );}7) Beispielseite mit Server- und Client-Nutzung
Kopieren Sie den Code in die Zwischenablage
import I18nProvider from "@/components/I18nProvider";import { initI18next } from "@/app/i18n/server";import type { Locale } from "@/i18n.config";import ClientComponent from "@/components/ClientComponent";import ServerComponent from "@/components/ServerComponent";// Erzwinge statisches Rendering für die Seiteexport const dynamic = "force-static";export default async function AboutPage({ params: { locale },}: { params: { locale: Locale };}) { const namespaces = ["common", "about"] as const; const i18n = await initI18next(locale, [...namespaces]); const tAbout = i18n.getFixedT(locale, "about"); return ( <I18nProvider locale={locale} namespaces={[...namespaces]}> <main> <h1>{tAbout("title")}</h1> <ClientComponent /> <ServerComponent t={tAbout} locale={locale} count={0} /> </main> </I18nProvider> );}Übersetzungen (jeweils eine JSON-Datei pro Namespace unter src/locales/...):
Kopieren Sie den Code in die Zwischenablage
{ "title": "Über", "description": "Beschreibung der Über-Seite", "counter": { "label": "Zähler", "increment": "Erhöhen" }}Kopieren Sie den Code in die Zwischenablage
{ "title": "À propos", "description": "Description de la page À propos", "counter": { "label": "Compteur", "increment": "Incrémenter" }}Client-Komponente (lädt nur den benötigten Namespace):
Kopieren Sie den Code in die Zwischenablage
"use client";import React, { useState } from "react";import { useTranslation } from "react-i18next";const ClientComponent = () => { const { t, i18n } = useTranslation("about"); const [count, setCount] = useState(0); const numberFormat = new Intl.NumberFormat(i18n.language); return ( <div> <p>{numberFormat.format(count)}</p> <button aria-label={t("counter.label")} onClick={() => setCount((c) => c + 1)} > {t("counter.increment")} </button> </div> );};export default ClientComponent;Stellen Sie sicher, dass die Seite/der Provider nur die benötigten Namespaces enthält (z. B.
about). Wenn Sie React < 19 verwenden, sollten Sie schwere Formatter wieIntl.NumberFormatmemoizen.
Synchroner Server-Komponent, eingebettet unter einer Client-Grenze:
Kopieren Sie den Code in die Zwischenablage
type ServerComponentProps = { t: (key: string) => string; locale: string; count: number;};const ServerComponent = ({ t, locale, count }: ServerComponentProps) => { const formatted = new Intl.NumberFormat(locale).format(count); return ( <div> <p>{formatted}</p> <button aria-label={t("counter.label")}>{t("counter.increment")}</button> </div> );};export default ServerComponent;8) SEO: Metadaten, Hreflang, Sitemap, Robots
Die Übersetzung von Inhalten ist ein Mittel, um die Reichweite zu verbessern. Implementieren Sie mehrsprachiges SEO gründlich.
Best Practices:
- Setzen Sie
langunddiran der Wurzel - Fügen Sie
alternates.languagesfür jede Locale hinzu (+x-default) - Listen Sie übersetzte URLs in der
sitemap.xmlauf und verwenden Siehreflang - Schließen Sie lokalisierte private Bereiche (z.B.
/fr/admin) inrobots.txtaus
Kopieren Sie den Code in die Zwischenablage
import type { Metadata } from "next";import { locales, defaultLocale, localizedPath } from "@/i18n.config";export async function generateMetadata({ params,}: { params: { locale: string };}): Promise<Metadata> { const { locale } = params; // Importiere das korrekte JSON-Bündel aus src/locales const messages = (await import("@/locales/" + locale + "/about.json")) .default; const languages = Object.fromEntries( locales.map((locale) => [locale, localizedPath(locale, "/about")]) ); return { title: messages.title, description: messages.description, alternates: { canonical: localizedPath(locale, "/about"), languages: { ...languages, "x-default": "/about" }, }, };}export default async function AboutPage() { return <h1>Über</h1>;}Kopieren Sie den Code in die Zwischenablage
import type { MetadataRoute } from "next";import { locales, defaultLocale, abs } from "@/i18n.config";export default function sitemap(): MetadataRoute.Sitemap { const languages = Object.fromEntries( locales.map((locale) => [locale, abs(locale, "/about")]) ); return [ { url: abs(defaultLocale, "/about"), lastModified: new Date(), changeFrequency: "monthly", priority: 0.7, alternates: { languages }, }, ];}Kopieren Sie den Code in die Zwischenablage
import type { MetadataRoute } from "next";import { locales, defaultLocale, localizedPath } from "@/i18n.config";const ORIGIN = "https://example.com";const expandAllLocales = (path: string) => [ localizedPath(defaultLocale, path), ...locales .filter((locale) => locale !== defaultLocale) .map((locale) => localizedPath(locale, path)),];export default function robots(): MetadataRoute.Robots { const disallow = [ ...expandAllLocales("/dashboard"), ...expandAllLocales("/admin"), ]; return { rules: { userAgent: "*", allow: ["/"], disallow }, host: ORIGIN, sitemap: ORIGIN + "/sitemap.xml", };}9) Middleware für Locale-Routing
Erkennt die Locale und leitet bei Fehlen auf eine lokalisierte Route weiter.
Kopieren Sie den Code in die Zwischenablage
import { NextResponse, type NextRequest } from "next/server";import { defaultLocale, locales } from "@/i18n.config";const PUBLIC_FILE = /\.[^/]+$/; // schließt Dateien mit Erweiterungen ausexport function middleware(request: NextRequest) { const { pathname } = request.nextUrl; if ( pathname.startsWith("/_next") || pathname.startsWith("/api") || pathname.startsWith("/static") || PUBLIC_FILE.test(pathname) ) { return; } const hasLocale = locales.some( (locale) => pathname === "/" + locale || pathname.startsWith("/" + locale + "/") ); if (!hasLocale) { const locale = defaultLocale; const url = request.nextUrl.clone(); url.pathname = "/" + locale + (pathname === "/" ? "" : pathname); return NextResponse.redirect(url); }}export const config = { matcher: [ // Alle Pfade abgleichen, außer denen, die mit diesen beginnen, und Dateien mit einer Erweiterung "/((?!api|_next|static|.*\\..*).*)", ],};10) Performance- und DX-Best Practices
- Setze html
langunddir: Erledigt insrc/app/[locale]/layout.tsx. - Teile Nachrichten nach Namespace auf: Halte Bundles klein (
common.json,about.jsonusw.). - Minimiere Client-Payload: Übergebe auf Seiten nur die benötigten Namespaces an den Provider.
- Bevorzuge statische Seiten: Verwende
export const dynamic = 'force-static'undgenerateStaticParamspro Locale. - Synchronisiere Server-Komponenten: Übergebe vorab berechnete Strings/Formatierungen statt asynchroner Aufrufe zur Renderzeit.
- Memoisiere aufwändige Operationen: Besonders im Client-Code für ältere React-Versionen.
- Cache und Header: Bevorzuge statisches oder
revalidateRendering gegenüber dynamischem Rendering, wenn möglich.
11) Testing und CI
- Füge Unit-Tests für Komponenten hinzu, die
tverwenden, um sicherzustellen, dass Schlüssel existieren. - Validieren Sie, dass jeder Namespace in allen Sprachen die gleichen Schlüssel enthält.
- Fehlende Schlüssel während der CI vor der Bereitstellung anzeigen.
Intlayer automatisiert einen Großteil davon (siehe nächsten Abschnitt).
12) Intlayer oben drauf hinzufügen (Automatisierung)
Intlayer hilft Ihnen, JSON-Übersetzungen synchron zu halten, auf fehlende Schlüssel zu testen und bei Bedarf mit KI zu ergänzen.
Installieren Sie die Intlayer-Abhängigkeiten:
Kopieren Sie den Code in die Zwischenablage
npm install intlayer @intlayer/sync-json-plugin --save-devnpx intlayer initKopieren Sie den Code in die Zwischenablage
import { type IntlayerConfig, Locales } from "intlayer";import { locales, defaultLocale } from "@/i18n";import { syncJSON } from "@intlayer/sync-json";export const locales = [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH];const config: IntlayerConfig = { internationalization: { locales, defaultLocale, }, ai: { apiKey: process.env.OPENAI_API_KEY, }, plugins: [ syncJSON({ source: ({ locale }) => `./locales/${locale}.json`, }), ],};export default config;Fügen Sie Paketskripte hinzu:
Kopieren Sie den Code in die Zwischenablage
{ "scripts": { "i18n:fill": "intlayer fill", "i18n:test": "intlayer test" }}Übliche Abläufe:
pnpm i18n:testin CI, um Builds bei fehlenden Schlüsseln fehlschlagen zu lassenpnpm i18n:filllokal, um KI-Übersetzungen für neu hinzugefügte Schlüssel vorzuschlagen
Sie können CLI-Argumente angeben; siehe die Intlayer CLI-Dokumentation.
13) Fehlerbehebung
- Schlüssel nicht gefunden: Stellen Sie sicher, dass die Seite/der Provider die korrekten Namespaces auflistet und die JSON-Datei unter
src/locales/<locale>/<namespace>.jsonexistiert. - Falsche Sprache/kurzer englischer Blitz: Überprüfen Sie die Lokalerkennung in
middleware.tsund den Provider-Parameterlng. - Probleme mit RTL-Layout: Vergewissern Sie sich, dass
dirvonisRtl(locale)abgeleitet wird und dass Ihr CSS[dir="rtl"]berücksichtigt. - SEO-Alternativen fehlen: Bestätigen Sie, dass
alternates.languagesalle Locales undx-defaultenthält. - Bundles zu groß: Teilen Sie Namespaces weiter auf und vermeiden Sie das Importieren ganzer
locales-Bäume auf der Client-Seite.
14) Was kommt als Nächstes
- Fügen Sie weitere Sprachen und Namespaces hinzu, wenn die Funktionen wachsen
- Lokalisieren Sie Fehlerseiten, E-Mails und API-gesteuerte Inhalte
- Erweitern Sie die Intlayer-Workflows, um automatisch PRs für Übersetzungsupdates zu öffnen
Wenn Sie einen Starter bevorzugen, probieren Sie die Vorlage: https://github.com/aymericzip/intlayer-next-i18next-template.