Search code examples
djangolocalizationinternationalizationdjango-south

Content translation in Django, compatible with South


I'm looking for a solution to have translatable database fields in Django, and the solution needs to be compatible with South.

I already found django-transmeta and transdb. Only the latter seems to be compatible with South, but I'm not sure about that. Also, transdb values don't look nice in the database (something the customer actually cares about).

Has anybody worked with such a combination? I'm not particaluary focussed on South, but it seems to be the way to go for schema migration in Django.


Solution

  • Because we did know that the number of languages will never change, we switched to having fields for each language and a simple mechanism for automatically reading the correct field for the current language. Our implementation is used like this:

    class Question(models.Model):
        __metaclass__ = LocalizeModelBase
    
        text_de = models.TextField(verbose_name=_(u"question text (german)"))
        text_en = models.TextField(verbose_name=_(u"question text (english)"))
        text = Translate
    

    Question().text then automatically returns the correct field, based on the current language with a fallback to the default language.

    The implementation behind it should be pretty much self-explaining:

    from django.db.models.base import ModelBase
    from django.utils.translation import get_language
    from django.conf import settings
    
    __all__ = ('Translate', 'LocalizeModelBase')
    
    # a dummy placeholder object
    Translate = object()
    
    
    class LocalizeModelBase(ModelBase):
        """This meta-class provides automatically translated content properties. Set
        a model field to `Translate` and it will automatically return the property
        with the name of the current language. E.g. if there is a normal member
        `text_de`, a member `text = Translate` and the current language is `de`, then
        an object will return the content of `text_de` when it is asked for the value
        of `text`.
        """
        def __new__(metacls, classname, bases, classDict):
            # find all classDict entries that point to `Translate`
            for key in classDict.keys():
                if classDict[key] is Translate:
                    # replace them with a getter that uses the current language
                    classDict[key] = make_property(key)
            return super(LocalizeModelBase, metacls).__new__(metacls, classname, bases, classDict)
    
    
    def make_property(k):
        """Creates a new property that implements the automatic translation
        described above. Every use of `Translate` in a class definition will be
        replaces with a property returned by this function."""
        def pget(self):
            try:
                # try to return the attribute for the current language
                return getattr(self, "%s_%s" % (k, get_language()))
            except AttributeError:
                # use the default language if the current language is not available
                return getattr(self, "%s_%s" % (k, settings.LANGUAGE_CODE))
        return property(pget)