Unabhängige Produktvergleiche & Kaufberatung - Wir verdienen an qualifizierten Verkäufen
Zurück zum Blog
I18n Guide: Android, React & Vue Lokalisierung komplett

I18n Guide: Android, React & Vue Lokalisierung komplett

11. Dezember 2025
Jonas Höttler
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? {#was-ist-i18n}

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 {#dateiformate}

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 {#android-i18n}

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 {#react-i18n}

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 {#vue-i18n}

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 {#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}

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 {#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 {#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

Teilen:

Weitere Artikel