二、python-Babel (pythonweb国际化)
上节我们利用gettext模块进行国际化,但是翻译文件的提取和生成需要借助外界工具xgettext完成。那么babel是用来干什么的呢?Babel提供了两部分功能:一是遵照标准gettext的提取和编译工具,二是提供对各种语言环境显示名称、本地化数字和日期格式等的支持。
2.1 pybabel - Babel命令行工具:
虽然Python标准库包含一个gettext模块,使应用程序能够进行国际化,但它要求开发人员使用GNU工具(如xgettext、msgmerge和msgfmt)构建这些目录。虽然xgettext确实支持从Python文件中提取消息,但它不知道如何处理PythonWeb应用程序中常见的其他类型的文件(html、js),例如模板,也不提供简单的扩展机制来添加此类支持。
Babel通过提供一个框架来解决这个问题,在这个框架中,可以根据配置灵活从不同类型文件中提取消息,并且还消除了对GNU gettext工具的依赖(因为这些工具不一定在所有平台上都可用)。Babel提供的命令行工具在babel.messages包下,它的入口是 babel.messages.frontend:main ,提供了以下四个功能:
- extract - 从一系列文件提取翻译,生成翻译模板pot文件(portable object,可移植对象)
- compile - 将po文件编译成mo文件(mo, 机器对象)
- init - 根据pot文件模板创建新的翻译目录
- update - 根据pot文件模板更新翻译目录
(1) 前提准备:
当通过pip安装Babel完成后,会在PYTHON_ROOT/Scripts下生成一个 pybabel.exe, pybabel-script.py 文件,这是python为符合命令行生成的一个可执行文件,入口就是 babel.messages.frontend:main,使用pybabel --help查看用法
pip install Babel
pybabel --help
- 程序目录:
babel_
|____templates
|____index.html
|____app.py
|____babel.cfg
- 文件内容:
# app.py
from gettext import translation
import os
root_dir = os.path.join(os.path.dirname(__file__), "i18n")
t = translation(domain="messages", localedir=root_dir, languages=["zh"])
_ = t.gettext
py_str1 = _('Hello World -py1 !')
py_str3 = _("""
Hello World -py3!
""")
# templates/index.html
<!DOCTYPE html>
<title>{{ _("Html Title") }}</title>
<h1>{{ _("Hello World!") }}</h1>
(2) 提取命令extract:
- 命令格式:pybabel extract [options] dir
- 功能说明:从一系列文件提取翻译,生成翻译模板pot文件
- 命令选项:
选项 | 说明 |
---|---|
–charset=CHARSET | 输出文件编码,默认UTF-8 |
-k keywords | 要提取消息的关键字,空格隔开(默认只从调用了gettext(),ngettext()和_()提取消息) |
-F mapping_file | 指明提取配置文件 |
-o OUTPUT | 指明输出文件, 默认./messages.pot |
配置文件告诉pybabel要从当前目录及其子目录下所有的*.py文件,templates目录及其子目录下所有的*.html文件里面搜寻可翻译的文字即所有调用gettext(),ngettext()和_()方法时传入的字符串,-k lazy_gettext来提醒pybabel要搜索该方法的调用 |
pybabel extract -k lazy_gettext -F babel.cfg -o messages.pot .
# babel.cfg
```
[python: **.py]
[jinja2: **/templates/**.html]
```
(3) 初始化命令init:
- 命令格式: pybabel init [options]
- 功能说明:根据pot文件模板创建新的翻译目录
- 命令选项:
选项 | 说明 |
---|---|
-d output_dir, --output-dir=OUTPUT_DIR | 指定了输出目录 |
-l locale, --locale=LOCALE | 指定了翻译语言,二级子目录 |
-D domain, --domain=DOMAIN | 指定了po文件名称 |
-i input_file, --input-file=INPUT_FILE | 指定翻译文件模板pot文件 |
=>生成文件: <output_dir>/<locale>/LC_MESSAGES/<domain>.po |
pybabel init -d i18n -l zh-D messages -i messages.pot
# => i18n/CN/LC_MESSAGES/messages.po
# 接下来修改对应msgstr对应的翻译内容
(4) 编译命令compile:
命令和init上述列出的选项相同,但是我们需要在(3)init生成的messages.po同级生成messages.mo文件,让gettext读取解析,直接使用-d output_dir选项,会将output_dir目录下的所有po文件都会被编译成mo文件。
pybabel compile -d i18n
(5) 增量更新命令update:
如果代码中的待翻译的文字被更改过或新增,我们需要重新生成”messages.pot”翻译文件模板。此时,要是再通过pybabel init
命令来创建po文件的话,会丢失之前已翻译好的内容,这个损失是很大的,update提供了方法,将修改及新增内容放到之前编写过的messages.po文件中。
pybabel update -i messages.pot -d i18n
2.2 jinja2国际化
jinja2 是一个可扩展的模板渲染引擎。jinja2自身提供了一部分扩展在ext.py中,他的内置扩展提供了国际化功能允许在html模板中使用{{ _(“”)}} 表达式来提取翻译:
(1) 加载扩展:
jinja2使用时首先需要创建环境Envoriment, 构造时提供了extentions选项,此外还可以在初始化后通过add_extention动态添加扩展,扩展通过全路径包名识别。
jinja_env = Environment(
loader=FileSystemLoader(template_path), autoescape=True,
extensions=['jinja2.ext.i18n']
)
jinja_env.add_extension('jinja2.ext.do')
(2) 扩展调用:
jinja2允许在模板取值{{}}时通过"_ 、gettext 、 ngettext"来国际化,也可以通过trans控制标签取值。
{{ _("Hello World") }}
{% trans %}
Hello World -html!
{% endtrans%}
翻译内容带参数时
{{ _("Hello World1, %(user)s")|format(user="HTML")}}
{# 这个标签的翻译不好用,不像pythonic代码, 建议用上面的#}
{% trans user="HTML"%}
Hello World2, {{user}}
{% endtrans %}
{# 新样式格式化 #}
{{ _('Hello World3, %(user)s!', user=name) }}
(3) 程序调用:
需要在模板渲染的时候,将gettext的实现传入上下文中,app.py打印index.html的内容:
_ = t.gettext
template_path = os.path.join(os.path.dirname(__file__), "templates")
# 环境初始化,加载扩展
jinja_env = Environment(
loader=FileSystemLoader(template_path), autoescape=True,
extensions=['jinja2.ext.i18n', 'jinja2.ext.do']
)
# 模板渲染时添加gettext的实现到上下文
template_name = "index.html"
tmpl = jinja_env.get_template(template_name)
data = tmpl.render({"gettext": _})
print(data)
本文代码地址:python-i18n.git