Django i18n

August, 19 2013

What does i18n mean?

i18n = i followed by 18 letters followed by n = internationalization

That is it, just a short hand. It isn't the name of standard governmental or otherwise.

When I watched the first videos about Django many years ago, they declared that Django had great internationalization support including a Welsh translation. It sounded great and I wanted to try to use it, but I was never involved in a project that really needed it, until now!

At Thalmic Labs we were able to translate the company website into German and Korean in a matter of weeks, the most time was devoted to getting the translations done, very little was figuring out i18n in Django. It was only a few hours of hitting my head against the wall.

Here is a bit of what I learned along the way. Hopefully it helps you.

Resources

First watch this excellent talk by Jacob Burch. He helps to fill in some of the gaps from the i18n documentation which you should also have a look at.

Definitely grab Jacob's version of poxx, you won't need it in production or on staging so you can just install it locally:

pip install -e git+https://github.com/jacobb/poxx.git#egg=poxx

By the end of this post you will have poxx generated fake translations appearing on your website. The first step is to mark your strings in your templates, code and javascript for translation. The docs cover that pretty well so I am going to pick up after that.

The Locale Directory

I was using a Django 1.4 directory layout so when I ran

python manage.py makemessages

And got the somewhat unhelpful error message:

"This script should be run from the Django SVN tree or your project or app tree. If you did indeed run it from the SVN checkout or your project or application, maybe you are just missing the conf/locale (in the django tree) or locale (for project and application) directory? It is not created automatically, you have to create it by hand if you want to enable i18n for your project or application."

What it means is Django can't find ./locale, so it will give up trying to do anything. I think the error message has been improved in newer versions of Django.

There are two options of where to place locale directories. One locale directory for the entire project or one locale directory per app. I think one per app is the right answer especially for large projects. However to get things working without spending a bunch of extra time fighting with makemessages and compilemessages, one locale directory for your entire project is easier.

It is easier to run makemessages and compilemessages, but you need to tell Django explicitly where your translations are, because by default it only looks for locale directories under each app in INSTALLED_APPS. You can use the LOCALE_PATHS setting for this:

LOCALE_PATHS = (
    os.path.join(BASE_DIR, "locale"), # Assuming BASE_DIR is where your manage.py file is
)

Make a locale directory beside your manage.py file, then you can run:

python manage.py makemessages -l de

This will create a django.po file from the german locale under locale/de/LC_MESSAGES. The file will have message id, but the values (the german translations) will be empty. Great first step done! Now for the poxx.

Poxx.py

Jacob is right about poxx.py, it is very useful for making sure translations are working, and making sure you are translating everything you want to.

You can run it on the german .po file you created above:

poxx.py locale/de/LC_MESSAGES/django.po

Now run compilemessages:

python manage.py compilemessages

Now you are almost ready to see fake poxxified translations.

i18n_patterns

The fastest way to control what language you are viewing is to follow the documentation to hook up the i18n_patterns in your root url.py. One tradeoff is it will prepend your default locale (say 'en') to all your urls under the i18n_patterns, but all the redirection will happen automatically.

In the end we used the cookie based approached with a language chooser, but for getting it done in one sitting, stick to i18n_patterns.

Now you should see your poxxified translations by running your server: http://127.0.0.1:8000/de/your/translated/view/

The poxx translations replace normal characters with weird unicode characters which means your text is still readable, but odd looking. Normal looking text that means those strings weren't marked for translation. Fix any you find and then re-run makemessages, poxx.py and compilemessage to make sure your fix worked.

If you want to test this out but don't have project handy to work on you can try an example project I built: https://bitbucket.org/amjoconn/django-i18n-example. It already has most strings marked for translation so you can run through the messages and poxx.py steps to see how they work.

i18n is a complex topic, but a worthy one to sink your teeth into. The more multi-lingual the web is the better place it will be.

By the way, Thalmic Labs is great company which is looking for a full time web developer.


Tweet comments, corrections, or high fives to @amjoconn