1. Maciej Wiśniowski
  2. natcam_trans


Introduction ============ 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. Installation =========== Just add 'natcam_trans' to INSTALLED_APPS. Features ======== 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 Usage ===== 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 settings.py: 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 path) - 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') title_pl >>> translation.activate('sk') >>> get_translated_name('title') title_sk - get_all_translated_names >>> from natcam_trans.utils import get_all_translated_names >>> get_all_translated_names('title') ['title_sk', 'title_pl', 'title_en'] Tips ==== - 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 }} </div> {% 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 bootstrap.py --distribute 2. bin/buildout 3. bin/test # runs nosetest; bin/test -v -s runs test in more verbose way without hiding output