python-i18n / pythonweb国际化
国际化可以方便我们以一种语言(英语)编写程序中的字符串,但是可以向不同国家的人提供对应的语言翻译,那么python语言如何实现国际化呢? python内置模块gettext为python模块和应用提供了国际化(i18n)和本地化支持,它既支持GNU-gettext的消息翻译,又提供了更适合python基于类的程序消息翻译接口。
一、 python-gettext (内置模块)
1.1 gettext生成翻译
GNU-gettext 提供C代码的国际化,GNU-gettext制定了翻译文件的格式,提供一个工具集(一系列命令)来解析文件。Windows用户可以从官网下载压缩包解压,Linux用户通过yum install xgettext完成安装。完成国际化的一般步骤:
- (1) gettext - 从源程序提取消息,生成翻译文件messages.pot (以msgid/msgstr对组织), 修改msgstr对应的翻译;
- (2) msgfmt - 将messages.pot编译成messages.mo文件(一种格式标准,现在大家都遵守)。
- (3) 程序配置时,需要指明mo文件位置,以供程序解析加载(类似字典对)。
GNU-gettext只提供C风格字符串(双引号)的识别,对于python特有的三引号和单引号那应该怎么办呢?其实我们要提取和编译也可以不用安装xgettext工具,在python安装目录下 ROOT_PYTHON/Tools/i18n 提供了这两个工具(没有可以在cpython源码中下载 py-i18n-tools),按照GNU-gettext标准,pygettext.py 来扫描python源码生成pot文件,mgsfmt.py来编译pot文件生成mo文件(有兴趣的朋友可以阅读了解下mo文件生成格式)。下面我们来使用python-gettext进行一下翻译:
(1) 编写源代码 app.py
- translation用来解析mo文件, 返回一个标准的GNUTranslations;
- gettext 用于从解析后的字典里取值,没有翻译值,则保持原样;
from gettext import translation
import os
root_dir = os.path.join(os.path.dirname(__file__), "i18n")
# 从 localedir/languages[x]/LC_MESSAGES/domain.mo 读取文件, localedir请传入一个绝对路径
t = translation(domain="zh_CN", localedir=root_dir, languages=["zh"])
_ = t.gettext
c_str = _("Hello World")
py_str = _('Hello World, %s') % "app"
(2) 提取源代码消息:
生成的pot文件格式头由一对空msgid/msgstr组成,其后紧跟的一堆为文件的元信息,其中Content-Type用于说明文件格式及编码,中文请改为charset=UTF-8。
python pygettext.py app.py
# gettext 后跟一个文件或目录, 目录则递归扫描
# -o 指定输出文件名称,无该选项则默认为messages.pot
- pot文件格式 / po文件格式:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2021-11-27 23:37+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: i18n_\app.py:12
msgid "Hello World"
msgstr ""
(3) 编译pot文件:
修改messesges.pot文件的msgstr为对应中文翻译
messages.pot -> messages.po
python msgfmt.py messages.po
# -o 指定输出文件名称,无该选项则默认为 输入文件名称.mo
# 将messages.mo -> zh_CN.mo 并移动到同级目录 i18n/zh/LC_MESSAGES下
```
注意 文件结构
app.py
|
i18n/zh/LC_MESSAGES/zh_CN.mo
|
messages.po
|
messages.mo
```
1.2 gettext模块详解:
mo文件位置拼接: localedir/languages[x]/LC_MESSAGES/domain.mo
(1) find函数:
-
功能说明:根据指定参数定位mo翻译文件位置
-
返回值:列表 [mo文件位置]
-
参数说明:
参数 说明 domain/localedir 翻译文件名称 / 语言文件根目录 languages 语言列表
不提供则在环境变量找 ‘LANGUAGE’, ‘LC_ALL’, ‘LC_MESSAGES’, ‘LANG’,找不到返回空列表all 是否返回全部languages的mo文件位置,默认为False, 返回第一个找到的文件
(2) translation函数:
-
功能说明:翻译解析
-
返回值:翻译解析类(解析好的)
-
参数说明:
参数 说明 domain/localedir/languages 同上 class_ 翻译类:默认是GNUTranslations fallback 是否回退,如果找不到mo文件,falllback为真返回NULLTranslations,否则抛出OSError异常
默认fallback为Falsecodeset 输出翻译(msgstr)的编码
(3) NULLTranslations
-
功能:无行为翻译类,默认返回原始消息msgid
-
成员:
成员 说明 _info/ info() mo文件头信息(元数据),由_parse()解析设置 _charset / charset() msgstr编码,由_parse()解析头部content-type设置 _output_charset _()调用时对msgstr的再编码,不提供设置,由子类继承设置 _fallback / add_fallback() 添加自定义NULLTranslations 子类,实现自定义翻译功能(回调预留) _parse() mo文件解析,NULLTranslations 不提供实现,返回msgid gettext()/ lgettext() 返回解析的msgstr / msgstr再编码(_output_chartset 或者系统默认) ngettext()/ lngettext() 暂时未搞清(英语中的复数,对应不同翻译大概) install(names) 将_ , gettext, ngettext 注册到内置模块builtin,可直接使用而无需导入
默认只将_注册,names可选项列表[“gettext”, “ngettext”]
(4) GNUTranslations
-
功能:标准mo文件解析类, 继承NULLTranslations, 实现_parse函数
-
成员:
成员 说明 _catalog msgid/msgstr键值对, 解析后的mo文件数据放在这里面 _parse() 完成mo文件解析 注意GNUTranslations使用NULLTranslations 的初始化函数,自身不提供init入口,重写了gettext相关函数。下节讲述另一个python国际化框架Babel。
本文代码地址:python-i18n.git