Hi all,

sorry if this gets very long, but I will try to write down my current
opinion and experience with so called "model translation". I have put up
multiple sites using translatable content and I have written some apps
to help me doing so (none of which are public so far, as I hated my
first approaches after a few days/weeks and I'm not sure about these
apps so far). Anyway I tried to focus on some kind of 80%-solution,
which does only work for some (most) cases, but only needed 20% of the
work (http://en.wikipedia.org/wiki/Pareto_principle). I'll write a
little about these solutions at the end, but it is not be the focus of
this email.

Currently I think there are two completely different approaches for
doing model translations:

1. You have an object for each language. This object contains some
language-attribute which makes it easy to filter stuff. Admin and views
are no-brainers.

Of course this solution is only suitable for some special cases. News
for example might only exist in one language, so its perfectly fine (and
even preferred) to have different content for each language.

Language switching might become a problem, if you want to link to the
current object in a different language. Fixing this can be done by
adding a some kind of group-model, to group all the translations of one
object into one translation group. This can even be done using generic
foreign keys, which makes this an easy and reusable solution. I created
one generic app for this, but so far this is not public and I'm pretty
sure it missed many things.

Of course having one object for each language will become nasty if you
need common data to be equal for each object translations. You could
sync this, but...

2. Having some kind of common data, which needs to be equal for every
translation should really be solved inside the database. There exist
many solutions for this, which all fix some problems and create new. The
three most common solutions use language-suffixes, putting translations
into separate models or using some dict/pickle approach.

First I will try to write down what I think is important for providing a
solid solution.

a) Getting out of the users way

If I want to fetch some object I don't want to care about translations.
This is even true if I need to filter/order by some translated
attribute. I don't want to write stuff like (cur_lang ==
translation.get_language()):
Entry.objects.get(**{'slug_%s' % cur_lang: 'something'})
or:
Entry.objects.get(common__language=cur_lang, common__slug='something')
What I want is the plain old:
Entry.objects.get(slug='something')
or perhaps:
Entry.objects.localize().get(slug='something')

The same thing is true for accessing the attributes, but most approached
solve this, so no need to bring this up.

btw dict/pickle solutions fail to provide access to the data in the
query, regardless of how hard you try. So they fail big here.

b) The should not be too much overhead involved

You currently can choose between loading all translations, needing an
additional SQL query or unpickling some data. None of this is ideal. I
personally think a JOIN could be acceptable, but of course this also is
some overhead.

c) It should allow (not support) special cases

Sometime you need strange things like some field is optional in one
language while being needed in some other language. There might even be
fields that do not need to exist at all for one out of 10 languages. The
needs might of course be much simpler. As this cases are somewhat
esoteric they should not be a show-stopper for model translations. But
having heard about this might prevent some solution being to might
tightened up.

This btw is one thing about the "put all common data into it's own model
and JOIN away" I don't like. All common data needs to follow the same
rules, this may not be possible in all cases.

d) Managing languages should be easy

I don't think this needs to be the huge problem everybody likes to call
it. For me south solves this pretty well. If we get something like south
to be in core or the so called "official solution" managing changes in
translations becomes easy.

e) One might add it should be possible to add translations for third
party apps or create translations for your apps without changing the
basis. I think this is only part true.

As this adds a ton of new dependencies and side-effect I personally
think you should be able to do something to use translateable models. Of
course the changes you need should be as minimal as possible. 

Third-party-apps are a special case as you probably cannot maintain your
own copy. But I think really thinking avout third-party-apps should be
done when a solid solution is ready. Trying to solve all problems at
once just makes you go crazy. (Of course keeping third-party-apps in
mind is preferred)

e) Model relations should not become to nasty. Creating a translatable
Many2Many/ForeignKey inside your translations will get ugly with most
solutions. I think currently this is only easy when adding all fields to
one model (suffix). But reverse relations will suck this way. Usecase:
Every translations needs different tags.

=> So this only leaves adding fields with language suffixes, at least if
we look at what Django currently provides (I'll write down some things
about this later). It is easy, fast enough and yes, it will eat your
RAM.

So, about the other solutions: dict/pickle fails for using the fields
inside queries, so for me they are out of question. I think many people
use them (I like to call this the "gettext" approach), but I certainly
do not see any point in this. Of course it might probably be handy when
translating legacy apps.

What about "putting common data into its own model"? I like this
solution, I even like this solution so much I tried to implement it
several times. BUT you cannot get it to use a nice query. Most of the
time you will need to fetch the translation inside an separate query as
select_related() cannot fetch the translation even if the JOIN is unique
(qs.filter(common__language='xx') will create a unique JOIN). This
certainly could be improved.

Of course there's the thing about "getting into my way", which currently
every implementation of using multiple models currently has. I don't
think we should need to think about the different models here. Actually
model inheritance solves this, so perhaps the best approach might be to
get model inheritance more generic, so it could be used for other
things, too. Allowing users to define their own JOINs while keeping all
attributes inside the same object and not needing to do something
special inside queries is definitely a nice feature (and there might be
more use-cases for this, versioning models for example).

SQL could be something like:
SELECT ... FROM entry OUTER JOIN entry_trans ON
(entry.id=entry_trans.entry_id AND entry_trans.language='en') WHERE ...

I don't know the Django internals enough, but if this could be done
externals model translation should be possible without much hassle.


Other Django enhancements:

Add some LanguageField! Why not add some Language field? This should be
pretty easy. Currently I use some field in every project which basicly
only is a CharField with predefined max_length. This would certainly
make things more easy and allow multiple (third-party-)apps to share
some generics.

Virtual fields? Adding support for some kind of virtual fields might
enhance things. This just came into my mind, so it might be wrong.

Extendible QuerySets: I prefer to put new filters into QuerySets (and
adding an Manager for each new method), so I can choose to use
Entry.localized.all() or Entry.objects.localize() how I want. Adding
there methods to the QuerySet also allows to use it with related
managers (User.entries.localize()), which really is great. But having
some Manager for every possible QuerySet while allowing stacking of
QuerySets gets complicated fast. This probably only is true if you need
to add parameters to your Manager which get passed to the QuerySet.


Further problems:

Language selection: This is about how Django detects the user language
and how the user is able to select the language. Django could provide
more defaults here, which might be detecting the language based on the
request path, request domain or some other practical informations. I
currently use the request path for translations.

Only having the option to change language by cookie is bad for most
cases. Every public site needs to provide different URLs, so people can
link to one translation, search engines can crawl all translations, ...

...which brings me to i18n URLs. I currently have an urls.py for every
translation and use something like {% url foo_bar language=... %}. This
could certainly be improved, I think.


My solution(s):

Currently I have two apps, which help me do translations.

The first one allows me to group translations together, this is only
useful when having different content for each language (->
language-attribute). The app itself is pretty easy, but helps me get
translations organized (admin integration) and enhance the user
experience (language links go directly to translations).

The second app is my solution to adding language-suffix-fields to a
model. It is as simple as it gets, by not providing any help adding
these fields, you have to define all fields yourself (which is useful,
as all fields may have different options and even type). The app
provides a class to implement the "access the right attribute" glue
(name_en = CharField(...)\n name = I18NAttribute()).

In addition I have developed a QuerySet which provides a
localize()-method, that does:
 * If the model has some language-field it just returns 
   filter(language=cur_lang)
 * If you have I18NAttributes inside your model it will rewrite
   calls to filter/order_by() to use the right field:
   filter(name='...') -> filter(name_xx='...')
   filter(name__contains='...') -> filter(name_xx__contains='...')
   order_by('name') -> order_by('name_xx')
   ...models.Q-filters do not work of course

These apps are as simple as I could implement them, but they both helped
me a lot more than any other full blown solution. This is why I think we
should create better tools for doing such things inside Django instead
of trying to provide a solution to solve everything.

I hope I haven't missed something essential. Model translations really
touches most of the parts of Django (urls.py, QuerySet, views and of
course models). I intentionally have left out some aspects, because they
are not relevant to most users (for example translated content and full
text search (haystack)).


Thanks for reading this far,
David Danier

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-develop...@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to