Weil dein i18n-Setup in deinem Kopf und in deiner Config steckt, nicht im Code, den der Agent gerade vor sich hat. Cursor generiert eine Komponente aus dem Kontext neu, der gerade sichtbar ist, und einfacher JSX-Text ist die schnellste Lösung, die funktioniert. Nichts im Repo widerspricht, also landet das Literal im Code. globalize.now ist KI-gestützte Lokalisierungsinfrastruktur, die diese Lücke schließt: Sie extrahiert die neuen Strings, generiert die Schlüssel und synchronisiert die Übersetzungen bei jedem Git Push.

Das ist der Fehlermodus, vor dem einen niemand warnt. i18n einzurichten ist eine Sache von einem Nachmittag. Es eingerichtet zu halten, während ein Agent deine Komponenten zweimal täglich umschreibt, ist das eigentliche Problem.

Warum fügt Cursor nach dem Einrichten von i18n immer wieder hartcodierte Strings ein?

Drei Dinge treffen gleichzeitig zu, und zusammen garantieren sie das Leck.

Der Agent sieht eine Datei, keine Konvention. Wenn du Cursor bittest, ein Einstellungs-Panel hinzuzufügen, liest er die Komponente, die er gerade bearbeitet, und vielleicht ein, zwei Nachbarn. Dein Locale-Verzeichnis, dein Namespace-Schema und die Regel, dass jeder nutzersichtbare String durch eine Übersetzungsfunktion läuft, sind in diesem Fenster nicht sichtbar, außer du bringst sie dort hinein.

Unübersetzter Text ist der statistische Default. Der überwältigende Großteil des React-Codes in den Trainingsdaten jedes Modells schreibt Strings inline. Wenn der Agent das wahrscheinlichste nächste Token für ein Button-Label wählt, schlägt Save changes t('settings.save') allein aufgrund der Häufigkeit. Du verlangst von ihm, jedes einzelne Mal das seltenere Muster zu wählen – ohne Feedback, wenn er es nicht tut.

Nichts bricht. Das ist der entscheidende Punkt. Ein hartcodierter String besteht die Typprüfung. Er wird gebaut. Er wird gerendert. Er besteht deine Tests. Nirgendwo in deiner Pipeline erscheint eine rote Markierung, die sagt, dass ein Nutzer in Deutschland gleich einen englischen Toast sieht. Der Defekt ist unsichtbar, bis jemand die Sprache wechselt und scrollt.

Vergleiche das mit einem fehlenden Import oder einem falschen Prop-Typ, die dem Agent innerhalb von Sekunden korrigiert werden. Unübersetzter Text ist die eine Fehlerklasse, die deine Toolchain stillschweigend akzeptiert – und deshalb die eine Fehlerklasse, die sich ansammelt.

Welche Strings sickern am häufigsten durch?

Die zuletzt hinzugefügten, unter der geringsten Prüfung.

Geh durch eine beliebige KI-unterstützte Codebase, die mehrsprachig gestartet ist, und das überlebende Englisch findet sich an vorhersehbaren Stellen:

  • Fehler- und Erfolgs-Toasts, besonders solche, die beim Fixen eines unrelated Bugs eingefügt wurden
  • Empty States und Ladetexte, die einmal geschrieben und nie wieder geprüft werden
  • Formular-Validierungsmeldungen, oft inline in einem Schema generiert
  • Accessibility-Attribute: aria-label, alt, title
  • Bestätigungsdialoge und Copy für destruktive Aktionen
  • Alles innerhalb eines catch-Blocks

Das Muster ist konsistent. Deine primären Flows werden übersetzt, weil du sie dir ansiehst. Die Randbereiche bleiben hartcodiert, weil niemand sie in einer zweiten Sprache öffnet.

Es gibt davon auch eine Version zweiter Ordnung. Wenn ein Agent ein Literal neben bereits übersetztem Code einfügt, erfindet er manchmal „hilfsbereit“ einen Schlüssel, der nicht zu deinem Namespace-Schema passt – am Ende hast du errors.saveFailed in einer Datei und settings_save_error in einer anderen. Jetzt hast du sowohl unübersetzte Strings als auch einen fragmentierten Key-Space. Falls du an diesem Punkt noch früh dran bist, behandelt unser Guide zur Lokalisierung einer KI-generierten App die initiale Bereinigung.

Können .cursor/rules oder AGENTS.md das verhindern?

Sie helfen, und du solltest eine solche Regeldatei anlegen, aber sie sind eine Empfehlung, keine Durchsetzung.

Der Layer der Instruction-Dateien hat sich vor Kurzem standardisiert. AGENTS.md begann 2025 als herstellerneutrale Konvention und wird inzwischen von der Agentic AI Foundation unter der Linux Foundation verwaltet. Cursor, Codex, Copilot, Gemini CLI, Aider, Windsurf und Zed lesen die Datei nativ – eine einzige Datei im Root deines Repos erreicht also jeden Agent, den dein Team nutzt. Cursor unterstützt zusätzlich gescopte Projektregeln als .mdc-Dateien unter .cursor/rules/, mit Frontmatter zur Steuerung von Beschreibung, Datei-Globs und ob die Regel immer angewendet wird. Das praktische Setup 2026 ist ein AGENTS.md im Root für Portabilität plus gescopte Cursor-Regeln dort, wo sich Auto-Attach lohnt.

Ein funktionierender i18n-Block sieht so aus:

## Internationalization

- Never write user-facing text as a literal in JSX or in a string passed to a UI component.
- All user-facing text goes through the translation function from `next-intl`.
- Keys follow `feature.element.state` in lowerCamelCase, e.g. `settings.save.error`.
- Add the English value to `messages/en.json` only. Never hand-edit other locale files.
- This includes toasts, empty states, validation messages, `aria-label`, `alt` and `title`.

Das senkt die Leckrate spürbar. Auf null bringt es sie nicht, weil Regeldateien im Context Window mit allem anderen um Aufmerksamkeit konkurrieren und lange Agent-Sessions abdriften. Betrachte diesen Layer als Frequenzreduktion, nicht als Garantie.

Wie bringst du einen Build dazu, bei einem unübersetzten String zu scheitern?

Füge eine Lint-Regel hinzu, die ein Literal in JSX als Fehler behandelt, und führe sie in der Continuous Integration aus.

Der Standard ist die no-literal-string-Regel aus eslint-plugin-i18next. Standardmäßig validiert sie reinen Text in JSX-Markup statt jedes Literal in der Datei – genau das macht sie auf einer echten Codebase nutzbar, statt eine Wand aus False Positives zu erzeugen. Sie unterstützt Ignore-Patterns per regulärem Ausdruck, Callee-Ausnahmen für Funktionen, die du als sicher kennst, sowie einen reinen Markup-Modus.

Eine Startkonfiguration:

// eslint.config.js
import i18next from 'eslint-plugin-i18next';

export default [
  {
    files: ['**/*.{jsx,tsx}'],
    plugins: { i18next },
    rules: {
      'i18next/no-literal-string': [
        'error',
        {
          markupOnly: true,
          ignoreAttribute: ['data-testid', 'className'],
          ignore: ['^[0-9]+$', '^\\s*$'],
        },
      ],
    },
  },
];

Führe sie in deiner Pipeline aus, nicht nur im Editor:

npx eslint . --max-warnings=0

Jetzt hat der unsichtbare Defekt eine rote Markierung. Der Agent bekommt für einen unübersetzten String denselben Feedback-Loop, den er bereits für einen Typfehler bekommt – er kann seinen eigenen Fehler also noch in derselben Session beheben, statt ihn einem Nutzer in einer anderen Sprache zu überlassen. Für das größere Bild, wie du i18n von Grund auf in ein Cursor-Projekt einbindest, siehe unsere 15-Minuten-Anleitung für Cursor und Next.js und die Cursor Integrationsseite.

Warum reicht eine fehlschlagende Lint-Regel nicht aus?

Weil ein roter Build dir sagt, dass ein String existiert. Er übersetzt ihn nicht.

Sobald die Regel anschlägt, muss immer noch jemand einen Schlüssel wählen, der zu deinen bestehenden Namespaces passt, den englischen Ausgangswert hinzufügen und die weiteren Sprachen erzeugen. Bei einem Solo-Projekt, das mehrmals täglich released wird, wird diese Arbeit aufgeschoben. Lang genug aufgeschoben, wird die ehrliche Lösung zum Hinzufügen der Datei zu einer Ignore-Liste – und dann bist du wieder da, wo du angefangen hast, nur mit zusätzlicher Config.

Das ist die Lücke, die aus einem gelösten Problem wiederkehrende Altlasten macht. Layer eins und zwei ändern, wie oft das Problem auftritt und wie schnell du es bemerkst. Keiner von beiden erledigt die eigentliche Arbeit.

Wie sieht das dreischichtige Setup aus?

Instruktion, Durchsetzung, Automatisierung. Jeder Layer fängt auf, was der vorherige übersehen hat.

Layer eins, Instruktion. Schreibe den i18n-Block ins AGENTS.md im Root deines Repos und füge eine gescopte .cursor/rules/i18n.mdc mit Globs für deine Komponentenverzeichnisse hinzu. Aufwand: fünfzehn Minuten, einmalig. Effekt: von vornherein weniger geschriebene Literale.

Layer zwei, Durchsetzung. Aktiviere no-literal-string und lass die Continuous Integration darauf fehlschlagen. Aufwand: ein Nachmittag zum Feintunen der Ignore-Patterns auf einer bestehenden Codebase. Effekt: Kein Literal erreicht unbemerkt deinen Main-Branch, und der Agent kann sich selbst korrigieren.

Layer drei, Automatisierung. Lass Extraktion, Keying und Übersetzung ohne dein Zutun geschehen. Das ist der Layer, den globalize.now besetzt. Er sitzt oberhalb deiner i18n-Runtime, statt sie zu ersetzen: next-intl oder i18next rendert die Strings weiterhin. globalize.now erkennt den neuen hartcodierten Text, generiert Schlüssel im Einklang mit den bereits in deinem Projekt verwendeten Namespaces, übersetzt sie in jede konfigurierte Sprache und committet die aktualisierten Übersetzungsdateien bei jedem Git Push zurück. Einmal einrichten – das Repository bei globalize.now verbinden – und es gibt keinen manuellen Schritt mehr, den man auslassen könnte. Du kannst es auch in deinen Agent installieren mit:

npx skills add globalize-now/globalize-skills

Pläne starten bei 5 € im Monat; der Starter-Plan für 20 € enthält monatlich 20 € Übersetzungsguthaben (etwa 200.000 Wörter), Verbrauch über das enthaltene Volumen hinaus wird zum gleichen Preis pro Zeichen abgerechnet. Keine Kosten pro Seat und keine pro Sprache, und bei der Anmeldung gibt es 5 € Übersetzungsguthaben, ohne Kreditkarte. Mehr Details für Engineers gibt es auf der Entwickler-Seite, die kürzere Version für alle, die mit KI-Tools releasen, auf der Vibe-Coder-Seite.

Der Sinn der drei Layer ist, dass keiner von ihnen davon abhängt, dass deine Disziplin über hundert Agent-Sessions hinweg durchhält. Die Regeldatei senkt die Rate, die Lint-Regel macht Fehler laut, und die Synchronisation macht das Fixen kostenlos. Diese Kombination hält eine mehrsprachige App mehrsprachig, während ein Agent sie ständig umschreibt.

Dein i18n-Setup ist nicht der fragile Teil. Es ist die Schleife zwischen dem String, den der Agent schreibt, und dem Erreichen der Übersetzungsdatei. globalize.now schließt diese Schleife bei jedem Push.

globalize.now konvertiert hartcodierte App-Texte in übersetzungsreife Locale-Dateien und hält sie aktuell, während Sie veröffentlichen.

Probiere globalize.now kostenlos