If we develop a Flask webapp, we may need to support more languages after a certain level of development. This article discusses the basic steps to support multiple languages.
Step 1: Install flask-babel
First we have to install the flask-babel. This package allows Flask to support multiple languages.
pip install flask-babel
Step 2: Configure the app to use flask-babel
In our flask app, where you initialize the flask app (usually __init__.py), add the following import.
from flask_babel import Babel
Then we need to pass the app to Babel, which is initialized after the Flask app is initialized, and pass the app as a parameter.
app = Flask(__name__)
babel = Babel(app) #after app is created
We also need to add a localeselector so that every time we receive an HTTP request for a method that requires a multilingual translation, it will be called to get the user's language. Add the following method to __init__.py:
@babel.localeselector
def get_locale():
return request.accept_languages.best_match(app.config['LANGUAGES'])
get_locale() matches the accept language in the HTTP header with the supported languages we are going to configure, in this example we only support English and Traditional Chinese, the supported languages should be added in config.py:
LANGUAGES = ['en', 'zh']
Step 3: Mark the strings to be translated
After configuring Flask-babel, the next step is to mark all the strings that need to be translated. This is a very tedious task. Let's just outline the principles here:
- In all .py files, change the strings that need to be translated and are dynamically generated only after an HTTP request is received to _()
Example:
from flask_babel import _
# ...
flash(_("You are now logged in"))
#This replaces flash("You are now logged in")
2. Some strings in .py files are generated before the HTTP request is received, and these strings need to be handled with l_(). For example, in form.py.
from flask_babel import lazy_gettext as _l
class LoginForm(FlaskForm): username = StringField(_l('Username'))
username = StringField(_l('Username'))
# ...
3. Strings in all .html files that need to be translated should also be handled with {{ _() }}.
<h1>{{ _('Welcome!') }}</h1>
Step 4: Retrieve String Translation File
The next step is to use pybabel to retrieve the string we just labeled. To do this we need to make a configuration file, we need to make a file called babel.cfg in the root directory of the project. This file basically tells pybabel which paths to go to in order to find the files that need to be translated.
1 [python: app/**.py]
2 [jinja2: app/templates/**.html]
3 extensions=jinja2.ext.autoescape,jinja2.ext.with_
Once you have the configuration file, you can retrieve a messages.pot file from the same folder using the following command:.
pybabel extract -F babel.cfg -k _l -o messages.pot .
Each language we want to support needs a corresponding translation file. So we need to run the following commands for each language. For example, to support zh, use the following command.
pybabel init -i messages.pot -d app/translations -l zh
After running it, we can see the corresponding messages.po file under app/translations/zh.
Step 5: Translation
The contents of messages.po contains all the strings we just tagged. For example, if we had assumed the string Username was marked in our example, my messages.po would look like this:
# Chinese translations for PROJECT.
# Copyright (C) 2021 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR , 2021.
#
msgid ""
msgid "" "Project-Id-Version.
"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 \n"
"Language: zh\n"
"Language-Team: zh \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 " Username"
msgid "Username" msgstr ""
The first part can be ignored. All we have to do is fill in the msgstr with the corresponding translation. If there are more than one string, there will be more than one msgstr to fill. When it's done, it will look like this:
# Chinese translations for PROJECT.
# Copyright (C) 2021 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR , 2021.
#
msgid ""
msgid "" "Project-Id-Version.
"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 \n"
"Language: zh\n"
"Language-Team: zh \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 "用户名稱"
msgid "Username" msgstr "用户名稱"
Step 6: Compile Translation File
This step is very simple. After the translation is done, you can compile it. Enter the command in the root directory:
pybabel compile -d app/translations
This command will generate the corresponding messages.mo under app/translations, and then we can go online and test it.
Step 7: Testing
If you are using Google Chrome, you can add the preferred language to Traditional Chinese in Preferences->Advanced and then open the website. Then you should be able to see the Traditional Chinese version.
How to add a new string
Of course, we will add new strings in the future. Since we already have a translation file, we don't want to repeat the translation process. If we want to update the translation file, we first need to mark the new string and then run the following command.
pybabel extract -F babel.cfg -k _l -o messages.pot .
pybabel update -i messages.pot -d app/translations
The first command is to retrieve the strings to messages.pot again, but in the second command we don't reinitialize the translation file, we use update instead, so that the previously translated strings are not affected. Instead of initializing the translated file, we use update in the second command.
Thank you for reading this post. If you like my post, please follow up withFacebook Fan Specialist,Twitter,IGThe