Best practice for implementing multilingual content in template

34 views
Skip to first unread message

Emil

unread,
Jan 27, 2008, 12:03:25 PM1/27/08
to Django users
Hi!

I'm currently working on a couple of sites where basically all content
is to be available in two languages. I haven't used django-
multilingual or anything readymade, but simply created double fields
in my models for all content in the db that needs to be available in
both languages. For example, a post with a title might have "title_en"
and "title_sv".

Now, I'm trying to figure out the most efficient and DRY way of
outputting these fields based on current language in the template. My
current solution is to use a template tag from djangosnippets (by
Jacob Kaplan-Moss if my memory serves me) that implements a pair of
simple switch/case tags, so the template might look something like so:

{% switch LANGUAGE_CODE %}
{% case "en" %}{{ title_en }}{% endcase %}
{% case "sv" %}{{ title_sv }}{% endcase %}
{% endswitch %}

The above solution definitely works, but leaves some things to be
desired. First, it leaves a lot of whitespace in the rendered document
if I lay it out nicely with newlines etc, like above. Not a big deal,
admittedly, but i like my pages to look good when I view source, it's
an illness I have, sorry. The alternative is to put all of the above
code on one line, which makes my templates fairly messy and hard to
read, which I don't like either.

I tried a different solution today. I found the built-in include tag,
and decided to try to implement a slightly more DRY solution, just to
see what worked. I moved the switch code to a separate HTML-snippet
which defined two blocks, one for English and one for Swedish. I then
tried creating a snippet for a piece of bilingual content that
extended the switch-snippet and defined the two blocks. That way, I
had to create snippets for each piece of bilingual content, but made
the main templates easier to read plus I didn't have to repeat writing
the switch-tag code every time.

Unfortunately, the include tag doesn't seem to support extending
(which I suspected, but anyway) so the solution wasn't ideal. I could
move each bilingual snippet to a separate snippet (and make the main
templates look better), but would still have to repeat the switch-tag
code every time.

So, finally, my question is this: How have you implemented this
functionality? Any tips for making this more hassle- and clutter-free?
Separate templates in some other way? Some smart template tag?

//emil

Wanrong Lin

unread,
Jan 27, 2008, 1:34:37 PM1/27/08
to django...@googlegroups.com
Emil,

I am having the same problem too. I have not gone that far to
implementing the details of templates, but my planned solution is to use
just one template without any language dependent switching. Instead, I
will write some code to extract the relevant fields (title_en or
title_sv as in your example) for user selected language and supply that
as the context to the template. Just want to give an alternative approach.

Wanrong

Emil

unread,
Jan 27, 2008, 1:47:16 PM1/27/08
to Django users
On 27 Jan, 19:34, Wanrong Lin <wanrong....@gmail.com> wrote:
> Emil,
>
> I am having the same problem too. I have not gone that far to
> implementing the details of templates, but my planned solution is to use
> just one template without any language dependent switching. Instead, I
> will write some code to extract the relevant fields (title_en or
> title_sv as in your example) for user selected language and supply that
> as the context to the template. Just want to give an alternative approach.
>
> Wanrong

Thanks, Wanrong. I see how you mean... I'll think about how to
implement something like that.

Any other takes on this by anyone?

//emil

Jonathan Ballet

unread,
Jan 27, 2008, 4:59:53 PM1/27/08
to django...@googlegroups.com, bjorklu...@gmail.com
Hi!

Le Sun, 27 Jan 2008 09:03:25 -0800 (PST), Emil
<bjorklu...@gmail.com> a écrit :

> Hi!
>
> I'm currently working on a couple of sites where basically all content
> is to be available in two languages. I haven't used django-
> multilingual or anything readymade, but simply created double fields
> in my models for all content in the db that needs to be available in
> both languages. For example, a post with a title might have "title_en"
> and "title_sv".
>

[...]


>
> {% switch LANGUAGE_CODE %}
> {% case "en" %}{{ title_en }}{% endcase %}
> {% case "sv" %}{{ title_sv }}{% endcase %}
> {% endswitch %}

Depending on your model, you can do this switch statement inside a
model method.
Something like this :

==============================================
from django.db import models
from django.conf import settings

class Post(models.Model):
title_en = ...
title_sv = ...

def title(self):
if settings.LANGUAGE_CODE == 'en':
return self.title_en
else:
return self.title_sv
==============================================

... and so, your only need to specify "{{ post.title }}" in your
template.
However, it can be a bit verbose if you have several fields with the
same scheme as "title_*".
Or maybe, if you like dark magic art, you can try to set up a
__getattr__ method in your Post class, like that :

=============================
def __getattr__(self, attribute):
lang = settings.LANGUAGE_CODE
return getattr(self, "%s_%s" % (attribute, lang))
=============================

It seems to work :
>>> from django.conf import settings
>>> p = m.Post.objects.all()[0]
>>> settings.LANGUAGE_CODE
'en'
>>> p.title
u'en title'
>>> settings.LANGUAGE_CODE = 'sv'
>>> p.title
u'sv title'
>>> settings.LANGUAGE_CODE = 'en'
>>> p.title
u'en title'

... but you might want to refine this, because it's a bit hacky and it
explodes if you specify an non-existing attribute (recursives calls, and
the like).


Or, if you are not-so-evil, you can try to make a template filter,
which takes in parameters a field, and it will try to do this (instead
of the tricky use of getattr in the Model class).
Something like :

==================================
from django.conf import settings
from django.template.defaultfilters import stringfilter
from django import template

register = template.Library()
@register.filter
@stringfilter
def translate(obj, field):
return getattr(obj, "%s_%s" % (field, settings.LANGUAGE_CODE))
==================================

And you should be able to use it like that in your templates :
{{ post|translate:"title" }}
But it's not very pretty neither.


The first proposition is the cleaner, but it's not really dry.
The second one can be great, but you can shoot yourself in the foot
with __getattr__. Be careful.


I hope it might help (or give ideas ...)

- Jonathan

code_berzerker

unread,
Jan 27, 2008, 5:33:48 PM1/27/08
to Django users
I think that better idea would be doing something like that (I'm using
even more complicated models myself):

class Page(models.Model):
#parent page in tree of pages
parent = models.ForeignKey('self', related_name="children",
blank=True, null=True)
slug = models.SlugField(unique=True)
#name for admin page listing
name = models.CharField(max_length=255)
#short description for admin page listing
descr = models.TextField()
#relative order of silblings
order = models.IntegerField()
author = models.ForeignKey(User)

class Content(models.Model):
#melongs to which page
page = models.ForeignKey(Page)
author = models.ForeignKey(User)
#version in what language
lang = models.CharField(max_length=3)
title = models.CharField(max_length=255)
content = models.TextField()
COMPONENTS = (
('article', 'Article'),
('news', 'News'),
('partners', 'Partners'),
)
component = models.CharField(max_length=50, choices=COMPONENTS)
#is page published for this language version
published = models.BooleanField()
#template = models.ForeignKey(Template)

We got relational database so lets use relations to be more efficient
and have less data redunddancy.
To explain a bit more class Page has one row per each page and has
related as many rows in Content class as the number of languages. Just
keep current language in session and use it to filter content rows to
get the one you need. All the data that is shared among instances of
page for each language is kept in Page class (less data redundancy).
All the data that differs between languages is kept in Content (we
read from database to memory only the data for one language - thats
efficiency).

Hope that helps :)

Emil

unread,
Jan 28, 2008, 10:01:26 AM1/28/08
to Django users
Thank you all for the response. I think Jonathans version with custom
methods to get the right field in the template would be the simplest
for my moderate level of complexity, for now at least. But I've gotten
some ideas on how to do it differently now, so thanks again!

//emil

Ivan Illarionov

unread,
Jan 28, 2008, 2:45:23 PM1/28/08
to Django users
IMO code_berzerker's approach is the best for complex models. But in
many cases that don't require a lot of flexibility it can be better to
use simpler approach with text fields for each language and function/
property to get the right field from templates. I even wrote the
metaclass to make this DRY.

http://pastebin.com/m715ba882

You might need to edit the LANGUAGES tuple for your needs and add
__metaclass__ = MultilingualModelBase in the beginning of your
multilingual model classes. Properties will be automatically created.
If your model has `title_en` and `title_sv` this metaclass will create
`title` property that will get the right filed based on
settings.LANGUAGE_CODE.

Hope this helps.

--Ivan

Ivan Illarionov

unread,
Jan 28, 2008, 3:15:59 PM1/28/08
to Django users
Made some corrections:
http://pastebin.com/d6337d211

On Jan 28, 10:45 pm, Ivan Illarionov <ivan.illario...@gmail.com>
wrote:

Emil

unread,
Jan 28, 2008, 4:31:24 PM1/28/08
to Django users
Thanks for the tips, Ivan!

//emil
Reply all
Reply to author
Forward
0 new messages