教程¶
本页从一个空目录走到一个用日语打招呼的程序。共五步,不要求任何 gettext 经验, 每条命令都附带它实际产生的输出——因此每走一步,你都知道自己是否在正确的轨道上。
你需要 Python 3.14 或更新版本,因为 t-string 是 3.14 中的新语法。
日语是本页的示例目标语言,但没有任何东西依赖这一选择。想用别的语言,只需把第 4 步
里的 ja 换掉——那个 locale 代码是唯一指明这一选择的地方。
1. 安装¶
[babel] extra 会带来 Babel,即第 3 步中把消息收集进目录文件的工具。它是
开发期工具:生产代码只靠标准库即可完成渲染。
2. 在代码中标记一条消息¶
创建 app.py:
t"Hello {name}" 看起来像 f-string,但 t 前缀让文本与值保持分离,而不是当场
合并。正是这种分离让 tr() 能够为整句 Hello {name} 查找翻译,然后再把值插入
进去。
现在运行它:
目前尚未安装任何翻译,因此源文本按原样渲染。使用本库的程序从不要求目录才能 运行——英语(或你的源语言)就是内置的回退。
3. 提取消息¶
比起源代码,翻译者通常是对着目录工作的,因此在你和他们之间流转的是一个叫做 目录的小文件。迈向目录的第一步,是把代码中每条被标记的消息收集出来。
创建 babel.cfg,告诉 Babel 如何找到你的消息:
然后提取到模板文件(.pot):
$ mkdir -p locales
$ pybabel extract -F babel.cfg -c "Translators:" -o locales/messages.pot .
extracting messages from app.py (encoding="utf-8")
writing PO template file to locales/messages.pot
locales/messages.pot 现在为每条消息包含一个条目:
msgid 是你的代码将要查找的 key。空的 msgstr 是填写翻译的地方——但不是在
这个文件里:.pot 是模板,下一步会为每种语言复制一份。
4. 翻译并编译¶
从模板创建日语目录:
$ pybabel init -i locales/messages.pot -d locales -l ja
creating catalog locales/ja/LC_MESSAGES/messages.po based on locales/messages.pot
打开 locales/ja/LC_MESSAGES/messages.po 并填写 msgstr:
保持 {name} 原样不动——占位符是值在译文句子中找到自己位置的方式,翻译可以把
它移动到目标语言需要的任何位置。在真实项目中,这个 .po 文件就是你交给翻译者
或上传到翻译平台的东西;两种情况下格式相同。
目录以文本形式编辑,但以二进制形式(.mo)加载,因此需要编译:
$ pybabel compile -d locales
compiling catalog locales/ja/LC_MESSAGES/messages.po to locales/ja/LC_MESSAGES/messages.mo
这条命令同时也是一道安全网。如果翻译损坏了占位符——比如把 {name} 写成了
{nome}——它会拒绝通过:
$ pybabel compile -d locales
error: locales/ja/LC_MESSAGES/messages.po:24: translation does not match the
source placeholders: {name} is missing; {nome} is not in the source message
1 errors encountered.
有一点现在就值得知道:它会报错并以非零状态退出,但仍然会把 .mo 写出来。在真实
项目中,必须由 CI 依据那个退出状态停下来——生产实践
会把它配置好。
5. 运行¶
第 2–4 步用的是 tr(),它会去找目录却什么也没找到。现在目录已经存在,把它加载
进来并绑定一次:Translator 持有一个目录,这样各个调用点就不必再点名它,而 _
是这个结果在 gettext 中的约定名称。
让 app.py 指向编译好的目录。点击标记,看看每一行在做什么:
import gettext
from gettext_tstrings import Translator
_ = Translator(gettext.translation("messages", localedir="locales", languages=["ja"])) # (1)!
name = "Ada"
print(_(t"Hello {name}")) # (2)!
- 标准库加载编译好的
.mo,Translator把它绑定为一个可调用对象。_是 gettext 中“翻译这个”的约定名称——之所以这么短,是因为它会出现在每一条 面向用户的字符串上。它执行的翻译与tr相同,只是绑定到了一个目录。 - 在调用处:t-string 的文本变成查找 key
Hello {name},目录给出答案こんにちは {name},答案先与源占位符核对无误,然后才把值放进去。
这就是完整的循环,值得把它看成一幅图:
flowchart LR
mark["1–2 标记<br>代码中的 t-string"] --> extract["3 提取<br>messages.pot"]
extract --> translate["4 翻译<br>ja/…/messages.po"]
translate --> compile["4 编译<br>ja/…/messages.mo"]
compile --> run["5 运行<br>こんにちは Ada"]
标记 → 提取 → 翻译 → 编译 → 运行。 本站的其余内容都是这五步之一的细化。