Schnelle Antwort
Wenn deine Next.js Übersetzungen nicht funktionieren, prüfe der Reihe nach: (1) Übersetzungsschlüssel existiert in der JSON-Datei mit korrekter Verschachtelung, (2) JSON-Datei ist valide (keine Trailing Commas), (3) Locale wird korrekt in der Middleware erkannt, (4) Provider/Context umschließt deine Komponenten, (5) Richtiger Hook verwendet (Server vs. Client Component). 90% der Probleme sind Tippfehler in Keys oder fehlende JSON-Einträge.
Ich habe hunderte i18n-Setups in React- und Next.js-Projekten debuggt. Die gute Nachricht? Übersetzungsfehler fallen fast immer in eine von etwa 10 vorhersehbaren Kategorien. Die schlechte Nachricht? Sie können unglaublich frustrierend zu diagnostizieren sein, wenn man nicht weiß, wonach man suchen muss.
Dieser Guide behandelt jedes Übersetzungsproblem, das mir in Produktion begegnet ist, sortiert von häufig nach selten. Der Fokus liegt auf Next.js 14/15 mit App Router, aber das meiste gilt auch für den Pages Router.
Problem #1: Übersetzungsschlüssel wird statt Übersetzung angezeigt
Symptom: Du siehst common.buttons.submit auf dem Bildschirm statt "Absenden"
Das ist das häufigste Problem, und es bedeutet meistens eines der folgenden:
Der Key existiert nicht in deiner JSON
JSON1// ❌ Dein Code verwendet: t('common.buttons.submit') 2// Aber deine en.json enthält: 3{ 4 "common": { 5 "button": { // Beachte: "button" nicht "buttons" 6 "submit": "Submit" 7 } 8 } 9}
Lösung: Prüfe deine JSON-Struktur sorgfältig. Nutze eine IDE mit JSON-Pfad-Vorschau oder ein Tool wie IntlPull, das Keys validiert.
Du verwendest den falschen Namespace
TSX1// ❌ Falsch: sucht im Standard-Namespace 2const t = useTranslations(); 3t('checkout.title'); 4 5// ✅ Richtig: Namespace angeben 6const t = useTranslations('checkout'); 7t('title');
Die JSON-Datei wird nicht geladen
Prüfe deine i18n-Konfiguration:
TypeScript1// i18n/request.ts für next-intl 2import { getRequestConfig } from 'next-intl/server'; 3 4export default getRequestConfig(async ({ locale }) => ({ 5 messages: (await import(`../messages/${locale}.json`)).default 6}));
Stelle sicher, dass der Dateipfad korrekt ist. Ein häufiger Fehler ist, Messages in /public/locales abzulegen, während die Konfiguration /messages erwartet.
Problem #2: Ungültige JSON-Syntax
Symptom: App stürzt ab mit "Unexpected token" oder Übersetzungen scheitern still
JSON ist strikt. Folgendes macht alles kaputt:
JSON1// ❌ Trailing Comma (am häufigsten) 2{ 3 "welcome": "Hello", 4 "goodbye": "Bye", // <-- Dieses Komma macht alles kaputt 5} 6 7// ❌ Einfache Anführungszeichen 8{ 9 'welcome': 'Hello' // Muss doppelte Anführungszeichen sein 10} 11 12// ❌ Nicht-escapte Anführungszeichen in Werten 13{ 14 "message": "Klicke "hier" um fortzufahren" // Muss escaped werden 15} 16 17// ✅ Korrekt 18{ 19 "welcome": "Hello", 20 "goodbye": "Bye", 21 "message": "Klicke \"hier\" um fortzufahren" 22}
Lösung: Nutze einen JSON-Validator. VS Code hebt Syntaxfehler hervor. Führe cat en.json | python -m json.tool aus, um per CLI zu validieren.
Problem #3: Übersetzung fehlt für bestimmte Locale
Symptom: Englisch funktioniert, aber Spanisch/Deutsch/usw. zeigt Keys
Du hast den Key zu en.json hinzugefügt, aber die anderen Locales vergessen:
/messages
en.json ✅ Hat "checkout.newFeature": "Try our new feature"
es.json ❌ Dieser Key fehlt komplett
de.json ❌ Dieser Key fehlt komplett
Lösung: Nutze ein Übersetzungsmanagement-Tool wie IntlPull, das fehlende Übersetzungen über alle Locales hinweg trackt. Oder richte einen Fallback ein:
TypeScript1// next-intl Konfiguration mit Fallback 2export default getRequestConfig(async ({ locale }) => ({ 3 messages: { 4 ...(await import(`../messages/en.json`)).default, // Fallback 5 ...(await import(`../messages/${locale}.json`)).default 6 } 7}));
Problem #4: Middleware erkennt Locale nicht
Symptom: Zeigt immer Standardsprache an, URL-Locale wird ignoriert
TypeScript1// ❌ Middleware läuft nicht auf deinen Routes 2export const config = { 3 matcher: ['/api/:path*'] // Matcht nur API-Routes! 4}; 5 6// ✅ Korrekter Matcher für i18n 7export const config = { 8 matcher: ['/((?!api|_next|.*\\..*).*)'] 9};
Stelle außerdem sicher, dass deine Middleware-Datei am richtigen Ort liegt – sie muss als middleware.ts im Projekt-Root sein, nicht in /app oder /src/app.
Locale-Erkennungslogik prüfen
TypeScript1// middleware.ts 2import createMiddleware from 'next-intl/middleware'; 3 4export default createMiddleware({ 5 locales: ['en', 'es', 'de', 'fr'], 6 defaultLocale: 'en', 7 localePrefix: 'always' // oder 'as-needed' 8});
Debug-Tipp: Füge console.log zur Middleware hinzu, um zu sehen, welche Locale erkannt wird:
TypeScript1export default function middleware(request: NextRequest) { 2 console.log('Erkannte Locale:', request.nextUrl.pathname); 3 // ... Rest der Middleware 4}
Problem #5: Hydration Mismatch Fehler
Symptom: Konsole zeigt "Text content does not match server-rendered HTML"
Das passiert, wenn Server und Client unterschiedliche Übersetzungen rendern:
Ursache 1: Client Hook in Server Component verwenden
TSX1// ❌ Server Component verwendet Client Hook 2// app/[locale]/page.tsx (Server Component standardmäßig) 3import { useTranslations } from 'next-intl'; // Das ist hier tatsächlich okay 4 5export default function Page() { 6 const t = useTranslations('home'); 7 return <h1>{t('title')}</h1>; // Funktioniert in next-intl! 8}
Moment, das funktioniert tatsächlich in next-intl, weil es den Context erkennt. Aber mit react-i18next:
TSX1// ❌ Mit react-i18next in Server Component 2'use server'; 3import { useTranslation } from 'react-i18next'; // Funktioniert nicht 4 5// ✅ Serverseitige Funktion verwenden 6import { getTranslations } from 'next-intl/server'; 7 8export default async function Page() { 9 const t = await getTranslations('home'); 10 return <h1>{t('title')}</h1>; 11}
Ursache 2: Datums-/Zeitformatierung ohne Zeitzone
TSX1// ❌ Server könnte UTC sein, Client ist lokale Zeitzone 2{formatDate(new Date())} 3 4// ✅ Zeitzone immer explizit angeben 5import { format } from 'date-fns-tz'; 6{format(new Date(), 'PPP', { timeZone: userTimezone })}
Problem #6: Provider umschließt Komponenten nicht
Symptom: "Could not find IntlProvider" oder ähnliche Context-Fehler
TSX1// ❌ Provider fehlt im Layout 2// app/[locale]/layout.tsx 3export default function Layout({ children }) { 4 return <html><body>{children}</body></html>; 5} 6 7// ✅ Mit Provider 8import { NextIntlClientProvider } from 'next-intl'; 9import { getMessages } from 'next-intl/server'; 10 11export default async function Layout({ children, params: { locale } }) { 12 const messages = await getMessages(); 13 return ( 14 <html lang={locale}> 15 <body> 16 <NextIntlClientProvider messages={messages}> 17 {children} 18 </NextIntlClientProvider> 19 </body> 20 </html> 21 ); 22}
Problem #7: Dynamische Keys funktionieren nicht
Symptom: t(status.${status}) gibt den Key zurück
TSX1// ❌ Dynamisch konstruierte Keys 2const key = `status.${order.status}`; 3t(key); // Manche Bundler können das nicht optimieren 4 5// ✅ Explizites Mapping verwenden 6const statusMessages = { 7 pending: t('status.pending'), 8 shipped: t('status.shipped'), 9 delivered: t('status.delivered') 10}; 11return statusMessages[order.status]; 12 13// ✅ Oder t.raw() für dynamische Keys in next-intl 14t(`status.${order.status}`); // Das funktioniert tatsächlich in next-intl
Problem #8: Pluralisierung funktioniert nicht
Symptom: Zeigt "{count, plural, one {# item} other {# items}}" wörtlich an
Du verwendest ICU-Format, aber die Library parst es nicht:
JSON1// Deine JSON 2{ 3 "items": "{count, plural, one {# Artikel} other {# Artikel}}" 4}
TSX1// ❌ Variable nicht übergeben 2t('items'); 3 4// ✅ Count-Variable übergeben 5t('items', { count: 5 }); // "5 Artikel"
Prüfe, ob deine Library ICU unterstützt
- next-intl: Volle ICU-Unterstützung ✅
- react-i18next: Benötigt
i18next-icuPlugin - next-translate: Nur einfache Pluralisierung
Problem #9: Umgebungs-/Build-Probleme
Keys funktionieren im Dev, brechen in Produktion
TypeScript1// ❌ Dynamische Imports könnten zur Build-Zeit fehlschlagen 2const messages = await import(`@/messages/${locale}.json`); 3 4// ✅ Sicherstellen, dass alle Locales statisch bekannt sind 5import en from '@/messages/en.json'; 6import es from '@/messages/es.json'; 7 8const messages = { en, es }; 9export const getMessages = (locale: string) => messages[locale];
"Module not found" nach Hinzufügen neuer Locale
Nach dem Hinzufügen einer neuen Locale-Datei den Dev-Server neu starten. Next.js cached die Modul-Auflösung.
Terminalrm -rf .next && npm run dev
Problem #10: Link/Navigation verliert Locale
Symptom: Klick auf interne Links setzt auf Standardsprache zurück
TSX1// ❌ Normaler Link verliert Locale 2import Link from 'next/link'; 3<Link href="/about">Über uns</Link> 4 5// ✅ Navigation von next-intl verwenden 6import { Link } from '@/i18n/navigation'; // Deine konfigurierte Navigation 7<Link href="/about">Über uns</Link> // Behält Locale automatisch bei 8 9// ✅ Oder Locale manuell einbinden 10import { useLocale } from 'next-intl'; 11const locale = useLocale(); 12<Link href={`/${locale}/about`}>Über uns</Link>
Debugging-Checkliste
Wenn Übersetzungen nicht funktionieren, geh diese Checkliste durch:
| Prüfung | Befehl/Aktion |
|---|---|
| JSON-Syntax valide | `cat messages/en.json |
| Key existiert | Exakten Key in JSON-Datei suchen |
| Locale-Datei existiert | ls messages/ |
| Middleware läuft | console.log hinzufügen, Server-Output prüfen |
| Provider vorhanden | layout.tsx auf IntlProvider prüfen |
| Korrekter Import | Server: getTranslations, Client: useTranslations |
| Cache geleert | rm -rf .next && npm run dev |
Häufige Fehlermeldungen erklärt
| Fehler | Bedeutung | Lösung |
|---|---|---|
Missing message: "key" | Key nicht in JSON | Key zu allen Locale-Dateien hinzufügen |
Unable to find next-intl locale | Middleware setzt Locale nicht | middleware.ts Platzierung und Konfiguration prüfen |
Hydration failed | Server/Client Mismatch | Korrekten Hook für Component-Typ verwenden |
Cannot read property 't' of undefined | Provider fehlt | Mit IntlClientProvider umschließen |
ENOENT: no such file | Dateipfad falsch | Messages-Ordner-Pfad in Konfiguration prüfen |
Prävention: Diese Probleme komplett vermeiden
1. TypeScript für typsichere Keys verwenden
TypeScript1// Types aus deiner JSON generieren 2// Mit next-intl eine Types-Datei erstellen: 3type Messages = typeof import('./messages/en.json'); 4declare global { 5 interface IntlMessages extends Messages {} 6}
Jetzt meldet TypeScript einen Fehler bei ungültigen Keys.
2. Ein Übersetzungsmanagement-System nutzen
Tools wie IntlPull machen automatisch:
- JSON-Syntax validieren
- Fehlende Übersetzungen pro Locale tracken
- Key-Tippfehler durch Autocomplete verhindern
- Keys über alle Locales synchronisieren
3. CI-Checks hinzufügen
YAML1# .github/workflows/i18n-check.yml 2- name: JSON-Dateien validieren 3 run: | 4 for f in messages/*.json; do 5 python -m json.tool "$f" > /dev/null || exit 1 6 done 7 8- name: Auf fehlende Keys prüfen 9 run: npx intlpull check --config intlpull.config.json
4. Integrationstests für kritische Pfade
TypeScript1// e2e/i18n.spec.ts 2test('Checkout-Seite rendert in allen Locales', async ({ page }) => { 3 for (const locale of ['en', 'es', 'de']) { 4 await page.goto(`/${locale}/checkout`); 5 // Sollte keine rohen Übersetzungskeys enthalten 6 await expect(page.locator('body')).not.toContainText('checkout.'); 7 } 8});
Wann man Übersetzungsmanagement nutzen sollte
Wenn du diese Probleme wiederholt erlebst, denk über ein TMS nach:
| Szenario | DIY JSON-Dateien | Übersetzungsmanagement |
|---|---|---|
| < 50 Keys, 1-2 Entwickler | ✅ Funktioniert | Overkill |
| > 200 Keys, mehrere Entwickler | ❌ Merge-Konflikte | ✅ Single Source of Truth |
| > 3 Sprachen | ❌ Schwer zu synchronisieren | ✅ Erkennung fehlender Keys |
| Externe Übersetzer | ❌ JSON-Übergabe-Albträume | ✅ Eingebauter Workflow |
IntlPull (kleine Eigenwerbung) ist genau dafür gebaut. Es integriert sich in deinen Git-Workflow, erkennt fehlende Übersetzungen in CI, und unterstützt OTA-Updates, damit du für Übersetzungsfixes nicht neu deployen musst.
Häufig gestellte Fragen
Warum gibt meine t()-Funktion den Key statt der Übersetzung zurück?
Dein Übersetzungsschlüssel existiert nicht in der JSON-Datei oder im Namespace. Prüfe auf Tippfehler im Key, stelle sicher, dass die JSON-Struktur zum erwarteten Pfad im Code passt, prüfe ob du den richtigen Namespace verwendest, und validiere, dass die JSON-Datei geladen wird. Das ist das häufigste i18n-Problem.
Warum funktionieren meine Übersetzungen auf Englisch, aber nicht in anderen Sprachen?
Der Übersetzungsschlüssel fehlt in anderen Locale-Dateien. Wenn du einen neuen Key zu en.json hinzufügst, musst du ihn auch zu es.json, de.json usw. hinzufügen. Nutze ein TMS wie IntlPull um fehlende Übersetzungen automatisch zu erkennen, oder richte Fallback-Locales in deiner Konfiguration ein.
Wie behebe ich Next.js i18n Hydration Mismatch Fehler?
Server und Client rendern unterschiedlichen Content. Das passiert meistens bei Datums-/Zeitformatierung (Server ist UTC, Client lokal), Verwendung von Client Hooks in Server Components, oder Locale-Mismatch zwischen Server und Client. Gib Zeitzonen explizit an, verwende korrekte Hooks für den Component-Typ, und stelle sicher, dass die Locale korrekt übergeben wird.
Warum erkennt meine Next.js Middleware die Locale nicht?
Dein Middleware Matcher-Pattern matcht deine Routes nicht. Stelle sicher, dass middleware.ts im Projekt-Root liegt (nicht in /app), dein Matcher die benötigten Routes einschließt, und das locales-Array deinen unterstützten Sprachen entspricht. Füge console.log zur Middleware hinzu, um zu debuggen, was erkannt wird.
Wie debugge ich "Could not find IntlProvider" Fehler?
Deine Komponente ist nicht vom i18n-Provider umschlossen. Prüfe, dass NextIntlClientProvider (oder der Provider deiner Library) in deiner app/[locale]/layout.tsx ist und alle Kind-Komponenten umschließt. Der Provider muss Messages und die aktuelle Locale erhalten.
Warum verursacht meine JSON-Datei "Unexpected token" Fehler?
Deine JSON-Syntax ist ungültig. Häufige Probleme: Trailing Commas, einfache statt doppelte Anführungszeichen, nicht-escapte Anführungszeichen in Werten, oder fehlende Kommas. Lass deine JSON durch einen Validator wie python -m json.tool laufen, um die genaue Fehlerstelle zu finden.
Wie bringe ich Übersetzungen in Next.js Server Components zum Laufen?
Verwende serverseitige Übersetzungsfunktionen. In next-intl nutze getTranslations() aus next-intl/server in Server Components. Der normale useTranslations() Hook funktioniert in next-intl durch Context-Erkennung, aber für react-i18next brauchst du explizite serverseitige Funktionen.
Warum brechen meine Übersetzungen in Produktion, funktionieren aber in Development?
Dynamische Imports können zur Build-Zeit fehlschlagen. Stelle sicher, dass alle Locale-Dateien vor dem Build existieren, verwende statische Imports oder verifizierte dynamische Imports, und leere den .next Cache vor dem Bauen. Produktions-Builds optimieren Imports anders als der Dev-Modus.
Wie verhindere ich, dass die Locale beim Navigieren verloren geht?
Verwende die Link-Komponente deiner i18n-Library statt Next.js Link. In next-intl konfiguriere Navigation-Exports in i18n/navigation.ts und importiere Link von dort. Alternativ füge die Locale manuell in href ein: /${locale}/about.
Wie teste ich, ob alle Übersetzungen existieren?
Füge CI-Checks hinzu, die JSON validieren und fehlende Keys erkennen. Validiere JSON-Syntax mit einem Linter, vergleiche Keys über Locale-Dateien hinweg, und führe Integrationstests aus, die verifizieren, dass Seiten keine rohen Übersetzungskeys anzeigen. IntlPulls CLI bietet automatische Erkennung fehlender Übersetzungen.
Zusammenfassung
Die meisten Next.js i18n-Probleme fallen in vorhersehbare Kategorien:
- Key-Tippfehler → TypeScript-Types und Autocomplete nutzen
- Ungültige JSON → In CI validieren
- Fehlende Locale-Daten → TMS mit Sync-Prüfung nutzen
- Falscher Hook/Context → Server vs. Client Component Regeln befolgen
- Middleware-Probleme → Datei-Platzierung und Matcher-Konfiguration prüfen
Das Ökosystem ist deutlich besser geworden. Mit App Router und modernen Libraries wie next-intl ist i18n wesentlich zuverlässiger als mit dem Pages Router. Aber es ist immer noch Code, und Code hat Bugs.
Programmiere defensiv: typsichere Keys, CI-Validierung und gutes Tooling sparen dir Stunden beim Debugging.
Brauchst du Hilfe beim Verwalten von Übersetzungen im großen Maßstab? Starte kostenlos mit IntlPull — automatische Erkennung fehlender Übersetzungen, KI-Übersetzung und nahtlose next-intl-Integration.
