# General Translation Knowledge Base: 如何翻译 i18n JSON 文件？
URL: https://generaltranslation.com/zh/kb/translate-json-files.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: 只翻译 JSON 区域设置文件中的字符串值。非复数键、{{name}} 之类的占位符以及标签都应保持不变，但复数键会因语言而异：例如波兰语需要 inbox_few 和 inbox_many。请使用能识别文件格式的工具或 API，然后先解析并验证输出，再进行审校。
Category: 翻译
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

本文讨论的是 i18next、react-intl、next-intl 和 vue-i18n 等国际化库所加载的区域设置文件，这类文件中的每个键都对应一个 UI 字符串。普通的数据 JSON (如 API 响应、产品目录) 则需要另作处理，因为对于这类数据，需要逐个字段决定哪些内容要翻译。

## JSON 区域设置文件的结构

大多数区域设置文件采用嵌套结构，如下所示，将键按对象分组。扁平文件则把同一路径写成单个以点分隔的键，例如 `"nav.home": "Home"`。

```json title="locales/en/common.json"
{
  "nav": { "home": "Home", "save": "Save" },
  "greeting": "Hello, {{name}}!",
  "inbox_one": "You have {{count}} new message",
  "inbox_other": "You have {{count}} new messages",
  "terms": "Read our <1>terms</1> before you continue."
}
```

文件中的字符串语法取决于所用的库，翻译时必须原样保留：

* **i18next** 使用双花括号 (`{{name}}`) 进行插值，并按照各语言的复数规则，将复数形式存储为带有[后缀](https://www.i18next.com/translation-function/plurals) (如 `_one`、`_few`、`_many` 和 `_other`) 的独立键。react-i18next 的 `<Trans>` 组件还会添加编号标签，例如 `<1>terms</1>`。
* **react-intl 和 next-intl** 存储的是 ICU 消息，其占位符使用单花括号，复数形式直接写在字符串内部：`{count, plural, one {# message} other {# messages}}`。 (参见[什么是 ICU 消息格式？](/kb/icu-message-format)) 
* **vue-i18n** 使用单花括号占位符，并用竖线分隔各复数形式：`no apples | one apple | {count} apples` (参见 [vue-i18n 复数处理](https://vue-i18n.intlify.dev/guide/essentials/pluralization)) 。

键的命名方式同样重要，因为译者往往只能看到键和值。 (参见[应如何命名 i18n 翻译键？](/kb/i18n-key-naming))

## 把 JSON 粘贴到翻译工具里会出什么问题

通用翻译工具会把文件当作普通文本处理，文件结构因此很容易遭到破坏：

* **键被翻译。** `"nav"` 变成了 `"nawigacja"`，应用就再也找不到对应的字符串。
* **占位符被改动。** `{{name}}` 变成了 `{{nombre}}`，或者少了一个花括号，导致插值在运行时失败。
* **标签损坏。** `<1>terms</1>` 被翻译、重新编号或直接删掉，链接因此无法包裹正确的文字。
* **文件不再是有效的 JSON。** 弯引号、译文字符串中未转义的 `"`，或末尾多出的逗号，都会导致整个文件解析失败。
* **缺少复数键。** 逐个翻译 `inbox_one` 和 `inbox_other` 这两个键，波兰语就会缺少 i18next 所需的 `inbox_few` 和 `inbox_many` 键，于是“5 messages”无法以正确的波兰语形式显示。 (各语言的复数类别请参阅[什么是 ICU 消息格式？](/kb/icu-message-format)。) 
* **短字符串含义不明。** `"save": "Save"` 在德语中既可以是 *speichern* (保存文件) ，也可以是 *sparen* (节省开支) ，而单凭键名往往看不出是哪一个。

## 翻译 JSON 区域设置文件的几种方式

| 方式                                          | 键和占位符                                              | 复数形式                | 短字符串的上下文                                   | 跟进变更               |
| ------------------------------------------- | -------------------------------------------------- | ------------------- | ------------------------------------------ | ------------------ |
| 手动翻译，或由译员直接在文件中翻译                           | 只要操作者足够细心，就不会出错                                    | 由操作者添加，前提是熟悉相关规则    | 操作者可以随时询问                                  | 每次变更都需手动处理         |
| 在线 JSON 翻译工具 (上传文件，下载译文)                    | 因工具而异；建议先用包含大量占位符的文件测试                             | 因工具而异               | 因工具而异                                      | 每次变更都需重新上传文件       |
| 编写脚本调用 DeepL 或 Google Cloud Translation API | 需自行遍历 JSON，只发送值；DeepL 的文档 API 也支持 `.json`，并默认保护占位符 | 缺失的键需自行生成           | DeepL 提供 `context` 参数；Google 不支持逐条字符串提供上下文 | 重新运行脚本，并对输出进行差异比对  |
| 将文件连同提示词交给 LLM                              | 通常会保留，但无法保证                                        | 只要你明确要求并列出类别，就会添加   | 取决于你在提示词中提供的内容                             | 重新运行提示词，并验证每次的结果   |
| General Translation CLI                     | 读取 JSON 结构并翻译值                                     | 翻译现有条目；需检查目标语言的复数键组 | 支持按键设置备注和长度限制，以及共享的词汇表和自定义提示词              | 复用匹配的译文；在构建前生成更新   |
| 翻译管理系统 (TMS)                                | 按格式解析，因此键不会被破坏                                     | 取决于 TMS 和文件格式       | 每个键可附带截图、评论和描述                             | 通过 TMS 的代码仓库集成同步文件 |

其中两种脚本方式需要进一步说明。DeepL 的[文档 API](https://developers.deepl.com/api-reference/document) 将 `json` 列为支持的格式，默认会原样保留 `{stars}` 或 `{{userName}}` 等占位符，并保护 ICU 复数骨架，只翻译各分支中的文本。Google 的[文档翻译](https://docs.cloud.google.com/translate/docs/advanced/translate-documents)支持 Office 文件和 PDF，但不支持 JSON；面向普通用户的 Google 翻译网站也只允许上传 `.docx`、`.pdf`、`.pptx` 和 `.xlsx` 文件。因此，使用 Google 时，你需要自行提取字符串值，再发送到文本 API。

如果只需一次性翻译一个小文件，可以仔细手动翻译，或使用 DeepL 的文档 API。如果你愿意自行负责验证并反复运行，可以使用脚本或 LLM 提示词。如果你希望生成并审校过的消息目录随代码变更一同发布，同时继续沿用现有的渲染库，请使用 General Translation。如果由译员团队在代码库之外协作，请使用[翻译管理系统](/kb/what-is-a-translation-management-system)。

## 如何使用 General Translation 翻译 JSON 文件

如果这些文件存放在你的代码仓库中，并且每次发布都会变化，那么可以在构建流程中加入一条命令，让它们始终保持最新。General Translation 的 CLI `gt` 可以翻译 JSON 区域设置文件，包括基于 i18next 和 ICU 的消息目录。你可以继续沿用现有的渲染库。使用 `npm install gt --save-dev` 安装，运行 [`npx gt init`](/docs/cli/reference/commands/init) 创建 `gt.config.json` 并登录，然后将 `files.json.include` 指向你的源文件。`[locale]` 占位符用于告诉 CLI 从哪里读取源区域设置的文件，以及将各个目标区域设置的文件写入到哪里：

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["de"],
  "files": {
    "json": {
      "include": ["locales/[locale]/*.json"]
    }
  }
}
```

对于现有项目，请先运行 [`npx gt upload`](/docs/cli/reference/commands/upload)，导入源内容和已审校的译文。

随后，[`npx gt translate`](/docs/cli/reference/commands/translate) 会读取 `locales/en/` 下的所有文件，并将德语文件写入 `locales/de/`，供你审校和提交；添加 `--dry-run` 可仅列出找到的文件，而不调用 API。本示例使用英语和德语，两者的基数消息都使用上文所示的 `one` 和 `other` 类别。后续运行时，可复用匹配的译文。

如果你的 `package.json` 声明了 `i18next` 或 `next-intl`，CLI 会保留该库的字符串语法，包括 ICU 消息。 (参见 [JSON 格式参考](/docs/cli/reference/formats/json-files)。) 要明确“Save”这类短字符串的含义，请添加配套文件 `common.metadata.json`，使用相同的键，并为每个字符串提供 `context` 备注或 `maxChars` 限制。 (参见[键级元数据](/docs/cli/reference/keyed-metadata)。) 例如，可以为 `nav.save` 添加“将编辑后的文档保存到磁盘”这样的备注，让服务获得的不只是一个含义模糊的标签。项目上下文组中的词汇表术语和自定义提示词同样会生效。

请在[仪表板编辑器](/docs/platform/dashboard/guides/reviewing-translations)中审校生成的措辞；如果审校人员编辑的是下载的文件，请使用 [`npx gt save-local`](/docs/cli/reference/commands/save-local)。下文的检查用于验证消息结构，与语言层面的审校相互独立。

## 在发布前检查翻译后的文件

使用与应用程序相同的消息格式和区域设置规则来验证输出：

* **解析 JSON 并比较非复数键。** 既要检查顶层键，也要检查嵌套键路径。即使 JSON 能够成功解析，缺失或被重命名的键仍可能导致查找失败。请根据应用程序的 schema 处理数组和非字符串值。
* **按组检查 i18next 复数形式。** 将 `inbox_one` 和 `inbox_other` 等键按基础键分组，然后检查目标区域设置所需的复数类别。波兰语还需要 `inbox_few` 和 `inbox_many`；它们是必须补充的键，而不是多余的键。日语可以只使用 `inbox_other`。i18next 遵循 `Intl.PluralRules`，并额外支持特殊的 `_zero` 形式；请根据基数或序数消息使用相应的规则。 (参见 [i18next 复数规则](https://www.i18next.com/translation-function/plurals)。) 
* **在每种翻译形式中保留占位符和标签。** 新增的复数形式同样需要检查。`{{name}}` 必须仍然指向 `name`；即使编号标签的顺序发生变化，它们也必须仍然对应预期的组件。
* **解析 ICU 消息并比较其参数。** 请使用解析器 (例如 [FormatJS ICU message parser](https://formatjs.github.io/docs/icu-messageformat-parser/)) ，而不是用正则表达式处理嵌套花括号。仅靠解析还不够：将复数参数从 `count` 改为 `total` 在语法上依然有效，但已无法匹配应用程序传入的值。请检查参数名称和类型、富文本标签、select 分支、`=0` 等精确数值选择器以及复数偏移量。应允许特定于区域设置的复数类别；ICU 必需的 `other` 分支可作为后备内容，因此 ICU 消息无需包含所有 CLDR 类别。

缺失复数键并不会明显报错。下面这个波兰语文件包含全部四个类别；如果逐键翻译，则只会得到 `inbox_one` 和 `inbox_other`：

```json title="locales/pl/common.json"
{
  "inbox_one": "Masz {{count}} nową wiadomość",
  "inbox_few": "Masz {{count}} nowe wiadomości",
  "inbox_many": "Masz {{count}} nowych wiadomości",
  "inbox_other": "Masz {{count}} nowej wiadomości"
}
```

使用 i18next 26.5.3 加载并设置 `fallbackLng: 'en'` 后，凡是需要 `few` 或 `many` 形式的数量，这个逐键翻译的文件都会回退到英文，而不是回退到波兰语的 `other` 形式：

```text title="Output"
count  category  key-by-key file              complete file
1      one       Masz 1 nową wiadomość        Masz 1 nową wiadomość
2      few       You have 2 new messages      Masz 2 nowe wiadomości
5      many      You have 5 new messages      Masz 5 nowych wiadomości
22     few       You have 22 new messages     Masz 22 nowe wiadomości
1.5    other     Masz 1.5 nowej wiadomości    Masz 1.5 nowej wiadomości
```

小数显示为 `1.5`，是因为该值没有经过数字格式化；如果可能出现小数，请按区域设置对其进行格式化。最后，通过应用程序的国际化库加载这些消息，并在实际上下文中审阅。用能触发每种复数形式的数量进行测试，必要时还应包括零和小数。语法检查无法判断译文是否准确，也无法判断文本是否适配界面。对于自定义插值规则和 vue-i18n 的竖线语法，需要针对这些格式调整检查方式。

## 要点总结

* 只翻译值，保留消息语法。非复数键保持不变；复数键可能因语言而异。
* 复数类别因语言而异：波兰语需要 `_few` 和 `_many` 键，而英语源文件中并没有这些键。
* DeepL 的文档 API 支持 JSON，并默认保护占位符；Google 的文档翻译不支持 JSON。
* 如需使用 `gt` CLI 翻译这些文件，请将 `files.json.include` 设置为 `[locale]` 模式，然后运行 [`npx gt translate`](/docs/cli/reference/commands/translate)；键级元数据可为每个字符串添加上下文和长度限制。
* 按目标区域设置验证 i18next 复数键组；对于 ICU 消息，应进行解析，而不是用正则表达式检查嵌套语法。

## Related questions

- [什么是 ICU 消息格式？](https://generaltranslation.com/zh/kb/icu-message-format.mdx): ICU 消息格式是一种语法，可在单个可翻译字符串中同时包含变量、复数形式、select 选择和序数，例如 {count, plural, one {# file} other {# files}}。每种语言只保留自身所需的 CLDR 复数类别：英语使用 one 和 other，波兰语则额外增加了 few 和 many。
- [应该如何命名 i18n 翻译键？](https://generaltranslation.com/zh/kb/i18n-key-naming.mdx): 应按文本出现的位置及其含义来命名键，例如 checkout.summary.title，而不是照搬英文措辞。即使英文相同，不同用途也应各用一个键：德语会把按钮上的 Open 译为 Öffnen，而把状态 Open 译为 Offen。
- [General Translation 与 i18next、react-intl 和 next-intl 对比：应该选择哪个国际化库？](https://generaltranslation.com/zh/kb/general-translation-vs-i18n-libraries.mdx): i18next、react-intl 和 next-intl 等开源库本身并不翻译任何内容。它们只负责显示你在各语言目录文件中提供的译文，这些译文由你、你的译者或其他服务编写。General Translation 的库同样开源，承担同样的工作，此外 General Translation 还会以你的代码为上下文完成翻译。
- [不使用翻译文件能实现 i18n 吗？](https://generaltranslation.com/zh/kb/i18n-without-translation-files.mdx): 可以。借助 General Translation 的 gt-react 和 gt-next 库，你可以为 React 和 Next.js 实现 i18n，既无需翻译键，也不用为每种语言维护 JSON 文件。文案以内联方式写在 JSX 的 `<T>` 组件中，gt translate 命令会在构建时结合代码中的上下文，为每种语言生成译文。Locadex 还能通过拉取请求让译文保持最新。
- [AI 翻译的准确度足以用于生产环境应用吗？](https://generaltranslation.com/zh/kb/is-ai-translation-accurate.mdx): 对于使用广泛的语言，通常可以，但前提是它要先通过针对你自己的语言、UI 字符串和高风险内容的评估。在 WMT25 中，Gemini 2.5 Pro 在 16 个语言对中有 10 个达到或超过了人工参考译文的水平，但文档级基准测试无法说明简短的界面字符串是否已经可以上线。

## Previous and next questions

- Previous question: [如何检测用户的浏览器语言？](https://generaltranslation.com/zh/kb/detect-user-language.mdx)
- Next question: [如何让网站支持多语言？](https://generaltranslation.com/zh/kb/make-website-multilingual.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/zh/kb/sitemap.md) for every article.
