原创: 志学Python 志学Python
01 flask-babel 简介
你猜对了,Flask-Babel正是用于简化翻译工作的。可以使用pip命令安装它:
(venv) $ pip install flask-babel
Flask-Babel的初始化与之前的插件类似:
app/__init__.py: Flask-Babel实例
# ...from flask_babel import Babelapp = Flask(__name__)# ...babel = Babel(app)
作为本章的一部分,我将向你展示如何将应用翻译成西班牙语,因为我碰巧会这种语言。我当然也可以与翻译机制合作来支持其他语言。为了跟踪支持的语言列表,我将添加一个配置变量:
config.py:支持的语言列表
class Config(object): # ... LANGUAGES = ['en', 'es']
我为本应用使用双字母代码来表示语言种类,但如果你需要更具体,还可以添加国家代码。例如,你可以使用en-US,en-GB和en-CA来支持美国、英国和加拿大的英语以示区分。
Babel实例提供了一个localeselector装饰器。为每个请求调用装饰器函数以选择用于该请求的语言:
app/__init__.py:选择最匹配的语言
from flask import request# ...@babel.localeselectordef get_locale(): return request.accept_languages.best_match(app.config['LANGUAGES'])
这里我使用了Flask中request对象的属性accept_languages。 request对象提供了一个高级接口,用于处理客户端发送的带Accept-Language头部的请求。该头部指定了客户端语言和区域设置首选项。该头部的内容可以在浏览器的首选项页面中配置,默认情况下通常从计算机操作系统的语言设置中导入。大多数人甚至不知道存在这样的设置,但是这是有用的,因为应用可以根据每个语言的权重,提供优选语言的列表。为了满足你的好奇心,下面是一个复杂的Accept-Languages头部的例子:
Accept-Language: da, en-gb;q=0.8, en;q=0.7
这表示丹麦语(da)是首选语言(默认权重= 1.0),其次是英式英语(en-GB),其权重为0.8,最后是通用英语(en),权重为0.7
要选择最佳语言,你需要将客户请求的语言列表与应用支持的语言进行比较,并使用客户端提供的权重,查找最佳语言。这样做的逻辑有点复杂,但它已经全部封装在best_match()方法中了,该方法将应用提供的语言列表作为参数并返回最佳选择
02 标记文本以在Python源代码中执行翻译
好吧,坏消息来了。支持多语言的常规流程是在源代码中标记所有需要翻译的文本。文本标记后,Flask-Babel将扫描所有文件,并使用gettext工具将这些文本提取到单独的翻译文件中。不幸的是,这是一个繁琐的任务,并且是启用翻译的必要条件。
我将在这里向你展示标记操作的几个示例,你也可以从下载包获取本章完整的更改集,当然,也可以直接查看GitHub的页面。
为翻译而标记文本的方式是将它们封装在一个函数调用中,该函数调用为_(),仅仅是一个下划线。最简单的情况是源代码中出现的字符串。下面是一个flask()语句的例子:
from flask_babel import _# ...flash(_('Your post is now live!'))
_()函数用于原始语言文本(在这种情况下是英文)的封装。该函数将使用由localeselector装饰器装饰的选择函数,来为给定客户端查找正确的翻译语言。 _()函数随后返回翻译后的文本,在本处,翻译后的文本将成为flash()的参数。
但是不可能每个情况都这么简单,试想如下的另一个flash()调用:
flash('User {} not found.'.format(username))
该文本具有一个安插在静态文本中间的动态组件。 _()函数的语法支持这种类型的文本,但它基于旧版本的字符串替换语法:
flash(_('User %(username)s not found.', username=username))
还有更难处理的情况。有些字符串文字并非是在发生请求时分配的,比如在应用启动时。因此在评估这些文本时,无法知道要使用哪种语言。一个例子是与表单字段相关的标签,处理这些文本的唯一解决方案是找到一种方法来延迟对字符串的评估,直到它被使用,比如有实际上的请求发生了。Flask-Babel提供了一个称为lazy_gettext()的_()函数的延迟评估的版本:
from flask_babel import lazy_gettext as _lclass LoginForm(FlaskForm): username = StringField(_l('Username'), validators=[DataRequired()]) # ...
在这里,我正在导入的这个翻译函数被重命名为_l(),以使其看起来与原始的_()相似。这个新函数将文本包