用 gettext 做 i18n 不难,但易卡在路径、编码、域不一致三处;确保 .mo 路径结构正确(localedir/language/lc_messages/domain.mo)、languages 参数传对(如 ['zh_cn'])、所有字符串用 _() 包裹即可跑通。

直接说结论:用 gettext 做 i18n 不难,但容易卡在路径、编码、域(domain)不一致这三处;只要确保 .mo 文件路径结构正确、languages 参数传对、所有字符串都用 _() 包裹,就能跑通。
如何提取源码中的待翻译字符串生成 .pot 模板
关键不是“能不能提”,而是“提得全不全”。Python 自带的 pygettext 工具默认只扫描 .py 文件,且对嵌套调用(比如 _("Hello").upper())或模板字符串里的 _() 会漏掉。
- 推荐用
xgettext(GNU gettext 工具链),它更稳定,支持--from-code=UTF-8显式指定编码 - 命令示例:
xgettext -d messages -o messages.pot --from-code=UTF-8 *.py templates/*.html - 必须加
-d messages,否则生成的.pot里 domain 是messages,后续加载时gettext.translation('messages', ...)才能匹配上 - 如果代码里用了
ngettext或带python-brace-format的字符串,xgettext会自动标记复数上下文和格式类型,msgfmt编译时才能正确处理
为什么 .mo 文件总加载失败
90% 的加载失败不是代码问题,是目录结构或环境变量没对齐。gettext 查找 .mo 的路径是硬编码逻辑:{localedir}/{language}/LC_MESSAGES/{domain}.mo。
-
localedir必须是绝对路径,相对路径在某些部署场景(如 systemd 服务、Docker)下会失效;建议用os.path.abspath('./locale') -
language来自languages=[lang]参数,不是LANG环境变量值本身——例如LANG=zh_CN.UTF-8时,应传['zh_CN'],不是['zh_CN.UTF-8'] - 检查文件是否存在:
./locale/zh_CN/LC_MESSAGES/messages.mo—— 注意大小写、层级、文件名(messages.mo中的messages必须和translation()第一个参数一致) - Linux 下常见坑:
LC_MESSAGES被设为C,会绕过用户语言设置;可临时用export LC_MESSAGES=zh_CN.UTF-8测试
如何安全地在运行时切换语言(非全局)
别用 gettext.install(),它会污染 builtins._,多个模块混用时极易冲突。真要动态切语言,就老实用类实例。
- 每个语言建独立
translation实例:zh_trans = gettext.translation('messages', localedir='./locale', languages=['zh_CN'], fallback=True) - 调用时显式用:
zh_trans.gettext("Save"),而不是依赖全局_() - Web 场景中,把
translation.gettext作为_传入模板上下文,比在模板里 import gettext 更干净 - 注意
fallback=True:当目标语言.mo缺失时,回退到原始字符串(不是英文),避免显示空字符串
最易忽略的一点:所有 .po 文件头部的 "Content-Type: text/plain; charset=UTF-8" 必须存在且正确,否则中文会变乱码——这不是 Python 层能自动修复的,是 msgfmt 编译时读取的元信息。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











