Inhaltsverzeichnis
React i18n: Der vollständige Leitfaden zur Internationalisierung in React-Apps
Internationalisierung (i18n) ist eine jener Funktionen, die von außen einfach wirkt und sich beim Implementieren schnell in ein wochenlangen Kaninchenloch verwandelt. Eine React-App, die Nutzer auf Englisch begrüßt, braucht mehr als reine Zeichenkettenersetzung, um ein globales Publikum wirklich zu bedienen. Sie benötigt Währungsformatierung, Pluralregeln, Datumslokalisierung, Unterstützung für Rechts-nach-links-Layouts und einen Übersetzungsworkflow, der nicht bei jeder Umbenennung eines Schlüssels durch einen Entwickler zusammenbricht.
Dieser Leitfaden führt durch alles, was Sie wissen müssen, um 2026 eine produktionsreife mehrsprachige React-App zu veröffentlichen – von der Wahl der richtigen Bibliothek bis hin zur Vermeidung von Fehlern, die Sie später Zeit kosten.
TL;DR / Die wichtigsten Erkenntnisse
- React hat kein eingebautes i18n. Die
Intl-API des Browsers übernimmt die Formatierung, aber Sie benötigen eine dedizierte Bibliothek, um Übersetzungsstrings, dynamisches Laden und Locale-Routing zu verwalten. - react-intl (FormatJS) und react-i18next sind die am weitesten verbreiteten Optionen mit großen Communities und Jahren der Produktionserfahrung.
- LinguiJS ist die beste Wahl, wenn Sie Compile-Time-Extraktion und minimale Laufzeit-Bundle-Größe wünschen.
- Better i18n ist der neueste Teilnehmer – er fügt KI-gestützte Übersetzungen und CDN-Auslieferung hinzu, aber seine Community ist kleiner als die etablierten Alternativen.
- Die richtige Bibliothek hängt von Ihrer Teamgröße, den SSR-Anforderungen und davon ab, wie viel Ihnen die Automatisierung des Übersetzungsworkflows im Vergleich zur rohen Ökosystem-Reife wert ist.
Warum React eine i18n-Bibliothek braucht
React rendert Benutzeroberflächen. Es verwaltet keine Übersetzungen, erkennt keine Browser-Locales, lädt Sprachdateien nicht asynchron und behandelt nicht die komplexen Formatierungsregeln, die sich zwischen Sprachen unterscheiden. Das ist beabsichtigt – React ist eine View-Bibliothek.
Der Browser wird mit der Intl-API ausgeliefert, die wirklich leistungsfähig ist. Intl.DateTimeFormat, Intl.NumberFormat und Intl.PluralRules bieten locale-bewusste Formatierung ohne externe Abhängigkeiten. Aber Intl löst Formatierung, nicht String-Verwaltung.
Was Sie über Intl hinaus noch benötigen:
- Ein String-Katalog – Eine Struktur zum Speichern übersetzter Strings, die nach Locale und Nachrichten-ID geordnet sind.
- Eine React-Bindung – Hooks und Komponenten, die aus dem Katalog lesen und neu rendern, wenn sich die Locale ändert.
- Dynamisches Laden – Nur die Strings der aktuellen Locale laden, nicht alle auf einmal.
- Pluralisierung und Interpolation – Behandlung von
"1 Element"versus"3 Elemente"und Einfügen dynamischer Werte wie Benutzernamen in Nachrichten. - Ein Übersetzungsworkflow – Wie Übersetzer neue Strings erhalten, sie übersetzen und wie diese Übersetzungen zurück in Ihre App gelangen.
Dies alles selbst zu entwickeln ist möglich, aber nicht ratsam. Die unten aufgeführten Bibliotheken haben diese Probleme über Millionen von Produktionseinsätzen gelöst. Beginnen Sie mit einer davon.
Beliebte React-i18n-Bibliotheken im Vergleich
| Bibliothek | Bundle-Größe | ICU-Unterstützung | TypeScript-Unterstützung | SSR-Unterstützung | Lernkurve |
|---|---|---|---|---|---|
| react-intl (FormatJS) | ~20KB gzipped | Vollständig | Stark | Ja | Mittel |
| react-i18next | ~6KB gzipped | Via Plugin | Stark | Ja | Niedrig-Mittel |
| LinguiJS | ~2KB Laufzeit | Vollständig | Stark | Ja | Mittel-Hoch |
| Better i18n React SDK | ~2KB | Teilweise | Vollständig (abgeleitet) | Ja | Niedrig |
Bundle-Größen sind Näherungswerte und hängen von Tree-Shaking und den verwendeten Funktionen ab. Das ICU-Nachrichtenformat (International Components for Unicode) ist der Standard für komplexe Pluralisierung, Geschlechtsauswahl und bedingten Text – nicht jede Bibliothek unterstützt es nativ.
react-intl (FormatJS)
react-intl ist Teil der FormatJS-Suite, die von Formatly (ehemals Yahoo) gewartet wird. Es ist seit Jahren der De-facto-Standard für komplexes i18n in React und bietet die vollständigste Implementierung des ICU-Nachrichtenformats im Ökosystem.
Installation
npm install react-intl
Ihre App mit IntlProvider umschließen
IntlProvider erstellt den Locale-Kontext für Ihren gesamten Komponentenbaum. Jeder FormattedMessage- und useIntl-Aufruf darunter liest aus diesem Kontext.
// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { IntlProvider } from "react-intl";
import App from "./App";
import enMessages from "./locales/en.json";
import frMessages from "./locales/fr.json";
const messages: Record<string, Record<string, string>> = {
en: enMessages,
fr: frMessages,
};
const userLocale = navigator.language.split("-")[0] ?? "en";
const activeLocale = userLocale in messages ? userLocale : "en";
ReactDOM.createRoot(document.getElementById("root")!).render(
<IntlProvider locale={activeLocale} messages={messages[activeLocale]}>
<App />
</IntlProvider>
);
Ihr Nachrichten-Katalog ist eine flache JSON-Datei:
// src/locales/en.json
{
"greeting": "Hello, {name}!",
"itemCount": "{count, plural, one {# item} other {# items}}",
"welcomeBack": "Welcome back, <bold>{name}</bold>!"
}
FormattedMessage-Komponente
Verwenden Sie FormattedMessage, wenn Sie Rich-Text-Formatierung oder Inline-JSX innerhalb Ihrer Übersetzungen benötigen:
// src/components/Welcome.tsx
import React from "react";
import { FormattedMessage } from "react-intl";
interface WelcomeProps {
name: string;
itemCount: number;
}
export function Welcome({ name, itemCount }: WelcomeProps) {
return (
<div>
<h1>
<FormattedMessage
id="greeting"
values={{ name }}
/>
</h1>
<p>
<FormattedMessage
id="itemCount"
values={{ count: itemCount }}
/>
</p>
<p>
<FormattedMessage
id="welcomeBack"
values={{
name,
bold: (chunks) => <strong>{chunks}</strong>,
}}
/>
</p>
</div>
);
}
useIntl-Hook
Für imperativen Einsatz – Strings außerhalb von JSX formatieren, in Event-Handlern oder für aria-label-Attribute – verwenden Sie den useIntl-Hook:
// src/components/SearchBar.tsx
import React from "react";
import { useIntl } from "react-intl";
export function SearchBar() {
const intl = useIntl();
const placeholder = intl.formatMessage({
id: "search.placeholder",
defaultMessage: "Search products...",
});
const formattedPrice = intl.formatNumber(29.99, {
style: "currency",
currency: "USD",
});
const formattedDate = intl.formatDate(new Date(), {
year: "numeric",
month: "long",
day: "numeric",
});
return (
<div>
<input type="search" placeholder={placeholder} aria-label={placeholder} />
<span>{formattedPrice}</span>
<time>{formattedDate}</time>
</div>
);
}
Die Dokumentation von react-intl befindet sich unter formatjs.io/docs/react-intl.
react-i18next
react-i18next ist die React-Bindung für i18next, das am weitesten verbreitete JavaScript-i18n-Framework. Seine Stärke ist Flexibilität: Es funktioniert mit React, React Native, Node.js und Nicht-React-Umgebungen. Wenn Ihr Team i18next bereits anderswo einsetzt, ist das gemeinsame Grundverständnis ein echter Vorteil.
Installation
npm install i18next react-i18next
Konfiguration
// src/i18n/config.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import enCommon from "./locales/en/common.json";
import enDashboard from "./locales/en/dashboard.json";
import frCommon from "./locales/fr/common.json";
import frDashboard from "./locales/fr/dashboard.json";
i18n
.use(initReactI18next)
.init({
resources: {
en: {
common: enCommon,
dashboard: enDashboard,
},
fr: {
common: frCommon,
dashboard: frDashboard,
},
},
lng: "en",
fallbackLng: "en",
defaultNS: "common",
interpolation: {
escapeValue: false, // React escaped Werte bereits
},
});
export default i18n;
Importieren Sie die Konfiguration am Einstiegspunkt Ihrer App, bevor irgendeine Komponente gerendert wird:
// src/main.tsx
import "./i18n/config";
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
useTranslation-Hook und Namespace-Aufteilung
Die Namespace-Aufteilung ist eines der nützlichsten Features von react-i18next für große Anwendungen. Anstatt eine riesige JSON-Datei zu laden, teilen Sie Übersetzungen nach Feature oder Domäne auf und laden Namespaces bei Bedarf.
// src/i18n/locales/en/common.json
{
"nav.home": "Home",
"nav.dashboard": "Dashboard",
"nav.settings": "Settings",
"button.save": "Save",
"button.cancel": "Cancel"
}
// src/i18n/locales/en/dashboard.json
{
"title": "Your Dashboard",
"stats.users": "{{count}} active user",
"stats.users_other": "{{count}} active users",
"welcome": "Welcome back, {{name}}!"
}
// src/components/Dashboard.tsx
import React from "react";
import { useTranslation } from "react-i18next";
interface DashboardProps {
userName: string;
activeUsers: number;
}
export function Dashboard({ userName, activeUsers }: DashboardProps) {
// Den "dashboard"-Namespace gezielt laden
const { t } = useTranslation("dashboard");
return (
<main>
<h1>{t("title")}</h1>
<p>{t("welcome", { name: userName })}</p>
<p>{t("stats.users", { count: activeUsers })}</p>
</main>
);
}
// src/components/Nav.tsx
import React from "react";
import { useTranslation } from "react-i18next";
export function Nav() {
// Verwendet den Standard-Namespace "common"
const { t, i18n } = useTranslation();
const switchLanguage = (locale: string) => {
i18n.changeLanguage(locale);
};
return (
<nav>
<a href="/">{t("nav.home")}</a>
<a href="/dashboard">{t("nav.dashboard")}</a>
<button onClick={() => switchLanguage("fr")}>Francais</button>
<button onClick={() => switchLanguage("en")}>English</button>
</nav>
);
}
Die react-i18next-Dokumentation befindet sich unter react.i18next.com.
LinguiJS
LinguiJS verfolgt einen grundlegend anderen Ansatz. Anstatt Übersetzungsschlüssel in JSON-Dateien zu verwalten, die Sie per ID referenzieren, schreiben Sie Ihre Benutzeroberfläche in natürlicher Sprache direkt in Ihrem JSX. Ein CLI extrahiert dann diese Strings in einen Katalog, den Übersetzer ausfüllen. Zur Build-Zeit werden die Nachrichten in ein optimiertes Binärformat kompiliert.
Das Ergebnis ist ein kleineres Laufzeit-Bundle und ein geringeres Risiko fehlerhafter Schlüsselreferenzen – erfordert aber einen Build-Schritt, den die anderen Bibliotheken nicht benötigen.
Installation
npm install @lingui/react @lingui/core npm install --save-dev @lingui/cli @lingui/macro @lingui/vite-plugin
Konfiguration
// lingui.config.js
/** @type {import('@lingui/conf').LinguiConfig} */
module.exports = {
locales: ["en", "fr", "de"],
sourceLocale: "en",
catalogs: [
{
path: "src/locales/{locale}/messages",
include: ["src"],
},
],
format: "po",
};
@lingui/macro verwenden
Das Makro transformiert natürlichsprachige Strings zur Compile-Zeit, sodass Ihr Quellcode wie einfaches Englisch wirkt und trotzdem vollständig lokalisierbar ist:
// src/components/ProductCard.tsx
import React from "react";
import { Trans, Plural } from "@lingui/macro";
import { useLingui } from "@lingui/react";
interface ProductCardProps {
productName: string;
price: number;
reviewCount: number;
}
export function ProductCard({ productName, price, reviewCount }: ProductCardProps) {
const { i18n } = useLingui();
const formattedPrice = i18n.number(price, {
style: "currency",
currency: "USD",
});
return (
<article>
<h2>
<Trans>Buy {productName} today</Trans>
</h2>
<p>{formattedPrice}</p>
<p>
<Plural
value={reviewCount}
one="# customer review"
other="# customer reviews"
/>
</p>
<p>
<Trans>
Free shipping on orders over <strong>$50</strong>.
</Trans>
</p>
</article>
);
}
Extraktions-Workflow
Nach dem Schreiben Ihrer Komponenten führen Sie das CLI aus, um Strings zu extrahieren:
# Alle Strings aus Quelldateien in den Katalog extrahieren npx lingui extract # Die .po-Dateien übersetzen (oder an Ihren Übersetzungsdienst senden) # Dann für die Produktion kompilieren npx lingui compile
Die kompilierten Kataloge sind klein und laden schnell. LinguiJS ist besonders attraktiv für Teams, die einen übersetzerfreundlichen PO-Datei-Workflow und die kleinstmögliche Laufzeit-Nutzlast wollen.
Die vollständige Dokumentation befindet sich unter lingui.dev.
Better i18n React SDK
Better i18n ist ein neuerer Teilnehmer im React-i18n-Bereich. Anstatt rein als Übersetzungs-Rendering-Bibliothek zu konkurrieren, bündelt es eine vollständige Workflow-Plattform: KI-gestützte Übersetzungen mit Markenkontexterkennung, automatische Schlüsselerkennung per AST-Scanning und CDN-Auslieferung, sodass Übersetzungsaktualisierungen ohne einen Rebuild live gehen.
Seien Sie ehrlich über die Kompromisse, bevor Sie es wählen: Die Community ist ein Bruchteil der Größe von react-intl oder i18next. Stack-Overflow-Antworten sind spärlich. Wenn Sie auf einen Grenzfall stoßen, sind Sie eher auf die offizielle Dokumentation oder den Support-Kanal angewiesen als auf Community-Lösungen. Die Bibliothek wird gut gepflegt und aktiv entwickelt, hat aber noch nicht die jahrelange Kampferfahrung, die die etablierten Optionen vorweisen können.
Installation
npm install @better-i18n/react
Einrichtung
// src/providers/I18nProvider.tsx
import React, { type ReactNode } from "react";
import { BetterI18nProvider } from "@better-i18n/react";
interface I18nProviderProps {
children: ReactNode;
locale: string;
}
export function I18nProvider({ children, locale }: I18nProviderProps) {
return (
<BetterI18nProvider
projectId={process.env.VITE_BETTER_I18N_PROJECT_ID!}
locale={locale}
>
{children}
</BetterI18nProvider>
);
}
// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { I18nProvider } from "./providers/I18nProvider";
const locale = navigator.language.split("-")[0] ?? "en";
ReactDOM.createRoot(document.getElementById("root")!).render(
<I18nProvider locale={locale}>
<App />
</I18nProvider>
);
useTranslations-Hook
// src/components/HeroSection.tsx
import React from "react";
import { useTranslations } from "@better-i18n/react";
interface HeroSectionProps {
userName: string;
planName: string;
}
export function HeroSection({ userName, planName }: HeroSectionProps) {
const t = useTranslations("hero");
return (
<section>
<h1>{t("title", { name: userName })}</h1>
<p>{t("subtitle", { plan: planName })}</p>
<a href="/signup">{t("cta")}</a>
</section>
);
}
Übersetzungsschlüssel werden im Better i18n Dashboard definiert oder vom CLI-Scanner automatisch aus Ihrem Quellcode ermittelt. Wenn ein Übersetzer eine Änderung genehmigt, wird sie über das CDN-Edge-Netzwerk von Cloudflare an Ihre App ausgeliefert – kein Rebuild oder Redeploy erforderlich.
Die Better i18n Dokumentation befindet sich unter better-i18n.com/docs.
Wann Better i18n gegenüber den Alternativen wählen: Ihr Team möchte KI-gestützte Übersetzungen direkt verfügbar haben, Sie sind mit einer verwalteten Plattform statt einer vollständig eigenständigen Bibliothek einverstanden, und Workflow-Automatisierung (automatische Schlüsselerkennung, CDN-Auslieferung, GitHub-PR-basierte Synchronisierung) ist Ihnen mehr wert als Ökosystembreite.
Wann etwas anderes wählen: Sie benötigen die größtmögliche Community, jahrelange Stack-Overflow-Antworten oder eine rein selbst gehostete Lösung ohne externe Dienstabhängigkeit. In diesem Fall sind react-i18next oder react-intl die sicherere Wahl.
Wichtige Implementierungsmuster
Sprachwechsel
Der Sprachwechsel sollte zustandsbehaftet und in der URL widergespiegelt sein, damit Nutzer lokalisierte Links teilen können. Der einfachste Ansatz speichert die Locale im Zustand und übergibt sie an Ihren Provider, aber ein URL-basierter Ansatz ist robuster für SSR und Deep Linking.
// src/hooks/useLocale.ts
import { useState, useCallback } from "react";
const SUPPORTED_LOCALES = ["en", "fr", "de", "es"] as const;
type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
function getInitialLocale(): SupportedLocale {
const fromUrl = window.location.pathname.split("/")[1] as SupportedLocale;
if (SUPPORTED_LOCALES.includes(fromUrl)) return fromUrl;
const fromBrowser = navigator.language.split("-")[0] as SupportedLocale;
if (SUPPORTED_LOCALES.includes(fromBrowser)) return fromBrowser;
return "en";
}
export function useLocale() {
const [locale, setLocale] = useState<SupportedLocale>(getInitialLocale);
const switchLocale = useCallback((next: SupportedLocale) => {
setLocale(next);
document.documentElement.lang = next;
// Optional: in die Router-History pushen für URL-basiertes Routing
}, []);
return { locale, switchLocale, supportedLocales: SUPPORTED_LOCALES };
}
Übersetzungen lazy nachladen
Das Vorladen aller Locale-Bundles verschwendet Bandbreite. Laden Sie nur die aktive Locale und rufen Sie andere bei Bedarf ab:
// src/i18n/loader.ts
const localeCache = new Map<string, Record<string, string>>();
export async function loadLocale(locale: string): Promise<Record<string, string>> {
if (localeCache.has(locale)) {
return localeCache.get(locale)!;
}
// Dynamischer Import – der Bundler teilt dies in einen separaten Chunk pro Locale auf
const messages = await import(`./locales/${locale}.json`);
const resolved = messages.default as Record<string, string>;
// Neuen Map-Eintrag zurückgeben statt an Ort und Stelle zu mutieren
const updated = new Map(localeCache);
updated.set(locale, resolved);
localeCache.clear();
updated.forEach((v, k) => localeCache.set(k, v));
return resolved;
}
SSR / SSG Überlegungen
Beim Server-seitigen Rendering verlagert sich die Locale-Erkennung von navigator.language zu HTTP-Headern (Accept-Language) oder URL-Segmenten. Die Locale muss vor Beginn des Renderings aufgelöst werden, damit Server und Client identisches HTML erzeugen.
// src/app/[locale]/layout.tsx (Next.js App Router Beispiel)
import { notFound } from "next/navigation";
import { type ReactNode } from "react";
const SUPPORTED_LOCALES = ["en", "fr", "de"];
interface LocaleLayoutProps {
children: ReactNode;
params: { locale: string };
}
export default function LocaleLayout({ children, params }: LocaleLayoutProps) {
if (!SUPPORTED_LOCALES.includes(params.locale)) {
notFound();
}
return (
<html lang={params.locale} dir={params.locale === "ar" ? "rtl" : "ltr"}>
<body>{children}</body>
</html>
);
}
export function generateStaticParams() {
return SUPPORTED_LOCALES.map((locale) => ({ locale }));
}
URL-basiertes Locale-Routing
Suchmaschinen behandeln example.com/fr/produits und example.com/en/products als separate, indexierbare Seiten. URL-basiertes Routing ist für SEO gegenüber cookie-basierter oder header-basierter Erkennung deutlich vorzuziehen. Strukturieren Sie Ihre Routen mit einem [locale]-Segment an der Wurzel und fügen Sie hreflang-Link-Tags im <head> für jede unterstützte Locale hinzu.
Pluralisierung
Jede hier behandelte Bibliothek geht mit Pluralisierung anders um, aber die zugrunde liegenden Regeln stammen aus dem CLDR (Common Locale Data Repository). Englisch hat zwei Pluralformen. Arabisch hat sechs. Programmieren Sie niemals Pluralisierungslogik selbst hart.
Mit react-i18next (unter Verwendung der Suffixe _one / _other):
{
"notification": "{{count}} notification",
"notification_other": "{{count}} notifications"
}
t("notification", { count: 1 }); // "1 notification"
t("notification", { count: 5 }); // "5 notifications"
Datums- und Zahlenformatierung
Verwenden Sie die eingebauten Formatierungshilfsfunktionen der Bibliothek oder greifen Sie direkt auf Intl zurück. Verketten Sie niemals formatierte Werte als Strings – die Reihenfolge der Einheiten unterscheidet sich je nach Locale.
// Direkt Intl verwenden (funktioniert mit jeder Bibliothek)
const formatter = new Intl.NumberFormat(locale, {
style: "currency",
currency: "EUR",
});
// "€1,234.56" in en-US, "1 234,56 €" in fr-FR – dieselbe Zahl, unterschiedliches Format
formatter.format(1234.56);
Häufige Fehler
String-Verkettung. Bauen Sie niemals Sätze zusammen, indem Sie übersetzte Fragmente mit + oder Template-Literalen verbinden. Die Wortreihenfolge variiert je nach Sprache. "Hello " + t("world") bricht in Sprachen, in denen die Begrüßung nach dem Nomen kommt. Übergeben Sie Interpolationsvariablen immer in die Übersetzungsfunktion.
Kontext vergessen. Das englische Wort „File" kann das Substantiv (ein Dokument) oder das Verb (einreichen) bedeuten. Übersetzer brauchen Kontext, um das richtige Wort zu wählen. Die meisten Bibliotheken unterstützen einen context-Parameter oder ein Beschreibungsfeld – nutzen Sie es.
Ladezustände nicht berücksichtigen. Wenn Übersetzungen asynchron geladen werden, gibt es ein Zeitfenster, in dem Ihre Komponente rendert, bevor Strings verfügbar sind. Rendern Sie ein Skeleton, einen Spinner oder gar nichts – aber zeigen Sie Nutzern niemals rohe Übersetzungsschlüssel wie "dashboard.title".
Alle Locales zusammen bündeln. Französische, deutsche, spanische und japanische Übersetzungen an einen Nutzer auszuliefern, der nur Englisch liest, verschwendet Bandbreite und erhöht Ihre Time to Interactive. Verwenden Sie dynamische Imports oder eine CDN-basierte Auslieferungsstrategie, um nur das zu laden, was benötigt wird.
Locale-spezifische Formatierung hart kodieren. Kodieren Sie Datumsformate wie MM/DD/YYYY nicht hart. Deutsche Nutzer erwarten DD.MM.YYYY. Delegieren Sie die Formatierung immer an Intl oder die Formatierungshilfsfunktionen Ihrer Bibliothek.
Übersetzte HTML als Rohmarkup rendern. Das Injizieren übersetzter Strings als rohes HTML ist ein XSS-Risiko, wenn Ihre Übersetzungsquelle jemals kompromittiert wird. Verwenden Sie die sicheren Rich-Text-Komponenten Ihrer Bibliothek – FormattedMessage mit Element-Werten in react-intl oder die Trans-Komponente in react-i18next – anstatt HTML-Strings direkt zu rendern.
FAQ
F: Brauche ich ICU-Nachrichtenformat-Unterstützung?
ICU ist der Standard für komplexe Nachrichten mit Pluralen, Geschlechtsauswahl und bedingtem Text. Wenn Ihre App Sätze wie „John und 3 weitere finden das gut" hat, macht ICU das Ausdrücken dieser Regeln sauber und portierbar. Wenn Ihre App nur einfache String-Ersetzung benötigt, ist ICU optionaler Overhead. react-intl und LinguiJS unterstützen ICU nativ. react-i18next benötigt das Plugin i18next-icu.
F: Welche Bibliothek funktioniert am besten mit dem Next.js App Router?
react-i18next und react-intl unterstützen den App Router mit etwas Konfiguration. Das Next.js-Ökosystem hat sich um next-intl für App-Router-Projekte konsolidiert, wegen seiner erstklassigen Server-Component-Unterstützung. Better i18n liefert ebenfalls ein dediziertes Next.js SDK. LinguiJS funktioniert, erfordert aber sorgfältige Behandlung des Kompilierungsschritts in der Build-Pipeline.
F: Wie sollte ich meine Übersetzungsdateien strukturieren?
Für kleine Apps ist eine einzelne JSON-Datei pro Locale (en.json, fr.json) in Ordnung. Für größere Apps teilen Sie nach Feature oder Domäne auf (Namespaces in i18next, separate Kataloge in LinguiJS). Vermeiden Sie tief verschachtelte Objekte – flache oder einstufige Schlüssel sind einfacher zu verwalten und weniger anfällig für Merge-Konflikte.
F: Wie gehe ich mit Übersetzungen um, die Markup wie Fettdruck oder Links enthalten?
Die meisten Bibliotheken unterstützen Rich Text über ein Render-Prop- oder Slot-System. In react-intl übergeben Sie ein JSX-Element als Wert an FormattedMessage. In react-i18next verwenden Sie die Trans-Komponente. Diese Ansätze halten HTML in React, wo es korrekt escaped und bereinigt werden kann, anstatt Markup in Ihren String-Katalog zu mischen.
F: Wann sollte ich anfangen, über i18n nachzudenken? Früher als Sie denken. i18n nachträglich in eine bestehende Codebasis einzubauen bedeutet, jeden hartkodierten String, jedes Datumsformat, jede Pluralisierungsannahme und jedes Layout zu suchen, das mit längeren deutschen Wörtern bricht. Wenn es irgendeine Chance gibt, dass Ihre App nicht-englischsprachige Nutzer erreicht, fügen Sie i18n-Infrastruktur in Ihrer Ersteinrichtung hinzu, auch wenn Sie am ersten Tag nur Englisch ausliefern.
Fazit
React i18n hat keine einzig richtige Antwort. Die richtige Bibliothek ist diejenige, die zum Workflow Ihres Teams, zur Komplexität Ihrer App und Ihrer Toleranz gegenüber Ökosystemrisiken passt.
Wenn Sie ein neues Projekt starten und den umfangreichsten Community-Support und das tiefste Ökosystem wollen, ist react-i18next der sichere Standard. Sein Namespace-System skaliert gut, die Dokumentation ist umfassend, und Sie werden Antworten auf die meisten Grenzfälle auf Stack Overflow finden.
Wenn Ihre App wirklich komplexe Nachrichtenformatierungsanforderungen hat – bedingte Geschlechtsbehandlung, mehrstufige Plurale, reiches Inline-Markup – ist react-intl die vollständigste Implementierung des ICU-Standards im React-Ökosystem.
Wenn Sie das kleinste Laufzeit-Bundle und einen Compiler-erzwungenen Extraktions-Workflow wollen, bietet LinguiJS eine überzeugende Alternative, insbesondere für Teams, die mit dem Hinzufügen eines Build-Schritts vertraut sind.
Wenn Sie KI-gestützte Übersetzungen, CDN-Auslieferung ohne Rebuild-Zyklus und eine verwaltete Plattform wollen, die die Übersetzungspipeline automatisiert, ist Better i18n eine Evaluierung wert – mit dem ehrlichen Vorbehalt, dass Sie auf ein jüngeres Ökosystem mit einer kleineren Community setzen.
Was auch immer Sie wählen: Entscheiden Sie sich früh, halten Sie Ihre Übersetzungsdateien organisiert und behandeln Sie i18n als erstklassiges Feature statt als nachträglichen Gedanken. Ihre internationalen Nutzer werden den Unterschied bemerken.
Referenzen
- react-intl (FormatJS): formatjs.io/docs/react-intl
- react-i18next: react.i18next.com
- i18next core: www.i18next.com
- LinguiJS: lingui.dev
- Better i18n React SDK: better-i18n.com/docs
- Unicode CLDR Plural Rules: cldr.unicode.org/index/cldr-spec/plural-rules
- MDN Intl API reference: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl