next-intl im Next.js App Router in der Praxis
Internationalisierung in React wird oft unterschätzt, bis die erste Mehrsprachigkeit-Anforderung im Backlog landet. next-intl bietet für Next.js App Router eine vollständige i18n-Lösung: typsichere Übersetzungen, ICU-Pluralisierung, Datums- und Zahlenformatierung nach Locale, Sprachrouting und vollständiger Server-Component-Support ohne Client-Bundle-Overhead.
Inhaltsverzeichnis
- 1. Warum next-intl für React-i18n?
- 2. Setup: Middleware, Routing und Nachrichten-Dateien
- 3. useTranslations: Texte sicher und typsicher abrufen
- 4. ICU Message Format: Pluralisierung und Variablen
- 5. useFormatter: Datum, Zahlen und Währungen nach Locale
- 6. Übersetzungen in Server Components
- 7. Sprachrouting: Link und useRouter mit Locale
- 8. Typsichere Übersetzungsschlüssel mit TypeScript
- 9. next-intl vs. react-i18next im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum next-intl für React-i18n?
React Internationalisierung mit next-intl unterscheidet sich fundamental von älteren i18n-Lösungen wie react-i18next oder i18next. Der entscheidende Unterschied: next-intl wurde explizit für den Next.js App Router und das Server-Component-Modell entwickelt. Das bedeutet, dass Übersetzungen in Server Components ohne jeglichen Client-Bundle-Overhead abgerufen werden können – keine JavaScript-Library wird zum Client gesendet, wenn Texte nur serverseitig gerendert werden. Das ist besonders bei SEO-relevanten Inhalten ein wesentlicher Vorteil.
Ein weiterer Vorteil: next-intl nutzt nativ das ICU Message Format und den ECMA-402-Standard für Datums-, Zahlen- und Währungsformatierung. Das bedeutet, man bekommt Intl.DateTimeFormat und Intl.NumberFormat automatisch konfiguriert für das aktuelle Locale – ohne externe Bibliotheken. Typsichere Übersetzungsschlüssel via TypeScript und automatisches Locale-Routing über Middleware runden das Paket ab. next-intl ist die vollständigste, am besten in Next.js integrierte React Internationalisierung-Lösung, die aktuell verfügbar ist.
2. Setup: Middleware, Routing und Nachrichten-Dateien
Das Setup von next-intl besteht aus drei Teilen: Middleware für Locale-Detection und Routing, einer Routing-Konfiguration und den Nachrichten-Dateien pro Sprache. Die Middleware liest das aktuell aktive Locale aus der URL, dem Accept-Language-Header oder einem Cookie und leitet entsprechend weiter. Die Routing-Konfiguration definiert, welche Locales unterstützt werden und welches das Standardlocale ist. Die Nachrichten-Dateien sind JSON-Dateien, typischerweise unter messages/de.json und messages/en.json.
Die Verzeichnisstruktur im App Router folgt einem festen Muster: Alle lokalisierten Seiten liegen in einem [locale]-Segment, etwa app/[locale]/page.tsx. Die Middleware fängt Anfragen ab, die noch kein Locale-Segment enthalten, und leitet sie entsprechend weiter. Das next.config.js bindet das next-intl-Plugin ein, das die Nachrichten-Dateien automatisch unter der korrekten Route verfügbar macht. Diese drei Teile zusammen bilden das Fundament, auf dem alle anderen next-intl-Features aufbauen.
// middleware.ts — locale detection and routing
import createMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';
export default createMiddleware(routing);
export const config = {
// Match all routes except static files and API routes
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)'],
};
// i18n/routing.ts — central routing configuration
import { defineRouting } from 'next-intl/routing';
export const routing = defineRouting({
locales: ['de', 'en', 'fr'],
defaultLocale: 'de',
// Optional: prefix-based routing strategy
localePrefix: 'as-needed', // 'de' as root, 'en' as /en/...
});
// i18n/request.ts — provide messages to server components
import { getRequestConfig } from 'next-intl/server';
import { routing } from './routing';
export default getRequestConfig(async ({ requestLocale }) => {
const locale = await requestLocale;
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default,
};
});
3. useTranslations: Texte sicher und typsicher abrufen
Der Hook useTranslations ist die Hauptschnittstelle für Übersetzungen in Client Components. Er empfängt einen optionalen Namespace-Parameter, der einen verschachtelten Bereich in der JSON-Nachrichten-Datei anspricht. Das Ergebnis ist eine Funktion t, die mit einem Schlüssel aufgerufen wird und den übersetzten Text zurückgibt. Mit TypeScript und dem next-intl-TypeScript-Plugin werden die Schlüssel vollständig autovervollständigt – ein Tippfehler im Schlüssel ist ein TypeScript-Fehler, kein stiller Ausfall zur Laufzeit.
Die t-Funktion unterstützt Rich Text via React-Komponenten: t.rich('key', { strong: (chunks) => <strong>{chunks}</strong> }) rendert Teile einer Übersetzung fett, ohne HTML in die JSON-Datei zu schreiben. Das trennt Styling und Inhalt sauber voneinander und hält die Nachrichten-Dateien für Übersetzer lesbar. Für HTML-Entities und reinen Text gibt es t.markup und t.raw. Der Namespace-Ansatz strukturiert Übersetzungen nach Komponenten oder Seitenbereichen und verhindert, dass eine einzige große JSON-Datei unhandhabbar wird.
4. ICU Message Format: Pluralisierung und Variablen
Das ICU Message Format ist der Industriestandard für mehrsprachige Texte mit dynamischen Werten und Pluralisierung. next-intl unterstützt es vollständig. Eine einfache Variable wird mit geschweiften Klammern eingebettet: "greeting": "Hallo, {name}!" und mit t('greeting', { name: 'Max' }) aufgerufen. Pluralisierung folgt der ICU-Syntax: "items": "{count, plural, =0 {Keine Artikel} one {Ein Artikel} other {# Artikel}}". Das Schlüsselwort # wird dabei durch den tatsächlichen Zähler ersetzt.
Fortgeschrittene ICU-Features umfassen select für enumarierte Werte – etwa um geschlechterspezifische Texte zu generieren – und verschachtelte plural-Blöcke für komplexe grammatikalische Strukturen. Das ist besonders relevant für Sprachen wie Russisch oder Polnisch, die mehrere Pluralformen haben. next-intl nutzt intern den ECMA-402-Pluralisierer, der die korrekten Pluralregeln für alle Sprachen kennt. Entwickler müssen lediglich die ICU-Syntax in den JSON-Dateien korrekt angeben – die richtige Regel wird automatisch angewendet.
// messages/de.json — ICU message format examples
// {
// "Cart": {
// "title": "Warenkorb",
// "itemCount": "{count, plural, =0 {Leer} one {# Artikel} other {# Artikel}}",
// "welcome": "Hallo, {name}! Du hast {count, plural, one {# neues} other {# neue}} Nachrichten.",
// "discount": "Du sparst {amount, number, ::currency/EUR}",
// "lastSeen": "Zuletzt gesehen: {date, date, long}"
// }
// }
'use client';
import { useTranslations, useFormatter } from 'next-intl';
function Cart({ itemCount, userName }: { itemCount: number; userName: string }) {
const t = useTranslations('Cart');
const format = useFormatter();
return (
<div>
<h1>{t('title')}</h1>
{/* Pluralization handled automatically via ICU */}
<p>{t('itemCount', { count: itemCount })}</p>
{/* Variable interpolation with plural */}
<p>{t('welcome', { name: userName, count: 3 })}</p>
{/* Rich text: render parts with React components */}
<p>
{t.rich('discount', {
amount: 12.5,
// Wrap amount in strong — styling stays in component
number: (chunks) => <strong className="text-green-600">{chunks}</strong>,
})}
</p>
{/* Date formatting using the active locale automatically */}
<p>{format.dateTime(new Date(), { dateStyle: 'long' })}</p>
</div>
);
}
5. useFormatter: Datum, Zahlen und Währungen nach Locale
useFormatter gibt Zugriff auf locale-aware Formatierungsfunktionen, ohne manuell Intl.DateTimeFormat oder Intl.NumberFormat konfigurieren zu müssen. Das aktuelle Locale wird automatisch aus dem next-intl-Kontext genommen. format.dateTime formatiert Datum und Uhrzeit entsprechend den regionalen Konventionen: In Deutschland ist das DD.MM.YYYY, in den USA MM/DD/YYYY. format.number formatiert Zahlen mit dem korrekten Dezimaltrennzeichen und Tausendertrennzeichen. format.relativeTime gibt relative Zeitangaben wie „vor 3 Minuten" oder „in 2 Stunden" in der Sprache des aktuellen Locales aus.
Für Währungen bietet format.number den style: 'currency'-Modus: Die Zahl wird nach ECMA-402 formatiert, das Währungssymbol korrekt platziert und das Trennzeichen locale-konform gesetzt. Das ist besonders wichtig in E-Commerce-Anwendungen, wo Preise in verschiedenen Märkten korrekt dargestellt werden müssen: 1.234,56 € in Deutschland, £1,234.56 in Großbritannien, $1,234.56 in den USA. next-intl stellt sicher, dass dieselbe Zahl in jedem Locale korrekt formatiert wird – ohne manuelle Locale-Weitergabe an jede Formatierungsfunktion.
6. Übersetzungen in Server Components
Der entscheidende Vorteil von next-intl gegenüber react-i18next im Next.js App Router: Übersetzungen können direkt in Server Components abgerufen werden, ohne Client-Side-Code. Statt useTranslations wird die asynchrone getTranslations-Funktion aus next-intl/server verwendet. Diese Funktion liest das aktuelle Locale aus dem Request-Kontext und gibt die Übersetzungsfunktion zurück – synchron wartend im Server-Component-Kontext. Das bedeutet: Der gesamte i18n-Code bleibt auf dem Server, keine i18n-Library im Client-Bundle.
Für Metadaten und SEO bietet next-intl getTranslations auch in der generateMetadata-Funktion von Next.js. Seitentitel, Beschreibungen und Open-Graph-Tags können so direkt übersetzt werden, ohne dass der Client dafür JavaScript laden muss. Für statisch generierte Seiten (generateStaticParams) gibt next-intl alle konfigurierten Locales zurück, sodass jede Seite für jede Sprache statisch gebaut werden kann – perfekt für Core-Web-Vitals und Time-to-First-Byte.
// app/[locale]/products/page.tsx — Server Component with translations
import { getTranslations } from 'next-intl/server';
import { setRequestLocale } from 'next-intl/server';
import type { Metadata } from 'next';
// Generate static params for all locales (static site generation)
export function generateStaticParams() {
return [{ locale: 'de' }, { locale: 'en' }, { locale: 'fr' }];
}
// Translated metadata — no client JS needed
export async function generateMetadata({ params }: { params: { locale: string } }): Promise<Metadata> {
const t = await getTranslations({ locale: params.locale, namespace: 'ProductsPage' });
return {
title: t('metaTitle'),
description: t('metaDescription'),
};
}
// Server Component — translations on the server, zero client bundle
export default async function ProductsPage({ params }: { params: { locale: string } }) {
setRequestLocale(params.locale); // enable static rendering
const t = await getTranslations('ProductsPage');
return (
<main>
<h1>{t('heading')}</h1>
<p>{t('intro')}</p>
{/* Client component receives only the data it needs — not translation strings */}
<ProductList />
</main>
);
}
7. Sprachrouting: Link und useRouter mit Locale
next-intl exportiert eigene Link- und useRouter-Versionen, die das aktuelle Locale automatisch in URLs einfügen. Statt Next.js-eigene Link und useRouter zu verwenden, importiert man sie aus next-intl – oder genauer aus der generierten Navigationsmodule, die von createNavigation erzeugt werden. Damit landen alle internen Links korrekt unter /en/products oder /de/produkte, ohne dass das Locale explizit übergeben werden muss.
Für den Sprachwechsel gibt es keinen integrierten Language-Switcher-Komponent – das ist bewusst, weil das UI-Design des Sprachwechslers applikationsspezifisch ist. Die Implementierung ist einfach: Mit dem next-intl useRouter kann via router.replace(pathname, { locale: 'en' }) die aktuelle Seite in einer anderen Sprache aufgerufen werden. Das Locale wird in URL oder Cookie gespeichert, die Middleware erkennt es beim nächsten Request und rendert die Seite entsprechend. Alternativer Ansatz: via Link und dem locale-Prop direkt auf die lokalisierte URL verlinken.
8. Typsichere Übersetzungsschlüssel mit TypeScript
next-intl bietet mit dem TypeScript-Plugin vollständige Typsicherheit für Übersetzungsschlüssel. Nach einer einmaligen Konfiguration in global.d.ts werden alle Schlüssel, die an t() übergeben werden, gegen die Nachrichten-Dateien geprüft. Tippfehler in Schlüsseln, fehlende Übersetzungen für einzelne Sprachen und falsche Parameter-Typen werden zur Compile-Zeit erkannt. Das ist eine erhebliche Verbesserung gegenüber dem üblichen Muster, bei dem fehlende Übersetzungen erst zur Laufzeit durch einen leeren String auffallen.
Die Typsicherheit erstreckt sich auch auf Parameter: Wenn ein Schlüssel laut JSON-Datei einen name-Parameter erwartet, führt das Weglassen dieses Parameters zu einem TypeScript-Fehler. Wenn ein Schlüssel eine Zahl für Pluralisierung erwartet, ist ein String als Parameter ein Fehler. Diese Genauigkeit ist besonders wertvoll in größeren Teams, wo Übersetzungsschlüssel von verschiedenen Entwicklern genutzt werden. Die JSON-Nachrichten-Datei wird effektiv zur Typdefinition, die das gesamte Entwicklungserlebnis verbessert.
9. next-intl vs. react-i18next im Vergleich
react-i18next ist die ältere, ausgereiftere Lösung mit einem riesigen Ökosystem. next-intl ist jünger, aber explizit für den Next.js App Router entwickelt und bietet in diesem Kontext entscheidende Vorteile. Der direkte Vergleich zeigt, wo die Unterschiede liegen.
| Merkmal | react-i18next | next-intl | Gewinner |
|---|---|---|---|
| Server Components | Eingeschränkt, Workarounds nötig | Vollständiger Support (getTranslations) | next-intl |
| TypeScript | Typen via i18next.d.ts | Plugin, vollständig integriert | next-intl |
| Ökosystem | Sehr groß, viele Plugins | Kleiner, Next.js-fokussiert | react-i18next |
| Routing-Integration | Manuell / next-i18next nötig | Eingebaut, Middleware enthalten | next-intl |
| ICU-Format | Plugin benötigt | Native Unterstützung | next-intl |
Für neue Next.js-App-Router-Projekte ist next-intl die empfohlene Wahl. Für bestehende Projekte, die bereits mit react-i18next arbeiten und keine Notwendigkeit für Server-Component-Übersetzungen haben, lohnt sich die Migration nur, wenn die genannten Vorteile konkret benötigt werden. react-i18next bleibt die bessere Wahl für framework-agnostische React-Anwendungen außerhalb von Next.js.
Mironsoft
React Internationalisierung, next-intl-Setup und mehrsprachige Next.js-Apps
Mehrsprachige React-App mit next-intl aufbauen?
Wir implementieren vollständige i18n-Infrastruktur mit next-intl für Next.js App Router: Locale-Routing, typsichere Übersetzungen, Server-Component-Support, Pluralisierung und Formatierung für alle Zielmärkte.
i18n-Setup
Middleware, Routing-Konfiguration und Nachrichten-Dateistruktur einrichten
Migration
Bestehende react-i18next-Projekte auf next-intl migrieren
TypeScript-Integration
Typsichere Schlüssel, automatische Typ-Inferenz und CI-Prüfungen
10. Zusammenfassung
next-intl ist die vollständigste React Internationalisierung-Lösung für Next.js App Router. Das Setup über Middleware und Routing-Konfiguration ist klar strukturiert. useTranslations für Client Components und getTranslations für Server Components bieten konsistente APIs in beiden Rendering-Kontexten. ICU Message Format löst Pluralisierung und dynamische Texte ohne proprietäre Syntax. useFormatter formatiert Datum, Zahlen und Währungen locale-korrekt ohne manuelle Konfiguration. Typsichere Schlüssel via TypeScript-Plugin fangen Fehler zur Compile-Zeit ab.
Die wichtigsten Praxisregeln: Übersetzungen nach Komponenten in Namespaces aufteilen, um große monolithische JSON-Dateien zu vermeiden. getTranslations in Server Components verwenden, um i18n-Code aus dem Client-Bundle herauszuhalten. ICU-Format für alle dynamischen Texte mit Variablen und Pluralisierung nutzen. setRequestLocale in Server Components aufrufen, um statisches Rendering zu ermöglichen. Die generierten Navigationsmodule von createNavigation statt der Next.js-eigenen Link und useRouter verwenden.
next-intl React-Internationalisierung — Das Wichtigste auf einen Blick
Setup
Middleware für Locale-Detection, Routing-Konfiguration mit defineRouting, Nachrichten-Dateien pro Sprache unter messages/[locale].json.
Server vs. Client
useTranslations für Client Components, getTranslations (async) für Server Components. Server-i18n erzeugt keinen Client-Bundle-Overhead.
ICU & Formatierung
ICU Message Format für Pluralisierung und Variablen. useFormatter für Datum, Zahlen und Währungen nach aktivem Locale — kein manuelles Intl.
TypeScript
Plugin aktivieren für typsichere Schlüssel und Parameter. Fehlende Übersetzungen und Tippfehler werden zur Compile-Zeit erkannt.