# General Translation Knowledge Base: 如何翻译 YAML 区域设置文件？
URL: https://generaltranslation.com/zh/kb/translate-yaml-files.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: 只翻译字符串值，键、占位符（如 %{name} 或 {{name}}）和锚点均保持不变。包含冒号或以 % 或 { 开头的值需加引号；为每个文件设置区域设置根键或文件名，并补充目标语言所需的复数键。General Translation 的 CLI 会原地翻译 YAML 值，不会改动键。
Category: 翻译
Last updated: 2026-10-28
Authors: General Translation Team
Published: 2026-10-28

YAML 区域设置文件将键映射到面向用户的字符串，每种语言对应一个文件 (或一个顶层键) 。翻译时只替换字符串值，键、占位符和文件结构则必须原样保留，完全符合框架的预期。YAML 在这方面比 JSON 更棘手：未加引号的冒号或开头的 `%` 会导致解析失败，`no` 还可能被解析为布尔值。因此，即使译文本身读起来没问题，生成的文件仍可能被应用拒绝或误读。

## 常见的 YAML 区域设置布局

各框架存放语言标识的位置不同，占位符语法也各不相同，而翻译后的文件必须遵循同样的规则：

| 框架                                                                                                            | 源文件                             | 语言标识位置                      | 占位符                                | 复数形式                                    |
| ------------------------------------------------------------------------------------------------------------- | ------------------------------- | --------------------------- | ---------------------------------- | --------------------------------------- |
| [Rails I18n](https://guides.rubyonrails.org/i18n.html)                                                        | `config/locales/en.yml`         | 顶层键 (`en:`)                 | `%{name}`                          | `one` 和 `other` 键；借助 rails-i18n 可支持更多形式 |
| [Symfony](https://symfony.com/doc/current/translation.html#translation-resource-file-names-and-locations)     | `translations/messages.en.yaml` | 文件名 (`domain.locale.yaml`)  | `%name%`；`+intl-icu` 文件中为 `{name}` | 在字符串内使用 ICU 复数                          |
| [Hugo](https://gohugo.io/functions/lang/translate/#translation-tables)                                        | `i18n/en.yaml`                  | 文件名                         | Go 模板，例如 `{{ . }}`                 | CLDR 键 (`one`、`few`、`many`、`other`)     |
| i18next 搭配 [i18next-fs-backend](https://github.com/i18next/i18next-fs-backend)                                | `locales/en/common.yml`         | 目录                          | `{{name}}`                         | 带后缀的键，例如 `_one` 和 `_other`              |
| vue-i18n 搭配 [unplugin-vue-i18n](https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n) | `locales/en.yaml`               | 文件名                         | `{name}`                           | 在同一字符串中用竖线分隔各形式                         |

Rails 对结构的要求最为严格。[Rails 指南](https://guides.rubyonrails.org/i18n.html#configuring-the-load-paths-and-default-locale)会加载 `config/locales` 下的所有 `.yml` 和 `.rb` 文件，且每个文件中“顶层键就是区域设置”。因此，无论文件名是什么，西班牙语文件都必须以 `es:` 开头。

## 会破坏翻译后的文件的 YAML 陷阱

下面这个 Rails 文件的写法能确保每个值在翻译后都完好无损：

```yaml title="config/locales/en.yml"
en:
  checkout:
    title: "Note: prices include VAT"
    joined: "%{name} joined your team"
    confirm_no: "No"
    terms: |
      By paying, you agree to our terms.
      Refunds take 5 to 7 days.
    items:
      one: "%{count} item"
      other: "%{count} items"
```

每一行都用于防范一种常见错误：

* **冒号。** 不加引号的 `title: Note: prices include VAT` 是语法错误，而翻译还可能引入英文原文中没有的冒号。
* **以指示符开头的值。** 普通值不能以 `%`、`{`、`[`、`@` 或反引号开头。`joined: %{name} joined your team` 会解析失败，而 `{{name}} joined` 会被当作映射的开头。翻译会改变语序，英文中位于句中的占位符经常会挪到句首。Hugo 官方文档正是出于这个原因，给 `'{{ . }} day'` 加上了引号。
* **布尔值。** Ruby 的 YAML 解析器遵循 YAML 1.1，因此未加引号的 `yes`、`no`、`on` 和 `off` 会被转换为 `true` 和 `false`；而 YAML 1.2 解析器 (包括大多数 JavaScript 工具) 则会将它们读取为字符串。Rails 指南指出[这些键必须转义](https://guides.rubyonrails.org/i18n.html#configuring-the-load-paths-and-default-locale)，值也是如此。就连以 `no:` 为根键的挪威语文件，在 Ruby 中也会以 `false` 为键加载。
* **块标量。** `|` 保留换行，`>` 将换行折叠为空格。翻译后的各行必须保持块的缩进，否则值会提前结束，甚至导致整个文件无法解析。
* **锚点和别名。** `&name` 定义一个块，`*name` 复用该块，因此修改锚点处的值会同时影响所有别名。Rails 可以加载它们，但 unplugin-vue-i18n 明确表示其 YAML 资源[不支持别名](https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n)，因此在使用别名之前，请先确认你的加载器是否支持。

如果 `title` 和 `joined` 这两行都不加引号，并分别放在单独的文件中，Ruby 的解析器 (Ruby 2.6、Psych 3.1) 会在 Rails 读到任何一个键之前就拒绝这两个文件：

```text title="Output"
unquoted-colon.yml: mapping values are not allowed in this context at line 3 column 16
unquoted-placeholder.yml: found character that cannot start any token while scanning for the next token at line 3 column 13
```

最简单的做法是给每个值都加上引号：用双引号括起来的内容始终是字符串，`%{name}`、冒号和 `No` 都会原样保留为文本。

### 复数键因语言而异

英语使用 `one` 和 `other`，但俄语和波兰语还需要 `few` 和 `many`。缺少这些键并不会报错。如果使用 i18n gem 的复数处理后端，并搭配类似 rails-i18n 自带的俄语复数规则，那么从英语逐键翻译而来的文件，在所有本应使用 `few` 的数量上都会静默改用 `other`。下面两个俄语文件中的译文均出自 General Translation：

```text title="Output"
count  key-by-key (one, other)              complete (one, few, many, other)
1      У вас 1 непрочитанное сообщение.     У вас 1 непрочитанное сообщение.
3      У вас 3 непрочитанных сообщений.     У вас 3 непрочитанных сообщения.
5      У вас 5 непрочитанных сообщений.     У вас 5 непрочитанных сообщений.
22     У вас 22 непрочитанных сообщений.    У вас 22 непрочитанных сообщения.
```

[Rails 指南](https://guides.rubyonrails.org/i18n.html#locale-specific-rules)指出，默认只启用英语的复数规则；其他语言的规则需借助 I18n `Pluralization` 后端或 rails-i18n gem 补充。Hugo 的翻译表同样采用 CLDR 类别，涵盖从 `zero` 到 `other` 的各个类别。

### 区域设置根键必须修改

在 Rails 中，翻译后的文件如果仍以 `en:` 开头，并不会报错，而是会被当作英语加载：在一次使用 i18n gem 的测试中，一个根键仍为 `en:` 的西班牙语文件把英语标题覆盖成了西班牙语，而对 `es` 调用 `I18n.t` 时则返回了缺少翻译的提示。请在每个翻译后的文件中修改根键，并让 `config.i18n.available_locales` 保持同步。

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

| 方式                                                                                                               | 键和占位符                   | 复数键                                    | 短字符串的上下文            | 跟进变更                |
| ---------------------------------------------------------------------------------------------------------------- | ----------------------- | -------------------------------------- | ------------------- | ------------------- |
| 手动翻译                                                                                                             | 译者熟悉 YAML 时较为安全         | 仅当译者了解该语言的规则时才会添加                      | 译者可以询问              | 每次变更都需手动处理          |
| [i18n-tasks](https://github.com/glebm/i18n-tasks) `translate-missing` (Rails) 搭配 Google Translate、DeepL 或 OpenAI | 发送前将 `%{...}` 替换为 token | 加载 rails-i18n 规则后，`missing` 会报告缺失的复数形式 | 各后端独立的选项和提示词        | 补全缺失的键；修改过的英文不会重新翻译 |
| 翻译管理系统 (TMS)                                                                                                     | 按格式解析                   | 取决于所用 TMS                              | 截图、评论、词汇表           | 代码仓库同步或文件交换         |
| 用 LLM 脚本处理文件                                                                                                     | 通常会保留，但无法保证引号正确         | 仅在你明确要求时添加                             | 取决于你在提示词中提供的内容      | 每次都需重新运行并验证         |
| [General Translation 的 CLI](/docs/cli/reference/formats/yaml-files)                                              | 仅翻译字符串值，键保持不变           | 翻译源文件中已有的键                             | 词汇表、自定义提示词、按键设置的元数据 | 每次运行都会复用未变更的译文      |

只有一两个小文件时，手动编辑即可。如果 Rails 应用需要检查缺失的键，且你能接受用机器翻译填补空缺、再自行审校，可以选择 i18n-tasks。 (两者如何搭配使用，请参阅 [General Translation 与 i18n-tasks 对比](/kb/general-translation-vs-i18n-tasks)。) 如果文件由译者负责维护，可以选择 [TMS](/kb/what-is-a-translation-management-system)。如果希望所有区域设置文件都能随英文变更自动重新生成，并在应用、文档和营销网站之间统一术语，可以选择 General Translation。

## 使用 General Translation 翻译 YAML 文件

General Translation 的 CLI `gt` 可通过 `gt.config.json` 中的 `yaml` 条目翻译 `.yaml` 和 `.yml` 文件。`[locale]` 占位符用于标记语言代码所在的位置，因此文件名中包含区域设置的格式无需改动即可直接使用。例如在 Symfony 中，`translations/*.[locale].yaml` 会将 `messages+intl-icu.en.yaml` 映射为 `messages+intl-icu.fr.yaml`；在 Hugo 中：

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["de", "pt-BR"],
  "files": {
    "yaml": {
      "include": ["i18n/[locale].yaml"]
    }
  }
}
```

经 `gt` 2.26.11 验证，CLI 的路径解析会将 `i18n/en.yaml` 映射到 `i18n/de.yaml` 和 `i18n/pt-BR.yaml`，与 Hugo 以语言标签命名文件的方式一致。使用 [`npx gt login`](/docs/cli/reference/commands/login) 登录并设置项目 ID ([CLI 凭据](/docs/cli/guides/configuring#credentials)) ，使用 [`npx gt upload`](/docs/cli/reference/commands/upload) 导入已有译文，然后运行 [`npx gt translate`](/docs/cli/reference/commands/translate)。

General Translation 只翻译字符串值，其余内容一概不动：键、数字、布尔值和注释都原样保留；带锚点的块只翻译一次，其别名仍指向该块。因此，以下几个框架相关的细节仍需你自行处理：

* **Rails 区域设置根键。** CLI 不会重命名键，包括顶层的 `en:`；[`transform`](/docs/cli/reference/config#files) 选项重命名的是输出文件，而不是键。请为 CLI 保留不带根键的工作文件 (例如 `translations/en.yml`) ，并在导出到 `config/locales` 时再加上区域设置根键。 (简短的导出脚本请参阅 [General Translation 与 Rails I18n 对比](/kb/general-translation-vs-rails-i18n)。) 
* **复数键。** CLI 只翻译源文件中已有的复数键，因此由英文 `one`/`other` 源生成的俄语文件只包含 `one` 和 `other`。在加载 rails-i18n 的情况下运行 `i18n-tasks missing`，即可找出仍需补充的 `few` 和 `many` 键。
* **未加引号的 `yes`、`no`、`on` 和 `off`。** General Translation 按 YAML 1.2 规则解析，在该规则下这些值属于字符串，因此会被当作文本翻译。请在源文件中为它们加上引号——在 Rails 中本来也应如此。
* **库语法。** `i18next` 和 `next-intl` 支持 ICU 消息等自定义字符串语法；对于其他库，请在首次运行后检查使用了自定义语法的字符串。

如需消除&quot;Save&quot;这类短字符串的歧义，可添加配套文件 `.metadata.yaml`，为每个键设置 `context` 备注或 `maxChars` 限制 ([键级元数据](/docs/cli/reference/formats/yaml-files#metadata)) 。[上下文分组](/docs/platform/dashboard/guides/defining-context-for-translations)中的词汇表和自定义提示词可确保产品术语在你的 YAML 消息目录以及该组内所有其他项目中保持一致，审校人员也可以[在仪表板中编辑译文](/docs/platform/dashboard/guides/reviewing-translations)。如果有人在本地编辑了翻译后的文件，请在下次运行前执行 [`npx gt save-local`](/docs/cli/reference/commands/save-local)，以免这些修改被覆盖。

每次运行后，请按应用实际的加载方式验证这些文件：Rails 使用 `i18n-tasks missing` 和 `i18n-tasks check-consistent-interpolations`，Symfony 使用 `lint:translations`，Hugo 则执行一次构建。 (Symfony 的初始化设置请参阅 [General Translation 与 Symfony Translation 对比](/kb/general-translation-vs-symfony-translation)；如果你的消息目录是 JSON 格式，请参阅[如何翻译 i18n JSON 文件？](/kb/translate-json-files)。)

## 常见问题

### YAML 区域设置文件中的每个值都应该加引号吗？

给每个值都加上引号是最稳妥的习惯。用双引号括起来的值始终是字符串，因此无论翻译前还是翻译后，冒号、开头的 `%{name}` 以及 `No` 之类的词都不会改变文件的解析方式。

### 为什么我已经翻译了某种语言，Rails 应用仍然提示“translation missing”？

先检查文件的顶层键。如果文件的根节点仍是 `en:`，它会被当作英语加载，导致新的区域设置下没有任何键；另外，还要确认该区域设置已添加到 `config.i18n.available_locales` 中。

### 可以在区域设置文件中使用锚点和别名吗？

Rails 可以加载它们，但有些加载器不支持，例如 vue-i18n 所用的 unplugin-vue-i18n。翻译带锚点的值时，所有指向它的别名都会随之改变，因此只应将锚点和别名用于在所有位置都完全相同的文本。

## 要点总结

* 只翻译值；键、`%{name}` 和 `{{name}}` 之类的占位符以及块缩进均保持不变。
* 如果值包含冒号、以 `%` 或 `{` 开头，或者是 `yes`、`no`、`on`、`off` 等词，需要加引号，否则 Ruby 会将其解析为布尔值。
* 俄语和波兰语需要英语源文件中没有的 `few` 和 `many` 键；缺少这些键时，Rails 会静默回退到 `other`。
* 每个 Rails 文件都需要有自己的区域设置根键；以 `en:` 为根键的西班牙语文件会被当作英语加载。
* General Translation 的 CLI 通过 `"yaml": { "include": [...] }` 和 [`npx gt translate`](/docs/cli/reference/commands/translate) 翻译 YAML 字符串值，并保留键、注释和锚点；Rails 根键和缺失的复数键仍需你自行处理。

## Related questions

- [General Translation 与 Rails I18n：两者都需要吗？](https://generaltranslation.com/zh/kb/general-translation-vs-rails-i18n.mdx): Rails I18n 负责查找消息并格式化日期和数字。General Translation 负责生成译文，在英文内容变更时让译文保持同步，并为团队提供用于审校的仪表板。两者搭配使用，即可在每次发布时交付已翻译的 Rails 目录。
- [General Translation 与 i18n-tasks：应该选择哪一个？](https://generaltranslation.com/zh/kb/general-translation-vs-i18n-tasks.mdx): i18n-tasks 是一个 Ruby gem，用于查找缺失和未使用的 Rails I18n 键、整理区域设置文件，还能借助 Google Translate、DeepL、OpenAI、Yandex 或 watsonx 补全缺失的键。General Translation 则结合你的词汇表、自定义提示词和仪表板审校，翻译完整的区域设置文件。两者分工不同，许多团队完全可以搭配使用。
- [General Translation 与 Symfony Translation：该选哪一个？](https://generaltranslation.com/zh/kb/general-translation-vs-symfony-translation.mdx): Symfony Translation 负责在 PHP 和 Twig 中渲染消息。General Translation 则在 CI 中翻译其 YAML 消息目录，随源内容的变化持续同步更新，并为审校人员提供可直接编辑译文的仪表板。保留 Symfony 的渲染机制，同时为你的发布工作流加入自动化翻译。
- [如何翻译 i18n JSON 文件？](https://generaltranslation.com/zh/kb/translate-json-files.mdx): 只翻译 JSON 区域设置文件中的字符串值。非复数键、{{name}} 之类的占位符以及标签都应保持不变，但复数键会因语言而异：例如波兰语需要 inbox_few 和 inbox_many。请使用能识别文件格式的工具或 API，然后先解析并验证输出，再进行审校。
- [如何翻译 PO 和 POT 文件？](https://generaltranslation.com/zh/kb/translate-po-files.mdx): 填写每一个 msgstr（包括 Plural-Forms 头部字段要求的每种复数形式），并保持 msgid、msgctxt 和占位符不变。运行 msgfmt --check 可检查头部信息、复数形式数量，以及带有 format 标志的条目中的占位符；此外还需单独检查是否有空的 msgstr。General Translation 的 CLI 会根据你的 POT 模板，为每种语言生成一个翻译好的 PO 文件。

## Previous and next questions

- Previous question: [如何翻译 SEO 标题和meta 描述？](https://generaltranslation.com/zh/kb/translate-seo-metadata.mdx)
- Next question: [面向开发者的最佳 AI 翻译工具：翻译 API 与本地化平台对比](https://generaltranslation.com/zh/kb/best-ai-translation-tools.mdx)

## Sitemap

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