flask
flask_ext
python
babel
在Flask實作_ext_16_Flask_babel我們介紹了應用在不同語系時的datetime
格式化,這篇要介紹的是應用於多語系的文字翻譯。
一間跨國企業的官方網站一定會有著英文、繁體中文、簡體中文…等多國語言,因為你的客戶來自國際,所以你需要讓他們看的懂你的產品,比較笨一點的方法當然可以一個語系一個頁面,但是當你的版有一個小變動的時候你就需要變更多個頁面了,這時候就可以利用babel
來幫我們處理這部份的問題了。
透過flask_babel
做多語系之流程大致如下:
babel.cfg
pybabel extract -F babel.cfg -o messages.pot .
pybabel init -i messages.pot -d translations -l zh_TW
pybabel compile -d translations
pybabel extract -F babel.cfg -o messages.pot .
messages.pot
pybabel update -i messages.pot -d translations
pybabel compile -d translations
需要認識的function有三個:
gettext
ngettext
gettext
lazy_gettext
SQLAlchemy
一樣,在需要的時候再翻譯,多搭配Form使用。透過說明,我們絕對不可能理解究竟多語系在做些什麼事,所以我們要來實際操作一次,就可以從很抽象的說明變成原來如此我懂了。
『>>>』代表命令列直接執行
>>>number_of_pages=100
>>>ngettext(u'%(num)s page', u'%(num)s pages', number_of_pages)
'100 pages'
上面的案例是用來了解ngettext
的應用,如果我們網頁的頁數有10頁,那就呈現複數的pages,如果只有1頁,那就呈現單數的page。
接著,我們要來建置一個Python文件,如下:
from flask import Flask, render_template
from flask_babel import Babel, lazy_gettext, gettext, ngettext, refresh
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, IntegerField
app = Flask(__name__)
app.config['SECRET_KEY'] = 'development'
app.config['BABEL_DEFAULT_LOCALE'] = 'zh'
app.config['BABEL_DEFAULT_TIMEZONE'] = 'UTC'
babel = Babel(app)
class testForm(FlaskForm):
name = StringField(gettext('name'))
age = IntegerField(gettext('age'))
submit = SubmitField(gettext('submit'))
@app.route('/index')
def index():
refresh()
form = testForm()
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
第1~4行:import需求套件
第6~11行:初始化跟設置套件
第13~16行:建置一個簡單的form,並且利用gettext
來包住欄位名稱
第18~22行:設置一個路由,並且refresh
語系。
接著,我們在templates
內加入一個index.html
,內容很簡單,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>{{form.name.label}}{{form.name}}</div>
<div>{{form.age.label}}{{form.age}}</div>
<div>{{form.submit.label}}{{form.submit}}</div>
</body>
</html>
現在,我們要來執行實作多語系的流程:
配置文件本身必需跟專案初始化的Python文件置於同一資料夾,文件名稱設置為babel.cfg
[python: app.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_
前兩行是讓babel
知道要翻譯的文件在那,而第三行是應用在jinja2
模板渲染上,後面我們再回頭說明。
範例上我故意給了檔名app.py
,避免連一堆venv的Python文件都掃描了,否則一般可能使用**.py
pybabel extract -F babel.cfg -o messages.pot .
最後的『.』很重要,不是打錯,一定要有
如果有使用lazy_gettext
的話,則改執行如下指令
pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot .
參數說明:
執行之後可以看的到多了一個messages.pot
的檔案
內容如下:
# Translations template for PROJECT.
# Copyright (C) 2018 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2018-06-28 21:54+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
#: app.py:15
msgid "name"
msgstr ""
#: app.py:16
msgid "age"
msgstr ""
#: app.py:17
msgid "submig"
msgstr ""
可以看到我們設置在form內的三個欄位名稱有順利的被標記到!
pybabel init -i messages.pot -d translations -l zh
執行之後,專案內多了一個translations
資料夾
打開messages.po
,然後將翻譯輸入相對應的地方
# Chinese (Traditional, Taiwan) translations for PROJECT.
# Copyright (C) 2018 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2018-06-28 21:54+0800\n"
"PO-Revision-Date: 2018-06-28 22:08+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: zh_Hant_TW\n"
"Language-Team: zh_Hant_TW <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.6.0\n"
#: app.py:15
msgid "name"
msgstr "姓名"
#: app.py:16
msgid "age"
msgstr "年齡"
#: app.py:17
msgid "submig"
msgstr "提交"
pybabel compile -d translations
執行之後,資料夾內多了一個messages.mo
的檔案
到這邊,我們可以來測試一下專案,但是我們發現到沒有翻譯成功?
http://127.0.0.1:5000/index
失敗了?這是因為這form的生成並不在請求上下文中生成,所以我們無法直接使用gettext
來處理,必需透過lazy_gettext
在需要的時候再做翻譯,讓我們來調整一下表單,如下:
class testForm(FlaskForm):
name = StringField(lazy_gettext('name'))
age = IntegerField(lazy_gettext('age'))
submit = SubmitField(lazy_gettext('submit'))
第2~4行:調整為lazy_gettext
調整之後重新執行專案,這時候已經順利的取得翻譯了
在文件中的『extensions=jinja2.ext.autoescape,jinja2.ext.with_
』用途是應用在模板內的擴展。讓我們來簡單調整一下index.html
,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>{{form.name.label}}{{form.name}}</div>
<div>{{form.age.label}}{{form.age}}</div>
<div>{{form.submit.label}}{{form.submit}}</div>
<div>{{ _('Hello World!') }}</div>
</body>
</html>
第11行:『_』就代表著gettext
標記著這是一個要翻譯的部位,等一下記得掃描一下我嘿。
接著在Command執行下面的語法:
pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot .
執行之後的messages.pot
如下:
# Translations template for PROJECT.
# Copyright (C) 2018 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2018-06-30 07:34+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
#: app.py:14
msgid "name"
msgstr ""
#: app.py:15
msgid "age"
msgstr ""
#: app.py:16
msgid "submit"
msgstr ""
#: templates/index.html:11
msgid "Hello World!"
msgstr ""
第33~34行:新增了『Hello World!』的待翻譯字串
pybabel update -i messages.pot -d translations
# Chinese translations for PROJECT.
# Copyright (C) 2018 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2018-06-30 07:34+0800\n"
"PO-Revision-Date: 2018-06-30 07:32+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.6.0\n"
#: app.py:14
msgid "name"
msgstr "姓名"
#: app.py:15
msgid "age"
msgstr "年齡"
#: app.py:16
msgid "submit"
msgstr "提交"
#: templates/index.html:11
msgid "Hello World!"
msgstr ""
在我們執行update
之後可以看的到,原始有翻譯的部份是沒有被空值覆蓋,僅Hello World!
是待翻譯的,這簡化我們不少工作。
將Hello World
的翻譯補上之後,重新compile
pybabel compile -d translations
最後再執行一次專案,已經成功的翻譯了
我們成功的在幾個簡單的步驟下設計出多語系的版面,並且了解到在非上下文的一個請求中必需使用lazy_gettext
才有辦法成功取得翻譯,而且在jinja2模板上我們也可以透過{{_('要翻譯的文字')}}
這樣的表達式來讓系統知道這邊也有一個需要翻譯的文字。
後續你只需要初始化不同語系的文件出來將翻譯資料補上,你的系統就可以直接滿足更多的多國語系應用,但是不要誤會一件事,資料庫內所帶出來的資料並不會翻譯。
一直無法成功取得翻譯後的資料,這時候可以先確認路徑取得是否正常,flask_babel
在取得翻譯文件的資料夾路徑的邏輯如下:
translations