简介
python 国家化与本土化的示例。起因是一款软件没有做语言的适配。
python的国际化gettext模块。mattkang 。2017-12-08 中对 i18n 和 l10n 说的特别到位的地方
国际化的软件具备这样一种能力,当软件被移植到不同的语言及地区时,软件本身不用做内部工程上的改变或修正。
相关词汇
Internationalization (i18n) - 国际化
Localization (l10n) - 本地化、本土化
怎么做
遇事不觉看文档 国际化 - Python docs,提到了 gettext 和 locale,又看了 gettext 的文档,emm 没有一个比较好的例子,整得我不会用。。
Bing 搜索了下发现几篇写的还很不错的,但是依然搞不懂怎么一步一步使用。。。
终于在 Bing 国际版找到了一篇循序渐进的文章。
最初 GNU gettext 仅支持 C 或 C++ ,但其扩展版本 xgettext 扫描以多种语言(包括 Python)编写的代码,以查找被标记为可翻译的字符串。Python 发行版包括一些称为 pygettext.py 和 msgfmt.py 的特定程序,它们仅识别 Python 源代码而不识别其他语言。
大纲
- xgettext/pygettext 提取要翻译的文本
- 翻译 po 文件
- msgfmt 编译 po 文件为 mo 文件
- gettext 翻译
案例
这里采用 Manjaro linux 发行版的 xgettext 和 msgfmt 作为例子。下面是一个很简单的 py3 的程序(main.py),运行结果也显而易见。
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
print("Edit")
print("Cancel")
print("Confirm")
为了提取要翻译的词汇,这里加入引用,给要进行 i18n 的文本都放在 _() 中,此时运行程序的结果和上面一样。
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import gettext
_ = gettext.gettext
print(_("Edit"))
print(_("Cancel"))
print(_("Confirm"))
xgettext/pygettext 提取要翻译的文本
如下所示建立文件夹,locale 可以叫做别的名字,就保持默认吧
mkdir -p locale/zh_CN/LC_MESSAGES
生成 po 文件
xgettext -k_ -o locale/zh_CN/LC_MESSAGES/zh_CN.po main.py
文件结构树
├── locale
│ └── zh_CN
│ └── LC_MESSAGES
│ └── zh_CN.po
└── main.py
翻译 po 文件
刚才生成的文件(locale/zh_CN/LC_MESSAGES/zh_CN.po)如下所示
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-05 16:31+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"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: main.py:6
msgid "Edit"
msgstr ""
#: main.py:7
msgid "Cancel"
msgstr ""
#: main.py:8
msgid "Confirm"
msgstr ""
必须要需要修改的有两个地方,一个是 charset=CHARSET 要修改为 UTF-8(python3 默认这个编码),二是所有的 msgstr。上面的注释啥的看需求写哈
msgid 代表刚才提取出来的字符串,msgstr 代表其翻译,需要自己翻译。
改完之后是下面这个样子的
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# kearney <kearneyback@gmail.com>, 2021.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-05 16:31+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: kearney <kearneyback@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: main.py:6
msgid "Edit"
msgstr "编辑"
#: main.py:7
msgid "Cancel"
msgstr "取消"
#: main.py:8
msgid "Confirm"
msgstr "确认"
msgfmt 编译 po 文件为 mo 文件
翻译后生成mo最终文件
msgfmt -o locale/zh_CN/LC_MESSAGES/zh_CN.mo locale/zh_CN/LC_MESSAGES/zh_CN.po
文件结构树
├── locale
│ └── zh_CN
│ └── LC_MESSAGES
│ ├── zh_CN.mo
│ └── zh_CN.po
└── main.py
gettext 翻译
稍微修改一下代码,此时运行程序就会发现输出是刚才翻译后的中文,而不是程序中的英文
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import gettext
l10n = gettext.translation("zh_CN", localedir="locale", languages=["zh_CN"])
l10n.install()
_ = l10n.gettext
print(_("Edit"))
print(_("Cancel"))
print(_("Confirm"))
改进
上面写死了翻译为中文,理想状态下是根据用户的语言自动设定语言,没有对应的翻译包则默认显示英文。
这是一个通过 locale 获取本地语言和编码的案例
import locale
loc = locale.getlocale()
print(loc,loc[0],loc[1])
# 系统中文时输出: ('zh_CN', 'UTF-8') zh_CN UTF-8
# 系统英文时输出: ('en_US', 'UTF-8') en_US UTF-8
可以用 locale
命令获取当前的系统语言设置,可以使用 export LANG=en_US.UTF-8
或者 export LANG=zh_CN.UTF-8
来临时修改环境变量中的语言设置。
下面这个程序实现了当系统语言不是 en_US 的时候,会尝试寻找对应语言翻译包,找到了就使用,没找到就使用默认 en_US 语言,需要注意的是,下面的代码存在没有鉴别编码、没有考虑其它英语地区等等问题。
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import gettext
import locale
_ = gettext.gettext
try:
loc = locale.getlocale()
if not loc[0] =="en_US":
l10n = gettext.translation(loc[0], localedir="locale", languages=[loc[0]])
l10n.install()
_ = l10n.gettext
except Exception as e:
print(e,"\nUsing defalt language - English(en_US.UFT-8)")
print(_("Edit"))
print(_("Cancel"))
print(_("Confirm"))
总结
其实还可以做的更多,之前接触过一款开源软件,需要翻译,当时是进入一个网页,有不同的短语和对应谷歌翻译,志愿者可对其翻译进行修改、投票等。忘记是哪一个来着了,用互联网的思维去做软件本土化,毕竟不是每个软件都有翻译小组。
最后,不得晒一下我的博客啥的吗?
参考
- 国际化 - Python docs
- How to Translate Python Applications with the GNU gettext Module.Authored by Theo. Last updated on May 22nd, 2021 .
- python的国际化gettext模块。mattkang 。2017-12-08
- Python实例浅谈之十国际化支持。乌托邦2号。 2015-10-08
- i18n vs l10n — what’s the diff? 2011.12.14
- locale — 国际化服务 - Python docs
locale.getpreferredencoding
在 3.7 版更改: The function now always returns UTF-8 on Android or if the UTF-8 mode is enabled.