Zum Hauptinhalt springen

Internationalisierung (i18n) und Lokalisierung (l10n) in Cursor: react-intl, i18next und RTL-Unterstützung

Internationalisierung in Cursor

Internationalisierung (i18n) und Lokalisierung (l10n) machen Ihre Anwendung für Benutzer in verschiedenen Sprachen, Regionen und kulturellen Konventionen zugänglich. Cursor, ein KI-gestützter IDE-Editor, beschleunigt jede Phase des i18n-Workflows: Extrahieren übersetzbarer Strings, Konfigurieren von Übersetzungsbibliotheken, Generieren von Locale-Dateien, Implementieren von Right-to-Left (RTL)-Layouts und Automatisieren von Übersetzungen mit KI-gestützten Pipelines. Dieser Leitfaden behandelt react-intl, i18next, Übersetzungsdateiverwaltung, RTL-Unterstützung, Locale-bewusste Formatierung und KI-gestützte Übersetzungs-Workflows.

Voraussetzungen​

Stellen Sie vor dem Start sicher, dass Sie Folgendes haben:

  • Cursor (aktuellste Version empfohlen) als IDE-Editor
  • Node.js 18 oder höher
  • Ein React-Projekt (Vite oder Next.js empfohlen)
  • Grundlegende Vertrautheit mit JSON und React-Komponentenmustern
tipp

Planen Sie Ihre i18n-Strategie, bevor Sie UI-Code schreiben. Das Nachrüsten von i18n in eine bestehende Anwendung ist deutlich schwieriger als es von Anfang an zu integrieren. Bitten Sie Cursor, Ihre Übersetzungsinfrastruktur im ersten Sprint einzurichten, damit jede Komponente ab Tag eins internationalisiert ist.

i18n vs l10n verstehen​

  • Internationalisierung (i18n): Die Engineering-Arbeit, Ihre Anwendung auf mehrere Sprachen vorzubereiten. Dazu gehören das Extrahieren von Strings, die Verwendung von Locale-bewussten Formatierern, die Unterstützung von RTL-Layouts und das Design flexibler UI-Komponenten.
  • Lokalisierung (l10n): Die tatsächliche Übersetzung und kulturelle Anpassung von Inhalten für ein spezifisches Locale, einschließlich des Übersetzens von Strings, Anpassen von Datums- und Währungsformaten und Anpassen von Bildmaterial an lokale Normen.

Cursor hilft bei beidem: Es generiert die i18n-Infrastruktur und unterstützt bei der l10n durch KI-gestützte Übersetzung und Locale-Datei-Generierung.

i18next einrichten​

i18next ist das beliebteste i18n-Framework für JavaScript. Sein React-Begleiter, react-i18next, bietet Hooks und Komponenten für nahtlose Integration.

Installation und Konfiguration​

Bitten Sie Cursor, i18next in Ihrem React-Projekt einzurichten:

Set up i18next with react-i18next in my Vite + React + TypeScript project.
Support English (en), Spanish (es), Japanese (ja), and Arabic (ar).
Include a language switcher component, lazy-loaded translation
namespaces, and locale-aware date formatting. Configure the
detector to remember the user's choice in localStorage.

Cursor generiert die Konfiguration:

// src/i18n/index.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import HttpBackend from 'i18next-http-backend';

export const supportedLanguages = [
{ code: 'en', name: 'English', dir: 'ltr' as const },
{ code: 'es', name: 'Espanol', dir: 'ltr' as const },
{ code: 'ja', name: '日本語', dir: 'ltr' as const },
{ code: 'ar', name: 'العربية', dir: 'rtl' as const },
];

export const defaultLanguage = 'en';

i18n
.use(HttpBackend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: defaultLanguage,
supportedLngs: supportedLanguages.map((l) => l.code),
ns: ['common', 'navigation', 'forms', 'errors'],
defaultNS: 'common',
backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' },
detection: {
order: ['localStorage', 'navigator', 'htmlTag'],
lookupLocalStorage: 'i18nextLng',
caches: ['localStorage'],
},
interpolation: { escapeValue: false },
react: { useSuspense: true, bindI18n: 'languageChanged loaded' },
});

export default i18n;

Übersetzungsdatei-Struktur​

public/
locales/
en/
common.json
navigation.json
forms.json
errors.json
es/
common.json
...
ja/
common.json
...
ar/
common.json
...

Beispiel für englische Übersetzungen:

// public/locales/en/common.json
{
"app": {
"title": "My Application",
"tagline": "Build better, ship faster"
},
"welcome": "Welcome, {{name}}!",
"items_count": "You have {{count}} item",
"items_count_other": "You have {{count}} items",
"lastUpdated": "Last updated: {{date}}"
}
// public/locales/en/forms.json
{
"labels": {
"email": "Email Address",
"password": "Password",
"firstName": "First Name",
"lastName": "Last Name"
},
"placeholders": {
"email": "[email protected]",
"password": "Enter your password"
},
"buttons": {
"submit": "Submit",
"cancel": "Cancel"
},
"validation": {
"emailRequired": "Email is required",
"emailInvalid": "Please enter a valid email address",
"passwordMinLength": "Password must be at least {{min}} characters"
}
}
tipp

Organisieren Sie Übersetzungen in Namensräumen (common, navigation, forms, errors) anstatt in einer einzigen monolithischen Datei. Namensräume ermöglichen es, nur die für die aktuelle Seite benötigten Übersetzungen zu laden, was die initiale Bundle-Größe reduziert. Bitten Sie Cursor, Namensraumgrenzen basierend auf Ihrer Routenstruktur vorzuschlagen.

warnung

Hard-coden Sie niemals benutzersichtbare Strings in Ihren Komponenten. Selbst wenn Sie anfangs nur eine Sprache unterstützen, ist das Extrahieren von Strings in Übersetzungsdateien von Anfang an eine 10-Minuten-Investition, die Wochen späterer Refactoring-Arbeit spart. Verwenden Sie die t()-Funktion für jeden sichtbaren String, einschließlich Button-Beschriftungen, Fehlermeldungen und Tooltips.

react-i18next-Hooks verwenden​

Der useTranslation-Hook ist der primäre Weg, um in Funktionskomponenten auf Übersetzungen zuzugreifen.

Grundlegende Verwendung​

// src/components/LoginForm.tsx
import { useTranslation } from 'react-i18next';
import { useState } from 'react';

export function LoginForm() {
const { t } = useTranslation(['forms', 'errors']);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState<Record<string, string>>({});

function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const newErrors: Record<string, string> = {};

if (!email) {
newErrors.email = t('forms:validation.emailRequired');
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
newErrors.email = t('forms:validation.emailInvalid');
}

if (password.length < 8) {
newErrors.password = t('forms:validation.passwordMinLength', { min: 8 });
}

setErrors(newErrors);
}

return (
<form onSubmit={handleSubmit} className="max-w-md space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium">
{t('forms:labels.email')}
</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder={t('forms:placeholders.email')}
className="mt-1 w-full rounded border px-3 py-2"
aria-invalid={!!errors.email}
/>
{errors.email && (
<p className="mt-1 text-sm text-red-600" role="alert">
{errors.email}
</p>
)}
</div>

<div>
<label htmlFor="password" className="block text-sm font-medium">
{t('forms:labels.password')}
</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder={t('forms:placeholders.password')}
className="mt-1 w-full rounded border px-3 py-2"
aria-invalid={!!errors.password}
/>
{errors.password && (
<p className="mt-1 text-sm text-red-600" role="alert">
{errors.password}
</p>
)}
</div>

<button type="submit" className="w-full rounded bg-blue-600 px-4 py-2 text-white">
{t('forms:buttons.submit')}
</button>
</form>
);
}

Pluralisierung und Interpolation​

i18next behandelt Pluralisierungsregeln automatisch basierend auf dem Locale. Verschiedene Sprachen haben verschiedene Pluralformen (Englisch hat 2, Arabisch hat 6, Japanisch hat 1).

// src/components/ShoppingCart.tsx
import { useTranslation } from 'react-i18next';

export function ShoppingCart({ itemCount }: { itemCount: number }) {
const { t } = useTranslation('common');

return (
<div className="p-4">
<h2 className="text-lg font-bold">{t('shoppingCart')}</h2>
{/* i18next automatically selects singular or plural form */}
<p>{t('items_count', { count: itemCount })}</p>
</div>
);
}

Die arabische Übersetzungsdatei behandelt alle sechs Pluralformen:

// public/locales/ar/common.json (Arabic has 6 plural forms per CLDR rules)
{
"items_count_0": "لا توجد عناصر",
"items_count_1": "لديك عنصر واحد",
"items_count_2": "لديك عنصران",
"items_count_3": "لديك {{count}} عناصر",
"items_count_4": "لديك {{count}} عنصرًا",
"items_count_5": "لديك {{count}} عنصر"
}
tipp

Bitten Sie Cursor, Pluralisierungsregeln für jedes Locale zu generieren, das Sie unterstützen. Ein Prompt wie „Erstelle die korrekten Pluralform-Schlüssel für Arabisch, Russisch und Polnisch für den Übersetzungsschlüssel items_count, gemäß CLDR-Pluralregeln" produziert locale-korrekte Pluralformen, ohne dass Sie CLDR-Regeln auswendig lernen müssen.

react-intl einrichten​

react-intl ist eine alternative i18n-Bibliothek aus der FormatJS-Suite. Sie ist in Unternehmensanwendungen beliebt wegen ihrer strengen Typsicherheit und ICU-Message-Format-Unterstützung.

Provider-Konfiguration​

// src/i18n/IntlProvider.tsx
import { IntlProvider } from 'react-intl';
import { useState, useEffect, ReactNode, createContext, useContext } from 'react';

export type Locale = 'en' | 'es' | 'ja' | 'ar';

const messages: Record<Locale, () => Promise<Record<string, string>>> = {
en: () => import('../locales/en.json').then((m) => m.default),
es: () => import('../locales/es.json').then((m) => m.default),
ja: () => import('../locales/ja.json').then((m) => m.default),
ar: () => import('../locales/ar.json').then((m) => m.default),
};

export function IntlAppProvider({ children }: { children: ReactNode }) {
const storedLocale = localStorage.getItem('locale') as Locale | null;
const browserLocale = navigator.language.split('-')[0] as Locale;
const [locale, setLocale] = useState<Locale>(storedLocale || browserLocale || 'en');
const [localeMessages, setLocaleMessages] = useState<Record<string, string>>({});

useEffect(() => {
messages[locale]().then((msgs) => {
setLocaleMessages(msgs);
document.documentElement.dir = locale === 'ar' ? 'rtl' : 'ltr';
document.documentElement.lang = locale;
localStorage.setItem('locale', locale);
});
}, [locale]);

return (
<IntlProvider
locale={locale}
messages={localeMessages}
defaultLocale="en"
onError={(err) => import.meta.env.DEV && console.warn('[Intl]', err.message)}
>
<LanguageContext.Provider value={{ locale, changeLocale: setLocale }}>
{children}
</LanguageContext.Provider>
</IntlProvider>
);
}

interface LanguageContextType { locale: Locale; changeLocale: (locale: Locale) => void; }
const LanguageContext = createContext<LanguageContextType | null>(null);
export function useLanguage() {
const ctx = useContext(LanguageContext);
if (!ctx) throw new Error('useLanguage must be used within IntlAppProvider');
return ctx;
}

react-intl-Komponenten verwenden​

react-intl bietet deklarative Komponenten zur Formatierung. Die FormattedMessage-Komponente verarbeitet das ICU-Message-Format, während FormattedNumber, FormattedDate und FormattedTime sich automatisch an das Locale des Benutzers anpassen.

// src/components/ProductCard.tsx
import { FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl';
import { useLanguage } from '../i18n/IntlProvider';

export function ProductCard({ product }: { product: Product }) {
const { locale } = useLanguage();

return (
<div className="rounded-lg border p-4">
<h3 className="text-lg font-bold">{product.name}</h3>
{/* Currency adapts to locale automatically */}
<p className="text-xl text-green-600">
<FormattedNumber
value={product.price}
style="currency"
currency={locale === 'ja' ? 'JPY' : locale === 'ar' ? 'SAR' : 'USD'}
/>
</p>
<p className="text-sm text-gray-500">
<FormattedMessage
id="product.addedOn"
defaultMessage="Added on {date}"
values={{
date: <FormattedDate value={new Date(product.addedAt)} year="numeric" month="long" day="numeric" />,
}}
/>
</p>
<div className="mt-2">
<FormattedMessage
id="product.reviews"
defaultMessage="{count, plural, one {# review} other {# reviews}}"
values={{ count: product.reviewCount }}
/>
</div>
</div>
);
}
tipp

react-intl verwendet das ICU-Message-Format, das Pluralisierung, Selektion und Formatierung direkt im Message-String einbettet. Das bedeutet, dass Übersetzer die Struktur eines Satzes anpassen können, ohne Code zu ändern. Bitten Sie Cursor, die ICU-Message-Format-Syntax mit praktischen Beispielen zu erklären, wenn Sie von einfachen Key-Value-Übersetzungsdateien migrieren.

Right-to-Left (RTL)-Unterstützung​

RTL-Unterstützung ist unerlässlich für Sprachen wie Arabisch, Hebräisch und Persisch. Cursor hilft Ihnen, RTL-bewusste Layouts zu implementieren, die in beide Richtungen korrekt funktionieren.

CSS Logical Properties​

Verwenden Sie logische Eigenschaften (margin-inline-start) anstelle von physischen (margin-left), damit Layouts sich im RTL-Modus automatisch umkehren.

/* src/styles/rtl.css */
.card {
padding-inline-start: 1rem;
padding-inline-end: 1rem;
border-inline-start: 3px solid var(--accent);
}
.flex-container { display: flex; flex-direction: row; gap: 0.5rem; }
.nav-bar { inset-inline-start: 0; inset-inline-end: 0; }

/* Flip directional icons in RTL */
[dir="rtl"] .icon-rtl-flip { transform: scaleX(-1); }

RTL-bewusste React-Komponente​

// src/components/RTLNavigation.tsx
import { useTranslation } from 'react-i18next';
import { useMemo } from 'react';

export function RTLNavigation({ items }: { items: NavItem[] }) {
const { i18n } = useTranslation();
const isRTL = i18n.dir() === 'rtl';
const flipIcons = useMemo(() => new Set(['arrow', 'chevron', 'back', 'forward']), []);

return (
<nav
className="flex items-center gap-4 px-4 py-2"
style={{ direction: isRTL ? 'rtl' : 'ltr' }}
aria-label="Main navigation"
>
{items.map((item) => (
<a key={item.id} href={item.href} className="flex items-center gap-2 rounded px-3 py-1 hover:bg-gray-100">
{item.icon && (
<span
className="inline-block"
style={{ transform: isRTL && flipIcons.has(item.icon) ? 'scaleX(-1)' : 'scaleX(1)' }}
>
<Icon name={item.icon} />
</span>
)}
<span>{i18n.t(item.labelKey)}</span>
</a>
))}
</nav>
);
}
warnung

Verwenden Sie nicht text-align: left oder text-align: right in Ihrem CSS. Verwenden Sie stattdessen text-align: start und text-align: end. Physische Eigenschaften kehren sich im RTL-Modus nicht automatisch um, was zu falsch ausgerichtetem Text und defekten Layouts führt. Bitten Sie Cursor, Ihr CSS nach physischen Richtungseigenschaften zu scannen und diese in logische Entsprechungen umzuwandeln.

tipp

Testen Sie Ihr RTL-Layout, indem Sie Arabisch früh in der Entwicklung als Locale hinzufügen. Das Umkehren Ihres Layouts zu RTL offenbart hard-codierte physische Eigenschaften, nicht-spiegelnde Flex-Layouts und Icons, die umgekehrt werden sollten. Das Beheben dieser Probleme während der Entwicklung ist deutlich günstiger, als sie nach dem Launch zu entdecken.

Sprachumschalter-Komponente​

// src/components/LanguageSwitcher.tsx
import { useTranslation } from 'react-i18next';
import { supportedLanguages } from '../i18n';

export function LanguageSwitcher() {
const { i18n } = useTranslation();
const currentLanguage = supportedLanguages.find((l) => l.code === i18n.language) || supportedLanguages[0];

function changeLanguage(code: string) {
i18n.changeLanguage(code);
const lang = supportedLanguages.find((l) => l.code === code);
document.documentElement.dir = lang?.dir || 'ltr';
document.documentElement.lang = code;
}

return (
<div className="flex items-center gap-2">
<select
id="language-select"
value={currentLanguage.code}
onChange={(e) => changeLanguage(e.target.value)}
className="rounded border px-2 py-1 text-sm"
aria-label="Select language"
>
{supportedLanguages.map((lang) => (
<option key={lang.code} value={lang.code}>
{lang.name}
</option>
))}
</select>
</div>
);
}

Locale-bewusste Formatierung​

Datums-, Zahlen- und Währungsformatierung muss sich an das Locale des Benutzers anpassen. Sowohl i18next als auch react-intl bieten Formatierungswerkzeuge.

Die Intl-API direkt verwenden​

// src/lib/locale-formatters.ts

export function formatDate(date: Date, locale: string, options?: Intl.DateTimeFormatOptions): string {
return new Intl.DateTimeFormat(locale, options || { year: 'numeric', month: 'long', day: 'numeric' }).format(date);
}

export function formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string {
return new Intl.NumberFormat(locale, options).format(value);
}

export function formatCurrency(value: number, locale: string, currency: string): string {
return new Intl.NumberFormat(locale, { style: 'currency', currency, minimumFractionDigits: 2 }).format(value);
}

export function formatRelativeTime(date: Date, locale: string): string {
const diff = (date.getTime() - Date.now()) / 1000;
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
const units: Array<[Intl.RelativeTimeFormatUnit, number]> = [
['year', 60 * 60 * 24 * 365], ['month', 60 * 60 * 24 * 30],
['day', 60 * 60 * 24], ['hour', 60 * 60], ['minute', 60], ['second', 1],
];
for (const [unit, secs] of units) {
if (Math.abs(diff) >= secs) return rtf.format(Math.round(diff / secs), unit);
}
return rtf.format(0, 'second');
}

Formatierungsbeispiele über Locales hinweg​

// Dates adapt to locale conventions automatically:
formatDate(new Date('2026-09-02'), 'en-US'); // "September 2, 2026"
formatDate(new Date('2026-09-02'), 'es-ES'); // "2 de septiembre de 2026"
formatDate(new Date('2026-09-02'), 'ja-JP'); // "2026年9月2日"
formatDate(new Date('2026-09-02'), 'ar-SA'); // "٢ سبتمبر ٢٠٢٦"

// Currency formatting respects local symbols and decimal rules:
formatCurrency(1234.56, 'en-US', 'USD'); // "$1,234.56"
formatCurrency(1234.56, 'ja-JP', 'JPY'); // "¥1,235" (no decimals for JPY)
formatCurrency(1234.56, 'ar-SA', 'SAR'); // "١٬٢٣٤٫٥٦ ر.س"

// Relative time uses natural language per locale:
formatRelativeTime(new Date(Date.now() - 3600000), 'en'); // "1 hour ago"
formatRelativeTime(new Date(Date.now() - 3600000), 'ja'); // "1時間前"
formatRelativeTime(new Date(Date.now() - 3600000), 'ar'); // "منذ ساعة واحدة"
tipp

Verwenden Sie immer die native Intl-API zur Formatierung, anstatt benutzerdefinierte Formatierer zu schreiben. Die Intl-API verwendet die CLDR-Daten (Common Locale Data Repository), die in die JavaScript-Engine eingebettet sind, vom Plattform-Hersteller gepflegt wird und Randfälle wie nicht-gregorianische Kalender (japanische Ära, Hidschri-Kalender) automatisch behandelt.

KI-gestützte Übersetzungs-Workflows​

Cursor kann Übersetzungen für gesamte Locale-Dateien mit KI generieren. Dies ist kein Ersatz für professionelle menschliche Übersetzer, aber es produziert hochwertige Erstentwürfe, die ein Übersetzer prüfen und verfeinern kann.

Übersetzungen aus einer Quelldatei generieren​

Read my English translation file at src/locales/en/common.json and
generate equivalent translations for Spanish (es), Japanese (ja),
and Arabic (ar). Preserve all interpolation variables like {{name}},
pluralization keys, and namespace structure exactly. Output three
separate JSON files in the correct locale directories.

Cursor liest die Quelldatei und produziert drei übersetzte JSON-Dateien. Die KI bewahrt ICU-Message-Format-Variablen, Pluralisierungssuffixe und die JSON-Struktur.

Strings aus Quellcode extrahieren​

Scan all .tsx files in my src/ directory and extract every hard-coded
user-facing string into a translation key. Create a new common.json
file with the extracted strings organized by component. Then replace
each hard-coded string in the source with a useTranslation() call.

Cursor führt ein codebasisweites Refactoring durch: Es identifiziert String-Literale in JSX, generiert eine strukturierte Übersetzungsdatei und ersetzt jedes Vorkommen durch den entsprechenden t()-Aufruf.

warnung

KI-generierte Übersetzungen sollten immer von einem Muttersprachler geprüft werden, bevor sie in die Produktion gehen. KI kann kontextabhängige Begriffe falsch übersetzen, kulturelle Nuancen übersehen und grammatikalisch korrekte, aber unnatürliche Formulierungen produzieren. Verwenden Sie KI-Übersetzungen als Entwurf, nicht als Endprodukt. Markieren Sie alle KI-übersetzten Dateien mit dem Status „Review erforderlich" in Ihrem Übersetzungs-Management-Workflow.

Batch-Übersetzungsskript​

// scripts/translate.ts
import * as fs from 'fs/promises';
import * as path from 'path';

interface TranslationFile {
[key: string]: string | TranslationFile;
}

const SOURCE_LOCALE = 'en';
const TARGET_LOCALES = ['es', 'ja', 'ar'];
const SOURCE_DIR = path.join(__dirname, '../public/locales', SOURCE_LOCALE);
const OUTPUT_BASE = path.join(__dirname, '../public/locales');

async function translateFile(
source: TranslationFile,
targetLocale: string
): Promise<TranslationFile> {
const result: TranslationFile = {};
for (const [key, value] of Object.entries(source)) {
if (typeof value === 'string') {
// Replace with actual AI translation API call
result[key] = `[${targetLocale}] ${value}`;
} else {
result[key] = await translateFile(value, targetLocale);
}
}
return result;
}

async function runBatchTranslation() {
const namespaces = await fs.readdir(SOURCE_DIR);

for (const locale of TARGET_LOCALES) {
const outputDir = path.join(OUTPUT_BASE, locale);
await fs.mkdir(outputDir, { recursive: true });

for (const nsFile of namespaces.filter((f) => f.endsWith('.json'))) {
const sourceData = JSON.parse(
await fs.readFile(path.join(SOURCE_DIR, nsFile), 'utf-8')
) as TranslationFile;

const translated = await translateFile(sourceData, locale);
const outputPath = path.join(outputDir, nsFile);
await fs.writeFile(outputPath, JSON.stringify(translated, null, 2), 'utf-8');
console.log(`Translated ${nsFile} for ${locale} -> ${outputPath}`);
}
}
console.log('Batch translation complete.');
}

runBatchTranslation().catch(console.error);
tipp

Fügen Sie das Batch-Übersetzungsskript zu Ihrer CI-Pipeline hinzu. Wenn ein Entwickler neue englische Strings hinzufügt, generiert der CI-Job automatisch Entwurf-Übersetzungen für alle Ziel-Locales und erstellt einen Pull Request mit den neuen Schlüsseln, markiert als review-bedürftig. Dies hält alle Locale-Dateien ohne manuelle Koordination synchron.

Erkennung fehlender Übersetzungen​

Rendern Sie diese Komponente einmal am Root Ihrer Anwendung. In der Entwicklung protokolliert sie fehlende Schlüssel in der Konsole. In Produktion werden Berichte an einen Error-Tracking-Service gesendet.

// src/components/MissingTranslationLogger.tsx
import { useTranslation } from 'react-i18next';
import { useEffect, useRef } from 'react';

export function MissingTranslationLogger() {
const { i18n } = useTranslation();
const reportedKeys = useRef(new Set<string>());

useEffect(() => {
i18n.on('missingKey', (lngs: string[], ns: string, key: string) => {
const fullKey = `${ns}:${key}`;
if (reportedKeys.current.has(fullKey)) return;
reportedKeys.current.add(fullKey);

if (import.meta.env.DEV) {
console.warn(`[i18n] Missing: ${fullKey} for ${lngs.join(', ')}`);
}
// In production: reportToErrorService({ type: 'missing_translation', key: fullKey, locales: lngs });
});
}, [i18n]);

return null;
}

Zusammenfassung​

Internationalisierung und Lokalisierung in Cursor geben Ihnen die Werkzeuge, um wirklich globale Anwendungen zu bauen. Mit den Mustern dieses Leitfadens — i18next- oder react-intl-Konfiguration, Namensraum-basierte Übersetzungsdateien, ICU-bewusste Pluralisierung, RTL logische CSS-Eigenschaften, native Intl-API-Formatierung und KI-gestützte Übersetzungs-Pipelines — können Sie ab Tag eins jede Sprache und jedes Locale unterstützen. Planen Sie Ihre i18n-Strategie früh, verwenden Sie logische CSS-Eigenschaften anstelle von physischen, lassen Sie KI Erstentwurf-Übersetzungen generieren, und lassen Sie immer einen Muttersprachler prüfen, bevor Sie in die Produktion gehen.