Django :: i18n

What’s up with so many languages? E.g. in Switzerland there are four official languages, and in Norway there are two: bokmål and nynorsk (details). I’ll be describing what I had to do to get a good user experience with these two. The reason I care is because government contracts usually have a hard requirement that you support all official languages.

My first problem was to figure out which language codes I should use, you know like en-us for English as spoken in the US. After consulting five different browsers on two platforms (I need a Mac!) I ended up with no for bokmål and nn for nynorsk as the only sane choices.

The Django documentation does a great job of describing the process of marking up your code and templates. Basically, wherever you’ve got a string that’ll be presented to the user, change it from

      v = 'Some string'

to

      v = _('Some string')

and in your templates use either of:

      {% trans "Some string" %}

or

      {% blocktrans %}
      Some string
      {% endblocktrans %}

Then you’ll have to find a linux machine — if you install the right packages this can be done on Windows too, but it was just easier to turn to my left and use the other keyboard 😉

Put site-packages/django/bin on your path if you haven’t allready done so, that’s where make-messages.py and compile-messages.py live.

Go into your project directory and issue the following commands:

mkdir locale
make-messages.py -l no
make-messages.py -l nn
make-messages.py -l en

why did I add en (for English) as an option? Well, turns out that things go sour if you try to pass unicode outside the ascii range to _(). So

      v = _(u'Født')  # doesn't work
      v = _('Fodt')  # will work, then put Født as a translation.
      v = _('Date of birth')  # works and provides English support.

the make-messages commands above created django.po files somewhere under the locale directory. Edit them with emacs:

find locale -name "*.po" -print | xargs emacs

since Emacs has a po-mode that makes this very easy (or send them off to get translated, which is even easier). When you’re finished translating, compile your translations:

compile-messages.py

if you want to update your .po files use make-messages.py -a before compile-messages.py. Now, the site will display Norwegian bokmål to users that have set this in their browser and English to users that have set en as their language. It didn’t show nynorsk to users with nn set as their language though… Turns out Django doesn’t allow you to use a language unless it’s part of Django’s set of core languages (found in django/conf/locale/…). Sigh. A quick copy of django/conf/locale/no/* to django/conf/locale/nn and all was fine. (I’ll submit nynorsk translations when I get them back from our translator 😉 ).

I’m not sure whether it was necessary to change the LANGUAGES in settings.py, but I did:

      LANGUAGES = ( # IMO, these shouldn't be translated
          ('no', 'Norsk'),
          ('nn', 'Nynorsk'),
          ('en', 'English'),
          )

Now for the final touch… While all this automatic translation is cool, it assumes that users have set up their browser correctly — yeah, right! What we need is a little ‘gadget’ listing the languages available, so that you can click on what you want. Turns out this is pretty easy with Django, although perhaps a little spread out.

First, in your base template, add:

{{ lang_switch }}

then in urls.py add:

      (r'^i18n/', include('django.conf.urls.i18n')),

then define a function to create the necessary html:

      def switch_language(lang):
          languages = (
              ('no', u'Bokmål'.encode('utf-8')), # django doesn't always like unicode..
              ('nn', 'Nynorsk'),
              ('en', 'English'),
              )
          res = []
          for lc, lname in languages:
              if lang == lc:
                  res.append(lname) # no sense chosing what you're using
              else:
                  t = '<a href="/i18n/setlang/?language=%s">%s</a>' % (lc, lname)
                  res.append(t)
       
          return ' | '.join(res)

and use it in your context:

      return render_to_response(..., { "lang_switch" : switch_language(request.LANGUAGE_CODE) })

and that’s it.

The magic happens in the /i18n/setlang/ view, where Django sets the language in either a cookie or the current session (depending on what’s available). It then returns to the current page (based on the Referer header).

This entry was posted in django. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *