Communitygithub.com

Web i18n

Produktionsreife Internationalisierung und Lokalisierung für Web-Apps. Verwende diesen Skill beim Hinzufügen mehrerer Sprachen, Übersetzen der UI, Extrahieren hartcodierter Zeichenketten, Behandeln von Pluralformen/Geschlecht/ICU-Nachrichten, Locale-Routing, RTL-Layouts, lokalisierten Datums-/Zahlen-/Währungsangaben, hreflang/SEO oder Validieren von Übersetzungskatalogen. Framework-unabhängige Anleitung für React, Next.js, Vue, Svelte, Astro, Remix und Vanilla-JS mit Beispielen für next-intl, react-i18next, FormatJS und Lingui.

Was ist Web i18n?

Dieser Skill bietet eine umfassende, produktionsreife Anleitung für Web-Internationalisierung (i18n) und Lokalisierung (l10n). Er behandelt das gesamte Spektrum von der Architekturentscheidung über die Erkennung und Extraktion hartcodierter Zeichenketten bis hin zur Implementierung von Locale-Routing, Pluralregeln, RTL-Unterstützung und CI-gestützter Übersetzungsqualitätssicherung. Der Skill ist bewusst framework-agnostisch und enthält konkrete Beispiele und Referenzen für die gängigsten Bibliotheken wie next-intl, react-i18next, FormatJS und Lingui. Entwickler lernen, eine skalierbare und wartbare i18n-Architektur aufzubauen, die sowohl für kleine als auch für große mehrsprachige Anwendungen geeignet ist. Der Skill unterstützt die Agenten-Plattformen Claude Code, Cursor, Codex und GitHub Copilot und ist als Repository unter https://github.com/Nelsutro/agent-skills verfügbar. Er betont vor allem die Vermeidung von Anti-Patterns wie Satzkonkatenation, hartcodierten Formaten und englischsprachigen Schlüsseln, die typische Fehler in der Internationalisierung darstellen.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Nelsutro/agent-skills/tree/HEAD/web-i18n

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Web i18n?

Internationalisierung ist Architektur, kein Übersetzungsdurchlauf am Ende. Das Ziel ist eine UI, die für jedes Gebietsschema korrekt dargestellt wird – ohne Sonderfälle: keine konkatenierten Sätze, keine hartcodierten Formate, kein Layout, das bricht, wenn Deutsch 35 % länger ist oder Arabisch die Richtung umkehrt.

Wann dieser Skill verwendet wird

Verwende ihn, wenn der Benutzer darum bittet:

  • Eine Sprache, einen Sprachumschalter oder ein Locale-Routing hinzuzufügen
  • Eine App zu übersetzen, Zeichenketten zu extrahieren oder einen Übersetzungsworkflow einzurichten
  • Pluralisierungs-, Geschlechter- oder "1 item / 2 items"-Fehler zu beheben
  • Datums-, Zahlen-, Währungs-, relative Zeit- oder Listenangaben pro Locale zu formatieren
  • RTL (Arabisch, Hebräisch, Persisch, Urdu) zu unterstützen
  • Hreflang, lokalisierte Metadaten oder mehrsprachige Sitemaps hinzuzufügen
  • Ein bestehendes i18n-Setup, fehlende Schlüssel oder Übersetzungsdrift zu überprüfen
  • Zwischen next-intl, react-i18next, FormatJS, Lingui oder einem TMS zu wählen

Verwandte Referenzen befinden sich in references/ und Skripte in scripts/. Lies nur, was die Aufgabe benötigt. Pfade sind relativ zum Verzeichnis dieses Skills (die Agentenlaufzeit gibt es aus, wenn der Skill geladen wird).

Unverhandelbar

  1. Baue niemals einen Satz durch Verkettung übersetzter Fragmente. Übersetze die gesamte Nachricht mit Platzhaltern: "{count} neue Nachrichten", nicht t('neu') + ' ' + count + ' ' + t('nachrichten').
  2. Verwende niemals englischen Text als Schlüssel. Schlüssel sind stabile Identifikatoren (inbox.unreadCount), damit Textänderungen keine Lookups brechen oder alle Übersetzungen ungültig machen.
  3. Jede benutzersichtbare Zeichenkette wird externalisiert, einschließlich aria-label, alt, title, placeholder, Validierungsfehler, leere Zustände, Toasts, E-Mails, Push-Benachrichtigungen und PDF-Ausgabe.
  4. Hartcodiere niemals Formate. Kein toFixed(2) + '$', kein manuelles MM/DD/YYYY, keine englischen Monatsnamen, keine 24h-Annahmen. Verwende Intl.
  5. Ein Gebietsschema ist ein BCP-47-Tag, keine Sprache. pt-BR und pt-PT, zh-Hans und zh-Hant, es-MX und es-ES sind verschiedene Produkte. Speichere den vollständigen Tag.
  6. Der Locale-Status muss auf dem Server anforderungsbezogen sein. Setze das aktuelle Locale während SSR niemals in eine veränderliche Variable auf Modulebene; gleichzeitige Anfragen würden Sprachen ineinander lecken lassen.
  7. Übersetzungen sind nicht vertrauenswürdige Eingaben, wenn sie von einem TMS, einer Community oder einem CMS stammen. Übergib sie niemals an innerHTML/dangerouslySetInnerHTML ohne Bereinigung.

Arbeitsablauf

1. Prüfen vor Codeänderungen

  • Erkenne den Stack, den Router und die vorhandene i18n-Bibliothek, bevor du etwas vorschlägst.
  • Finde hartcodierte Zeichenketten:
    node scripts/scan-hardcoded.mjs src
    
    Behandle die Ausgabe als Kandidaten, nicht als Evangelium: überspringe Test-Fixtures, Konstanten, URLs und entwicklerorientierte Logs.
  • Inventarisiere jedes derzeit ausgelieferte Gebietsschema und den Speicherort der Kataloge.
  • Identifiziere zuerst die Bereiche mit dem höchsten Risiko: Authentifizierung, Checkout, Fehler, E-Mails und rechtliche Texte.

2. Architektur entscheiden (schreibe sie auf)

Beantworte diese Fragen, bevor du Code schreibst:

EntscheidungStandardAnmerkungen
Locale-StrategiePfadpräfix /es/Beste SEO und Cache-Fähigkeit. Subdomains/ccTLDs nur mit Grund.
Standard-LocaleQuellspracheLeite Erstbesucher ohne Überschreibung nicht automatisch in ein erratenes Locale weiter.
Locale-ErkennungURL → Cookie → Accept-Language → StandardURL gewinnt, damit geteilte Links nie die Sprache ändern.
KatalogformatJSON pro Locale pro NamespaceLazy-Lade-Namespaces; sende niemals alle Locales an den Client.
NachrichtensyntaxICU MessageFormatPortabel über Bibliotheken und TMS hinweg; unterstützt Plural/Select/Ordinal.
Schlüsselstilfeature.component.elementStabil, grepbar, kein englischer Text.
ÜbersetzungsquellsystemTMS oder Repo, nicht beidesWähle eines aus; synchronisiere in eine Richtung in CI.
FallbackLocale-Kette es-MX → es → enProtokolliere Fallbacks in der Entwicklung, um Lücken zu finden.

3. Externalisieren und strukturieren von Nachrichten

  • Ersetze Zeichenketten durch semantische Schlüssel und verschiebe den Text in den Basislocale-Katalog.
  • Verwende einen Schlüssel pro vollständigem Satz. Wenn ein Satz einen Link oder ein fettgedrucktes Wort enthält, verwende Rich-Text-Interpolation anstelle einer Aufteilung.
  • Halte Platzhalter benannt ({userName}), niemals positionell ({0}).
  • Füge kein HTML in Kataloge ein, es sei denn, die Bibliothek unterstützt explizit getypte Tags.
  • Gruppiere nach Produktoberfläche, nicht nach Seite: checkout.json, auth.json, errors.json.

Siehe references/icu.md für Plural-, Select-, Ordinal- und Rich-Text-Regeln.

4. Locale-Routing und -Rendering einrichten

  • Leite das Locale aus der Route ab, validiere es gegen die Liste der unterstützten Locales und gib 404 für unbekannte Locales zurück, anstatt stillschweigend zurückzufallen.
  • Stelle einen Sprachumschalter bereit, der den aktuellen Pfad und die Query-Strings beibehält.
  • Setze <html lang="..."> und <html dir="..."> auf dem serverseitig gerenderten Dokument.
  • Lade nur den Katalog des aktiven Locales plus den Standard-Fallback.
  • Mache denselben Katalog für Server- und Client-Rendering verfügbar; vermeide doppelte oder abweichende Übersetzungslogik.

Framework-spezifische Einrichtung und Fallstricke: references/frameworks.md. Routing, Erkennung, Hreflang und Sitemaps: references/routing-seo.md.

5. Locale-sensitive Werte behandeln

Verwende Intl (oder den Wrapper des Frameworks), niemals manuelle Formatierung:

new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount)
new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeZone }).format(date)
new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(-3, 'day')
new Intl.ListFormat(locale, { type: 'conjunction' }).format(items)
new Intl.PluralRules(locale).select(count)
new Intl.Collator(locale).compare(a, b)
  • Speichere Zeitstempel in UTC; rendere in der Zeitzone des Benutzers, nicht des Servers.
  • Währung ist nicht gleich Locale: Ein französischer Benutzer kann in USD bezahlen.
  • Sortiere/suche mit Intl.Collator, nicht mit < oder localeCompare() ohne Locale.
  • Groß-/Kleinschreibung: toLocaleUpperCase(locale), weil türkisches i zu İ wird.

6. Gestaltung für Erweiterung und RTL

  • Gehe von +30–40 % Textlänge aus (Deutsch, Finnisch, Russisch) und vermeide Schaltflächen mit fester Breite oder einzeilige Labels.
  • Lasse Container wachsen, erlaube Zeilenumbrüche und teste mit Pseudo-Lokalisierung:
    node scripts/pseudo-localize.mjs locales/en.json --out locales/en-XA.json
    
  • Verwende logische CSS-Eigenschaften (margin-inline-start, padding-inline, inset-inline) anstelle von left/right.
  • Spiegele richtungsabhängige Symbole (Pfeile, Chevrons, Fortschritt), nicht Logos, Uhren, Mediensteuerungen oder Häkchen.
  • Umfasse benutzergenerierte Inline-Inhalte mit <bdi> oder dir="auto".

Vollständige RTL-Checkliste: references/rtl.md.

7. Übersetzungs-QA in CI einrichten

Mindestens sollte der Build fehlschlagen, wenn Kataloge strukturell ungültig sind:

node scripts/check-locales.mjs locales --base en

Es prüft auf fehlende/zusätzliche Schlüssel, leere Werte, Platzhalterkonflikte und fehlerhafte ICU-Syntax. Füge die ICU-Kompilierung über die CLI deiner Bibliothek hinzu, wenn verfügbar:

npx formatjs compile-folder --ast locales/en locales/compiled/en

Füge Tests für Pluralkategorien, Datums-/Zahlenausgabe mit einer festen Zeitzone und RTL-Rendering hinzu. Siehe references/testing.md.

Wichtige Entscheidungen und Fallen

Bibliotheksauswahl

BedarfWähle
Next.js App Router, Server-Komponentennext-intl
React SPA, ausgereiftes Ökosystem, viele Pluginsreact-i18next
ICU-first, framework-unabhängig, CLI-ExtraktionFormatJS (react-intl)
Kompilierte Kataloge, kleine Laufzeit, TypsicherheitLingui
Vuevue-i18n
Sveltesvelte-i18n oder paraglide
Astrointegriertes i18n-Routing + deine Nachrichtenbibliothek
Vanilla / MinimalIntl + ein kleiner Katalog-Lader

Führe keine zweite i18n-Bibliothek in eine App ein, die bereits eine hat. Erweitere das bestehende Setup.

Pluralisierung

Pluralkategorien sind sprachspezifisch. Englisch hat one/other; Arabisch hat zero/one/two/few/many/other; Japanisch hat nur other. Schreibe niemals count === 1 ? ... : ....

"inbox.unread": "{count, plural, =0 {Keine ungelesenen Nachrichten} one {# ungelesene Nachricht} other {# ungelesene Nachrichten}}"

Geschlecht und grammatikalische Übereinstimmung

Verwende select für eine Geschlechtervariable und halte den Wert der Variable sprachneutral (female, male, neutral, unknown), nicht lokalisierte Wörter:

"profile.greeting": "{gender, select, female {Bienvenida, {name}} male {Bienvenido, {name}} other {Bienvenido/a, {name}}}"

Wenn eine Sprache Übereinstimmung bei Adjektiven, Nomen oder Artikeln erfordert, stelle das grammatikalische Geschlecht/den Fall als Argument zur Verfügung, anstatt die Zeichenkette zu manipulieren.

Rich-Text

Teile "Lesen Sie unsere " + link + " Bedingungen" nicht in drei Schlüssel auf. Verwende Tag-Interpolation:

"signup.terms": "Ich stimme den <link>Nutzungsbedingungen</link> zu"
t.rich('signup.terms', { link: (chunks) => <a href="/terms">{chunks}</a> })

SEO

  • Eine indexierbare URL pro Locale; liefere niemals maschinell übersetzte Inhalte hinter derselben URL aus.
  • Selbstreferenzierendes hreflang plus x-default; absolute URLs; inkludiere jedes Locale.
  • Lokalisiere <title>, Meta-Beschreibung, OG-Tags und strukturierte Daten.
  • Kombiniere hreflang nicht mit noindex auf lokalisierten Seiten.

Details in references/routing-seo.md.

Caching

  • Das Locale im Pfad macht CDN-Caching unkompliziert. Wenn du per Header verhandelst, sende Vary: Accept-Language und erwarte Cache-Fragmentierung.
  • Cache keine personalisierte Sprachpräferenz in einer gemeinsam genutzten CDN-Antwort.
  • Halte Content-Language konsistent mit dem gerenderten Locale.

Review-Checkliste

Bevor du i18n als abgeschlossen erklärst:

  • Keine hartcodierten benutzersichtbaren Zeichenketten in Komponenten, E-Mails oder Fehlern
  • Keine Satzkonkatenation; jede Nachricht ist eine vollständig übersetzbare Einheit
  • Schlüssel sind semantisch und stabil; kein englischer Text als Schlüssel
  • Jeder Platzhalter existiert in jedem Locale; ICU-Syntax kompiliert
  • Pluralformen verwenden Intl.PluralRules/ICU, nicht count === 1
  • Geschlecht/Übereinstimmung wird über select behandelt, nicht über Suffixe wie "/a"
  • Daten, Zahlen, Währung, relative Zeit verwenden Intl mit explizitem Locale/Zeitzone
  • <html lang> und dir sind beim ersten Rendern (SSR) korrekt und werden nicht in einem Effekt gesetzt
  • Der Sprachumschalter erhält Pfad/Query und funktioniert mit Deep Links
  • Unbekanntes Locale gibt 404 zurück, keinen stillen Fallback
  • Layout übersteht +35 % Expansion und RTL
  • hreflang, kanonisch, Sitemap und lokalisierte Metadaten sind korrekt
  • Katalogvalidierung läuft in CI
  • Fallback-Verhalten ist beabsichtigt und beobachtbar

Anti-Patterns

  • t('hallo') + ', ' + name — fragmentierte Sätze
  • key={englischerText} — übersetzter Text als Schlüssel
  • count === 1 ? 'Artikel' : 'Artikel' — englischsprachige Plural-Logik
  • new Date().toLocaleDateString() ohne Locale oder Zeitzone
  • value.toFixed(2) für Geldbeträge
  • locale === 'ar' && { direction: 'rtl' } über Komponenten verstreut, anstatt dir auf Dokumentebene zu setzen
  • Setzen von <html lang> innerhalb von useEffect — verursacht einen Flash und bricht Crawler
  • Laden jedes Locales in das anfängliche Bundle
  • Automatische Weiterleitung nach IP ohne Überschreibungsmöglichkeit
  • Übersetzen von Eigennamen, Produktnamen, Code-Identifikatoren oder URLs
  • maschinelles Übersetzen von Zeichenketten und Ausliefern ohne Überprüfung
  • Annahme, dass eine einzige Katalogdatei für mehr als ein paar hundert Schlüssel skalierbar ist

Verwandte Skills