Login

Multilingual Models

Author:
Archatas
Posted:
February 28, 2007
Language:
Python
Version:
Pre .96
Score:
5 (after 5 ratings)

A way to implement models with translatable content.

The translatable field of the default language has no extension, like "title" and the translations have extensions postfixes "_<two-letter language code>", like "title_la" or "title_mn". Method get_title() in this case returns the translation of the currently chosen language or the title of the default language, if the translation is not set.

The class XFieldList overrides the default list class and is used for modifying ordering and list_display settings according the chosen language. For example, when the German language is chosen, the list of translatable content objects will be ordered by German titles (not English).

At the time when the list of field names is assigned to ordering or list_display (at the import of the model), the currently chosen language is still not known. But the language is known when ordering and list_display lists are used in contributed administration or elsewhere.

The XFieldList returns the modified values of the passed-in list, when its methods/properties are triggered. XFieldList transforms field names ending "" (except the ones beginning with "", like "str_") to appropriate translations according the currently chosen language. For example ['title_', 'content_', 'is_published'] will be changed to ['title', 'content', 'is_published'] for the English language and to ['title_lt', 'content_lt', 'is_published'] for the Lithuanian language.

The best practice is to put XFieldList into a separate file and import it from different apps whenever needed.

It's worth mentioning that one implementing this should also know about Django internationalization.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from django.db import models
from django.utils import translation
from django.utils.translation import gettext_lazy as _

class XFieldList(list):
    """ List for field names.
    Changes "*_" to specific field names for the current language,
    i.e.
    "sort_order" => "sort_order"
    "title_" => "title", "title_de", or "title_es"
    "__str__" => "__str__"
    """
    def __init__(self, sequence=[]):
        self.sequence = sequence
    def __iter__(self):
        return iter(self._get_list())
    def __getitem__(self, k):
        return self._get_list()[k]
    def __nonzero__(self):
        return bool(self.sequence)
    def __len__(self):
        return len(self.sequence)
    def __str__(self):
        return str(self._get_list())
    def __repr__(self):
        return repr(self._get_list())
    def _get_list(self):
        language = translation.get_language()[:2]
        result = []
        for item in self.sequence:
            if item[:1]=="-":
                order = "-"
                item = item[1:]
            else:
                order = ""
            if item[:2] == "__" or item[-1:] != "_":
                result.append(order + item)
            else:
                if language == "en":
                    result.append(order + item[:-1])
                else:
                    result.append(order + item + language)
        return result

class TranslatableContent(models.Model):
    title = models.CharField(_("Title"), maxlength=255)
    title_de = models.CharField(_("Title (DE)"), maxlength=255, blank=True)
    title_lt = models.CharField(_("Title (LT)"), maxlength=255, blank=True)
    content = models.TextField(_("Content"))
    content_de = models.TextField(_("Content (DE)"), blank=True)
    content_lt = models.TextField(_("Content (LT)"), blank=True)
    is_published = models.BooleanField(_("Published"))
    class Admin:
        list_display = XFieldList(['title_', 'content_', 'is_published'])
    class Meta:
        verbose_name = _("translatable content")
        verbose_name_plural = _("translatable content")
        ordering = XFieldList(['title_', 'is_published'])
    def __str__(self):
        return self.get_title()
    def get_title(self, language=None):
        return getattr(self, "title_%s" % (language or translation.get_language()[:2]), "") or self.title
    def get_content(self, language=None):
        return getattr(self, "content_%s" % (language or translation.get_language()[:2]), "") or self.content

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 8 months ago

Comments

Fil (on April 19, 2007):

All the lines

return getattr(self, "title_%s" % language or translation.get_language()[:2], "") or self.title

should be

return getattr(self, "title_%s" % (language or translation.get_language()[:2]), "") or self.title

Notice the missing ( and ) between the language or translation part.

#

Archatas (on May 15, 2007):

Yes, Fil, you are right!

#

Please login first before commenting.