# General Translation Knowledge Base: General Translation 与 GNU gettext：两者都需要吗？
URL: https://generaltranslation.com/zh/kb/general-translation-vs-gettext.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: 继续用 GNU gettext 负责提取和渲染，再引入 General Translation 来生成和维护 PO 文件。General Translation 会把译者备注和消息上下文纳入生成过程，统一应用共享术语，并为审校人员提供 仪表板 编辑器。你现有的 gettext 调用和已编译的消息目录均保持原样。
Category: 国际化
Last updated: 2026-10-16
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-16

GNU gettext 至今仍是大多数软件翻译所依赖的工具链。你用 `_()` 或 `gettext()` 包裹面向用户的字符串，由 `xgettext` 将其提取到 POT 模板中，`msginit` 为每种语言创建 PO 文件，`msgmerge` 在源内容变更时更新这些文件，最后 `msgfmt` 将它们编译成程序在运行时加载的 `.mo` 文件。C 程序、PHP 的 gettext 扩展、Python 的 `gettext` 模块、WordPress、Django 和 Babel 都采用这种格式。但大部分翻译工作，gettext 都留给了你：从 1.0 版本开始，它虽然可以借助本地 LLM 起草条目，但每个 PO 文件里的每条 `msgstr`，最终仍要由你、你的译者或某项服务来完成。General Translation 不会取代工具链中的任何环节；它的 CLI 会根据你的模板生成这些 PO 文件，其余的 gettext 初始化设置都无需改动。

## gettext 的优势

* **源文本即键。** 代码中写的是 `_("Inbox")`，而不是键名；没有译文的字符串会[回退到源文本](https://www.gnu.org/software/gettext/manual/html_node/Interface-to-gettext.html)，而不会报错。
* **按语言定义复数规则。** 每个 PO 文件都带有[记录该语言复数规则的 `Plural-Forms` 头部字段](https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html)，`msginit` 会自动帮你填好。例如，波兰语采用三种复数形式的规则，因此每个复数条目都有三个 `msgstr[n]` 槽位。
* **上下文随字符串一起传递。** `msgctxt` (由 `pgettext` 生成) 可以[区分作动词的 &quot;Archive&quot; 和作名词的 &quot;Archive&quot;](https://www.gnu.org/software/gettext/manual/html_node/Contexts.html)；`#.` 注释用于向译者传递备注 (`xgettext --add-comments`) ；`#:` 行指向源代码位置；`python-format` 等标记则让 `msgfmt --check` 能够拒绝破坏占位符的译文。
* **安全更新。** 模板变更时，`msgmerge` 会[保留现有译文](https://www.gnu.org/software/gettext/manual/html_node/msgmerge-Invocation.html)，并将源文本有改动的条目标记为 fuzzy，同时保留旧的源文本以便对比 (`--previous`) 。
* **本地 LLM 草稿。** 从 1.0 版本开始，[`msgpre` 和 `spit` 可通过 ollama 调用本地安装的 LLM 对 PO 文件进行预翻译](https://www.gnu.org/software/gettext/manual/html_node/Pretranslating.html)，为译者审校提供初稿。
* **生态支持广泛。** Poedit 等 PO 编辑器和大多数翻译管理系统 (TMS) 都能读写 PO 格式，相关工具也都是[自由软件](https://www.gnu.org/software/gettext/manual/html_node/Licenses.html) (采用 GPL 许可，运行时库采用 LGPL) 。

## gettext 需要你自行处理的部分

下面是一个使用 Python 标准库 `gettext` 模块的简短 Python 脚本：

```python title="inbox.py"
import gettext, sys

lang = sys.argv[1]
count = int(sys.argv[2])
t = gettext.translation("inbox", localedir="locale", languages=[lang], fallback=True)
_ = t.gettext

# TRANSLATORS: 收件箱页面的标题
print(_("Inbox"))
print(t.ngettext("You have %d unread message.", "You have %d unread messages.", count) % count)
print(t.pgettext("email verb", "Archive"))
```

基于 [GNU gettext 1.0](https://lists.gnu.org/archive/html/info-gnu/2026-01/msg00007.html) 的 gettext 工作流：

```bash
xgettext --language=Python --keyword=pgettext:1c,2 --add-comments=TRANSLATORS -o po/inbox.pot inbox.py
msginit --no-translator --locale=pl_PL.UTF-8 -i po/inbox.pot -o po/pl.po
# 由人工填写 po/pl.po 中的所有 msgstr
msgfmt --check -o locale/pl/LC_MESSAGES/inbox.mo po/pl.po
```

填好波兰语文件后，运行 `python3 inbox.py pl 5` 会输出 &quot;Masz 5 nieprzeczytanych wiadomości.&quot;。之后，有人在代码中把 &quot;unread message&quot; 改成了 &quot;new message&quot;。`xgettext` 和 `msgmerge --update --previous` 会保留旧的波兰语译文，并将其标记为 fuzzy，因为它现在翻译的已经是另一段文本了：

```text title="po/pl.po"
#: inbox.py:10
#, fuzzy, python-format
#| msgid "You have %d unread message."
#| msgid_plural "You have %d unread messages."
msgid "You have %d new message."
msgid_plural "You have %d new messages."
msgstr[0] "Masz %d nieprzeczytaną wiadomość."
msgstr[1] "Masz %d nieprzeczytane wiadomości."
msgstr[2] "Masz %d nieprzeczytanych wiadomości."
```

`msgfmt` [会跳过 fuzzy 条目](https://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html) (它会报告 &quot;2 translated messages, 1 fuzzy translation&quot;) ，因此同一条命令现在会在波兰语界面中输出一句英文 &quot;You have 5 new messages.&quot;。旧的波兰语译文仍写着 &quot;unread&quot; (未读) ，因此需要更新译文。General Translation 可以在构建过程中基于新模板生成更新后的译文。

## 使用 General Translation 翻译 gettext 消息目录

General Translation 的 CLI 会读取你的 POT 模板，翻译其中的每个条目，并为每种目标语言生成一个 PO 文件。只需在 `gt.config.json` 中指向该模板，并指定各语言文件的输出位置即可。以下配置已在 `gt` 2.26.5 版本中验证：

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["de", "pl", "pt_BR"],
  "customMapping": {
    "pt_BR": { "code": "pt-BR" }
  },
  "files": {
    "pot": {
      "include": ["po/inbox.pot"],
      "transform": {
        "match": "^po/inbox\\.pot$",
        "replace": "po/{locale}.po"
      },
      "transformationFormat": "PO"
    }
  }
}
```

* **`pot` 可读取 PO 和 POT 文件**，而 `"transformationFormat": "PO"` 会让输出结果为翻译后的 `.po` 文件。 (参见 [PO / POT 格式参考](/docs/cli/reference/formats/po-pot-files)。) 
* **`transform` 用于指定输出文件名。** `{locale}` 与 `locales` 中写法完全一致的区域设置对应，因此会生成 `po/pl.po` 和 `po/pt_BR.po`。 (参见 [`transform`](/docs/cli/reference/config#files)。) 
* **沿用 gettext 的区域设置名称。** [`customMapping`](/docs/cli/reference/config#custom-mapping) 将 `pt_BR` 映射到标准区域设置代码 `pt-BR`。同样的模式也适用于其他目录结构，例如 WordPress 插件的 `languages/my-plugin.pot` 和 `languages/my-plugin-de_DE.po`。

配置好 CLI 后 (参见 [CLI 快速入门](/docs/cli/quickstart)) ，工作流如下：

```bash
xgettext --language=Python --keyword=pgettext:1c,2 --add-comments=TRANSLATORS -o po/inbox.pot inbox.py
msgconv --to-code=UTF-8 -o po/inbox.pot po/inbox.pot
npx gt translate
msgfmt --check -o locale/pl/LC_MESSAGES/inbox.mo po/pl.po
```

`xgettext` 会在模板文件头中保留 `charset=CHARSET` 作为占位符，而 `msgconv` 会将其设为 UTF-8，与 `msginit` 处理语言文件时的做法一致。[`npx gt translate`](/docs/cli/reference/commands/translate) 发送每个条目时，会将 PO 文件中与该条目相关的信息一并作为上下文发送：`msgctxt`、`#.` 译者注释、`#:` 源码引用以及格式标记。对于复数条目，General Translation 会写入目标语言的 `Plural-Forms` 头字段，并按该字段的要求填满每个 `msgstr[n]` 槽位 (波兰语为三个，阿拉伯语为六个) ，同时检查 `%d` 等占位符是否完整保留。

当 `msgid` 发生变化时，General Translation 会在下一次运行时为新文本生成翻译；未更改的条目则沿用之前的译文。首次生成翻译之前，请使用 [`npx gt upload`](/docs/cli/reference/commands/upload) 导入现有的 PO 译文。之后如果在本地 PO 编辑器中做了修改，可在再次生成之前使用 [`npx gt save-local`](/docs/cli/reference/commands/save-local) 将其同步回平台。

除了填充 `msgstr`，General Translation 还提供了完整技术栈的其余部分。[上下文分组](/docs/platform/dashboard/guides/defining-context-for-translations)中的词汇表和自定义提示词能让 PO 文件以及您翻译的所有其他用户交互界面保持术语和语气一致；审阅者可以在仪表板中[检查和编辑](/docs/platform/dashboard/guides/reviewing-translations)每个 PO 条目及其目标语言的各个复数形式；CLI 还可以在 CI 中运行，确保每次发布都带上最新的消息目录。

在构建流程中运行 Node.js CLI，然后使用 `msgfmt` 编译其输出的 PO 文件。您的应用程序仍会在本地加载编译后的译文。

## GNU gettext 与 General Translation 对比一览

| 功能          | GNU gettext                   | General Translation                                                |
| ----------- | ----------------------------- | ------------------------------------------------------------------ |
| 从代码中提取字符串   | 支持，使用 `xgettext`              | 直接读取你现有的 POT 模板                                                    |
| 为每种语言创建文件   | 支持，使用 `msginit`               | 支持，基于模板为每个已配置的区域设置生成                                               |
| 生成译文        | 自 1.0 起可用本地 LLM 生成草稿，再由译者完善   | 支持，使用 [`npx gt translate`](/docs/cli/reference/commands/translate) |
| 源字符串变更后     | `msgmerge` 将其标记为 fuzzy，等待人工审阅 | 下次运行时自动重新翻译；未更改的条目直接复用                                             |
| 复数形式        | `msginit` 写入文件头；需要你逐一填写每个槽位   | 写入文件头并填好所有槽位                                                       |
| 翻译上下文       | `msgctxt`、译者注释、源码位置引用、标记      | 同左，另支持词汇表和自定义提示词                                                   |
| 在运行时编译并查找译文 | 支持，使用 `msgfmt` 和 gettext 运行时  | 沿用你的 gettext 编译器和运行时                                               |
| 费用          | 自由软件                          | [按用量计费](https://generaltranslation.com/pricing)                    |

## 如何选择

如果你的 PO 文件已由译者或 TMS 填写，或者每条翻译都必须先由人工撰写才能发布，又或者你的构建环境无法运行 Node.js，那么单独使用 gettext 即可。如果你希望模板一有变动，所有 PO 文件就能立即翻译成每种语言，且复数槽位齐全、术语前后统一，无需等待一轮翻译，那就加上 General Translation。

无需任何迁移：General Translation 会保留你的 `_()` 调用、模板、PO 文件和运行时，只替代 `msgmerge` 与 `msgfmt` 之间的手动步骤。 (如需了解特定框架下的初始化设置，请参阅 [General Translation 与 Django 翻译框架对比](/kb/general-translation-vs-django-i18n)以及 [General Translation 与 Babel 和 Flask-Babel 对比](/kb/general-translation-vs-flask-babel)。)

## 要点总结

* GNU gettext 负责提取、合并、编译和查找译文；自 1.0 版本起，它还能借助本地 LLM 生成译文草稿，但并不能真正完成翻译。新增和改动的字符串在有人翻译或审校之前，会一直以源语言显示。
* General Translation 的 CLI 可将 POT 模板翻译为每种语言对应的 PO 文件，只需配置 `pot` 键、`"transformationFormat": "PO"`、用于指定输出路径的 `transform`，以及用于处理 `pt_BR` 等名称的 `customMapping`。
* 对于每个复数条目，General Translation 会写入目标语言的 `Plural-Forms` 头字段并填充每个 `msgstr[n]` 槽位，同时将 `msgctxt` 和译者注释用作上下文。`msgfmt` 和你现有的 gettext 运行时均无需改动。

## Related questions

- [General Translation 与 Django 翻译框架：两者都需要吗？](https://generaltranslation.com/zh/kb/general-translation-vs-django-i18n.mdx): 将 Django 的翻译框架与 General Translation 搭配使用。Django 负责消息提取、语言选择和渲染；General Translation 则基于你的产品术语和仪表板审阅，填充 Django 的 PO 文件（包括复数形式）。你的模板和 URL 模式无需改动，译文会随代码同步更新。
- [General Translation 与 Babel、Flask-Babel 对比：该选哪一个？](https://generaltranslation.com/zh/kb/general-translation-vs-flask-babel.mdx): Babel 可以为任意区域设置格式化日期、数字和货币，并管理 gettext 消息目录；Flask-Babel 则负责将其集成到 Flask 中，但两者都不会翻译你的字符串。General Translation 的 CLI 会为每种语言填充 Babel 的 PO 文件（包括复数形式），因此你可以继续使用 Flask-Babel，不必再手动翻译消息目录。
- [为什么选择 General Translation 而不是 TMS（Smartling、Lokalise、Phrase、Crowdin 或 Transifex）？](https://generaltranslation.com/zh/kb/general-translation-vs-tms.mdx): Smartling、Lokalise、Phrase、Crowdin 或 Transifex 等翻译管理系统（TMS）负责协调译员，但工程师仍需提取字符串、发送出去、等待译文，再把译文接回产品中。General Translation 结合上下文直接翻译代码中的内容，每次发布都同步交付所有语言，并基于开源库运行，不存在供应商锁定。
- [General Translation 与 Weblate：该选哪一个？](https://generaltranslation.com/zh/kb/general-translation-vs-weblate.mdx): Weblate 是一个可自托管、基于 Git 的本地化平台，专为社区或团队协作翻译代码仓库文件而设计。General Translation 则将译文生成融入开发与发布流程，依托已标记的 JSX 和共享的产品上下文完成翻译。你可以继续沿用现有的 gettext 文件，也可以改用内联编写，连源文案目录的维护工作也一并省去。
- [对开发者来说，最好的本地化软件是什么？](https://generaltranslation.com/zh/kb/best-localization-software.mdx): 选择一套能覆盖整条流水线的系统——从代码中的国际化库，到翻译和投递——而不是把独立的库、翻译管理系统和翻译供应商拼凑在一起。

## Previous and next questions

- Previous question: [General Translation 与 Django 翻译框架：两者都需要吗？](https://generaltranslation.com/zh/kb/general-translation-vs-django-i18n.mdx)
- Next question: [General Translation 与 i18n-tasks：应该选择哪一个？](https://generaltranslation.com/zh/kb/general-translation-vs-i18n-tasks.mdx)

## Sitemap

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