Say hello to l10n in python

In python world, many solutions for trivial problems could be found in standart library: from statistical functions to localization for your package. But somehow some things just don’t connect to each other, and you just stuck at combining this constructor in right way.

This gifs pretty sums up situation:

modern-development

Our task:

  • create python package, that has localized string
  • provide this string to django application

First thing, that you find at gettext docs:

import gettext
import os

BASE_DIR = os.path.dirname(__file__)
LOCALE_DIR = os.path.join(BASE_DIR, 'locale')

# try to set param fallback to False
t = gettext.translation('default', LOCALE_DIR, fallback=True)
_ = t.ugettext

message = _('Test!')
print(message)

To change language on your system, you can simply set enviroment variable LANG or LANGUAGE.

~ export LANG=ru
~ python test.py
Test!

This won’t work, because you don’t have translated phrases in your locale dir. Let’s compile them. Command pygettext grabs all string in ugettext function and dumps it to pot file. After it, you need to init new locale (note: if you need to merge with previos translations use msgmerge).

~ pygettext test.py
~ mkdir -p ./locale/{en,ru}/LC_MESSAGES/
~ msginit --input=messages.pot --locale=en_US.UTF-8 \
--output=./locale/en/LC_MESSAGES/default.po
~ msginit --input=messages.pot --locale=ru_RU.UTF-8 \
--output=./locale/ru/LC_MESSAGES/default.po
~ rm messages.pot

Now you can open default.po files with some tools, like POEdit (note: I prefer Sublime Text for small po). Change in ru po msgstr from ‘Test!’ to ‘Тест!’. When you run script again, output will be same (‘Test!’). This is because after modifying po, we need to compile it to binary format (known as mo).

~ msgfmt en/LC_MESSAGES/default.po -o en/LC_MESSAGES/default.mo
~ msgfmt ru/LC_MESSAGES/default.po -o ru/LC_MESSAGES/default.mo

Woo-hoo, output should be translated now, but it doesn’t work with django. What shall we do? Read the manual. So we need to rename namespace from default to django, but also django realize own class, so insted of using gettext.translation.ugettext you need to use django.utils.translation one. Full example can be found in repository.

Written on November 26, 2016