使用Python3中的gettext模块翻译Python源码以支持多语言
你写了一个python 3程序,还想要它适用于其他语言。你能复制全部代码库,然后刻意地检查每个.py文件,替换掉所有找到的文本字符串。但这意味着你有两份你代码的独立副本,每当你要做出个改动或修复个bug,你的工作量会加倍。而且如果你想要程序还适用于其他语言,就更糟了。
幸运的是,python给了一个解决办法,就是用gettext模块。
一个hack解法
你应该把你自己的解决办法统一改变。例如,你可以把你程序中的每个字符串替换为一个函数调用(函数名简单些,比如像_()一样),这会返回被翻译为该正确语言的字符串。举个例子,如果你的程序原本是:
print('hello world!')
……你可以将它改为:
print(_('hello world!'))
……函数_()会返回'hello world!'的翻译,它基于程序设置有的语言。比如,如果这个语言设置之前被存在一个叫language的全局变量中,函数_()看起来像这样:
def _(s): spanishstrings = {'hello world!': 'hola mundo!'} frenchstrings = {'hello world!': 'bonjour le monde!'} germanstrings = {'hello world!': 'hallo welt!'} if language == 'english': return s if language == 'spanish': return spanishstrings[s] if language == 'french': return frenchstrings[s] if language == 'german': return germanstrings[s]
这可以,但是你这是在重复造*。python的gettext模块可以做更多。gettext是一系列工具,文件格式在20世纪90年代被发明出来,来规范软件国际化(也叫i18n)。gettext是个作为对于所有编程语言的系统化的设计,但是我们会在本篇文章中只专注于python。
程序例子
设想你有个想要翻译的用python3写的简单“猜数字”游戏。。有四步来使这个程序国际化:
调整这个.py文件的源代码,这样使字符串输入进一个名为_()的函数。
用和python一起安装的pygettext.py文本,从源代码创建一个”pot”文件。
用这个免费的跨平台poedit软件,从pot文件创建.po和.mo文件。
再次调整你的.py文件源代码导入gettext模块的代码,设置语言。
第一步:添加 _() 函数
首先,检查你程序中的所有需要被翻译和用_()的调用来替代的字符串。针对python使用的gettext系统用_()作为得到翻译了的字符串的通用名,因为它是个短名。
注意:用格式型字符串而不是连接型字符串会是你的程序翻译起来更简单。例如,用连接型字符串你的程序会像这样:
print('good job, ' + myname + '! you guessed my number in ' + guessestaken + ' guesses!') print(_('good job, ') + myname + _('! you guessed my number in ') + guessestaken + _(' guesses!'))
this results in three separate strings that need to be translated, as opposed to the single string needed in the string formatting approach:
这会导致三个独立的字符串都需要翻译,然而相反的是在格式型的字符串中,只需翻译一个字符串:
print('good job, %s! you guessed my number in %s guesses!' % (myname, guessestaken))
print(_('good job, %s! you guessed my number in %s guesses!') % (myname, guessestaken))
当你改完“猜数字”源代码后,它。你并不能运行它,因为_()函数还没定义。这个变化只是让pygettext.py文本可以找到所有需要翻译的字符串。
第二步:用pygettext.py提取字符串
在你python安装(windows上的c:python34toolsi18n)中的tools/i18n就是pygettext.py文本。对于可译字符串普通 gettext unix 命令解析 c/c++ 源码并且 xgettext unix 命令可以解析其他语言,而pygettext.py则知道怎样去解析python源码。它会找到所有字符串并产生个”pot”文件。
在windows上我已经运行了这个文本像这样:
c:>py -3.4 c:python34toolsi18npygettext.py -d guess guess.py
这创建了一个pot文件,叫guess.pot。这只是个普通纯文本文件,它列出来了全部的在源码中寻找_()的调用的要翻译的字符串。你可以.
第三步:用poedit翻译字符串
你可以用文本编辑器填写翻译但是免费的poedit软件会更容易从这儿下载http://poedit.net. 选择 > new from pot/po file… 然后选择你的guess.po文件。
poedit会问你想要翻译成什么语言。我们举例用西班牙语:
填写翻译吧。(我用 http://translate.google.com,所以对于真的使用西班牙语的人会感觉有点奇怪。)
现在储存文件在它的gettext形式的文件夹里。保存会创建.po文件(一个人类可读的文本文件不同于原始.pot文件,除了是有西语翻译的)和一个.mo文件(一个gettext会读取的机器可读版本。这些文件会存在一个特定的文件夹内,为的是让gettext能够找到他们。他们看起来像这样(比如西语文件中的”es”和德语文件中”de”):
./guess.py ./guess.pot ./locale/es/lc_messages/guess.mo ./locale/es/lc_messages/guess.po ./locale/de/lc_messages/guess.mo ./locale/de/lc_messages/guess.po
这些两种性质的语言像西语中的”es”和德语中的 ”de” 被称作iso 639-1 codes 是语言的标准缩写。你不一定要用他们,但是遵循标准是有道理的。
第四步:给你程序加上gettext代码
现在你有包含翻译的.mo文件,调整你的python代码去用它。在你的程序中加上下面的:
import gettext es = gettext.translation('guess', localedir='locale', languages=['es']) es.install()
第一个 'guess' 是”定义域”,这其实是意味着guess.mo文件名中“猜”的部分。 localedir是你创建的locale文件夹的目录地址。这会是相对或绝对的路径。'es'描述在locale文件夹下面的文件。lc_messages文件夹是个标准名
install()方法会导致调用_()返回翻译为西语的字符串。如果你想回到原始的英语只需要分配一个lambda函数值给_,这会返回当时输入的字符串:
import gettext es = gettext.translation('guess', localedir='locale', languages=['es']) print(_('hello! what is your name?')) # prints spanish _ = lambda s: s
你可以检查准备翻译的”guess the number”源码。如果你想要运行此程序,并解压这个压缩文件和它的locale文件夹和.mo安装文件。
延伸阅读
我怎样都称不上是 i18n or gettext的专家,如果我的教程讲解不够好,请一定要留言。大多数情况下,你的软件运行时不会转换语言,而是会去读language,lc_all,lc_messages,和lang这些环境变量中的一个来确定计算机的工作地点。我会边学习边更新本教程的。