How lazy can text get?

by admin on 15 May 2008

Inspired by Malcolm Tredinnick‘s appearance on TWiD 19, I decided to create a small dummy site and work through the steps of translating it to German.

Setting up all the translation files was easy enough, but then the first thing that bit me was overlooking this instruction on Django’s Internationalization documentation page:

Always use lazy translations in Django models.

Such a short, innocent sentence! Easy to miss. But the author (Malcolm himself, I believe) means it. Always use lazy translations in models.

My model contained:

models.py
from django.db import models
from django.newforms import ModelForm
from django.utils.translation import ugettext as _

PROJECT_STATUSES = (('active', _('active')), ('someday', _('someday')))

class Project(models.Model):
    title = models.CharField(max_length=200)
    description = models.CharField(max_length=1000, blank=True)
    status = models.CharField(max_length=10, choices=PROJECT_STATUSES)

#.....

class ProjectForm(ModelForm):
    class Meta:
        model = Project

I then displayed the model form (ProjectForm) in a template. The template also contained a language switcher, following the directions concerning the set_language redirect view. I ran the app and used the set_language redirect to set my language to German (the default language of the app was English.) I expected the combobox — the one auto-generated by using the choices parameter when defining the status member of the model — to show me German options, because I’d defined those options (when setting the values of the PROJECT_STATUSES tuple) to have translated text values.

Of course they displayed English options instead of German, because I used ugettext instead of ugettext_lazy. The question is, do I understand why ugettext doesn’t work? One of my goals during TDI is to understand how and why things work the way they do in Django. I haven’t dug into this yet, but I think it can be explained something like this: at the time the models.py is evaluated and, therefore, the time the PROJECT_STATUSES variable is filled, the thread (or whatever it’s called in this context) is still running under the site’s default locale. Since that’s English, when ugettext gets called it just returns the original string, since I haven’t specified any English translations. Only later (I guess when about to render a response?) is the user’s (i.e., session’s) locale evaluated and used. By that time, PROJECT_STATUSES values have already been sent through the translation engine, so they’re not sent through again.

So maybe ugettext_lazy returns some sort of thingamajig that doesn’t call into the translation engine until its __unicode__ function is accessed. I would guess that’s what the documentation means by…

The translation itself will be done when the string is used in a string context.

I look forward to digging deeper by:

  • Searching for and learning about the place in Django code where the switch from the default to the session-specific locale takes place.
  • Reading through the ugetext_lazy code to see how it works.

Next post: