Execute o Lingo.dev no GitLab CI para que toda merge request que adiciona ou altera strings de origem volte com as traduções já preenchidas. O pipeline executa lingo push, o engine traduz apenas as chaves novas ou alteradas, e o resultado recebe commit direto na branch da MR — as traduções aparecem no diff da MR e passam por revisão antes de alguém fazer o merge. Nada cai na sua branch padrão sem ser visto.
Exemplo prático
Uma configuração completa e pronta para rodar está em gitlab.com/lingo.dev/gitlab-cicd-example.
Pré-requisitos#
Uma organização e um engine do Lingo.dev, além de uma chave de API de serviço (Dashboard → API Keys → create, type service).
Um projeto configurado para o Lingo.dev. Gere isso uma vez com:
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.jsondefine os idiomas de origem e destino, além dos globs de origem:json{ "sourceLocale": "en", "targetLocales": ["es", "fr", "de", "zh"], "files": [{ "pattern": "locales/en.json" }], "orgId": "org_...", "engineId": "eng_..." }Uma baseline já com commit. Na primeira vez, traduza tudo e faça commit para que o CI tenha um lockfile para comparar no diff:
bashnpx @lingo.dev/cli@latest push --backfill-missing --wait git add locales .lingo && git commit -m "chore(i18n): baseline translations"
Tokens de acesso#
Adicione duas variáveis de CI/CD masked em Settings → CI/CD → Variables:
LINGO_API_KEY— sua chave de serviço do Lingo.dev (lingo_sk_...). O CLI a usa automaticamente para autenticação.GITLAB_PUSH_TOKEN— um Project Access Token com o escopowrite_repository(função Developer). Isso permite que o CI faça commit das traduções de volta na branch da MR.
Crie o Project Access Token em Settings → Access tokens. CI_JOB_TOKEN não consegue enviar commits, então é necessário um token dedicado para a etapa de commit de volta. Project access tokens exigem um plano pago do GitLab.
Pipeline#
Faça commit deste .gitlab-ci.yml. Ele roda em merge requests direcionadas à branch padrão e envia as traduções de volta para a branch de origem da 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}"Teste#
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)O pipeline da MR executa lingo push --wait, faz commit de locales/{...}.json junto com o .lingo/lock.json atualizado na branch da MR, e as traduções aparecem no diff. Um revisor ajusta qualquer valor e depois faz o merge.
Como as edições humanas são preservadas#
lingo push preserva edições manuais por chave:
- Edite uma string de destino (com a origem em inglês inalterada) → essa string é mantida; todas as outras chaves continuam sendo traduzidas.
- A origem em inglês por trás de uma chave editada muda → uma nova tradução é gerada para essa chave (o significado mudou).
- Uma nova chave de origem é adicionada → ela é traduzida e adicionada, inclusive em arquivos que contêm edições manuais.
Assim, a correção feita por um revisor na MR sobrevive a todas as execuções posteriores do pipeline, enquanto chaves novas e alteradas entram automaticamente.
Modos de push#
lingo push— incremental; o padrão no CI. Traduz apenas chaves novas ou alteradas e preserva todo o resto. Adicione--waitno CI para que ele aguarde até que as saídas sejam gravadas (1.6.0+ é assíncrono por padrão).lingo push --backfill-missing— bootstrap de primeiro push / novo idioma; preenche arquivos de destino que ainda não existem. Não é para mudanças contínuas.lingo push --force --yes— traduz tudo novamente do zero (sobrescreve edições manuais). Raro.
Personalização#
- Auto-commit na branch padrão em vez de MRs: use um gatilho em
$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHe envie de volta para$CI_DEFAULT_BRANCH. É mais simples, mas a saída da IA cai na branch padrão sem revisão. - Fixar strings específicas: use
preservedKeys/lockedKeysem.lingo/config.jsonpara manter determinadas chaves fixas mesmo quando a origem delas mudar. - GitLab self-hosted: funciona sem alterações. No gitlab.com, contas novas precisam passar pela verificação de identidade antes que runners compartilhados executem jobs de CI.
