Warum Lokalisierung automatisieren?#

Manuelle Lokalisierungs-Workflows sind der Flaschenhals in den Release-Zyklen der meisten Teams:

text
Entwickler: "Ich habe das Update gepusht."
PM: "Hast du die neuen Strings an den Übersetzer geschickt?"
Entwickler: "...mache ich jetzt."
PM: "Wann kommen die Übersetzungen zurück?"
Entwickler: "Vielleicht nächste Woche?"

Indem du Lokalisierung in deine CI/CD Pipeline integrierst, werden neue Strings automatisch übersetzt und deine lokalisierte App ist immer versandbereit.

Das Ziel: Kontinuierliche Lokalisierung#

Vor der Automatisierung:

text
Code → Manueller Export → E-Mail an Übersetzer → Tage warten → Manueller Import → Test → Deploy

Nach der Automatisierung:

text
Code → Push → CI erkennt neue Strings → Auto-Übersetzen → PR mit Übersetzungen → Merge → Deploy

Der Unterschied: Tage vs. Minuten.

Architektur-Überblick#

Eine typische automatisierte Lokalisierungs-Pipeline hat drei Komponenten:

  1. String-Erkennung: Neue oder geänderte Strings in deinem Commit identifizieren
  2. Übersetzung: Strings an deinen Übersetzungsservice senden (shipglobal.dev)
  3. Integration: Übersetzte Dateien zurück in dein Repository committen
text
┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│  Git Push    │ →  │  CI Pipeline │ →  │  Übersetzen  │
│  (neue       │    │  (Änderungen │    │  (API-Aufruf)│
│   Strings)   │    │   erkennen)  │    └──────┬──────┘
└─────────────┘    └──────────────┘           │
                                              ▼
┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│  Merge      │ ←  │  PR mit      │ ←  │  Übersetzungen│
│             │    │  Übersetzungen│    │  committen   │
└─────────────┘    └──────────────┘    └─────────────┘

Implementierung: GitHub Actions#

Hier ist ein praktischer GitHub Actions Workflow, der neue Strings bei jedem Push übersetzt:

Basis-Workflow#

yaml
# .github/workflows/localize.yml
name: Localize

on:
  push:
    branches: [main, develop]
    paths:
      - 'src/locales/en/**'      # Englische Quelldateien beobachten
      - 'src/strings/en.json'     # Oder deine spezifische Datei

jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Auf String-Änderungen prüfen
        id: changes
        run: |
          # Erkennen ob Quellsprach-Dateien geändert wurden
          CHANGED=$(git diff --name-only HEAD~1 HEAD -- src/locales/en/)
          echo "changed_files=$CHANGED" >> $GITHUB_OUTPUT
          if [ -z "$CHANGED" ]; then
            echo "has_changes=false" >> $GITHUB_OUTPUT
          else
            echo "has_changes=true" >> $GITHUB_OUTPUT
          fi

      - name: Über shipglobal.dev API übersetzen
        if: steps.changes.outputs.has_changes == 'true'
        env:
          SHIPGLOBAL_API_KEY: ${{ secrets.SHIPGLOBAL_API_KEY }}
        run: |
          # Quelldatei hochladen und Übersetzungen abrufen
          for lang in de es fr ja pt; do
            curl -X POST https://api.shipglobal.dev/v1/translate \
              -H "Authorization: Bearer $SHIPGLOBAL_API_KEY" \
              -F "file=@src/locales/en/strings.json" \
              -F "target_language=$lang" \
              -o "src/locales/$lang/strings.json"
          done

      - name: Pull Request erstellen
        if: steps.changes.outputs.has_changes == 'true'
        uses: peter-evans/create-pull-request@v5
        with:
          title: 'chore: Übersetzungen aktualisieren'
          body: 'Automatisiertes Übersetzungs-Update ausgelöst durch Änderungen an englischen Quellstrings.'
          branch: translations/auto-update
          commit-message: 'chore: Übersetzungen für neueste String-Änderungen aktualisieren'

Erweiterter Workflow mit Validierung#

yaml
# .github/workflows/localize-advanced.yml
name: Localize (Advanced)

on:
  push:
    branches: [main]
    paths:
      - 'src/locales/en/**'

jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Node.js einrichten
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Neue Strings erkennen
        id: detect
        run: |
          # Aktuelle und vorherige englische Strings vergleichen
          git show HEAD~1:src/locales/en/strings.json > /tmp/old_strings.json 2>/dev/null || echo '{}' > /tmp/old_strings.json
          NEW_KEYS=$(node -e "
            const old = require('/tmp/old_strings.json');
            const curr = require('./src/locales/en/strings.json');
            const newKeys = Object.keys(curr).filter(k => !old[k] || old[k] !== curr[k]);
            console.log(newKeys.length);
          ")
          echo "new_key_count=$NEW_KEYS" >> $GITHUB_OUTPUT

      - name: Neue Strings übersetzen
        if: steps.detect.outputs.new_key_count > 0
        env:
          SHIPGLOBAL_API_KEY: ${{ secrets.SHIPGLOBAL_API_KEY }}
        run: |
          TARGET_LANGUAGES="de es fr ja pt ko it"
          for lang in $TARGET_LANGUAGES; do
            curl -X POST https://api.shipglobal.dev/v1/translate \
              -H "Authorization: Bearer $SHIPGLOBAL_API_KEY" \
              -F "file=@src/locales/en/strings.json" \
              -F "target_language=$lang" \
              -o "src/locales/$lang/strings.json"
          done

      - name: Übersetzungen validieren
        run: |
          # Alle Übersetzungsdateien auf gültiges JSON prüfen
          for file in src/locales/*/strings.json; do
            python3 -m json.tool "$file" > /dev/null || {
              echo "Ungültiges JSON: $file"
              exit 1
            }
          done

          # Prüfen ob alle Dateien die gleichen Keys haben
          node -e "
            const fs = require('fs');
            const en = Object.keys(require('./src/locales/en/strings.json')).sort();
            const dirs = fs.readdirSync('./src/locales').filter(d => d !== 'en');
            for (const dir of dirs) {
              const keys = Object.keys(require('./src/locales/' + dir + '/strings.json')).sort();
              const missing = en.filter(k => !keys.includes(k));
              if (missing.length) {
                console.error(dir + ' fehlende Keys:', missing);
                process.exit(1);
              }
            }
            console.log('Alle Übersetzungen haben übereinstimmende Keys');
          "

      - name: Pull Request erstellen
        uses: peter-evans/create-pull-request@v5
        with:
          title: 'chore: Übersetzungen aktualisieren (${{ steps.detect.outputs.new_key_count }} neue Strings)'
          body: |
            Automatisiertes Übersetzungs-Update.
            - **Neue/geänderte Strings**: ${{ steps.detect.outputs.new_key_count }}
            - **Aktualisierte Sprachen**: de, es, fr, ja, pt, ko, it
            - **Validierung**: Bestanden
          branch: translations/auto-update

Implementierung: GitLab CI#

yaml
# .gitlab-ci.yml
localize:
  stage: post-build
  rules:
    - changes:
        - src/locales/en/**
  script:
    - |
      for lang in de es fr ja pt; do
        curl -X POST https://api.shipglobal.dev/v1/translate \
          -H "Authorization: Bearer $SHIPGLOBAL_API_KEY" \
          -F "file=@src/locales/en/strings.json" \
          -F "target_language=$lang" \
          -o "src/locales/$lang/strings.json"
      done
    - git config user.email "ci@yourapp.com"
    - git config user.name "CI Bot"
    - git add src/locales/
    - git commit -m "chore: Übersetzungen aktualisieren" || true
    - git push origin HEAD:translations/auto-update

Best Practices#

1. Nur übersetzen was sich geändert hat#

Übersetze nicht deine gesamte Datei bei jedem Push neu. Nutze Translation Memory um unveränderte Strings nicht erneut zu übersetzen und Kosten zu senken.

2. PRs erstellen, nicht auto-mergen#

Erstelle immer einen Pull Request für Übersetzungsänderungen. Das gibt deinem Team Sichtbarkeit und die Möglichkeit zum Review.

3. Übersetzungen validieren#

Füge Validierungsschritte hinzu:

  • JSON/XML Syntax-Validierung
  • Key-Vollständigkeitsprüfung (alle Keys in allen Sprachen vorhanden)
  • Platzhalter-Konsistenz (z.B. {{name}} existiert in Übersetzungen)
  • String-Längen-Warnungen (Übersetzungen deutlich länger als Quelle)

4. Branch Protection nutzen#

Lass Übersetzungs-PRs nicht deinen normalen Review-Prozess umgehen. Behandle sie wie Code-Änderungen.

5. Translation Memory cachen#

Wenn du shipglobal.dev nutzt, handhabt Translation Memory das automatisch — du zahlst nur für neue oder geänderte Strings.

6. Merge-Konflikte handhaben#

Übersetzungsdateien sind JSON/XML, und Merge-Konflikte sind häufig. Nutze eine Strategie:

  • Immer aus der englischen Quelldatei neu generieren
  • Oder ein Merge-Tool verwenden, das dein Dateiformat versteht

Plattform-spezifische Tipps#

iOS (Localizable.strings)#

bash
# .strings direkt zu shipglobal.dev hochladen
curl -X POST https://api.shipglobal.dev/v1/translate \
  -F "file=@en.lproj/Localizable.strings" \
  -F "target_language=de" \
  -o "de.lproj/Localizable.strings"

Android (strings.xml)#

bash
# strings.xml direkt hochladen
curl -X POST https://api.shipglobal.dev/v1/translate \
  -F "file=@app/src/main/res/values/strings.xml" \
  -F "target_language=de" \
  -o "app/src/main/res/values-de/strings.xml"

React / Next.js (JSON)#

bash
# i18n JSON-Datei hochladen
curl -X POST https://api.shipglobal.dev/v1/translate \
  -F "file=@public/locales/en/common.json" \
  -F "target_language=de" \
  -o "public/locales/de/common.json"

Monitoring und Alerts#

Richte Alerts für deine Lokalisierungs-Pipeline ein:

  • Übersetzungsfehler: Team benachrichtigen wenn die API nicht erreichbar ist
  • Key-Abweichungen: Alarm wenn Übersetzungen Keys fehlen
  • Budget-Alerts: Übersetzungskosten pro Monat tracken
  • PR-Alter: Alarm wenn Übersetzungs-PRs länger als 24 Stunden nicht gemerged sind

Fazit#

Die Automatisierung der Lokalisierung in deiner CI/CD Pipeline entfernt die größte Reibung beim Global-Gehen. Statt dass Lokalisierung ein manueller, fehleranfälliger Prozess ist, der Releases verzögert, wird sie ein automatisierter Schritt, der im Hintergrund passiert.

Starte einfach — ein Basis-Workflow der bei Push übersetzt und einen PR erstellt. Dann iteriere: füge Validierung, Monitoring und Optimierung hinzu, wenn deine Anforderungen wachsen.

Das Ergebnis: Deine App ist immer bereit zum Versand in jeder Sprache, mit null manuellem Übersetzungsmanagement.