# 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 初始化设置都无需改动。

## Video

The video is narrated in English, with English captions.

[General Translation vs. GNU gettext: do you need both?](https://caxecubb9c64cezv.public.blob.vercel-storage.com/films/kb-general-translation-vs-gettext.mp4) (46秒)

Keep GNU gettext for extraction and rendering, and let General Translation generate and maintain the PO files, with translator notes, message context, shared terminology, and Dashboard review. To set it up, point `gt.config.json` at your POT template with a transform for each language, and run `npx gt translate` between xgettext and msgfmt in your build.

## 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（Crowdin、Lokalise、Phrase）对比](https://generaltranslation.com/zh/kb/general-translation-vs-tms.mdx): 如果开发者希望译文生成、源自代码的上下文以及审校流程都与产品发布紧密衔接，请选择 General Translation。你可以保留现有的消息目录和渲染库，也可以直接编写内联 JSX，无需翻译键。TMS 适合以语言供应商、CAT 编辑器和 XLIFF 交换为核心的翻译项目，其集成可以自动完成文件传输。
- [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): 面向开发者的最佳本地化软件会直接在代码中编写内容的位置进行翻译，结合产品上下文，并让每种语言始终保持最新。General Translation 通过开源库、上下文组以及 Locadex（一个会提交拉取请求的 AI 智能体）做到了这一点。

## 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.
