在 GitLab CI 中运行 Lingo.dev,让每个新增或修改源字符串的合并请求返回时,翻译都已自动补齐。流水线会运行 lingo push,引擎只翻译新增或变更的键,并将结果直接提交到 MR 分支——翻译会出现在 MR diff 中,在人工合并前完成审查。默认分支不会悄无声息地混入任何未经审查的内容。
可运行示例
完整且可直接运行的示例见 gitlab.com/lingo.dev/gitlab-cicd-example。
前置条件#
你需要有一个 Lingo.dev organization 和 engine,以及一个服务 API 密钥(Dashboard → API Keys → create,类型选 service)。
还需要一个已为 Lingo.dev 配置好的项目。先通过以下命令生成一次:
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.json用于声明源语言/目标语言环境以及源文件 glob:json{ "sourceLocale": "en", "targetLocales": ["es", "fr", "de", "zh"], "files": [{ "pattern": "locales/en.json" }], "orgId": "org_...", "engineId": "eng_..." }还需要一个已提交的基线版本。第一次使用时,先翻译全部内容并提交,这样 CI 才有 lockfile 可用于 diff 对比:
bashnpx @lingo.dev/cli@latest push --backfill-missing --wait git add locales .lingo && git commit -m "chore(i18n): baseline translations"
访问令牌#
在 Settings → CI/CD → Variables 中添加两个已遮蔽的 CI/CD 变量:
LINGO_API_KEY—— 你的 Lingo.dev 服务密钥(lingo_sk_...)。CLI 会自动读取它完成身份验证。GITLAB_PUSH_TOKEN—— 一个带有write_repositoryscope 的项目访问令牌(角色为 Developer)。这样 CI 才能把翻译结果提交回 MR 分支。
在 Settings → Access tokens 下创建项目访问令牌。CI_JOB_TOKEN 无法推送提交,因此回写提交这一步需要单独的令牌。项目访问令牌需要使用付费版 GitLab 套餐。
流水线#
提交这个 .gitlab-ci.yml。它会在目标为默认分支的合并请求上运行,并将翻译结果推送回 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}"试试看#
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)MR 流水线会运行 lingo push --wait,将 locales/{...}.json 和更新后的 .lingo/lock.json 一并提交到 MR 分支,翻译随后就会出现在 diff 中。审查者可以调整任意值,然后再合并。
人工编辑为何能保留下来#
lingo push 会按键级别保留手动编辑:
- 如果你编辑了某个目标字符串(其英文源文未变)→ 该字符串会被保留;其他键仍会继续翻译。
- 如果某个已编辑键对应的英文源文发生变化 → 该键会重新生成新的翻译(因为含义变了)。
- 如果新增了一个源键 → 它会被翻译并添加进去,即使目标文件中包含手动编辑也是如此。
也就是说,审查者在 MR 中做出的修正会在后续每次流水线运行中保留下来,而新增和变更的键仍会自动进入流程。
push 模式#
lingo push—— 增量模式;也是 CI 默认模式。只翻译新增/变更的键,其余内容保持不变。请在 CI 中加上--wait,这样它会阻塞等待输出写入完成(1.6.0+ 默认为异步)。lingo push --backfill-missing—— 首次推送 / 新语言初始化模式;用于补齐尚不存在的目标文件。不适合持续更新时使用。lingo push --force --yes—— 从头重新翻译全部内容(会覆盖手动编辑)。很少使用。
自定义#
- **自动提交到默认分支,而不是 MR:**在
$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH上触发,并推送回$CI_DEFAULT_BRANCH。这样更简单,但 AI 输出会在未经审查的情况下直接进入默认分支。 - **固定特定字符串:**在
.lingo/config.json中使用preservedKeys/lockedKeys,即使源文本发生变化,也能让选定的键保持不变。 - **自托管 GitLab:**可直接照常使用。在 gitlab.com 上,新账户必须先完成身份验证,共享 runner 才会执行 CI 作业。
