I don't research your idea deeply but for first look it seems very
similar or same to django-multilingual.
http://code.google.com/p/django-multilingual/

Veena


On 11 čnc, 16:51, David Danier <[email protected]> wrote:
> Hi Marc, hi Gonzalo, hi django-dev,
>
> I'm writing this email, because I need some kind of future proof model
> translation in django in the near future. I tried many solutions and
> came up with two of my own (not released anywhere), but nothing seems to
> fit all needs translations might impose.
>
> So I thought about how model translations in django could work, and how
> this should be used by the developer. I send this email directly to
> Marc, because he is currently working on some i18n-stuff (GSoC). Gonzalo
> seems to be interested in an official solution
> (http://groups.google.com/group/django-developers/msg/0be7de2c154aa49d...),
> so you might be interested in some discussion about this. Perhaps
> Malcolm should be asked about how my proposal fits into the django ORM,
> as he has (re)written most of the ORM (-> queryset-refactor).
>
> I know Django 1.1 is on its way and developers might not have the time
> to really thnink about this now, but I needed to write it down. If
> someone answers and we can start working on this for post-1.1 would be
> great, of course. I, personally, don't need to wait. Anyway, according
> to the bugtracker theres not much delaying Django 
> 1.1:http://code.djangoproject.com/query?status=new&status=assigned&status...
>
> First to summarize the needs. It has to ...
> a) be fast, no heavy database-load should be needed
> b) work for third-party apps, as third party apps get unusable if you
> need i18n and they don't support it (this also means the existing
> third-party code must still work for translated models)
> c) be transparent for the user, you shouldn't need to think where your
> fields come from
> d) support missing translations without skipping the whole database-row,
> if you for example JOIN two tables (optional)
> e) be searchable through the normal ORM, translated fields should not be
> hidden in some serialized format
> f) be extendable, new fields shouldn't be a big problem
> g) convert existing data without a hassle (like sync_transmeta_db)
> h) keep context, if I fetch a german model from the database all
> relations should fetch german models, too (default-context should come
> from request and/or settings)
> i) be optional, not every project needs model translations, this is
> especially important for third-party-apps, supporting translations
> should not mean you have to use it
> j) be generic, different people have different needs
> k) support translating central fields (like slug), this goes
> hand-in-hand with easy searchability of the translated fields
> l) support getting all translations for every object in the database
> m) integrate well into the admin
>
> All existing solutions fail at some of these points, my own two
> solutions failed many of these points, too. So first, lets take a look
> at what exists:
>
> 1. Put every translation in extra fields inside one big table:
>    I'm not a fan of this, tends to get messy, especially for big sites
> with many translations. Developers need to think where their fields come
> from (Book.objects.filter(title_de=...)). Does not support third party apps.
>
> 2. Put all fields into its own model, use normal ORM for access.
>    I you look at this from a database perspective this is the best
> solution. But the django ORM does not really support you here. Access to
> the fields need an extra query, for every object. Even worse: If you use
> some translated fields in your query, you have to do this extra query
> anyway (Book.objects.filter(translations__slug='foobar',
> translations__language='de'), to get the german translation you have to
> execute an extra query, although the JOIN was already done).
>
> 3. Use serialization to save all fields into one big BLOB.
>    Do I need to say anything here? Not searchable, no way to really work
> with the translations on the DB-layer.
>
> 4. Special case: Create an own model for translations (like 2), but use
> the original table for some kind of DEFAULT_LANGUAGE. (pluggable-model-i18n)
>    I kind of like this, not only because you are able to enhance
> third-party-apps. Independent from that I think translations.register()
> seems to be a nice idea. But(!) this complicates things to much. You
> have to choose whether you want a field out of the DEFAULT_LANGUAGE or
> not for every field access/query.
>
> 5. gettext like approach.
>    Save a translation for possible string in the database, fallback to
> gettext. DB might explode here, as DB-load should be heavy, really heavy.
>
> So, what to make of these ideas people put into their code here, all
> projects have some advantage, while delivering some disadvantages. From
> a DB-perspective the second solutions seems to be best, from the
> usability-perspective the fourth solutions looks great. The first
> solution really shown its advantages when you look at queries
> (Book.objects.filter(name_en='this is easy')).
>
> First I want to throw one of my own solutions in, as I think this is
> interesting when looking at the admin. What I did was reversing the way
> we currently look at solution two, meaning the translation itself gets
> the model you work with and the rest is put into some "common" model:
>
> class BookCommon(models.Model):
>         some_field = models.CharField(...)
>
> class Book(models.Model):
>         common = models.ForeignKey(BookCommon)
>         language = models.CharField(...)
>         translated_field = ...
>
> Whats interesting here are two things:
>  * We can optimize DB-access using select_related():
>    Book.objects.select_related('common')
>    ...to we can cache the common attributes, great benefit
>  * The admin lists every translation for every objects, common fields
> need to be "copied" to BookForm. New translations can be created by just
> changing the language and hit "save as".
>
> Anyway, this solution did not work well, as relations between objects
> get a mess now (you normally have a ForeignKey to/from the Common-Model,
> so you are left with all problems of solution two when accessing related
> objects). Additionally you cannot access objects that have no
> translation in your current language, as
> Book.objects.filter(language='de') filters out all rows that doesn't
> have a translation. But I think this shows how the second solution can
> be enhanced to get this - from a DB-perspective good solution - to be
> more usable. It also is the solution that brought me to my current idea,
> even if not directly.
>
> So, we have this nice solution, that your database likes, but we have no
> way of using it in a way we can benefit from it. Making your database
> happy just isn't enough, but how could be use solution two the right
> way, when just dealing with SQL? The answer is pretty easy, use a JOIN:
> SELECT ... FROM book OUTER JOIN book_trans ON
> (book.id=book_trans.book_id AND book_trans.language='en') WHERE ...
>
> Let's explain this, most of it is quite obvious. But why the language
> inside the JOIN, could we not just use WHERE language='en'? Thats true,
> if we want to force our result to have a translation in the selected
> language. If you don't need this (for example because the fields are
> optional, the the whole translation is optional) you cannot use WHERE.
> But you can actually just change the query above to use a LEFT INNER JOIN:
> SELECT ... FROM book LEFT INNER JOIN book_trans ON
> (book.id=book_trans.book_id AND book_trans.language='en') WHERE ...
> Not you get a result, even if there is no row for the translation in
> book_trans. I think this is something model translations needs to
> support, solution two kind of supports this, too.
>
> Now lets digg into what pluggable-model-i18n does, as we want to support
> third-party apps, right? Why not just _remove_ the fields from the
> original model and dynamically create a new translation-model? This way
> you could change your database-layout if and only if needed, while
> keeping defining models easy. I think of some kind of
> translation.register() like pluggable-model-i18n uses. One example:
>
> class SomeObj(models.Model):
>         foo = models.CharField(...)
>         bar = models.CharField(...)
> class SomeObjTranslation(translation.ModelTranslation):
>         class Meta:
>                 fields = ('foo',)
> translation.register(SomeObj, SomeObjTranslation)
>
> could be converted to:
> class SomeObj(models.Model):
>         bar = models.CharField(...)
> class SomeObjTranslation(models.Model):
>         language = models.CharField(max_length=5, \
>                 choices=settings.LANGUAGES, \
>                 default=settings.LANGUAGE_CODE)
>         object = models.ForeignKey(SomeObj, related_name='translations')
>         foo = models.CharField(...)
>         class Meta:
>                 # only one translation per language and object
>                 unique_together = (('language', 'object'),)
>
> This was you can change third-party apps without needing the developers
> to even _care_ about translations, while still keeping the news models
> pretty easy. Of course you have to provide some kind of convert-script
> that manages to copy all values from the old table to the new one, but
> that should not be a big problem.
>
> No lets look at what usage should look like. As we want third-party apps
> to keep working no fields should be "renamed" ('field' ->
> 'translations__field'). In addition I think a query normally should get
> one translation of your object, this could be some DEFAULT_LANGUAGE or
> the request language by default. The model-object itself needs to allow
> transparent access to the fields, too. As obj.title will be translated
> you could say, your object represents one language at a time. I would
> suggest adding obj.switch_language('en') to load and transparently
> replace alle attributes (while keeping the old ones in some cache).
> Saving the objects must of course save all translations, too.
>
> Now you might notice, that this sounds familiar. Yes, model inheritance
> works similar. We have fields, that transparently are rewritten to the
> right table on queries, save() UPDATEs many tables and attributes just
> live in one object (obj.parent_field, instead of
> obj.parent.parent_field). If you look at this right, you will see, that
> the proposed translations are something like models using ...
>
> číst dál »
>
> [idea.txt5K ]=> Just some basic idea
> => Needs to rewrite things down till the django Query objects, so implementing
>    this is non-trivial
>
> >>> class SomeObj(models.Model):
> >>>    foo = models.CharField(...)
> >>>    bar = models.CharField(...)
>
> >>> class SomeObjTranslation(translation.ModelTranslation):
> >>>    class Meta:
> >>>            fields = ('foo',)
> >>>            # use INNER or LEFT JOIN (LEFT JOIN means you get None back,
> >>>            # if selected translation does not exist, INNER JOIN means
> >>>            # objects without a translation get skipped)
> >>>            allow_empty = True
>
> >>> translation.register(SomeObj, SomeObjTranslation)
>
> => Removes field 'foo' from SomeObj
> => Creates a new class with this field (+ language + object)
> => Sets up some useful methods, see below
> => Better: Change field 'foo' to point to SomeObjTranslation, like
>    model inheritance
>
> --> Gets rewritten to:
>
>
>
>
>
> >>> class SomeObj(models.Model):
> >>>   �...@property # has setter, too of course
> >>>    def foo(self):
> >>>            return self.translations....foo
> >>>   �...@property
> >>>    def language(self): ...
> >>>    bar = models.CharField(...)
>
> >>> class SomeObjTranslation(models.Model):
> >>>    language = models.CharField(max_length=5, choices=settings.LANGUAGES,
> >>>            default=settings.LANGUAGE_CODE)
> >>>    object = models.ForeignKey(SomeObj, related_name='translations')
> >>>    foo = models.CharField(...)
> >>>    class Meta:
> >>>            # only one translation per language and object
> >>>            unique_together = (('language', 'object'),)
>
> -->
>
> >>> SomeObj.objects.get(pk=1)
>
> => SELECT ... FROM someobj LEFT JOIN someobjtranslation ON
>                 (someobj.id = someobjtranslation.object_id AND
>                  someobjtranslation.language={{ DEFAULT_LANGUAGE }})
>                 WHERE ...
> => Cache for SomeObjTranslation is filled
> => DEFAULT_LANGUAGE = request language or settings.LANGUAGE_CODE
> => LEFT JOIN as allow_empty is True
>
> >>> SomeObj.objects.language('en').get(pk=1)
>
> => SELECT ... FROM someobj LEFT JOIN someobjtranslation ON
>                 (someobj.id = someobjtranslation.object_id AND
>                  someobjtranslation.language='en')
>                 WHERE ...
> => You can easily use some other language without needing to think about the 
> SQL
> => Cache for SomeObjTranslation is filled
>
> -->
>
> >>> class SomeOtherObj(models.Model):
> >>>    someobj = models.ForeignKey(SomeObj, related_name='other')
> >>>    foo = models.CharField(...)
>
> >>> class SomeOtherObjTranslation(translation.ModelTranslation):
> >>>    def some_method(self): ...
> >>>    class Meta:
> >>>            fields = ('foo',)
> >>>            # see above (SomeObjTranslation)
> >>>            allow_empty = False
>
> >>> translation.register(SomeOtherObj, SomeOtherObjTranslation)
>
> --> Gets rewritten to:
>
>
>
>
>
> >>> class SomeOtherObj(models.Model):
> >>>    someobj = models.ForeignKey(SomeObj, related_name='others')
> >>>   �...@property # has setter, too of course
> >>>    def foo(self):
> >>>            return self.translations....foo
> >>>   �...@property
> >>>    def language(self): ...
> >>>    def some_method(self): # all attributes of translation get mirrored 
> >>> here
> >>>            self.translations....some_method()
>
> >>> class SomeOtherObjTranslation(models.Model):
> >>>    language = models.CharField(max_length=5, choices=settings.LANGUAGES,
> >>>            default=settings.LANGUAGE_CODE)
> >>>    object = models.ForeignKey(SomeOtherObj, related_name='translations')
> >>>    foo = models.CharField(...)
> >>>    def some_method(self): ... # like when you define this on normal models
> >>>    class Meta:
> >>>            # only one translation per language and object
> >>>            unique_together = (('language', 'object'),)
>
> -->
>
> >>> obj = SomeObj.objects.get(pk=1)
> >>> obj.others
>
> => SELECT ... FROM someotherobj OUTER JOIN someotherobjtranslation ON
>                 (someotherobj.id = someotherobjtranslation.object_id AND
>                  someotherobjtranslation.language={{ obj.language }})
>                 WHERE ...
> => Language "context" of obj is used
> => OUTER JOIN as allow_empty is False, so this query may raise
>    SomeOtherObj.DoesNotExist on get(), even if this works for other languages
>
> >>> obj = SomeObj.objects.get(pk=1)
> >>> obj.others.language('en')
>
> => SELECT ... FROM someotherobj OUTER JOIN someotherobjtranslation ON
>                 (someotherobj.id = someotherobjtranslation.object_id AND
>                  someotherobjtranslation.language='en')
>                 WHERE ...
> => You can overwrite the language used on related managers, too
>
> >>> obj.switch_language('en')
>
> => SELECT ... FROM someobjtranslation WHERE
>                 someobjtranslation.object_id={{ obj.pk }} AND
>                 someobjtranslation.language='en'
> => Fill translation cache with 'en' (already loaded translation gets _not_
>    overwritten)>>> obj.others
>
> => SELECT ... FROM someotherobj LEFT JOIN someotherobjtranslation ON
>                 (someotherobj.id = someotherobjtranslation.object_id AND
>                  someotherobjtranslation.language={{ obj.language }})
>                 WHERE ...
> => context is used again, but changed to 'en' (query construction is the same)
>
> -->
>
> >>> SomeObj.objects.get(foo='bar')
>
> => SELECT ... FROM someobj LEFT JOIN someobjtranslation ON
>                 (someobj.id = someobjtranslation.object_id AND
>                  someobjtranslation.language={{ DEFAULT_LANGUAGE }})
>                 WHERE ... AND
>                 someobjtranslation.foo='bar'
> => Does select right table without needing to worry about where the field 
> comes
>    from
>
> >>> SomeObj.objects.language('en').get(foo='bar')
>
> => SELECT ... FROM someobj LEFT JOIN someobjtranslation ON
>                 (someobj.id = someobjtranslation.object_id AND
>                  someobjtranslation.language='en')
>                 WHERE ... AND
>                 someobjtranslation.foo='bar'
> => You can use normal queries without needing to worry about anything, even
>    language switching works
>
> -->
>
> >>> obj = SomeObj.objects.get(pk=1)
> >>> obj.save()
>
> => Does save SomeObj _and_ SomeObjTranslation
>
> -->
>
> >>> class BrokenObj(models.Model):
> >>>    foo = models.CharField(...)
> >>>    bar = models.CharField(...)
> >>>    class Meta:
> >>>            unique_together = (('foo', 'bar'),)
>
> >>> class BrokenObjTranslation(translation.ModelTranslation):
> >>>    class Meta:
> >>>            fields = ('foo',)
>
> >>> translation.register(BrokenObj, BrokenObjTranslation)
>
> => Fails as unique_together will not work anymore
> => There may be other places where splitting up the model produces side 
> effects

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

Reply via email to