從C#世界邁入python老是使人有一種如釋重負之感,一樣的效果一樣的功能,只須要付出1/10不到的代價,可能正是Python所倡導的簡美哲學所帶來的威力。html
我還深深地記得在ASP.NET中作全球化的經歷,可謂是苦不堪言。因爲 .net 是採用xml格式的資源文件做爲資源承載格式,致使對全球化資源的引用就必需要採用嚴格的合乎c#命名規範。這樣一來在資源的使用過程當中就增長「命名」這一複雜度。之前從不認爲有什麼問題,不過一但轉譯成多國版本或者要對資源文件進行更新就會面臨着巨大的工做量。
並且ASP.NET官方推薦的全球化作法則更是坑人,簡直就是將「Quickly and duty」發揮到了極至,一但作了也只好陷入永不休止似的維護地獄之中。python
仍是直到在接觸 Web2Py 時才發現他們對全球化的作法有點像樣了,Web2Py中沒有了中間關鍵字命名,而是將天然用詞直接做爲資源的搜索關鍵字,這對於長期用ASP.NET開發全球化項目的我無疑是一咱腦洞大開的過程。並且,製做默認語言模板的工做量仍是巨大的這個過程仍然須要手工處理,所以我一直在尋找更好的應用方案。web
直至在Flask 中遇到了 Flask-Babel 這個插件。花了10多分鐘就能上手了,看到它的用法簡直是讓人興奮不已——簡單、省事。django
Flask-Babel 就是在Flask中對Babel的插件,它有幾個很讓人印象深入的特點:編程
*.mo
格式,能經過其它的編輯器來維護字典gettext()
, _()
from flask import Flask from flask.ext.babel import Babel app = Flask(__name__) app.config.from_pyfile('babel.cfg') babel = Babel(app)
babel.cfg
配置文件babel.cfg
是一個放置於Flask項目根目錄下的Babel配置文件,它是一個固定的配置,如下是官方推薦的寫法:flask
[python: **.py] [jinja2: **/templates/**.html] extensions=jinja2.ext.autoescape,jinja2.ext.with_
若是採用了 Flask-Assets 插件的話須要修改一下 extensions
的設置c#
[python: **.py] [jinja2: **/templates/**.html] extensions=jinja2.ext.autoescape,jinja2.ext.with_,webassets.ext.jinja2.AssetsExtension
gettext()
/_()
接下來就能夠在python代碼內或者jinia頁面內使用 gettext()
方法引用全球化資源。其實此時咱們並無任何的資源文件,但這正是Babel最吸引人的地方——先使用再生成資源。bash
在 python 代碼內能夠這樣使用 gettext()
babel
from flask import Flask, render_template from flaskext.babel import Babel, gettext as _ app = Flask(__name__) app.config['BABEL_DEFAULT_LOCALE'] = 'zh' babel = Babel(app) @app.route('/') def hello(): s = _("Saturday") return render_template('index.html', day=day) if __name__ == '__main__': app.debug = True app.run()
以上代碼中_("Saturday")
就是從資源中獲取名爲Saturday
的資源,若是沒有資源文件或者沒有找到對應的區域就會直接輸出"Saturday"
app
而後就是在 jinja 模板內使用:
<p>{{ _("Hello, world!") }}</p> <p>{{ _("It's %(day)s today", day=day) }}</p>
同理,在其它的代碼和模塊內就是以這兩種方式使用全球化資源。
這是很重要的一步,也是Babel最省時省力的一步。Babel能夠從代碼和模板中抽出用了gettext()
的全部的資源名並生成到默認語言模板內。生成這個模板後就能夠翻譯成各類須要的本地化語言。
只須要在命令行內鍵入如下命令
$ pybabel extract -F babel.cfg -o messages.pot .
就會在Flask的項目根目錄下生成 messages.pot
的默認語言模板
接下來就是從默認模板翻譯成指定區域語言的資源文件了,也是經過命令行處理:
$ pybabel init -i messages.pot -d translations -l zh
這個指令的執行結果是按照messages.pot
將 中文(zh)資源文件(message.po
)生成至 translations
目錄。
目錄結構以下:
. ├── babel.cfg ├── messages.pot ├── static ├── templates └── translations └── zh └── LC_MESSAGES └─ message.po
message.po
就是目標資源文件,如今就能夠打開並進行相關的翻譯工做了。*.po
文件只是一個文本能夠直接編輯,或者能夠選擇一些專用的po編程工具也成。我比較推薦使用POEdit
注 當指區域時須要使用區域簡寫而不是區域全名,若是指定
zh-CN
(簡體中文)的話就直接採用zh
不然指令會出錯。
message.po
文件如下是 message.op
的內容
# Chinese translations for PROJECT. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # Ray <csharp2002@hotmail>, 2015. #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: csharp2002@hotmail.com\n" "POT-Creation-Date: 2015-03-29 22:46+0800\n" "PO-Revision-Date: 2015-03-29 21:49+0800\n" "Last-Translator: Ray <csharp2002@hotmail.com>\n" "Language-Team: zh <LL@li.org>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.3\n" #: views.py:103 #,fuzzy, python-format msgid "Articles tagged with:%(value)s" msgstr "標記%(value)s主題的文章"
注:
,fuzzy
這個關鍵字,若是須要編譯資源文件成爲*.mo
的話則須要將它刪除,不然資源文件編譯器會直接忽略掉整個資源文件而不進行編譯。
編譯過程很簡單,只須要執行如下指令 translations
下全部的 *.po
文件就會被編譯成二進制的 *.mo
資源文件。
$ pybabel compile -d translations
這可謂是 Babel 一個很爲開發者着想的功能,由於咱們的程序資源一定是須要變動與維護的,天然而然地資源文件的內容一定會有增減。當咱們翻譯了N種語言副本以後若是沒有相關工具而是由手工來作的話那將是一種極爲可怕的工做過程。幸運的是咱們只須要執行如下的指令,babel將爲更新默認模板和全部今後模板生成的全部資源文件的內容:
$ pybabel update -i messages.pot -d translations
默認狀況下 Flask-Babel 會讀取 flask.g.lang
自動切換當前請求上下文使用的語言區域。但在不少應用場景下咱們須要手工改變當前的區域語言,這種狀況下咱們就須要增長一個 get_local()
函數:
from flask import g, request @babel.localeselector def get_locale(): # 若是在g對象內有登入的用戶對象則從用戶對象中讀取 locale 區域信息 user = getattr(g, 'user', None) if user is not None: return user.locale # 此方法只須要返會一個區域字符串 return request.accept_languages.best_match(['de', 'fr', 'en']) @babel.timezoneselector def get_timezone(): """此函數與 get_locale 相似,只是向babel提供獲取時區的設置""" user = getattr(g, 'user', None) if user is not None: return user.timezone
當提供這兩個函數以後,在調用 gettext
時 Babel 會自動調用他們。這裏是經過裝飾器 @babel.localeselector
和 @babel.timezoneselector
實現相似重寫的功能,但這個寫法代碼量會比重寫類更少。
固然,Babel 提供的API不止本文中的這幾個,若是須要更詳細地瞭解能夠仔細地閱讀 Flask-Babel 的文檔。在這裏我旨在記錄 Babel 的最常規的用法以做備忘同時也分享給更多正在使用Flask的友人們。