Führe Lingo.dev in GitLab CI aus, damit jede Merge Request, die neue Quell-Strings hinzufügt oder bestehende ändert, mit bereits eingetragenen Übersetzungen zurückkommt. Die Pipeline führt lingo push aus, die Engine übersetzt nur neue oder geänderte Schlüssel, und das Ergebnis wird direkt in den MR-Branch committet — die Übersetzungen erscheinen im MR-Diff und werden geprüft, bevor jemand merged. Nichts landet ungesehen in deinem Standard-Branch.
Praxisbeispiel
Ein vollständiges, lauffähiges Setup findest du unter gitlab.com/lingo.dev/gitlab-cicd-example.
Voraussetzungen#
Eine Lingo.dev-Organisation und Engine sowie ein Service-API-Key (Dashboard → API Keys → create, Typ service).
Ein für Lingo.dev konfiguriertes Projekt. Erstelle es einmalig mit:
bashnpx @lingo.dev/cli@latest init # scaffolds .lingo/config.json npx @lingo.dev/cli@latest link # connects the project to your org + engine.lingo/config.jsondefiniert Quell- und Ziel-Sprachen sowie die Source-Globs:json{ "sourceLocale": "en", "targetLocales": ["es", "fr", "de", "zh"], "files": [{ "pattern": "locales/en.json" }], "orgId": "org_...", "engineId": "eng_..." }Eine committete Ausgangsbasis. Beim ersten Mal übersetzt du alles und committest es, damit CI eine Lockfile zum Vergleichen hat:
bashnpx @lingo.dev/cli@latest push --backfill-missing --wait git add locales .lingo && git commit -m "chore(i18n): baseline translations"
Access Tokens#
Lege unter Settings → CI/CD → Variables zwei maskierte CI/CD-Variablen an:
LINGO_API_KEY— dein Lingo.dev-Service-Key (lingo_sk_...). Die CLI liest ihn automatisch zur Authentifizierung ein.GITLAB_PUSH_TOKEN— ein Project Access Token mit dem Scopewrite_repository(Rolle Developer). Damit kann CI die Übersetzungen zurück in den MR-Branch committen.
Erstelle das Project Access Token unter Settings → Access tokens. CI_JOB_TOKEN kann keine Commits pushen, daher brauchst du für den Commit-back-Schritt ein eigenes Token. Project Access Tokens setzen einen kostenpflichtigen GitLab-Plan voraus.
Pipeline#
Committe diese .gitlab-ci.yml. Sie läuft für Merge Requests, die auf den Standard-Branch zielen, und pusht die Übersetzungen zurück in den Source-Branch der MR:
stages:
- localize
localize:
stage: localize
image: node:22-alpine
rules:
# Only on merge requests that target the default branch.
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH'
before_script:
- apk add --no-cache git
script:
# Pin the CLI version — never @latest; bump deliberately after testing.
# --wait blocks until the engine finishes and writes files: since 1.6.0
# `push` is async by default (it submits the run and exits), so CI must
# wait to have something to commit.
- npx -y @lingo.dev/cli@1.6.0 push --wait
- |
if [ -z "$(git status --porcelain)" ]; then
echo "Translations already up to date — nothing to commit."
exit 0
fi
git config user.name "lingo-bot"
git config user.email "bot@lingo.dev"
git add locales .lingo/lock.json
# [skip ci] keeps the bot's own commit from re-triggering this pipeline.
git commit -m "chore(i18n): sync translations [skip ci]"
git push "https://oauth2:${GITLAB_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" "HEAD:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}"So testest du es#
git checkout -b feat/new-strings
# add or change a key in locales/en.json
git commit -am "feat: add strings" && git push -u origin feat/new-strings
# open an MR feat/new-strings -> main (UI, or: glab mr create --fill --target-branch main)Die MR-Pipeline führt lingo push --wait aus, committet locales/{...}.json sowie die aktualisierte .lingo/lock.json in den MR-Branch, und die Übersetzungen erscheinen im Diff. Eine prüfende Person passt einzelne Werte an und merged anschließend.
So bleiben manuelle Änderungen erhalten#
lingo push behält manuelle Änderungen pro Schlüssel bei:
- Du bearbeitest einen Ziel-String (der englische Quelltext bleibt unverändert) → dieser String bleibt erhalten; alle anderen Schlüssel werden weiterhin übersetzt.
- Der englische Quelltext hinter einem bearbeiteten Schlüssel ändert sich → für diesen Schlüssel wird eine neue Übersetzung erzeugt (die Bedeutung hat sich geändert).
- Ein neuer Quellschlüssel wird hinzugefügt → er wird übersetzt und ergänzt, selbst in Dateien mit manuellen Änderungen.
So bleibt die Korrektur einer prüfenden Person in der MR auch bei jedem späteren Pipeline-Lauf erhalten, während neue und geänderte Schlüssel automatisch nachfließen.
Push-Modi#
lingo push— inkrementell; der Standard in CI. Übersetzt nur neue oder geänderte Schlüssel und lässt alles andere unverändert. Füge--waitin CI hinzu, damit der Prozess blockiert, bis die Ausgaben geschrieben wurden (ab 1.6.0 ist standardmäßig asynchron).lingo push --backfill-missing— erster Push / Bootstrap für neue Sprachen; füllt Zieldateien, die noch nicht existieren. Nicht für laufende Änderungen gedacht.lingo push --force --yes— alles von Grund auf neu übersetzen (überschreibt manuelle Änderungen). Selten.
Anpassung#
- Automatisch in den Standard-Branch committen statt in MRs: per
$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHauslösen und zurück nach$CI_DEFAULT_BRANCHpushen. Einfacher, aber KI-Ausgaben landen ohne Prüfung im Standard-Branch. - Bestimmte Strings festschreiben: Verwende
preservedKeys/lockedKeysin.lingo/config.json, um ausgewählte Schlüssel fix zu halten, selbst wenn sich ihr Quelltext ändert. - Self-hosted GitLab: funktioniert unverändert. Auf gitlab.com müssen neue Konten zuerst die Identitätsprüfung bestehen, bevor Shared Runners CI-Jobs ausführen.
