没有人能替你承受痛苦,也没有人能抢走你的坚强。
上一篇我们介绍了这个包的使用方式和在openstack中应用,这篇文章分析一下原理和源码
简介
i18n是用来进行国际化翻译的。经过调查,现在主要有两种翻译方式。
直接通过
gettext
方法,显示调用实例: horizon的国际化:
1
2
3
4scope.operationlogi18n = {
'Create Instance': gettext('Create Instance'),
'Shutdown Instance': gettext('Shutdown Instance'),
}分析:
直接调用gettext方法,获取翻译结果通过
_
方法使用懒汉加载模式_
方法实际是:1
2
3
4
5import oslo_i18n
# ref: https://docs.openstack.org/oslo.i18n/ocata/usage.html
DOMAIN = "myproject"
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
_ = _translators.primary这种方式具体参见后续源码分析。
i18n的懒加载模式
Lazy Translation¶
Lazy translation delays converting a message string to the translated form as long as possible, including possibly never if the message is not logged or delivered to the user in some other way. It also supports logging translated messages in multiple languages, by configuring separate log handlers.Lazy translation is implemented by returning a special object from the translation function, instead of a unicode string. That special message object supports some, but not all, string manipulation APIs. For example, concatenation with addition is not supported, but interpolation of variables is supported. Depending on how translated strings are used in an application, these restrictions may mean that lazy translation cannot be used, and so it is not enabled by default.
To enable lazy translation, call enable_lazy().
import oslo_i18n
oslo_i18n.enable_lazy()
以aodh为例,在aodh/service.py中有 oslo_i18n.enable_lazy()
这么一行代码,直接来看源码
源码分析
自己实现一个简单的i18n.py
文件:
1 | import oslo_i18n |
分析:
1)上述先设置了方法
2) 假设有需要使用国际化翻译的地方,可以使用如下形式进行国际化
引用`,_LI`等
1 | from i18n import _, _LI |
或者这种用法,推荐用_而不是_LI
1 | result = _('enter app_factory') |
3) _方法
内部调用了_make_translation_func方法,内容如下
1 | def _make_translation_func(self, domain=None): |
1 | class Message(six.text_type): |
上面分析了aodh已经开启了懒加载模式,所以这里实例化了一个Message类,调用 _translate_msgid
方法
经过调试发现,所谓的oslo_i18n的懒加载模式实际是获取localedir,获取当前环境变量中的语言,然后找到对应的翻译方法,对msgid进行翻译,获取对应的msgstr,因而能翻译出对应的语言。使用不使用懒加载模式,按照道理应该不影响。
来看下获取环境变量的过程, getdefaultlocale
1 | def getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')): |
这里整体的逻辑是在寻找语言编码的时候默认是按照这个顺序:(‘LC_ALL’, ‘LC_CTYPE’, ‘LANG’, ‘LANGUAGE’)去查找对应环境变量,一旦找到环境变量,就对该环境变量解析,
然后调用下面的方法:_parse_localename
1 | def _parse_localename(localename): |
按点号分割得到: (‘zh_CN’, ‘UTF-8’)
总结
i18n的懒加载翻译模式中通过使用oslo_i18n.TranslatorFactory(domain=DOMAIN).primary
方法进行翻译,里面调用=_make_translation_func
方法返回i18n
自己定义Message
对象,该对象包含了待翻译的信息,最后调用_translate_msgid
获取msgid
对应的国际化翻译内容msgstr
。其中需要设置两样东西:一个是项目本身的localedir
目录,为的是找到对应的mo,po
等文件信息;一个是设置语言环境变量,优先级从高到低如下: 'LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'
。设置LC_ALL
即可不用设置其他环境变量。另外注意: 设置不同语言环境变量,需要重启组件的服务才可以生效。
番外:.po和.mo文件
介绍:PO 是 Portable Object (可移植对象)的缩写形式;MO 是 Machine Object (机器对象) 的缩写形式。PO 文件是面向翻译人员的、提取于源代码的一种资源文件。当软件升级的时候,通过使用 gettext 软件包处理 PO 文件,可以在一定程度上使翻译成果得以继承,减轻翻译人员的负担。MO 文件是面向计算机的、由 PO 文件通过 gettext 软件包编译而成的二进制文件。程序通过读取 MO 文件使自身的界面转换成用户使用的语言。
po文件和mo文件通过msgfmt工具和pygettext转化。
- 创建po文件:在Python安装目录下的 ./Tools/i18n/ 中找到pygettext.py运行之,生成翻译文件模版messages.pot。内容如下:
1 | # SOME DESCRIPTIVE TITLE. |
将charset改为charset=UTF-8,其余的可以不用改动。其中的msgid为键值,对应你程序里写的文本,如:_(“New File”),而msgstr为翻译后的值。添加翻译语句:
1 | # SOME DESCRIPTIVE TITLE. |
保存该文件,并重命名为messages.po
- 创建mo文件:在Python安装目录下的 ./Tools/i18n/ 中找到msgfmt.py,在Python模式下,注意messages.po存在的路径:
1 | python msgfmt.py messages.po |
也可以使用msgfmt命令:
1 | msgfmt /aodh/aodh/locale/zh_CN/LC_MESSAGES/messages.mo -o aodh/aodh/locale/zh_CN/LC_MESSAGES/messages.mo |
将生成一个messages.mo文件。
- 建立翻译文件路径:在src目录下创建/locale/zh_CN/LC_MESSAGES/,将messages.po和messages.mo文件拷贝其中。
即:./src/locale/zh_CN/LC_MESSAGES/messages.po
./src/locale/zh_CN/LC_MESSAGES/messages.mo
- 建立demo.py,Python通过gettext模块支持国际化(i18n),可以实现程序的多语言界面的支持,如下引入gettext模块:
1 | # -*- coding: utf-8 -*- |
一切工作准备就绪,运行demo.py,查看是否输出中文:世界你好! Python是门好语言.
另外可以借助工具生成.po和.mo文件,比如Poedit、Zenata等。以下介绍Poedit:
Poedit
下载并安装Poedit,打开Poedit,上方工具栏File,新建,在弹出的弹框填入名称messages,确定后按ctrl+s,就创建了messages.po模板文件。将要翻译的语言写入,如下:
1 | # Copyright (C) 2018 THE PACKAGE'S COPYRIGHT HOLDER |
保存后,用Poedit打开po文件,并在File中的下拉框“编译为mo文件”,生成messages.mo文件。生成两个文件后拷贝在/locale/zh_CN/LC_MESSAGES/。
重写i18n的实现。i18n.py:
1 | #!/usr/bin/env python |
demo2.py:
1 | #!/usr/bin/env python |
保存,运行demo2.py,查看结果!
参考: