natcam_trans is an application based on django-transmeta and it's aim is to handle translatable content in Django's models. Each language is stored and managed automatically in a different column at database level.


Just add 'natcam_trans' to INSTALLED_APPS.


Automatic schema creation with translatable fields. Translatable fields integrated into Django's admin interface.

This application is based on django-transmeta but properly handles fields inherited from Abstract base classes.

It also adds:
  • 'nt_translate_fallback' and 'translate_verbose_names' parameters to 'Meta'
  • some useful utility functions are available:
    • get_translated_name,
    • get_untranslated_name,
    • get_all_translated_names,
    • get_languages_list,
    • sanitize_language,
    • fallback_language
  • <instance>.order_fields - instance method useful for ordering fields in ModelForm


Example model that uses NatcamTransMeta metaclass:

from django.db import models from natcam_trans.models import NatcamTransMeta

class Category(models.Model):

__metaclass__ = NatcamTransMeta

title = models.CharField(_('Title'), max_length=45, null=False, blank=False) code = models.CharField(_('Code'), max_length=45)

class Meta:
nt_translate = ('title', ) nt_translate_fallback = True translate_verbose_names = True
Assuming that we have languages defined in
LANGUAGES = (('pl', 'Polski'), ('en', 'English'), ('sk', 'Slovak')) LANGUAGE_CODE = 'en'

above code is roughly equivalent to:

class CategoryWithoutNatcamTrans(models.Model):

title_en = models.CharField(_('Title (en)'), max_length=45, null=False, blank=False) title_pl = models.CharField(_('Title (pl)'), max_length=45, null=True, blank=True) title_sk = models.CharField(_('Title (sk)'), max_length=45, null=True, blank=True) code = models.CharField(_('Code'), max_length=45)

title = property(...)

Working with live object might look like:

obj = Category(title_pl='polski', title_en='english', title_sk='slovak') # set language to polish translation.activate('pl') obj.title # returns 'polski'

# set language to english translation.activate('en') obj.title # returns 'english'

# if nt_translate_fallback = True then implicit getter tries # to find value by searching through all langs obj = Category(title_pl='polski') # title_en is not set!

# set language to polish translation.activate('pl') obj.title # returns 'polski'

# set language to english translation.activate('en') obj.title # returns 'polski'

What you have to do

  • Add __metaclass__ = NatcamTransMeta to your class (beware that class you're adding it to should be last class in inheritance


  • Define extra parameters in class Meta:

    nt_translate - list of field names that should be translable

    and optional:

    nt_translate_fallback - defines whether to try to find value for field

    in fields for other languages, eg: obj.title when current language is 'en' will look for value first in title_en, and if it's empty then it'll try to find value in title_pl, etc. - defaults to True

    translate_verbose_names - whether to update object's title with

    language name, eg: 'Title' will become 'Title (pl)', 'Title (en)' for new translated fields. This works for i18n, eg. _('Title') too - defaults to True

Important things to note

  • Fields that are not in default language (settings.LANGUAGE_CODE), in this case 'title_en', are set to be nullable and have blank = True.

  • Because translate_verbose_names == True field labels have now suffix with language name, eg. 'Title (en)'

  • There is no 'title' field anymore so it will NOT appear as a column in database. Resulting columns in database are 'title_pl' and 'title_en'

  • There is implicit getter for every translated field, in this case: 'title' that will return value in current language using proper value.

  • You CANNOT use:
    Category(title='polski') # there is no 'title' field any more,

    # you have to use 'title_pl' or 'title_en'

    Category.objects.filter(title='polski') # no 'title', remember? Category.objects.create(title='polski') # no 'title', again...

    You should always use explicit language code like:

    Category(title_pl='polski', title_en='english')

    When doing queries in current language you may use something like:

    from natcam_trans.utils import get_translated_name

    translated_title = get_translated_name('title') Category.objects.filter(**{translated_title = 'polski'})

How to add new language?

When you have to add new language then: 1. add new entry to LANGUAGES in settings file 2. create and run South migrations for applications using natcam_trans

Utility methods

  • get_translated_name
    >>> from natcam_trans.utils import get_translated_name
    >>> translation.activate('pl')
    >>> get_translated_name('title')
    >>> translation.activate('sk')
    >>> get_translated_name('title')
  • get_all_translated_names
    >>> from natcam_trans.utils import get_all_translated_names
    >>> get_all_translated_names('title')
    ['title_sk', 'title_pl', 'title_en']


  • Forms - fields, ordering - use 'order_fields' method defined for model instance, eg:

    class MyForm(forms.ModelForm):
    class Meta:

    model = MyModel

    def __init__(self, *args, **kwargs):

    super(MyForm, self).__init__(*args, **kwargs) self.fields.keyOrder = self.instance.order_fields(["name", "price", "description"])

    If name and description are translated field then self.fields.keyOrder will become:

    ["name_pl", "name_sk", "price", "description_pl", "description_sk"]

  • templates There are two useful tags that can be used in templates:

    • get_translation_languages_js
      Renders JavaScript code like:

      var TRANSLATION_LANGUAGES = ['pl', 'sk'];

      Might be used eg to assign TinyMCE to some fields

    • translatedfields Takes two arguments: 'form' and 'field_name' and renders block of code for each language. Adds 'tfield' to the block context which contains form field for specific language. Switches language for the processed language. For example:

      {% translatedfields form "description" %}
      <div class="field">

      {{ tfield.label_tag }}: {{ tfield }}


      {% endtranslatedfields %}

      If we have 'pl' and 'sk' languages then above block will be rendered twice for fields: description_pl and description_sk and respectively in polish and slovak languages.

HACKING natcam_trans

If you want to change something it is easy to setup isolated working environment: 1. python --distribute 2. bin/buildout 3. bin/test # runs nosetest; bin/test -v -s runs test in more verbose way without hiding output