如何讓Flask webapp支援多國語言

如果我們開發了一個Flask webapp,在開發到一定程度之後可能會需要支持更多國家的語言。這篇文章討論支援多國語言的基本步驟。

Step 1: 安裝 flask-babel

首先我們要安裝 flask-babel. 這個package可以讓Flask支援多國語言。

pip install flask-babel

Step 2: 配置app使用flask-babel

在我們的flask app中,初始化flask app的地方(通常是 __init__.py), 加上以下的import:

from flask_babel import Babel

然後我們還需要將app傳給Babel。在Flask app被初始化後初始化Babel,傳入app當參數∶

app = Flask(__name__)
babel = Babel(app) #after app is created

我們還需要加上localeselector,這樣每次收到HTTP request呼叫需要多國語言翻譯的method時就會呼叫它以取得使用者的語系。將以下這個方法加進 __init__.py:

@babel.localeselector
def get_locale():
    return request.accept_languages.best_match(app.config['LANGUAGES'])

get_locale()將HTTP header中的accept language與我們即將配置的支援語言做match。在這個範例中我們只支援英文和繁體中文,支援語言要在config.py裡加上:

LANGUAGES = ['en', 'zh']

Step 3: 標記需要翻譯的字串

在配置好Flask-babel後,下一步我們還需要將所有需要翻譯的字串都標記好。這是個非常繁瑣的工作。在這裡只大略講一下原則:

  1. 在所有.py檔案中,把需要翻譯且是在HTTP request收到後才會動態生成的字串改為_(<字串>)

例如:

from flask_babel import _

# ...

flash(_("You are now logged in")) 
#This replaces flash("You are now logged in")

2. 在.py檔案中有些字串是在收到HTTP request前就生成的字串,這些字串需要以l_(<字串>)處理。例如在form.py裡,

from flask_babel import lazy_gettext as _l

class LoginForm(FlaskForm):
    username = StringField(_l('Username'))
    # ...

3. 所有.html檔案中需要翻譯的字串也要用{{ _(<字串>) }}處理。

<h1>{{ _('Welcome!') }}</h1>

Step 4: 擷取字串翻譯檔案

下一步我們要用pybabel將剛剛標記的字串擷取出來。這也需要做一個配置,我們需要做一個babel.cfg檔在專案的根目錄下。這個檔案基本上是告訴pybabel要去哪些路徑找到有需要翻譯的檔案。

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

有了配置檔後在同一個資料夾下可以用以下命令擷取出一個messages.pot檔:

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

每種我們要支援的語言都需要一個對應的翻譯檔。所以我們需要針對每種語言執行以下的命令。例如要支援zh,使用以下命令。

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

執行完之後我們就可以在app/translations/zh下看到相對應的messages.po檔案。

Step 5: 翻譯

messages.po內容包含了我們剛才標記的所有字串。例如說我們在範例中假設標記了Username這個字串,我的messages.po就會是這個樣子:

# Chinese translations for PROJECT.
# Copyright (C) 2021 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2021-01-31 19:14-0800\n"
"PO-Revision-Date: 2021-01-31 16:13-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.0\n"

#: app/forms.py:14 app/forms.py:21
msgid "Username"
msgstr ""

前面的部份都可以忽略。我們要做的事情就是在msgstr中填入相對應的翻譯。如果有多個字串的話就會有多個msgstr要填。填好之後會是這樣:

# Chinese translations for PROJECT.
# Copyright (C) 2021 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2021-01-31 19:14-0800\n"
"PO-Revision-Date: 2021-01-31 16:13-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.0\n"

#: app/forms.py:14 app/forms.py:21
msgid "Username"
msgstr "使用者名稱"

Step 6: 編譯翻譯檔

這個步驟很簡單。在翻譯檔都做好後就可以編譯了。在根目錄下輸入指令:

pybabel compile -d app/translations

這個指令會在app/translations下生成對應的messages.mo,接下來我們就可以上線測試了。

Step 7: 測試

如果是使用Google Chrome的話,我們可以在偏好設定->進階中加入偏好語言為繁體中文然後打開網站。這樣應該就能夠看到繁體中文的版本。

如何加入新字串

當然,我們在未來的開發中還會再加上新字串。既然我們現在已經有翻譯檔了,當然不希望再重覆所有翻譯的過程。如果要更新翻譯檔,首先我們要標記新的字串,再來執行下面的指令。

pybabel extract -F babel.cfg -k _l -o messages.pot .
pybabel update -i messages.pot -d app/translations

第一個指令是再次擷取字串至messages.pot。但在第二個指令中我們不再重新初始化翻譯檔,而是使用update。這樣的話之前已經翻譯過的字串就不會受影響。之後再重新編譯即可。


謝謝你看完這篇文章。如果你喜歡我的讀書心得,請到Twitter追蹤新發佈的讀書心得。想要更了解我們嗎?追蹤或訂閱Facebook粉專和我們交流、獲得最新文章通知吧。

如果你覺得這篇文章不錯,請在下面用力的拍五次手,給我們一點鼓勵!
對於如何拍手有疑問嗎?請見快速教學

如果這篇文章或許會對你的朋友有幫助,請按下面的分享按鈕,將這篇文章分享給你的朋友。

若有興趣在你的網站或出版品引用這篇文章或部份內容,請來信索取授權。

發表迴響