mirror of
https://github.com/Hopiu/django-modeltranslation.git
synced 2026-03-16 22:10:31 +00:00
374 lines
14 KiB
ReStructuredText
374 lines
14 KiB
ReStructuredText
.. _usage:
|
|
|
|
Accessing Translated and Translation Fields
|
|
===========================================
|
|
|
|
Modeltranslation changes the behaviour of the translated fields. To
|
|
explain this consider the news example from the :ref:`registration` chapter
|
|
again. The original ``News`` model looked like this::
|
|
|
|
class News(models.Model):
|
|
title = models.CharField(max_length=255)
|
|
text = models.TextField()
|
|
|
|
Now that it is registered with modeltranslation the model looks
|
|
like this - note the additional fields automatically added by the app::
|
|
|
|
class News(models.Model):
|
|
title = models.CharField(max_length=255) # original/translated field
|
|
title_de = models.CharField(null=True, blank=True, max_length=255) # default translation field
|
|
title_en = models.CharField(null=True, blank=True, max_length=255) # translation field
|
|
text = models.TextField() # original/translated field
|
|
text_de = models.TextField(null=True, blank=True) # default translation field
|
|
text_en = models.TextField(null=True, blank=True) # translation field
|
|
|
|
The example above assumes that the default language is ``de``, therefore the
|
|
``title_de`` and ``text_de`` fields are marked as the *default translation
|
|
fields*. If the default language is ``en``, the ``title_en`` and ``text_en``
|
|
fields would be the *default translation fields*.
|
|
|
|
.. _rules:
|
|
|
|
Rules for Translated Field Access
|
|
---------------------------------
|
|
|
|
.. versionchanged:: 0.5
|
|
|
|
So now when it comes to setting and getting the value of the original and the
|
|
translation fields the following rules apply:
|
|
|
|
**Rule 1**
|
|
|
|
Reading the value from the original field returns the value translated to
|
|
the current language.
|
|
|
|
**Rule 2**
|
|
|
|
Assigning a value to the original field updates the value in the associated
|
|
current language translation field.
|
|
|
|
**Rule 3**
|
|
|
|
If both fields - the original and the current language translation field -
|
|
are updated at the same time, the current language translation field wins.
|
|
|
|
.. note:: This can only happen in the model's constructor or
|
|
``objects.create``. There is no other situation which can be considered
|
|
*changing several fields at the same time*.
|
|
|
|
|
|
Examples for Translated Field Access
|
|
------------------------------------
|
|
|
|
Because the whole point of using the modeltranslation app is translating
|
|
dynamic content, the fields marked for translation are somehow special when it
|
|
comes to accessing them. The value returned by a translated field is depending
|
|
on the current language setting. "Language setting" is referring to the Django
|
|
`set_language`_ view and the corresponding ``get_lang`` function.
|
|
|
|
Assuming the current language is ``de`` in the news example from above, the
|
|
translated ``title`` field will return the value from the ``title_de`` field::
|
|
|
|
# Assuming the current language is "de"
|
|
n = News.objects.all()[0]
|
|
t = n.title # returns german translation
|
|
|
|
# Assuming the current language is "en"
|
|
t = n.title # returns english translation
|
|
|
|
This feature is implemented using Python descriptors making it happen without
|
|
the need to touch the original model classes in any way. The descriptor uses
|
|
the ``django.utils.i18n.get_language`` function to determine the current
|
|
language.
|
|
|
|
.. todo:: Add more examples.
|
|
|
|
|
|
.. _multilingual_manager:
|
|
|
|
Multilingual Manager
|
|
--------------------
|
|
|
|
.. versionadded:: 0.5
|
|
|
|
Every model registered for translation is patched so that all its managers become subclasses
|
|
of ``MultilingualManager`` (of course, if a custom manager was defined on the model, its
|
|
functions will be retained). ``MultilingualManager`` simplifies language-aware queries,
|
|
especially on third-party apps, by rewriting query field names.
|
|
|
|
Every model's manager is patched, not only ``objects`` (even managers inherited from abstract base
|
|
classes).
|
|
|
|
For example::
|
|
|
|
# Assuming the current language is "de",
|
|
# these queries returns the same objects
|
|
news1 = News.objects.filter(title__contains='enigma')
|
|
news2 = News.objects.filter(title_de__contains='enigma')
|
|
|
|
assert news1 == news2
|
|
|
|
It works as follow: if the translation field name is used (``title``), it is changed into the
|
|
current language field name (``title_de`` or ``title_en``, depending on the current active
|
|
language).
|
|
Any language-suffixed names are left untouched (so ``title_en`` wouldn't change,
|
|
no matter what the current language is).
|
|
|
|
Rewriting of field names works with operators (like ``__in``, ``__ge``) as well as with
|
|
relationship spanning. Moreover, it is also handled on ``Q`` and ``F`` expressions.
|
|
|
|
These manager methods perform rewriting:
|
|
|
|
- ``filter()``, ``exclude()``, ``get()``
|
|
- ``order_by()``
|
|
- ``update()``
|
|
- ``only()``, ``defer()``
|
|
- ``values()``, ``values_list()``, with :ref:`fallback <fallback>` mechanism
|
|
- ``dates()``
|
|
- ``select_related()``
|
|
- ``create()``, with optional auto-population_ feature
|
|
|
|
In order not to introduce differences between ``X.objects.create(...)`` and ``X(...)``, model
|
|
constructor is also patched and performs rewriting of field names prior to regular initialization.
|
|
|
|
If one wants to turn rewriting of field names off, this can be easily achieved with
|
|
``rewrite(mode)`` method. ``mode`` is a boolean specifying whether rewriting should be applied.
|
|
It can be changed several times inside a query. So ``X.objects.rewrite(False)`` turns rewriting off.
|
|
|
|
``MultilingualManager`` offers one additional method: ``raw_values``. It returns actual values from
|
|
the database, without field names rewriting. Useful for checking translated field database value.
|
|
|
|
Auto-population
|
|
***************
|
|
|
|
.. versionchanged:: 0.6
|
|
|
|
There is special manager method ``populate(mode)`` which can trigger ``create()`` or
|
|
``get_or_create()`` to populate all translation (language) fields with values from translated
|
|
(original) ones. It can be very convenient when working with many languages. So::
|
|
|
|
x = News.objects.populate(True).create(title='bar')
|
|
|
|
is equivalent of::
|
|
|
|
x = News.objects.create(title_en='bar', title_de='bar') ## title_?? for every language
|
|
|
|
|
|
Moreover, some fields can be explicitly assigned different values::
|
|
|
|
x = News.objects.populate(True).create(title='-- no translation yet --', title_de='enigma')
|
|
|
|
It will result in ``title_de == 'enigma'`` and other ``title_?? == '-- no translation yet --'``.
|
|
|
|
There is another way of altering the current population status, an ``auto_populate`` context
|
|
manager::
|
|
|
|
from modeltranslation.utils import auto_populate
|
|
|
|
with auto_populate(True):
|
|
x = News.objects.create(title='bar')
|
|
|
|
Auto-population takes place also in model constructor, what is extremely useful when loading
|
|
non-translated fixtures. Just remember to use the context manager::
|
|
|
|
with auto_populate(): # True can be ommited
|
|
call_command('loaddata', 'fixture.json') # Some fixture loading
|
|
|
|
z = News(title='bar')
|
|
print(z.title_en, z.title_de) # prints 'bar bar'
|
|
|
|
There is a more convenient way than calling ``populate`` manager method or entering
|
|
``auto_populate`` manager context all the time:
|
|
:ref:`settings-modeltranslation_auto_populate` setting.
|
|
It controls the default population behaviour.
|
|
|
|
.. _auto-population-modes:
|
|
|
|
Auto-population modes
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
There are four different population modes:
|
|
|
|
``False``
|
|
[set by default]
|
|
|
|
Auto-population turned off
|
|
|
|
``True`` or ``'all'``
|
|
[default argument to population altering methods]
|
|
|
|
Auto-population turned on, copying translated field value to all other languages
|
|
(unless a translation field value is provided)
|
|
|
|
``'default'``
|
|
Auto-population turned on, copying translated field value to default language field
|
|
(unless its value is provided)
|
|
|
|
``'required'``
|
|
Acts like ``'default'``, but copy value only if the original field is non-nullable
|
|
|
|
|
|
.. _fallback:
|
|
|
|
Falling back
|
|
------------
|
|
|
|
Modeltranslation provides a mechanism to control behaviour of data access in case of empty
|
|
translation values. This mechanism affects field access, as well as ``values()``
|
|
and ``values_list()`` manager methods.
|
|
|
|
Consider the ``News`` example: a creator of some news hasn't specified its German title and
|
|
content, but only English ones. Then if a German visitor is viewing the site, we would rather show
|
|
him English title/content of the news than display empty strings. This is called *fallback*. ::
|
|
|
|
news.title_en = 'English title'
|
|
news.title_de = ''
|
|
print(news.title)
|
|
# If current active language is German, it should display the title_de field value ('').
|
|
# But if fallback is enabled, it would display 'English title' instead.
|
|
|
|
# Similarly for manager
|
|
news.save()
|
|
print(News.objects.filter(pk=news.pk).values_list('title', flat=True)[0])
|
|
# As above: if current active language is German and fallback to English is enabled,
|
|
# it would display 'English title'.
|
|
|
|
There are several ways of controlling fallback, described below.
|
|
|
|
.. _fallback_lang:
|
|
|
|
Fallback languages
|
|
******************
|
|
|
|
.. versionadded:: 0.5
|
|
|
|
:ref:`settings-modeltranslation_fallback_languages` setting allows to set the order of *fallback
|
|
languages*. By default that's the ``DEFAULT_LANGUAGE``.
|
|
|
|
For example, setting ::
|
|
|
|
MODELTRANSLATION_FALLBACK_LANGUAGES = ('en', 'de', 'fr')
|
|
|
|
means: if current active language field value is unset, try English value. If it is also unset,
|
|
try German, and so on - until some language yields a non-empty value of the field.
|
|
|
|
There is also an option to define a fallback by language, using dict syntax::
|
|
|
|
MODELTRANSLATION_FALLBACK_LANGUAGES = {
|
|
'default': ('en', 'de', 'fr'),
|
|
'fr': ('de',),
|
|
'uk': ('ru',)
|
|
}
|
|
|
|
The ``default`` key is required and its value denote languages which are always tried at the end.
|
|
With such a setting:
|
|
|
|
- for `uk` the order of fallback languages is: ``('ru', 'en', 'de', 'fr')``
|
|
- for `fr` the order of fallback languages is: ``('de', 'en')`` - Note, that `fr` obviously is not
|
|
a fallback, since its active language and `de` would be tried before `en`
|
|
- for `en` and `de` the fallback order is ``('de', 'fr')`` and ``('en', 'fr')``, respectively
|
|
- for any other language the order of fallback languages is just ``('en', 'de', 'fr')``
|
|
|
|
What is more, fallback languages order can be overridden per model, using ``TranslationOptions``::
|
|
|
|
class NewsTranslationOptions(TranslationOptions):
|
|
fields = ('title', 'text',)
|
|
fallback_languages = {'default': ('fa', 'km')} # use Persian and Khmer as fallback for News
|
|
|
|
Dict syntax is only allowed there.
|
|
|
|
.. versionadded:: 0.6
|
|
|
|
Even more, all fallbacks may be switched on or off for just some exceptional block of code using::
|
|
|
|
from modeltranslation.utils import fallbacks
|
|
|
|
with fallbacks(False):
|
|
# Work with values for the active language only
|
|
|
|
.. _fallback_val:
|
|
|
|
Fallback values
|
|
***************
|
|
|
|
.. versionadded:: 0.4
|
|
|
|
But what if current language and all fallback languages yield no field value? Then modeltranslation
|
|
will use the field's *fallback value*, if one was defined.
|
|
|
|
Fallback values are defined in ``TranslationOptions``, for example::
|
|
|
|
class NewsTranslationOptions(TranslationOptions):
|
|
fields = ('title', 'text',)
|
|
fallback_values = _('-- sorry, no translation provided --')
|
|
|
|
In this case, if title is missing in active language and any of fallback languages, news title
|
|
will be ``'-- sorry, no translation provided --'`` (maybe translated, since gettext is used).
|
|
Empty text will be handled in same way.
|
|
|
|
Fallback values can be also customized per model field::
|
|
|
|
class NewsTranslationOptions(TranslationOptions):
|
|
fields = ('title', 'text',)
|
|
fallback_values = {
|
|
'title': _('-- sorry, this news was not translated --'),
|
|
'text': _('-- please contact our translator (translator@example.com) --')
|
|
}
|
|
|
|
If current language and all fallback languages yield no field value, and no fallback values are
|
|
defined, then modeltranslation will use the field's default value.
|
|
|
|
.. _fallback_undef:
|
|
|
|
Fallback undefined
|
|
******************
|
|
|
|
.. versionadded:: 0.7
|
|
|
|
Another question is what do we consider "no value", on what value should we fall back to other
|
|
translations? For text fields the empty string can usually be considered as the undefined value,
|
|
but other fields may have different concepts of empty or missing values.
|
|
|
|
Modeltranslation defaults to using the field's default value as the undefined value (the empty
|
|
string for non-nullable ``CharFields``). This requires calling ``get_default`` for every field
|
|
access, which in some cases may be expensive.
|
|
|
|
If you'd like to fall back on a different value or your default is expensive to calculate, provide
|
|
a custom undefined value (for a field or model)::
|
|
|
|
class NewsTranslationOptions(TranslationOptions):
|
|
fields = ('title', 'text',)
|
|
fallback_undefined = {
|
|
'title': 'no title',
|
|
'text': None
|
|
}
|
|
|
|
The State of the Original Field
|
|
-------------------------------
|
|
|
|
.. versionchanged:: 0.5
|
|
.. versionchanged:: 0.12
|
|
|
|
As defined by the :ref:`rules`, accessing the original field is guaranteed to
|
|
work on the associated translation field of the current language. This applies
|
|
to both, read and write operations.
|
|
|
|
The actual field value (which *can* still be accessed through
|
|
``instance.__dict__['original_field_name']``) however has to be considered
|
|
**undetermined** once the field has been registered for translation.
|
|
Attempts to keep the value in sync with either the default or current
|
|
language's field value has raised a boatload of unpredictable side effects in
|
|
older versions of modeltranslation.
|
|
|
|
Since version 0.12 the original field is expected to have even more undetermined value.
|
|
It's because Django 1.10 changed the way deferred fields work.
|
|
|
|
.. warning::
|
|
Do not rely on the underlying value of the *original field* in any way!
|
|
|
|
.. todo::
|
|
Perhaps outline effects this might have on the ``update_translation_field``
|
|
management command.
|
|
|
|
|
|
.. _set_language: https://docs.djangoproject.com/en/dev/topics/i18n/translation/#set-language-redirect-view
|