一个强大的工具一般都支持扩展或插件的开发功能,来允许第三方通过开发新扩展或插件,扩充工具本身功能,并可以贡献给社区。Jinja2也不例外,Jinja2本身提供了一部分扩展,你可以在程序中启用。同时,你还可以创建自己的扩展,来扩充模板引擎功能。本篇会先介绍Jinja2自带的扩展”jinja2.ext.i18n”的使用,自定义扩展的开发会放在下一篇阐述。
任何时候使用Jinja2时,都需要先创建Jinja2环境,所以启用扩展的方法就是在创建环境时指定:
1 2 | from jinja2 import Environment jinja_env = Environment ( extensions = [ ‘jinja2.ext.i18n’ , ‘jinja2.ext.do’ ] ) |
但是你在使用Flask时,其已经有了一个Jinja2环境,你不能再创建一个,所以你需要想办法添加扩展。Flask对于扩展不像过滤器或测试器那样封装了添加方法和装饰器,这样你就只能直接访问Flask中的Jinja2环境变量来添加。
1 2 3 4 | from flask import Flask app = Flask ( __name__ ) app . jinja_env . add_extension ( ‘jinja2.ext.i18n’ ) app . jinja_env . add_extension ( ‘jinja2.ext.do’ ) |
我们已经介绍了四个Jinja2内置扩展的使用:”jinja2.ext.autoescape”, “jinja2.ext.with_”, “jinja2.ext.do”和”jinja2.ext.loopcontrols”。除了这几个以外,Jinja2还有一个非常重要的扩展,就是提供本地化功能的”jinja2.ext.i18n”。它可以与”gettext”或”babel”联合使用,接下来我们采用”gettext”来介绍怎么使用这个本地化扩展。
建议大家先去了解下Python gettext相关知识,篇幅关系本文就不准备细讲。这里我们使用Python源代码(记住不是安装包)中”Tools/i18n”目录下的工具来创建翻译文件。
1 | $ python pygettext . py |
上述命令会在当前目录下生成一个名为”message.pot”的翻译文件模板,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ORGANIZATION # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid “” msgstr “” “Project-Id-Version: PACKAGE VERSION\n” “POT-Creation-Date: 2016-02-22 21:45+CST\n” “PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n” “Last-Translator: FULL NAME <EMAIL@ADDRESS>\n” “Language-Team: LANGUAGE <LL@trustauth.cn>\n” “MIME-Version: 1.0\n” “Content-Type: text/plain; charset=CHARSET\n” “Content-Transfer-Encoding: ENCODING\n” “Generated-By: pygettext.py 1.5\n” |
2. 将”message.pot”中”CHARSET”和”ENCODING”替换成”UTF-8″。同时你可以更改注释信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Jinja2 i18n Extention Sample # Copyright (C) 2016 trustauth.cn # Billy J. Hee <billy@trustauth.cn>, 2016. # msgid “” msgstr “” “Project-Id-Version: PACKAGE VERSION\n” “POT-Creation-Date: 2016-02-22 21:45+CST\n” “PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n” “Last-Translator: FULL NAME <EMAIL@ADDRESS>\n” “Language-Team: LANGUAGE <LL@trustauth.cn>\n” “MIME-Version: 1.0\n” “Content-Type: text/plain; charset=UTF-8\n” “Content-Transfer-Encoding: UTF-8\n” “Generated-By: pygettext.py 1.5\n” |
修改完后,将其另存为翻译文件”lang.po”。
3.在”lang.po”中添加你要翻译的文字,比如
1 2 | msgid “Hello World!” msgstr “世界,你好! |
将其加在文件末尾。这里”msgid”指定了待翻译的文字,而”msgstr”就是翻译后的文字。
4. 生成”lang.mo”文件
我们依然使用”Tools/i18n”目录提供的工具,”msgfmt.py”:
1 | $ python msgfmt . py lang . po |
执行完后,当前目录生成了”lang.mo”文件。注意,只有这个”*.mo”文件才能被应用程序识别。另外,推荐一个工具Poedit,很强的图形化po编辑工具,也可以用来生成mo文件,非常好用,Mac和Windows下都能用。
5. 将po, mo文件加入应用
我们在当前Flask工程下创建子目录”locale/zh_CN/LC_MESSAGES/”,并将刚才生成的”lang.po”和”lang.mo”文件放到这个目录下。这里”locale”子目录的名字可以更改,其他的个人建议不要改。
让我们在Flask应用代码中启用”jinja2.ext.i18n”,并加载刚创建的翻译文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #coding:utf8 import gettext from flask import Flask , render_template app = Flask ( __name__ ) # 加载扩展 app . jinja_env . add_extension ( ‘jinja2.ext.i18n’ ) # ‘lang’表示翻译文件名为”lang.mo”,’locale’表示所有翻译文件都在”locale”子目录下, # ‘zh_CN’表示二级子目录,含义上讲就是加载中文翻译。所以下面的代码会加载文件: # “locale/zh_CN/LC_MESSAGES/lang.mo” gettext . install ( ‘lang’ , ‘locale’ , unicode = True ) translations = gettext . translation ( ‘lang’ , ‘locale’ , languages = [ ‘zh_CN’ ] ) translations . install ( True ) app . jinja_env . install_gettext_translations ( translations ) |
这个”install_gettext_translations()”方法就是Jinja2提供来加载”gettext”翻译文件对象的。加载完后,你就可以在模板中使用本地化功能了。方法有两种:”{% trans %}”语句或”gettext()”方法。我们先来试下”{% trans %}”语句,在模板文件中,我们加上:
1 | < h1 > { % trans % } Hello World ! { % endtrans % } < / h1 > |
运行下,有没有看到页面上打印了”世界,你好!”,恭喜你,成功了!使用”gettext()”方法如下,效果同”{% trans %}”语句一样。
1 | < h1 > { { gettext ( ‘Hello World!’ ) } } < / h1 > |
Jinja2还提供了”_( )”方法来替代”gettext( )”,代码看起来很简洁,个人推荐使用这个方法。
1 | < h1 > { { _ ( ‘Hello World!’ ) } } < / h1 > |
上面的例子是在程序中指定本地化语言,你也可以在请求上下文中判断请求头”Accept-Language”的内容,来动态的设置本地化语言。
有时候,待翻译的文字内有一个变量必须在运行时才能确定,怎么办?我可以在翻译文字上加参数。首先,你要在po文件中定义带参数的翻译文字,并生成mo文件:
1 2 | msgid “Hello %(user)s!” msgstr “%(user)s,你好!” |
然后,你就可以在模板中,使用”{% trans %}”语句或”gettext()”方法来显示它:
1 2 | < h1 > { % trans user = name % } Hello { { user } } ! { % endtrans % } < / h1 > < h1 > { { _ ( ‘Hello %(user)s!’ ) | format ( user = name ) } } < / h1 > |
上例中,我们把模板中的变量”name”赋给了翻译文字中的变量”user”。翻译文字上可以有多个变量。
Jinja2从2.5版本开始,支持新的gettext样式,使得带参数的本地化更简洁,上面的例子在新样式中可以写成:
1 | < h1 > { { _ ( ‘Hello %(user)s!’ , user = name ) } } < / h1 > |
不过使用新样式前,你必须先启用它。还记得我们介绍过Jinja2加载翻译文件的方法吗?对,就是”install_gettext_translations()”。调用它时,加上”newstyle=True”参数即可。
1 | app . jinja_env . install_gettext_translations ( translations , newstyle = True ) |
英文有个特点就是名词有单/复数形式,一般复数都是单数后面加s,而中文就不区分了,哎,老外就是麻烦。所谓外国人创造的Python gettext,自然也对单/复数提供了特殊的支持。让我们现在po文件中,加上下面的内容,并生成mo文件:
1 2 3 4 | msgid “%(num)d item” msgid _plural “%(num)d items” msgstr [ 0 ] “%(num)d个物品” msgstr [ 1 ] “%(num)d个物品集” |
什么意思呢,这个”msgid_plural”就是指定了它上面”msgid”文字的复数形式。而”msgstr”的[0], [1]分别对应了单/复数形式翻译后的内容。为什么这么写?你别管了,照着写就是了。
在模板中,我们加上下面的代码:
1 2 3 | { % set items = [ 1 , 2 , 3 , 4 , 5 ] % } { { ngettext ( ‘%(num)d item’ , ‘%(num)d items’ , items | count ) } } < br / > { { ngettext ( ‘%(num)d item’ , ‘%(num)d items’ , items | first ) } } |
文章转载来自:trustauth.cn