i18n Flask Babel test

Background

This is using Flask-Babel.

Setup

Make sure you've got flask (the web framework) and flask_babel (the i18n library that plays nicely with it):

pip install flask
pip install flask_babel

The repo has no translations in it because I want to check/replicate the process from scratch.

How to run

Once it's setup, just run the Flask app (by default, Flask runs app.py):

flask run

Then hit localhost:5000 and you should see:

A simple string
Value: 42
4 Apples

Note mysettings.cfg controls what languages you get back: to start with it's en.

Kill flask (Crtl-C) and run the translation process...

How to translate

Extract strings

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

This will use the mapping from the babel.cfg file and store the generated template in messages.pot.

Generate the translation file

Pick your language: here de for German:

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

-d translations tells pybabel to store the translations in this folder. This is where Flask-Babel will look for translations.

Translate the words

Now edit the translations/de/LC_MESSAGES/messages.po file as needed.

For example (this is just the string mapping part of the file):

#: app.py:12
msgid "A simple string"
msgstr "Eine einfache Zeichenfolge"

#: app.py:14
#, python-format
msgid "Value: %(value)s"
msgstr "Wert: %(value)s"

#: app.py:16
#, python-format
msgid "%(num)s Apple"
msgid_plural "%(num)s Apples"
msgstr[0] "%(num) Apfel"
msgstr[1] "%(num) Äpfel"

Compile the translation

First time (new file):

pybabel compile -d translations

Subsequently (updating translations):

pybabel update -i messages.pot -d translations

That creates messages.mo in the translations/de directory.

Afterwards some strings might be marked as fuzzy (where it tried to figure out if a translation matched a changed key). If you have fuzzy entries, make sure to check them by hand and remove the fuzzy flag before compiling. There won't be any fuzzy strings the first time you run this.

Run with translation

See if that worked: change Flask default language to German:

Edit mysettings.cfg to

BABEL_DEFAULT_LOCALE = 'de'

Now start the app up again:

flask run

then hit localhost:5000 again: you should see this:

Eine einfache Zeichenfolge
Wert: 42
4 Äpfel

Did that work?

Spoiler: not for me: I'm getting this:

    return get_domain().ngettext(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/flask_babel/__init__.py", line 605, in ngettext
    return s if not variables else s % variables
ValueError: unsupported format character 'A' (0x41) at index 7

Note there's a specific troubleleshooting tip in the flask-babel docs that looked encouraging... but nope.


More detail when extracting

Detail for later:

If you are using the lazy_gettext() function you should tell pybabel that it should also look for such function calls:

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