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

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
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": {
"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"
}
}
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.
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": "