GitHub App

Moses Skoda工程师预计阅读 4 分钟

Lingo.dev GitHub App 使用 .lingo/config.json 为仓库启用持续本地化。

前置条件#

安装应用前,请先确认你已具备以下条件:

  • 一个已启用 GitHub 集成功能的 Lingo.dev 组织
  • 该组织中的一个本地化引擎
  • 对要连接的 GitHub 组织或仓库拥有管理员权限

如果你不是 GitHub 组织管理员,也可以先发起安装请求。Lingo.dev 要访问所选仓库,必须先由 GitHub 组织管理员批准该请求。

安装 GitHub 应用#

  1. 在 Lingo.dev 中打开你的组织。
  2. 前往 Studio > All integrations。
  3. 在 GitHub 卡片中,点击 Install on GitHub。
  4. 在 GitHub 中,选择要安装此应用的账户或组织。
  5. 选择 All repositories 或 Only select repositories。
  6. 点击 Install。

GitHub 会将你带回 Lingo.dev,进入 Connect GitHub 页面:

  1. 选择这个 GitHub 账户要通过哪个 Lingo.dev 组织进行本地化。系统会默认选中你上次打开的组织。
  2. 点击 Connect。
  3. 看到 GitHub connected 后,点击 View integration 打开该连接。

该连接会显示在 Studio 侧边栏的 Connected 下。打开 Settings 标签页,你可以看到 Account、Account type、Installation、Repositories,以及 Connection 当前是否正常。Workflow runs 标签页会列出该应用的每次运行,包括对应的仓库,以及触发运行的是推送还是拉取请求。

如果之后要添加或移除仓库,请打开该连接的 Settings 标签页,并使用 Manage on GitHub:仓库访问权限始终需要在 GitHub 中的安装项里选择。

如果你提交的是安装请求,而不是直接完成安装,Lingo.dev 会显示 Installation requested:这表示还需要 GitHub 组织管理员批准。批准后,GitHub 会将你带回 Connect GitHub 页面以完成后续操作。管理员可以在 GitHub 组织的 Settings > GitHub Apps 区域查看该请求。GitHub 也会在组织设置中向组织所有者显示待处理的应用请求。

添加仓库配置#

在已安装此应用的仓库中创建 .lingo/config.json:

json
{
  "engineId": "eng_abc123",
  "sourceLocale": "en",
  "targetLocales": ["es", "fr", "de"],
  "files": [
    { "pattern": "docs/en/**/*.md" },
    { "pattern": "docs/en/**/*.mdx" },
    { "pattern": "locales/en.json" }
  ],
  "github": {
    "workflows": {
      "onPushToDefaultBranch": { "enabled": true },
      "onPullRequest": { "enabled": true }
    },
    "safety": {
      "requireApproval": false
    }
  }
}
字段必填说明
engineId是用于翻译此仓库的 Lingo.dev 引擎。
sourceLocale是源文件路径中使用的源语言区域设置,例如 en 或 en-US。
targetLocales是要翻译到的语言区域代码。最多支持 50 个唯一区域设置。
files是相对于仓库根目录的源文件模式。最多支持 100 个模式。
github.workflows.onPushToDefaultBranch.enabled否当默认分支上的源文件发生变化时运行。默认启用。
github.workflows.onPullRequest.enabled否当拉取请求中的源文件发生变化时运行。默认禁用。
github.safety.requireApproval否自动推送或 PR 工作流执行翻译前需要审批。默认禁用。

使用 CLI 写入仓库配置。请依次运行以下命令:

  1. 运行 npx @lingo.dev/cli init,将源语言区域、目标语言区域和文件匹配模式写入 .lingo/config.json。
  2. 运行 npx @lingo.dev/cli login,登录并将凭据保存到本地的 ~/.lingo/credentials.json 中。
  3. 运行 npx @lingo.dev/cli link,选择组织和引擎。这会将 orgId 和 engineId 添加到 .lingo/config.json 中。

init 命令不会写入 github 部分。如需该部分,请从上方示例中复制。详情请参阅 CLI 快速入门。

文件模式#

files 模式用于指向源文件(默认区域设置文件)。应用会根据这些模式检查变更文件,并且只处理匹配且受支持的源文件。

这些模式相对于仓库根目录、区分大小写,并支持使用:

  • * 匹配单个路径段内的内容
  • **/ 匹配任意目录层级

模式不能以 / 开头,也不能包含 ..。

json
{
  "files": [
    { "pattern": "docs/en/**/*.md" },
    { "pattern": "src/content/en/**/*.mdx" },
    { "pattern": "messages/en.jsonc" }
  ]
}

文件选项#

files 中的每个条目除了自身的 pattern 外,还可以附带其他选项。所有选项均为可选,且各自只适用于特定格式。

选项适用范围说明
format全部覆盖根据文件扩展名推断出的格式。对于 OpenAPI YAML("yaml-openapi")为必填项。
include / exclude全部用于进一步细化条目匹配文件范围的 glob 列表,可与 pattern 搭配使用或替代它。
translateFrontmatterFieldsMarkdown、MDX、Markdoc需要翻译的 frontmatter 键。默认为 title 和 description。
translateComponentPropsMDX、Markdoc需要翻译的 MDX 组件 props 和 Markdoc 标签属性。
lockedKeysJSON、JSONC保持源值、不参与翻译的键路径。
preservedKeysJSON、JSONC保留现有目标值、不重新翻译的键路径。
injectLocaleJSON、JSONC将目标区域代码写入输出中的指定键(默认为 language)。

translateComponentProps 条目既可以是 prop 名称(适用于任意组件或标签上的该 prop),也可以是一个对象,用于将 props 限定到指定组件或标签:

json
{
  "files": [
    {
      "pattern": "src/content/en/**/*.mdx",
      "translateFrontmatterFields": ["title", "description"],
      "translateComponentProps": [
        "alt",
        { "component": ["Callout", "Hero"], "props": ["title", "subtitle"] }
      ]
    },
    {
      "pattern": "locales/en.json",
      "lockedKeys": ["app.version"],
      "injectLocale": { "enabled": true, "key": "language" }
    }
  ]
}

本地化文件写入位置#

应用会根据源路径和配置中的区域设置代码推导出每个目标路径:

源路径目标区域设置输出路径
docs/en/guide.mdesdocs/es/guide.md
docs/en-US/guide.mdfr-FRdocs/fr-FR/guide.md
locales/en.jsondelocales/de.json
README.mdeses/README.md
Localizable.xcstringses-MXLocalizable.xcstrings(同一文件)

请使用完整的目录名或文件名作为区域设置代码。例如,如果源文件位于 docs/en-US/,应设置 "sourceLocale": "en-US",而不是 "en"。如果源字符串位于 messages/en.json,则应设置 "sourceLocale": "en"。

当源路径包含区域设置目录时,应用会替换该目录;当源路径本身是以区域设置命名的文件时,应用会替换文件名。如果两种情况都不存在,应用会在源文件旁新建一个目标区域设置目录,并将翻译后的文件写入其中。

字符串目录是个例外。由于一个 .xcstrings 文件就包含了所有语言区域,目标路径与源路径相同——应用会将每个语言区域写入这同一个文件,不会再新增语言区域目录或文件名。

工作流#

推送到默认分支#

当默认分支上新增或修改了匹配的源文件时,应用会对其进行翻译,并将本地化文件提交到:

txt
lingo/translations/<default-branch>

随后,它会从该翻译分支向默认分支发起新的拉取请求或更新现有拉取请求。如果翻译分支已存在,新提交会追加到该分支。

如果同一次推送中新增或修改了目标文件,应用会将这些文件视为已处理,并将其带入翻译 PR,而不是覆盖它们。

拉取请求#

当 github.workflows.onPullRequest.enabled 设置为 true 时,应用会检查拉取请求中的变更,查找匹配的源文件,并将翻译后的文件直接提交到 PR 分支。

应用不会写入 fork 的 PR 分支,也不会通过 PR 评论向默认分支写入内容。只有当拉取请求处于打开状态时,应用才会提交翻译。

在 PR 中,Lingo.dev 会更新一条评论,列出已翻译的文件以及所有失败信息。

增量更新与恢复#

对于已有的目标文件,应用只会翻译它能检测到的源内容变更,而不会重新生成整个文件。对于新的目标文件,它会为每个已配置的目标区域设置创建对应的本地化文件。

如果之前的 PR 本地化遗漏了某项源内容变更,或在更新目标文件前失败,下一次 PR 同步时可通过比对 PR 源内容与基础分支源内容来补回遗漏的变更并完成翻译。

如果在当前处理的提交中源文件已不存在,应用会跳过该文件。

审批模式#

如果你希望自动翻译运行前先经过人工审批,请将 github.safety.requireApproval 设置为 true。

对于默认分支上的推送,Lingo.dev check run 会显示 Approve 和 Deny 操作。对于拉取请求,应用会发布一条翻译提案评论;请回复:

txt
/lingo approve

带作用域的 /lingo translate 命令无需经过此审批流程。

手动 PR 命令#

在拉取请求的评论中使用 /lingo,即可翻译源文件变更、补齐缺失的目标文件,或重新翻译选定的键。

翻译拉取请求中的源文件变更,同时保留未变更的翻译:

txt
/lingo translate

要补齐缺失的目标文件,请指定文件通配模式:

txt
/lingo translate docs/en/**/*.md
txt
/lingo translate docs/en/**/*.mdx --locales fr,es
txt
/lingo translate docs/en/**/*.md --force

命令参考:

命令说明
/lingo显示帮助。
/lingo help显示帮助。
/lingo translate翻译此 PR 中的源文件变更,同时保留未变更的翻译。
/lingo translate --locales fr,es将此 PR 中的源文件变更翻译为已配置且选定的目标语言区域。
/lingo translate <glob>...为匹配的源文件翻译缺失的目标文件。
/lingo translate <glob>... --locales fr,es将运行范围限制在已配置的目标区域设置内。命令解析器会将区域设置值转换为小写。
/lingo translate <glob>... --force翻译当前范围内所有匹配的源文件和区域设置,并覆盖现有目标文件。
/lingo translate [<glob>...] --key <pattern>重新翻译现有翻译中匹配的命名键,同时保留其他翻译。重复使用 --key 可指定多个模式。
/lingo translate [<glob>...] --key <pattern> --context "text"为所有选定的键和语言区域提供指导,仅在本次运行中生效。需配合 --key 使用;--context 只需提供一次。
/lingo approve批准 PR 中待处理的翻译提案。

命令必须单独占一行。Glob 会与同时匹配你已配置的 files 源模式的文件进行匹配。与配置模式相同,命令 glob 不能以 / 开头,也不能包含 ..。

明确指定文件通配模式且未使用 --key 时,/lingo translate <glob>... 会以补齐模式运行:仅创建 PR 分支上缺失的目标文件。要重新生成这些已有的目标文件,请添加 --force。

重新翻译选定的键#

使用 --key 更新选定的命名键,同时保留其他翻译,包括其他键中已接受的修改:

txt
/lingo translate locales/en.json --key "auth.login" --locales fr,es

键模式支持精确匹配、前缀匹配和通配符匹配:

模式选择范围
auth.login匹配命名键 auth.login 及该前缀下的所有后代键。
auth匹配 auth 及其后代键,例如 auth.login 和 auth.logout。
auth.*通过通配模式匹配键,例如 auth.login 和 auth.logout。

重复使用 --key 可选择多个模式。请用引号括起包含空格的键模式。文件通配模式和 --locales 可缩小请求范围;省略文件通配模式则会选择所有已配置的源文件:

txt
/lingo translate --key "auth.*" --key "checkout.submit" --locales fr

按键限定范围的翻译要求文件格式支持命名键,且每个选定的目标语言区域都已有翻译。不支持命名键的格式和尚无翻译的语言区域会被跳过。要先创建缺失的目标文件,请运行 /lingo translate <glob>...,不加 --key。在按键限定范围的命令中添加 --force,不会扩大所选键的范围,也不会替换其他翻译。

为单次运行提供专属上下文#

添加 --context,可说明选定键的含义,或提供语气和大小写方面的指导:

txt
/lingo translate --key "keyname" --context "button label, keep sentence case"

--context 需配合 --key 使用。上下文只需提供一次,即可应用于本次运行中所有选定的键和语言区域。上下文会随运行记录保留,供重试和查看历史时使用,但不会更改已保存的引擎规则或指令,也不会被后续运行继承。现有的术语表、占位符和输出格式规则仍然适用。

请将命令写在同一行。包含空格的上下文需用单引号或双引号括起;也可以使用 --context="button label"。要在文本中使用与外层引号相同的引号,请用反斜杠转义:

txt
/lingo translate --key "checkout.submit" --context "Use \"Save\" as a button label"

在用引号括起的上下文中,只有 \\ 和外层同类引号前的反斜杠会被解码。$name 和 ${name} 等占位符、\n 等序列以及标点符号均按字面保留。

支持的格式#

GitHub App 会根据文件扩展名识别以下格式:

  • JSON(.json)
  • JSONC(.jsonc)
  • Markdown(.md)
  • MDX(.mdx)
  • Markdoc
  • Xcode 字符串目录(.xcstrings)

同样支持以 YAML 编写的 OpenAPI 文档,但不会自动识别。请在文件模式上设置 "format": "yaml-openapi":

json
{
  "files": [
    { "pattern": "openapi/en.yaml", "format": "yaml-openapi" }
  ]
}

Xcode 字符串目录#

Xcode 字符串目录会将所有语言区域保存在同一个 .xcstrings 文件中,因此应用会直接在原文件中翻译,而不是为每个语言区域单独创建一个文件。目标路径与源路径相同:每个目标语言区域都会写入同一个文件,与源语言以及任何已翻译的语言区域并存。将源模式直接指向该目录,并将 sourceLocale 设置为其开发语言:

json
{
  "sourceLocale": "en",
  "targetLocales": ["es-MX", "fr-CA", "pt-BR"],
  "files": [{ "pattern": "**/*.xcstrings" }]
}

大规模更新#

应用可能会将翻译输出拆分为多个提交。当一次运行需要在单个提交中写入超过 100 个文件,或翻译文件内容总量超过 5 MB 时,就会出现这种情况。

出现这种情况时,提交信息会包含批次编号,例如:

txt
feat: Lingo.dev translations (1/3)
feat: Lingo.dev translations (2/3)
feat: Lingo.dev translations (3/3)

没有匹配项时会发生什么#

如果缺少 .lingo/config.json,应用会静默跳过该仓库。配置文件存在后,如果配置无效,则会生成失败的 check run;在 PR 中,还会附带一条包含校验错误的评论。

如果没有变更文件匹配你的源模式,应用会在不写入任何翻译的情况下完成运行。对于 /lingo translate,机器人会回复简短说明,例如没有匹配文件、没有匹配区域设置,或所有目标文件都已存在。

添加 .lingo/config.json 的那次推送,只会翻译该次推送中有更改且符合匹配条件的源文件。已有但未更改的源文件需要单独补齐翻译。要为这些文件创建缺失的目标文件,请从同一仓库内的分支发起拉取请求,而非从派生仓库发起。然后在拉取请求中留言 /lingo translate <glob>。

后续步骤#