CLI 原生支持 monorepo。你可以在根目录放一个 i18n.json,配合递归 ** glob 自动发现包;也可以使用单个根配置,为每个包显式指定路径;或者在每个包里单独放置 i18n.json,再结合矩阵策略的 GitHub Action 使用。
如何选择合适的模式#
| 场景 | 模式 | 配置文件 | GitHub Action |
|---|---|---|---|
| 所有包共享相同的目录结构和 locale,并且希望自动纳入新包 | 递归单配置 | 根目录一个 i18n.json,配合 ** glob | 单步执行,默认 working-directory |
| 所有包共享相同的源 locale 和目标 locale | 单配置 | 根目录一个 i18n.json | 单步执行,默认 working-directory |
| 不同包需要不同的目标 locale | 按包配置 | 每个包一个 i18n.json | 使用 working-directory 的矩阵策略 |
| 不同包需要不同的引擎(术语表、品牌语调) | 按包配置 + 独立引擎 | 每个包一个 i18n.json,各自绑定自己的 engineId | 使用 working-directory 的矩阵策略 |
模式 1:递归单配置#
当所有包都遵循相同的目录结构(例如 apps/<name>/locales/[locale].json)时,可以在根目录的单个 i18n.json 中使用递归 ** glob。只要新包符合这套结构,就会被自动发现:
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.15",
"locale": {
"source": "en",
"targets": ["es", "fr", "de", "ja"]
},
"buckets": {
"json": {
"include": ["apps/**/locales/[locale].json"]
}
}
}CLI 默认会忽略 node_modules、.git、dist、build、.next 和 .turbo,因此递归模式不会深入 vendored 目录或构建产物目录。如果还有其他内容想跳过,可以自行添加 exclude 条目。
GitHub Action 与下面的模式 2 完全一致:单步执行,使用默认工作目录。
如果所有包共享相同的源 locale、目标 locale、目录结构和本地化引擎,而且你不想每次新增包时都去修改 i18n.json,就适合使用这种模式。
模式 2:单配置,共享 locale#
在仓库根目录放一个 i18n.json,然后用 bucket 路径显式指向各个包:
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.15",
"locale": {
"source": "en",
"targets": ["es", "fr", "de", "ja"]
},
"buckets": {
"json": {
"include": [
"apps/web/locales/[locale].json",
"apps/dashboard/locales/[locale].json"
]
},
"markdown": {
"include": ["packages/docs/content/[locale]/*.md"]
}
}
}GitHub Action:标准单步配置:
name: Translate
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
translate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: lingodotdev/lingo.dev@main
with:
api-key: ${{ secrets.LINGODOTDEV_API_KEY }}
pull-request: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}如果所有包共享相同的源 locale、目标 locale 和本地化引擎,但目录结构差异较大,不适合用递归 glob 一把抓,就适合使用这种模式。
模式 3:按包配置,不同 locale#
每个包都有自己的 i18n.json,并独立定义目标 locale:
apps/web/i18n.json:
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.15",
"locale": {
"source": "en",
"targets": ["es", "fr", "de", "ja", "ko", "zh-Hans"]
},
"buckets": {
"json": {
"include": ["locales/[locale].json"]
}
}
}apps/marketing/i18n.json:
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.15",
"locale": {
"source": "en",
"targets": ["es", "fr", "de"]
},
"buckets": {
"json": {
"include": ["locales/[locale].json"]
},
"markdown": {
"include": ["content/[locale]/*.md"]
}
}
}GitHub Action:配合 working-directory 的矩阵策略:
name: Translate
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
translate:
runs-on: ubuntu-latest
strategy:
matrix:
package: [apps/web, apps/marketing]
steps:
- uses: actions/checkout@v4
- uses: lingodotdev/lingo.dev@main
with:
api-key: ${{ secrets.LINGODOTDEV_API_KEY }}
working-directory: ${{ matrix.package }}
pull-request: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}矩阵中的每个条目都会让 CLI 仅作用于对应包的 i18n.json。该 Action 只会翻译该包配置中声明的文件。
模式 4:按包配置,独立引擎#
结构与模式 3 相同,但每个包连接到不同的本地化引擎。这样一来,每个包都能拥有自己的术语表、品牌语调和模型配置。
apps/product/i18n.json:
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.15",
"locale": {
"source": "en",
"targets": ["es", "fr", "de", "ja"]
},
"buckets": {
"json": {
"include": ["locales/[locale].json"]
}
},
"engineId": "eng_ProductApp123"
}apps/docs/i18n.json:
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.15",
"locale": {
"source": "en",
"targets": ["es", "fr"]
},
"buckets": {
"markdown": {
"include": ["content/[locale]/*.md"]
}
},
"engineId": "eng_DocsEngine456"
}GitHub Action 的 YAML 与模式 3 完全相同——每个 i18n.json 中的 engineId 都会自动把翻译路由到正确的引擎。
lockfile 的工作方式#
每个 i18n.json 所在的位置,都会在同一目录下生成对应的 i18n.lock 文件。lockfile 会记录哪些源内容已经翻译过,从而支持增量更新。模式 1 和 2 在根目录下各有一个 lockfile;模式 3 和 4 则是每个包各自拥有自己的 lockfile。
FAQ#
新增包会自动被发现吗?
只有模式 1(递归 **)可以。模式 3 和 4 下,每新增一个包,都需要在 workflow 的 matrix.package 列表里增加一个新条目。
我可以为不同包设置不同的 locale 吗?
可以。使用模式 3 或 4。每个包的 i18n.json 都可以独立声明自己的 locale.targets 数组。
我需要一个 workflow 文件,还是多个?
一个采用矩阵策略的 workflow 文件就能覆盖所有包。矩阵中的每个条目都会针对不同的 working-directory 运行,从而把 CLI 的作用范围限定在对应包的配置上。
不同包可以共用术语表,但使用不同的 locale 吗?
可以。让两个包都指向同一个 engineId,再在各自的 i18n.json 中配置不同的 locale.targets。引擎中的术语表会应用到所请求的对应语言对上。
