Software localization

Flask App Tutorial on Localization

Flask-Babel extends the Python micro-framework of Flask through i18n and l10n support. Learn how to make the most of it with this tutorial.
Software localization blog category featured image | Phrase

Flask, the lightweight Python microframework for creating web apps, and Phrase, the translation management platform, work great together. This tutorial will guide you through the process of localizing Flask applications using Flask-Babel and Phrase.

Flask-Babel is a Flask extension that adds internationalization (i18n) and localization (l10n) support to any Flask application. Phrase, on the other hand, is a translation management tool that features a powerful in-context editor that makes translating more convenient.

Get Started With Flask-Babel

Let’s begin by installing the required dependencies:

pip install Flask-Babel

This will install Flask-Babel, aswell as the pybabel command-line tool.

Next, import Flask-Babel and hook it to your app like so:

from flask import Flask, [...]

from flask.ext.babel import Babel, gettext

app = Flask(__name__)

babel = Babel(app)

In your configuration, add a dictionary named LANGUAGES. In this example, we add two locales, english (‘en’) and german (‘de’).

# add to your app.config or file


    'en': 'English',

    'de': 'Deutsch'


We will use this dictionary for a little helper function that Babel offers:

# add to you main app code


def get_locale():

    return request.accept_languages.best_match(app.config['LANGUAGES'].keys())

This convenient tool will automatically choose the best matching locale, based on the Accept-Language header from the incoming request.

Hint: for testing purposes, you can directly return a language code, for example: return ‘de’

One more thing! Create a config file for Babel. We will chew through the details later on, but for now, simply create a file named babel.cfg in the top-level directory of you app:

[python: **.py]

[jinja2: **/templates/**.html]


Tagging Strings

Now it’s time to tag all the Strings you want to translate. In a typical Flask app, there will be two types of Strings that require translating. One being hard-coded Strings in your .py files, the other being Strings in your .html Jinja2 templates.

Tag them by adding a gettext(‘String’) call to them, like so:

slogan = 'This app is awesome.'

flash('Login failed')


slogan = gettext('This app is awesome.')

flash(gettext('Login failed'))

For your Jinja2 templates, this is also very straightforward, for example:

<b>Free Trial</b>

<input type="submit" value="Sign up"/>


<b>{{ gettext('Free Trial') }}</b>

<input type="submit" value="{{ gettext('Sign up') }}"/>

Hint: you can use _() as a shortcut for gettext().

Note that the english string is also the key (msgid) that gettext will use for when looking up the corosponding Translation value (msgstr).

Building Locales

When all strings are tagged, it is time to build a catalog for the Locales we want to create. Run:

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

This checks all files specified in babel.cfg and searches thru them to find tagged strings and outputs them to messages.pot.

Next, run:

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

This will use the index from messages.pot to build a german (‘de’) locale in our translations directory. Don’t worry, if the directory doesn’t exist yet, pybabel will create it for you.

Finally it is time to translate the Strings.

With your favorite text editor, open ‘translations/de/LC_MESSAGES/messages.po’. You can now start translating the msgstr values. When you are done editing, there is only one step left, compile all .po files in your translation directory:

pybabel compile -d translations

Done! Start playing around with your app. Remember, the locale is chosen based on the Accept-Language Header that is being sent by your Browser.

Check out our full Flask-Babel example app, a modified version of Flaskr, the official Flask demo app.

Get Translations With Phrase

Phrase provides tools for software translation management. Its WYSIWYG In-Context Editor (Demo) enables you and your copywriters or translators to change translations on your website in any web browser.

Let’s integrate the In-Context Editor in our example from above.

In order to expose your tagged Strings to the In-Context-Editor, we will be using the Flask-Phrase, a package that you can install via:

pip install Flask-Phrase

Next, we hook our app to Flask-Phrase and import the gettext provided by Flask-Phrase. Extending the example from above, this would look like this:

from flask import Flask, [...]

from flask.ext.babel import Babel

from flask_phrase import Phrase, gettext

app = Flask(__name__)

babel = Babel(app)

phrase = Phrase(app)

Hint: the gettext provided by flask_phrase will simply proxy the call to Flask-Babel when not in editing mode.

Next, add the following to your Flask app config:

# add to your app.config or file




Almost done. In your .html Jinja2 templates, add this JavaScript snippet along with the Project-ID that can be found in the Phrase Translation Center:


    window.PHRASEAPP_CONFIG = {

        projectId: "YOUR-PROJECT-ID"


    (function() {

        var phraseapp = document.createElement('script'); phraseapp.type = 'text/javascript'; phraseapp.async = true;

        phraseapp.src = ['https://', '', new Date().getTime()].join('');

        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(phraseapp, s);



That’s it. Make sure PHRASEAPP_ENABLED is set to ‘True’ so that your strings will be rendered in a special format for the Phrase editor. Check out our full example app with Flask-Babel and Phrase built-in.

Further Reading

Be sure to subscribe and receive all updates from the Phrase blog straight to your inbox. You’ll receive localization best practices, about cultural aspects of breaking into new markets, guides and tutorials for optimizing software translation, and other industry insights and information. Don’t miss out!