使用Python3中的gettext模块翻译Python源码以支持多语言


Posted in Python onMarch 31, 2015

你写了一个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。这只是个普通纯文本文件,它列出来了全部的在源码中寻找_()的调用的要翻译的字符串。你可以在这儿看guess.pot文件.
第三步:用Poedit翻译字符串

你可以用文本编辑器填写翻译但是免费的Poedit软件会更容易从这儿下载http://poedit.net. 选择 > New from POT/PO file… 然后选择你的guess.po文件。

使用Python3中的gettext模块翻译Python源码以支持多语言

Poedit会问你想要翻译成什么语言。我们举例用西班牙语:

使用Python3中的gettext模块翻译Python源码以支持多语言

填写翻译吧。(我用 http://translate.google.com,所以对于真的使用西班牙语的人会感觉有点奇怪。)

使用Python3中的gettext模块翻译Python源码以支持多语言

现在储存文件在它的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这些环境变量中的一个来确定计算机的工作地点。我会边学习边更新本教程的。

Python 相关文章推荐
Python 专题二 条件语句和循环语句的基础知识
Mar 19 Python
python urllib爬取百度云连接的实例代码
Jun 19 Python
python线程池threadpool使用篇
Apr 27 Python
matplotlib给子图添加图例的方法
Aug 03 Python
解决python 无法加载downsample模型的问题
Oct 25 Python
浅谈Python反射 & 单例模式
Mar 21 Python
Python_查看sqlite3表结构,查询语句的示例代码
Jul 17 Python
Python操作Sonqube API获取检测结果并打印过程解析
Nov 27 Python
通过python连接Linux命令行代码实例
Feb 18 Python
python numpy矩阵信息说明,shape,size,dtype
May 22 Python
Python3爬虫中pyspider的安装步骤
Jul 29 Python
python3字符串输出常见面试题总结
Dec 01 Python
python根据出生日期获得年龄的方法
Mar 31 #Python
用Python进行一些简单的自然语言处理的教程
Mar 31 #Python
用Python制作在地图上模拟瘟疫扩散的Gif图
Mar 31 #Python
以一段代码为实例快速入门Python2.7
Mar 31 #Python
11个并不被常用但对开发非常有帮助的Python库
Mar 31 #Python
Python的Flask框架中@app.route的用法教程
Mar 31 #Python
使用Python的Flask框架实现视频的流媒体传输
Mar 31 #Python
You might like
PHP在同一域名下两个不同的项目做独立登录机制详解
2017/09/22 PHP
JS 类型转换常见方法小结
2010/05/31 Javascript
jQuery中创建实例与原型继承揭秘
2011/12/21 Javascript
《JavaScript高级程序设计》阅读笔记(二) ECMAScript中的原始类型
2012/02/27 Javascript
JS获取月份最后天数、最大天数与某日周数的方法
2015/12/08 Javascript
【经验总结】编写JavaScript代码时应遵循的14条规律
2016/06/20 Javascript
bootstrap模态框跳转到当前模板页面 框消失了而背景存在问题的解决方法
2020/11/30 Javascript
jQuery实现给input绑定回车事件的方法
2017/02/09 Javascript
js实现百度登录框鼠标拖拽效果
2017/03/07 Javascript
原生JS实现ajax与ajax的跨域请求实例
2017/12/01 Javascript
angular项目中bootstrap-datetimepicker时间插件的使用示例
2018/03/15 Javascript
详解es6超好用的语法糖Decorator
2018/08/01 Javascript
学习使用ExpressJS 4.0中的新Router的用法
2018/11/06 Javascript
vue改变对象或数组时的刷新机制的方法总结
2019/04/24 Javascript
koa-router路由参数和前端路由的结合详解
2019/05/19 Javascript
详解如何在Vue项目中发送jsonp请求
2019/10/25 Javascript
[02:43]中国五虎出征TI3视频
2013/08/02 DOTA
python+selenium实现163邮箱自动登陆的方法
2017/12/31 Python
对python pandas 画移动平均线的方法详解
2018/11/28 Python
anaconda中更改python版本的方法步骤
2019/07/14 Python
Django admin.py 在修改/添加表单界面显示额外字段的方法
2019/08/22 Python
python实现自动化报表功能(Oracle/plsql/Excel/多线程)
2019/12/02 Python
Python3和PyCharm安装与环境配置【图文教程】
2020/02/14 Python
python如何删除文件、目录
2020/06/23 Python
Python实现一个优先级队列的方法
2020/07/31 Python
CSS3移动端vw+rem不依赖JS实现响应式布局的方法
2019/01/23 HTML / CSS
极简的HTML5模版
2015/07/09 HTML / CSS
TripAdvisor德国:全球领先的旅游网站
2017/12/07 全球购物
廉价连衣裙和婚纱礼服在线销售:Tbdress
2019/02/28 全球购物
大学生活动总结怎么写
2014/04/29 职场文书
2014教师评职称工作总结
2014/11/10 职场文书
Css预编语言及区别详解
2021/04/25 HTML / CSS
一篇文章弄懂MySQL查询语句的执行过程
2021/05/07 MySQL
MySQL查看表和清空表的常用命令总结
2021/05/26 MySQL
css3中2D转换之有趣的transform形变效果
2022/02/24 HTML / CSS
Java中API的使用方法详情
2022/04/06 Java/Android