展开

文章目录

修改历史

修改历史记录

  1. 2022-05-04 09:06:09
  2. 2022-04-29 11:42:05
  3. 2022-03-07 09:54:47
  4. 2022-02-28 09:52:13
  5. 2022-02-27 22:35:46
  6. 2022-02-27 21:12:22

flask-babel在工厂模式下的使用

2022-02-27 21:06:00 Python 132

简介

flask-babel是一个flask支持国际化的扩展,通过该扩展可以很简单的实现flask应用的国际化。查看官方文档以及网络上的一些示例教程,但示例教程都是通过单文件flask应用来进行演示国际化的,因此记录一下通过工厂模式创建的flask应用如何使用flask-babel实现国际化。

1. 使用flask-babel国际化流程

通过flask-babel的官方文档以及网络上的一些简单示例教程,flask-babel进行国际化的流程如下图所示

首先通过pip install flask-babel进行扩展的安装

pip install flask-babel

安装完成之后通过下面的代码进行初始化

from flask import Flask
from flask_babel import Babel

app = Flask(__name__)
babel = Babel(app)

初始化完成之后就需要在项目的根目录新建flask-babel的配置文件babel.cfg,文件名取什么都无所谓,这里只是好做区分,在babel.cfg文件中一般会输入下面的内容即可

[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_

之后通过extract命令将需要翻译的字符串抽离出来保存到根目录的模板文件中去

pybabel extract -F babel.cfg -o messages.pot .

这里的-F参数可以指定相应babel配置文件-o 参数指定抽离的模板文件名称 . 则表示将模板文件保存到当前目录下,当然也可以指定到其他路径,抽离完模板文件之后,通过init命令初始化我们的目标语言模板文件,基于上一步骤抽离的pot模板文件

pybabel init -i messages.pot -d translations -l zh

-i 参数指定根模板文件的路径,-d 指定目标语言的模板文件保存路径(上面命令就是将目标语言的模板文件保存到当前目录的translations/zh目录下) -l 参数则是指定转换的语言,这里指定为中文,通过flask-babel实现国际化的流程大致就是如此,具体细节可以参考官方文档以及网络资料。

2. 工厂模式应用使用flask-babel

单文件应用都是在实例化Flask后直接通过Babel(app)进行初始化的,下面通过一个示例演示如何在工厂模式应用中使用flask-babel。

2.1 项目目录结构

项目目录结构如上图所示

  • app: 存放flask应用源代码
  • translations: 存放翻译文件
  • babel.cfg: flask-babel配置文件
  • .flaskenv: flask环境变量文件
  • message.pot: flask-babel 模板翻译文件3

2.2 工厂模式初始化babel

在app目录下的__init__.py文件中输入如下代码

from flask import Flask, request
from app.extensions import babel
from app.views.index import idx_bp
import os

basedir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))


def create_app():
    app = Flask(__name__)
    app.config['BABEL_DEFAULT_LOCALE'] = 'zh'
    app.config['BABEL_TRANSLATION_DIRECTORIES'] = basedir + '/translations'
    app.register_blueprint(idx_bp)
    register_extensions(app)

    @babel.localeselector
    def get_local():
        cookie = request.cookies.get('locale')
        if cookie in ['zh', 'en']:
            return cookie
        return request.accept_languages.best_match(app.config.get('BABEL_DEFAULT_LOCALE'))

    return app


def register_extensions(app):
    babel.init_app(app)

flask-babel是通过装饰器函数localselector去获取当前需要渲染的语言,在create_app工厂方法中我们通过get_local函数去获取请求中cookie是否包含有local的值,如果有则返回cookie的值,如果没有则获取请求中accept_language获取与默认配置最匹配的语言,每次请求进入都会自动调用该函数,因此就可以达到动态切换语言的目的了。

与此同时,通过BABEL_DEFAULT_LOCALE以及BABEL_TRANSLATION_DIRECTORIES分别来配置默认语言以及默认翻译文件存储路径,如果不配置翻译文件默认存储位置,flask-babel默认会去app/translations目录下查找,这与我们翻译文件的路径是不一样。

2.3 蓝图中的翻译

flask-babel提供了三个函数来标志我们需要翻译的字符串分别是gettext、ngettext、lazy_gettext,其中gettext可以标志单个字符串,ngettext可以标记多个字符串,lazy_gettext只在使用到该字符串的时候才进行翻译,在views文件中新建index.py文件,输入下面的代码

from flask import Blueprint, render_template
from flask_babel import gettext
idx_bp = Blueprint('index', __name__)


@idx_bp.route('/')
@idx_bp.route('/index')
def index():
    content = gettext('This is a flask-babel sample application')
    return render_template('index.html', content=content)

2.4 模板中的翻译

除了可以标记python文件中的字符串,还可以标记模板文件中的字符串,在templates目录中新建index.html文件,输入下面的代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask Babel Sample </title>
    <link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
</head>
<body>
<div class="container">
    <h3>{{ _("Factory mode use Flask-Babel") }}</h3>
    <hr>
    <p>1.{{ _('Blueprint Sample') }}</p>
    <p>{{ content }}</p>
    <p>2.{{ _('Template Sample') }}</p>
    <p>{{ _("I have a dream that one day this nation will rise up and live out the true meaning of its creed: 'We hold these truths to be self-evident, that all men are created equal.") }}</p>
    <p>{{ _("I have a dream that one day on the red hills of Georgia, the sons of former slaves and the sons of former slave owners will be able to sit down together at the table of brotherhood.") }}</p>
    <p>{{ _("I have a dream that one day even the state of Mississippi, a state sweltering with the heat of injustice, sweltering with the heat of oppression, will be transformed into an oasis of freedom and justice.") }}</p>
    <p>{{ _("I have a dream that my four little children will one day live in a nation where they will not be judged by the color of their skin but by the content of their character.") }}</p>
    <button class="btn zh"><a class="link" href="">中文</a></button>
    <button class="btn en"><a class="link" href="">English</a></button>
</div>
</body>
</html>

在模板文件,将需要标志的字符串通过_()进行包括,为什么采用_()进行包裹,因为在babel.cfg文件中添加了extensions=jinja2.ext.autoescape,jinja2.ext.with_,在使用extract命令的时候会自动将其抽离出来。

2.5 抽离模板生成翻译文件

在使用gettext或者_()标记好需要翻译的字符串之后,通过extract命令自动抽离出python文件、模板文件中所有被标记的字符串

pybabel extract -F babel.cfg -o message.pot .

运行结束后会在项目根目录中生成一个message.pot文件,文件内容如下图所示(只截取了部分内容),可以看到文件中的内容都是事先标记好的字符串

上面的message.pot文件为模板文件,通过init命令可以根据模板文件初始化目标语言po文件

pybabel init -i message.pot -d translations -l zh

上述命令会根据上一个步骤中生成的模板文件生成对应语言的翻译文件,在translations/zh/LC_MESSAGES目录中可以看到message.po文件,文件内容与message.pot文件内容一直,我们只需要在msgstr字段中填入对应语言的翻译内容即可,如下代码所示

# Chinese translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-27 20:36+0800\n"
"PO-Revision-Date: 2022-02-27 19:54+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: zh\n"
"Language-Team: zh <LL@li.org>\n"
"Plural-Forms: nplurals=1; plural=0\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"

#: app/templates/index.html:10
msgid "Factory mode use Flask-Babel"
msgstr "Flask-Babel在工厂模式下的应用"

#: app/templates/index.html:12
msgid "Blueprint Sample"
msgstr "蓝图示例"

#: app/templates/index.html:14
msgid "Template Sample"
msgstr "模板示例"

#: app/templates/index.html:15
msgid ""
"I have a dream that one day this nation will rise up and live out the "
"true meaning of its creed: 'We hold these truths to be self-evident, that"
" all men are created equal."
msgstr "我梦想有一天,这个国家会站立起来,真正实现其信条的真谛:“我们认为这些真理是不言而喻的——人人生而平等。”"

#: app/templates/index.html:16
msgid ""
"I have a dream that one day on the red hills of Georgia, the sons of "
"former slaves and the sons of former slave owners will be able to sit "
"down together at the table of brotherhood."
msgstr "我梦想有一天,在佐治亚的红山上,昔日奴隶的儿子将能够和昔日奴隶主的儿子坐在一起,共叙兄弟情谊。"

#: app/templates/index.html:17
msgid ""
"I have a dream that one day even the state of Mississippi, a state "
"sweltering with the heat of injustice, sweltering with the heat of "
"oppression, will be transformed into an oasis of freedom and justice."
msgstr "我梦想有一天,甚至连密西西比州这个正义匿迹,压迫成风的地方,也将变成自由和正义的绿洲。"

#: app/templates/index.html:18
msgid ""
"I have a dream that my four little children will one day live in a nation"
" where they will not be judged by the color of their skin but by the "
"content of their character."
msgstr "我梦想有一天,我的四个孩子将在一个不是以他们的肤色,而是以他们的品格优劣来评价他们的国度里生活。"

#: app/views/index.py:17
msgid "This is a flask-babel sample application"
msgstr "这是一个flask-babel的简单示例应用"

这时候浏览器打开http://127.0.01:5000首页显示页面内容如下,页面显示的中文,因为默认配置的语言为zh

3. 动态切换语言

在第二节中,通过读取请求中cookie的值去判断当前语言,因此我们可以在index.py文件中添加下面的代码实现动态语言切换功能,代码片段如下

@idx_bp.route('/set-locale/<language>')
def set_locale(language):
    resp = redirect(request.referrer)
    if language:
        resp.set_cookie('locale', language, max_age=30 * 24 * 60 * 60)
    return resp

然后将index.html文件中两个a标签的href改为如下代码

<button class="btn zh"><a class="link" href="{{ url_for('index.set_locale', language='zh') }}">中文</a></button>
<button class="btn en"><a class="link" href="{{ url_for('index.set_locale', language='en') }}">English</a></button>

这样就可以点击按钮就可以动态切换语言了,如下图

4. 注意事项

  1. 工厂模式将localselector装饰函数放到create_app下即可
  2. 注意翻译文件的路径,如果不是在项目源码目录下,需要配置BABEL_TRANSLATION_DIRECTORIES
  3. 如果更新了标记字符串,使用pybabel update命令不要使用init命令,否则会初始化已翻译的内容
  4. 如果更新了标记字符串,记得要重启应用
  5. 项目代码:https://github.com/weijiang1994/flask-babel-sample

0条评论