Allgemeine Artikel

I18n Guide: Android, React & Vue Lokalisierung komplett

Internationalisierung (i18n) ist unverzichtbar, um mit deiner App ein globales Publikum zu erreichen. Egal ob du für Android, React oder Vue entwickelst – dieser umfassende Guide behandelt alles, was du über die korrekte Implementierung von i18n wissen musst: von Dateiformaten über Best Practices bis hin zu häufigen Fehlern.

Jonas HöttlerJonas Höttler
11. Dezember 2025
15 min Lesezeit

Der ultimative I18n Guide für Android, React und Vue: JSON, XML und Best Practices für Übersetzungen

Internationalisierung (i18n) ist unverzichtbar, um mit deiner App ein globales Publikum zu erreichen. Egal ob du für Android, React oder Vue entwickelst – dieser umfassende Guide behandelt alles, was du über die korrekte Implementierung von i18n wissen musst: von Dateiformaten über Best Practices bis hin zu häufigen Fehlern.

Inhaltsverzeichnis

  1. Was ist i18n und warum ist es wichtig?
  2. Übersetzungsdateiformate: JSON vs XML
  3. Android i18n Implementierung
  4. React i18n mit i18next
  5. Vue i18n Implementierung
  6. Plattformübergreifende Best Practices
  7. Häufige Fehler und wie du sie vermeidest
  8. Übersetzungs-Management Workflow
  9. Bibliotheken-Vergleich

Was ist i18n und warum ist es wichtig?

i18n (Internationalisierung) ist der Prozess, deine Anwendung so zu gestalten, dass sie ohne Code-Änderungen an verschiedene Sprachen und Regionen angepasst werden kann. Der Begriff "i18n" kommt von den 18 Buchstaben zwischen "i" und "n" in "internationalization".

Wichtige Vorteile:

  • Größere Marktreichweite: Erreiche 75% der Internetnutzer, die kein Englisch sprechen
  • Bessere Nutzererfahrung: Nutzer bevorzugen Apps in ihrer Muttersprache
  • Höherer Umsatz: Lokalisierte Apps haben 26% höhere Konversionsraten
  • SEO-Vorteile: Ranke für Suchanfragen in lokalen Sprachen

i18n vs L10n:

  • i18n (Internationalisierung): Deinen Code für mehrere Sprachen vorbereiten
  • L10n (Lokalisierung): Inhalte tatsächlich für bestimmte Regionen übersetzen

Übersetzungsdateiformate: JSON vs XML

JSON-Format

JSON ist das bevorzugte Format für Web-Anwendungen (React, Vue) und wird zunehmend auch in mobilen Apps verwendet.

Vorteile:

  • Leichtgewichtig und einfach zu parsen
  • Native Unterstützung in JavaScript
  • Verschachtelte Strukturen zur Organisation
  • Breite Tool-Unterstützung

Beispielstruktur:

{ "common": { "save": "Speichern", "cancel": "Abbrechen", "delete": "Löschen", "loading": "Wird geladen..." }, "auth": { "login": "Anmelden", "logout": "Abmelden", "register": "Konto erstellen", "forgotPassword": "Passwort vergessen?" }, "errors": { "network": "Netzwerkfehler. Bitte versuche es erneut.", "required": "Dieses Feld ist erforderlich", "invalidEmail": "Bitte gib eine gültige E-Mail-Adresse ein" } }

XML-Format

XML ist das native Format für Android und bietet mehr Struktur für komplexe Übersetzungen.

Vorteile:

  • Native Android-Unterstützung
  • Eingebaute Pluralisierung
  • Kommentare für Übersetzer möglich
  • Starke Typisierung mit Attributen

Beispielstruktur:

<?xml version="1.0" encoding="utf-8"?> <resources> <string name="save">Speichern</string> <string name="cancel">Abbrechen</string> <!-- Authentifizierung --> <string name="login">Anmelden</string> <string name="logout">Abmelden</string> <!-- Pluralisierung --> <plurals name="items_count"> <item quantity="one">%d Artikel</item> <item quantity="other">%d Artikel</item> </plurals> </resources>

Wann welches Format verwenden?

AnwendungsfallEmpfohlenes Format
Android NativeXML (strings.xml)
React/Vue WebJSON
React NativeJSON
Plattformübergreifend (geteilt)JSON
Komplexe PluralisierungXML oder ICU-Format

Android i18n Implementierung

Projektstruktur

app/src/main/res/
├── values/
│   └── strings.xml          # Standard (Englisch)
├── values-de/
│   └── strings.xml          # Deutsch
├── values-es/
│   └── strings.xml          # Spanisch
├── values-fr/
│   └── strings.xml          # Französisch
├── values-zh-rCN/
│   └── strings.xml          # Chinesisch (vereinfacht)
└── values-ar/
    └── strings.xml          # Arabisch (RTL)

Basis strings.xml

<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Meine App</string> <string name="welcome_title">Willkommen</string> <string name="welcome_message">Danke, dass du unsere App nutzt!</string> <!-- Mit Parametern --> <string name="greeting">Hallo, %1$s!</string> <string name="items_selected">%1$d von %2$d ausgewählt</string> <!-- Mit HTML --> <string name="terms_link"><![CDATA[ Ich stimme den <a href="https://example.com/terms">Nutzungsbedingungen</a> zu ]]></string> </resources>

Strings in Kotlin verwenden

// In Activity/Fragment val welcome = getString(R.string.welcome_title) val greeting = getString(R.string.greeting, userName) // Pluralisierung val itemsText = resources.getQuantityString( R.plurals.items_count, count, count ) // In Jetpack Compose @Composable fun WelcomeScreen() { Text(text = stringResource(R.string.welcome_title)) Text(text = stringResource(R.string.greeting, userName)) }

Android JSON-Alternative

Für dynamische Übersetzungen oder servergesteuerte Inhalte:

class TranslationManager(private val context: Context) { private var translations: Map<String, Any> = emptyMap() suspend fun loadTranslations(locale: String) { // Aus Assets oder API laden val json = context.assets.open("i18n/$locale.json") .bufferedReader().use { it.readText() } translations = Json.decodeFromString(json) } fun t(key: String, vararg args: Any): String { val keys = key.split(".") var value: Any? = translations for (k in keys) { value = (value as? Map<*, *>)?.get(k) } return (value as? String)?.let { if (args.isNotEmpty()) String.format(it, *args) else it } ?: key } } // Verwendung val tm = TranslationManager(context) tm.loadTranslations("de") val text = tm.t("auth.login") // "Anmelden"

React i18n mit i18next

Installation

npm install i18next react-i18next i18next-browser-languagedetector

Projektstruktur

src/
├── i18n/
│   ├── index.ts
│   └── locales/
│       ├── en/
│       │   ├── common.json
│       │   └── auth.json
│       ├── de/
│       │   ├── common.json
│       │   └── auth.json
│       └── es/
│           ├── common.json
│           └── auth.json
└── App.tsx

i18n Konfiguration

// src/i18n/index.ts import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; // Übersetzungen importieren import enCommon from './locales/en/common.json'; import enAuth from './locales/en/auth.json'; import deCommon from './locales/de/common.json'; import deAuth from './locales/de/auth.json'; const resources = { en: { common: enCommon, auth: enAuth, }, de: { common: deCommon, auth: deAuth, }, }; i18n .use(LanguageDetector) .use(initReactI18next) .init({ resources, fallbackLng: 'en', defaultNS: 'common', interpolation: { escapeValue: false, // React escaped bereits }, detection: { order: ['localStorage', 'navigator', 'htmlTag'], caches: ['localStorage'], }, }); export default i18n;

Übersetzungsdateien

// locales/en/common.json { "save": "Save", "cancel": "Cancel", "delete": "Delete", "loading": "Loading...", "greeting": "Hello, {{name}}!", "itemsCount_one": "{{count}} item", "itemsCount_other": "{{count}} items" }
// locales/de/common.json { "save": "Speichern", "cancel": "Abbrechen", "delete": "Löschen", "loading": "Wird geladen...", "greeting": "Hallo, {{name}}!", "itemsCount_one": "{{count}} Artikel", "itemsCount_other": "{{count}} Artikel" }

Übersetzungen in Komponenten verwenden

import { useTranslation } from 'react-i18next'; function MyComponent() { const { t, i18n } = useTranslation(); const changeLanguage = (lng: string) => { i18n.changeLanguage(lng); }; return ( <div> <h1>{t('greeting', { name: 'Max' })}</h1> <p>{t('itemsCount', { count: 5 })}</p> <button onClick={() => changeLanguage('en')}>English</button> <button onClick={() => changeLanguage('de')}>Deutsch</button> </div> ); }

i18next Platzhalter-Syntax

i18next verwendet doppelte geschweifte Klammern für Interpolation:

{ "welcome": "Willkommen, {{username}}!", "orderStatus": "Bestellung #{{orderId}} ist {{status}}", "price": "Preis: {{amount, currency(EUR)}}", "date": "Datum: {{date, datetime}}" }
t('welcome', { username: 'Hans' }) t('orderStatus', { orderId: 123, status: 'versendet' })

React i18next Pluralisierung

i18next behandelt Pluralisierung mit Suffixen:

{ "message_zero": "Keine Nachrichten", "message_one": "Eine Nachricht", "message_other": "{{count}} Nachrichten" }
t('message', { count: 0 }) // "Keine Nachrichten" t('message', { count: 1 }) // "Eine Nachricht" t('message', { count: 5 }) // "5 Nachrichten"

Vue i18n Implementierung

Installation

npm install vue-i18n@9

Projektstruktur

src/
├── i18n/
│   ├── index.ts
│   └── locales/
│       ├── en.json
│       ├── de.json
│       └── es.json
├── main.ts
└── App.vue

Vue i18n Konfiguration

// src/i18n/index.ts import { createI18n } from 'vue-i18n'; import en from './locales/en.json'; import de from './locales/de.json'; const i18n = createI18n({ legacy: false, // Composition API verwenden locale: 'de', fallbackLocale: 'en', messages: { en, de, }, }); export default i18n;
// main.ts import { createApp } from 'vue'; import App from './App.vue'; import i18n from './i18n'; const app = createApp(App); app.use(i18n); app.mount('#app');

Übersetzungsdateien

// locales/en.json { "nav": { "home": "Home", "about": "About", "contact": "Contact" }, "auth": { "login": "Log In", "logout": "Log Out" }, "greeting": "Hello, {name}!", "items": "no items | one item | {count} items" }
// locales/de.json { "nav": { "home": "Startseite", "about": "Über uns", "contact": "Kontakt" }, "auth": { "login": "Anmelden", "logout": "Abmelden" }, "greeting": "Hallo, {name}!", "items": "keine Artikel | ein Artikel | {count} Artikel" }

Vue i18n in Templates

<script setup lang="ts"> import { useI18n } from 'vue-i18n'; const { t, locale } = useI18n(); function changeLanguage(lang: string) { locale.value = lang; } </script> <template> <nav> <a href="/">{{ t('nav.home') }}</a> <a href="/about">{{ t('nav.about') }}</a> </nav> <h1>{{ t('greeting', { name: 'Max' }) }}</h1> <p>{{ t('items', 5) }}</p> <select v-model="locale"> <option value="en">English</option> <option value="de">Deutsch</option> </select> </template>

Vue i18n Übersetzung in JavaScript

Für Übersetzungen außerhalb von Templates:

// In Composition API import { useI18n } from 'vue-i18n'; export function useMyComposable() { const { t } = useI18n(); function showNotification() { alert(t('notifications.success')); } return { showNotification }; } // Außerhalb von Komponenten (z.B. in Stores) import i18n from '@/i18n'; export function getErrorMessage(code: string) { return i18n.global.t(`errors.${code}`); }

Plattformübergreifende Best Practices

1. Einheitliche Schlüssel-Benennung

Verwende die gleiche Schlüsselstruktur auf allen Plattformen:

Plattform   | Schlüssel-Format
------------|------------------
Android     | auth_login
React       | auth.login
Vue         | auth.login

Empfohlen: Verwende eine gemeinsame Quelle

// shared/translations/de.json { "auth": { "login": "Anmelden", "logout": "Abmelden" } }

Dann für jede Plattform transformieren:

// build-scripts/transform-android.js function toAndroidXml(json) { let xml = '<?xml version="1.0" encoding="utf-8"?>\n<resources>\n'; function flatten(obj, prefix = '') { for (const [key, value] of Object.entries(obj)) { const fullKey = prefix ? `${prefix}_${key}` : key; if (typeof value === 'string') { xml += ` <string name="${fullKey}">${escapeXml(value)}</string>\n`; } else { flatten(value, fullKey); } } } flatten(json); xml += '</resources>'; return xml; }

2. Platzhalter standardisieren

Verschiedene Frameworks verwenden unterschiedliche Platzhalter-Syntax:

PlattformSyntaxBeispiel
Android%1$s, %2$dHallo, %1$s!
i18next{{name}}Hallo, {{name}}!
Vue i18n{name}Hallo, {name}!
ICU{name}Hallo, {name}!

Lösung: ICU Message Format als Quelle verwenden

{ "greeting": "Hallo, {name}!" }

Beim Build transformieren:

// Für Android "Hallo, %1$s!" // Für i18next "Hallo, {{name}}!"

3. Pluralisierungsstrategie

Verwende ICU Plural-Format als Standard:

{ "items": "{count, plural, =0 {Keine Artikel} one {# Artikel} other {# Artikel}}" }

4. Kontext für Übersetzer

Stelle immer Kontext bereit:

{ "play": "Abspielen", "_play.context": "Button zum Starten der Videowiedergabe", "play_game": "Spielen", "_play_game.context": "Button zum Starten eines Spiels" }

Für Android XML:

<string name="play" comment="Button zum Starten der Videowiedergabe">Abspielen</string>

5. String-Verkettung vermeiden

// ❌ Schlecht - Wortstellung variiert je nach Sprache const msg = t('hallo') + ' ' + name + '!'; // ✅ Gut const msg = t('greeting', { name });

6. Textlängen-Expansion beachten

Deutsche Texte sind oft 30% länger als englische. Designe flexibel:

/* Buttons wachsen lassen */ .button { min-width: 100px; width: auto; padding: 8px 16px; }

7. RTL-Unterstützung

Designe immer mit RTL (Rechts-nach-Links) im Hinterkopf:

/* Logische Properties verwenden */ .card { margin-inline-start: 16px; /* Nicht margin-left */ padding-inline-end: 8px; /* Nicht padding-right */ }
<!-- Android: start/end statt left/right verwenden --> <TextView android:layout_marginStart="16dp" android:layout_marginEnd="8dp" />

Häufige Fehler und wie du sie vermeidest

Fehler 1: Fehlende Fallback-Sprache

Problem: App stürzt ab, wenn Übersetzung fehlt.

Lösung:

// React i18next i18n.init({ fallbackLng: 'en', saveMissing: true, missingKeyHandler: (lng, ns, key) => { console.warn(`Fehlende Übersetzung: ${key}`); } });

Fehler 2: Hardcodierte Strings im Code

Problem: Strings sind im gesamten Codebase verstreut.

Lösung: Linting-Regeln verwenden:

// .eslintrc { "rules": { "i18next/no-literal-string": "error" } }

Fehler 3: Datums-/Zahlenformatierung

Problem: String-Verkettung für Datumsangaben verwenden.

Lösung: Intl API nutzen:

// Datum const formatted = new Intl.DateTimeFormat(locale, { dateStyle: 'long' }).format(date); // Zahlen const price = new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(19.99);

Fehler 4: Unvollständige Übersetzungen

Problem: Mit fehlenden Übersetzungen deployen.

Lösung: CI-Checks hinzufügen:

# Auf fehlende Übersetzungen prüfen npx i18next-parser --fail-on-warnings

Fehler 5: Übersetzung im falschen Kontext

Problem: Gleiches Wort, unterschiedliche Bedeutung ("Speichern" als Datei speichern vs. Geld sparen).

Lösung: Eindeutige Schlüssel mit Kontext verwenden:

{ "save_file": "Speichern", "save_money": "Sparen", "post_noun": "Beitrag", "post_verb": "Posten" }

Fehler 6: Geschlechtsspezifische Sprache

Problem: Sprachen wie Deutsch haben grammatikalisches Geschlecht.

Lösung: ICU SelectFormat verwenden:

{ "welcome": "{gender, select, male {Willkommen, Herr {name}} female {Willkommen, Frau {name}} other {Willkommen, {name}}}" }

Übersetzungs-Management Workflow

Empfohlener Workflow

1. Entwickler fügt Schlüssel hinzu  →  "auth.login": "Anmelden"
2. Push ins Repository              →  Git Commit
3. CI extrahiert Strings            →  i18next-parser / formatjs extract
4. Sync zum TMS                     →  Crowdin / Lokalise / Phrase
5. Übersetzer arbeiten              →  Im TMS-Interface
6. Auto-Sync zurück                 →  Webhook / CI Job
7. Deploy mit neuen                 →  Build enthält Übersetzungen
   Übersetzungen

Beliebte Translation Management Systeme

ToolIdeal fürPreise
CrowdinOpen Source, große TeamsGratis für OSS
LokaliseEntwickler-ErfahrungAb 120€/Monat
PhraseEnterpriseAb 25€/Monat
POEditorKleine TeamsGratis-Tier verfügbar
TransifexGroße ProjekteAb 99$/Monat

CI-Integration Beispiel

# .github/workflows/i18n.yml name: i18n Sync on: push: paths: - 'src/i18n/locales/**' jobs: sync: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Neue Schlüssel extrahieren run: npx i18next-parser - name: Zu Crowdin hochladen uses: crowdin/github-action@v1 with: upload_sources: true upload_translations: false env: CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_TOKEN }}

Bibliotheken-Vergleich

React i18n Bibliotheken

BibliothekBundle-GrößeFeaturesIdeal für
i18next42kbVollständig, PluginsDie meisten Projekte
react-intl35kbICU-Format, formatjsEnterprise
LinguiJS5kbMinimal, Compile-TimePerformance

Vue i18n Bibliotheken

BibliothekBundle-GrößeFeaturesIdeal für
vue-i18n30kbOffiziell, vollständigDie meisten Projekte
vue-i18next42kbi18next-ÖkosystemPlattformübergreifend
fluent-vue15kbMozilla FluentKomplexe Grammatik

Android i18n Optionen

AnsatzKomplexitätIdeal für
strings.xmlNiedrigStandard-Apps
JSON + CustomMittelDynamische Inhalte
ICU4JHochKomplexe Formatierung

Quick-Start Checkliste

  • Übersetzungsdatei-Struktur einrichten
  • Fallback-Sprache konfigurieren
  • Sprachwechsler implementieren
  • RTL-Unterstützung hinzufügen
  • Pluralisierung einrichten
  • Datums-/Zahlenformatierung konfigurieren
  • Warnungen bei fehlenden Übersetzungen aktivieren
  • CI-Extraktion einrichten
  • Translation Management Tool verbinden
  • Übersetzungsprozess für das Team dokumentieren

Fazit

i18n von Anfang an richtig zu implementieren, spart unzählige Stunden später. Die wichtigsten Erkenntnisse:

  1. Das richtige Format wählen: JSON für Web, XML für Android nativ
  2. Plattformübergreifend standardisieren: ICU-Format als Quelle der Wahrheit nutzen
  3. Häufige Fehler vermeiden: Kein Hardcoding, keine Verkettung
  4. Workflow automatisieren: CI/CD-Integration mit TMS
  5. Mit echten Sprachen testen: Nicht nur mit Englisch testen

Egal ob du für Android, React oder Vue entwickelst – diese Best Practices stellen sicher, dass deine App mit minimalem Aufwand global skalieren kann.

Weiterführende Links