Accessing Translated and Translation Fields

The modeltranslation app changes the behaviour of the translated fields. To explain this consider the news example from the Registering Models for Translation chapter again. The original News model looked like this:

class News(models.Model):
    title = models.CharField(max_length=255)
    text = models.TextField()

Now that it is registered with the modeltranslation app the model looks like this - note the additional fields automatically added by the app:

class News(models.Model):
    title = models.CharField(max_length=255)  # original/translated field
    title_de = models.CharField(null=True, blank=True, max_length=255)  # default translation field
    title_en = models.CharField(null=True, blank=True, max_length=255)  # translation field
    text = models.TextField()  # original/translated field
    text_de = models.TextField(null=True, blank=True)  # default translation field
    text_en = models.TextField(null=True, blank=True)  # translation field

The example above assumes that the default language is de, therefore the title_de and text_de fields are marked as the default translation fields. If the default language is en, the title_en and text_en fields would be the default translation fields.

Rules for Translated Field Access

Changed in version 0.5.

So now when it comes to setting and getting the value of the original and the translation fields the following rules apply:

Rule 1

Reading the value from the original field returns the value translated to the current language.

Rule 2

Assigning a value to the original field updates the value in the associated current language translation field.

Rule 3

If both fields - the original and the current language translation field - are updated at the same time, the current language translation field wins.


This can only happen in the model’s constructor or objects.create. There is no other situation which can be considered changing several fields at the same time.

Examples for Translated Field Access

Because the whole point of using the modeltranslation app is translating dynamic content, the fields marked for translation are somehow special when it comes to accessing them. The value returned by a translated field is depending on the current language setting. “Language setting” is referring to the Django set_language view and the corresponding get_lang function.

Assuming the current language is de in the news example from above, the translated title field will return the value from the title_de field:

# Assuming the current language is "de"
n = News.objects.all()[0]
t = n.title  # returns german translation

# Assuming the current language is "en"
t = n.title  # returns english translation

This feature is implemented using Python descriptors making it happen without the need to touch the original model classes in any way. The descriptor uses the django.utils.i18n.get_language function to determine the current language.


Add more examples.

Multilingual Manager

New in version 0.5.

Every model registered for translation is patched so that its manager becomes a subclass of MultilingualManager (of course, if a custom manager was defined on the model, its functions will be retained). MultilingualManager simplifies language-aware queries, especially on third-party apps, by rewriting query field names.

For example:

# Assuming the current language is "de",
# these queries returns the same objects
news1 = News.objects.filter(title__contains='enigma')
news2 = News.objects.filter(title_de__contains='enigma')

assert news1 == news2

It works as follow: if the translation field name is used (title), it is changed into the current language field name (title_de or title_en, depending on the current active language). Any language-suffixed names are left untouched (so title_en wouldn’t change, no matter what the current language is).

Rewriting of field names works with operators (like __in, __ge) as well as with relationship spanning. Moreover, it is also handled on Q and F expressions.

These manager methods perform rewriting:

  • filter(), exclude(), get()
  • order_by()
  • update()
  • create(), with optional auto-population feature

In order not to introduce differences between X.objects.create(...) and X(...), model constructor is also patched and performs rewriting of field names prior to regular initialization.

If one wants to turn rewriting of field names off, this can be easily achieved with rewrite(mode) method. mode is a boolean specifying whether rewriting should be applied. It can be changed several times inside a query. So X.objects.rewrite(False) turns rewriting off.


In create() you can set special parameter _populate=True to populate all translation (language) fields with values from translated (original) ones. It can be very convenient when working with many languages. So:

x = News.objects.create(title='bar', _populate=True)

is equivalent of:

x = News.objects.create(title_en='bar', title_de='bar') ## title_?? for every language

Moreover, some fields can be explicitly assigned different values:

x = News.objects.create(title='-- no translation yet --', title_de='enigma', _populate=True)

It will result in title_de == 'nic' and other title_?? == '-- no translation yet --'.

There is a more convenient way than passing _populate all the time: MODELTRANSLATION_AUTO_POPULATE setting. If _populate parameter is missing, create() will look at the setting to determine if population should be used.

Falling back

Modeltranslation provides mechanism to control behaviour of data access in case of empty translation values.

Consider News example: a creator of some news hasn’t specified it’s german title and content, but only english ones. Then if a german visitor is viewing site, we would rather show him english title/content of the news than display empty strings. This is called fallback.

There are several ways of controlling fallback, described below.

Fallback languages

New in version 0.5.

MODELTRANSLATION_FALLBACK_LANGUAGES setting allows to set order of fallback languages. By default it is only DEFAULT_LANGUAGE.

For example, setting


means: if current active language field value is unset, try english value. If it is also unset, try german, and so on - until some language yield non-empty value of the field.

There is also option to define fallback by language, using dict syntax:

    'default': ('en', 'de', 'fr'),
    'fr': ('de',),
    'uk': ('ru',)

The default key is required and its value denote languages which are always tried at the end. With such a setting:

  • for uk (Ukrainian) order of fallback languages is: ('ru', 'en', 'de', 'fr')
  • for fr order of fallback languages is: ('de', 'en') - fr obviously is not fallback, since it’s active language; and de would be tried before en
  • for en and de fallback order is ('de', 'fr') and ('en', 'fr'), respectively
  • for any other language order of fallback languages is just ('en', 'de', 'fr')

What is more, fallback languages order can be overridden per model, using TranslationOptions:

class NewsTranslationOptions(TranslationOptions):
    fields = ('title', 'text',)
    fallback_languages = {'default': ('fa', 'km')}  # use Persian and Khmer as fallback for News

Dict syntax is only allowed there.

Fallback values

New in version 0.4.

But what if current language and all fallback languages yield no field value? Then modeltranslation will use field’s fallback value, if one was defined.

Fallback values are defined in TranslationOptions, for example:

class NewsTranslationOptions(TranslationOptions):
    fields = ('title', 'text',)
    fallback_values = _('-- sorry, no translation provided --')

In this case, if title is missing in active language and any of fallback languages, news title will be '-- sorry, no translation provided --' (maybe translated, since gettext is used). Empty text will be handled in same way.

Fallback values can be also customized per model field:

class NewsTranslationOptions(TranslationOptions):
    fields = ('title', 'text',)
    fallback_values = {
        'title': _('-- sorry, this news was not translated --'),
        'text': _('-- please contact our translator ( --')

If current language and all fallback languages yield no field value, and no fallback values are defined, then modeltranslation will use field’s default value.

The State of the Original Field

Changed in version 0.5.

As defined by the Rules for Translated Field Access, accessing the original field is guaranteed to work on the associated translation field of the current language. This applies to both, read and write operations.

The actual field value (which can still be accessed through instance.__dict__['original_field_name']) however has to be considered undetermined once the field has been registered for translation. Attempts to keep the value in sync with either the default or current language’s field value has raised a boatload of unpredictable side effects in older versions of modeltranslation.


Do not rely on the underlying value of the original field in any way!


Perhaps outline effects this might have on the update_translation_field management command.