django-modeltrans - Translate model fields using a JSONField
¶
- Uses one
django.contrib.postgres.JSONField
(PostgreSQL JSONB field) for every record. - Django 1.11 and 2.0 (with their supported python versions)
- PostgreSQL >= 9.5 and Psycopg2 >= 2.5.4.
About the app:
Table of contents¶
Getting started¶
Add
'modeltrans'
your list ofINSTALLED_APPS
.Make sure the current data in your models is in the language defined in the
LANGUAGE_CODE
django setting.By default, django-modeltrans uses the languages in the
LANGUAGES
django setting. If you want the list to be different, add a list of available languages to yoursettings.py
:MODELTRANS_AVAILABLE_LANGUAGES = ('en', 'nl', 'de', 'fr')
.Add a
modeltrans.fields.TranslationField
to your models and specify the fields you want to translate:# models.py from django.db import models from modeltrans.fields import TranslationField class Blog(models.Model): title = models.CharField(max_length=255) body = models.TextField(null=True) i18n = TranslationField(fields=("title", "body"))Run
./manage.py makemigrations
to add thei18n
JSONField to each model containing translations.Each Model now has some extra virtual fields. In the example above:
title
allows getting/setting the default languagetitle_nl
,title_de
, … allow getting/setting the specific languages- If
LANGUAGE_CODE == 'en'
,title_en
is mapped totitle
.title_i18n
follows the currently active translation in Django, and falls back to the default language
The above could be used in a Django shell like this:
>>> b = Blog.objects.create(title="Falcon", title_nl="Valk")
>>> b.title
'Falcon'
>>> b.title_nl
'Valk'
>>> b.title_i18n
'Falcon'
>>> from django.utils.translation import override
>>> with override('nl'):
... b.title_i18n
...
'Valk'
# translations are stored in the field ``i18n`` in each model:
>>> b.i18n
{'title_nl': 'Valk'}
# if a translation is not available, None is returned.
>>> print(b.title_de)
None
# fallback to the default language
>>> with override("de"):
... b.title_i18n
'Falcon'
# now, if we set the German translation, it it is returned from ``title_i18n``:
>>> b.title_de = 'Falk'
>>> with override('de'):
... b.title_i18n
'Falk'
Forms¶
TranslationModelForm
is an adaptation of Django’s django.forms.ModelForm
that allows management of translation fields.
Assuming your model is translated with modeltrans,
you can use TranslationModelForm
to specify which languages to include form fields for.
For example, given a NewsRoom
model:
class NewsRoom(models.Model):
name = models.CharField(max_length=255)
text = models.CharField(max_length=255)
default_language = models.CharField(max_length=2)
i18n = TranslationField(fields=("name", "text"), fallback_language_field="default_language")
You can define a form using TranslationModelForm
as:
from modeltrans.forms import TranslationModelForm
class NewsRoomTranslationForm(TranslationModelForm):
class Meta:
fields = ("name", "text")
languages = ["browser", "fr", "fallback"]
fallback_language = "en"
This defines a form with at most three language inputs per field, say "nl"
, "fr"
and "en"
,
where "nl"
is the active browser language, and "en"
the defined fallback language.
Meta.exclude
can also be used to define which fields are in the form,
where the forms’ field_order
parameter can be used to define the field ordering.
Setting the form languages¶
languages
defines the languages included in the form.- Options are:
"browser"
: the language that is active in the browser session"fallback"
: the fallback language either defined in the form, the model instance, or in the system, in that order of priority- a language code: e.g.
"fr"
,"it"
- Default:
["browser", "fallback"]
- Ordering: the ordering defined in the declaration is preserved
- Duplicate languages are removed, e.g.
["browser", "fr", "fallback"]
, becomes["fr"]
if browser language and fallback are also"fr"
.
languages
can be defined in the form Meta
options as in the example above, or as a form kwarg as in:
form = NewsRoomTranslationForm(languages=["it", "fallback"])
Setting the fallback language¶
fallback_language
defines the fallback language in the form.
Requires "fallback"
to be included in languages
.
Can be defined via the form Meta
options as in the example above, and also be passed as a kwarg like languages
.
The following prioritization is followed:
fallback_language
passed as form parameter:Form(fallback_language="fr")
- the
Meta
optionfallback_language
:- e.g.
class Meta: fallback_language = "fr"
- A custom fallback of a model instance set via
fallback_language_field
:- e.g.
i18n = TranslationField(fields=("title", "header"), fallback_language_field="language_code")
- The default language of the system: If no
Meta
option is given fallback reverts toget_default_language()
Handling of field properties¶
Properties of translation form fields are inherited from the form field that is generated for the original model field. The label of the field is adjusted to include the relevant language and to designate the field as a translation or default fallback field, as follows:
- translation fields: “field name (NL, translation language)”
- fallback field: “field name (EN, default language)”
Admin¶
By default, each field is displayed for each language configured for django-modeltrans. This might work for a couple of languages, but with 2 translated fields and 10 languages, it already is a bit unwieldy.
The ActiveLanguageMixin
is provided to show only the default language (settings.LANGUAGE_CODE
) and
the currently active language. Use like this:
from django.contrib import admin
from modeltrans.admin import ActiveLanguageMixin
from .models import Blog
@admin.register(Blog)
class BlogAdmin(ActiveLanguageMixin, admin.ModelAdmin):
pass
Advanced usage¶
Custom fallback language¶
By default, fallback is centrally configured with MODELTRANS_FALLBACK.
That might not be sufficient, for example if part of the content is created for a single language which is not LANGUAGE_CODE
.
In that case, it can be configured per-record using the fallback_language_field
argument to TranslationField
:
class NewsRoom(models.Model):
name = models.CharField(max_length=255)
default_language = models.CharField(max_length=2)
i18n = TranslationField(fields=("name",), fallback_language_field="default_language")
You can traverse foreign key relations too:
class Article(models.Model):
content = models.CharField(max_length=255)
newsroom = models.ForeignKey(NewsRoom)
i18n = TranslationField(fields=("content",), fallback_language_field="newsroom__default_language")
- Note that
- if in this example no
newsroom
is set yet, the centrally configured fallback is used. - the original field _always_ contains the language as configured by
LANGUAGE_CODE
.
- if in this example no
With the models above:
nos = NewsRoom.objects.create(name="NOS (en)", default_language="nl", name_nl="NOS (nl)")
article = Article.objects.create(
newsroom=nos,
content="US-European ocean monitoring satellite launches into orbit",
content_nl="VS-Europeese oceaanbewakingssatelliet gelanceerd"
)
with override('de'):
# If language 'de' is not available, the records default_language will be used.
print(nos.name) # 'NOS (nl)'
# If language 'de' is not available, the newsroom.default_language will be used.
print(article.content) # 'VS-Europeese oceaanbewakingssatelliet gelanceerd'
Inheritance of models with translated fields.¶
When working with model inheritance, you might want to have different parameters to the i18n
-field for the
parent and the child model. These parameters can be overridden using the i18n_field_params
attribute and
on the child class:
from django.db import models
from modeltrans.fields import TranslationField
class ParentModel(models.Model):
info = models.CharField(max_length=255)
i18n = TranslationField(fields=("info",), required_languages=("en",))
class ChildModel(ParentModel):
child_info = models.CharField(max_length=255)
i18n_field_params = {
"fields": ("info", "child_info"),
"required_languages": ("nl",)
}
Database performance¶
Adding GIN indexes¶
In order to perform well while filtering or ordering on translated values,
the i18n
-field need a GIN index. Due to limitations in the way Django currently
allows to define indexes, they should be added manually:
from django.contrib.postgres.indexes import GinIndex
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=255)
i18n = TranslationField(fields=("name",))
class Meta:
indexes = [GinIndex(fields=["i18n"]), ]
Inner workings¶
Django-modeltrans uses a django.contrib.postgres.JSONField
to store field
translations in the table and adds some augmentation to the queries made by
Django’s QuerySet methods to allow transparent use of the translated values.
The inner workings are illustrated using this model:
class Blog(models.Model):
title = models.CharField(max_length=255)
body = models.TextField(null=True)
i18n = TranslationField(fields=("title", "body"))
When creating an object, translated fields in the constructor are transformed into a value in the i18n field. So the following two calls are equivalent:
Blog.objects.create(title="Falcon", title_nl="Valk", title_de="Falk")
Blog.objects.create(title="Falcon", i18n={"title_nl": "Valk", "title_de": "Falk"})
So adding a translated field does not need any migrations: it just requires
adding a key to the i18n
field.
When selecting objects django-modeltrans replaces any occurrence of a translated field with the appropriate JSONB key get operation:
Blog.objects.filter(title_nl="Valk")
# SELECT ... FROM "app_blog" WHERE (app_blog.i18n->>'title_nl')::varchar(255) = 'Valk'
Blog.objects.filter(title_nl__contains="a")
# SELECT ... FROM "app_blog" WHERE (app_blog.i18n->>'title_nl')::varchar(255) LIKE '%a%'
In addition to that, you can use <fieldname>_i18n
to filter on. That will use
COALESCE
to look in both the currently active language and the default
language:
from django.utils.translation import override
with override("nl"):
Blog.objects.filter(title_i18n="Valk")
# SELECT ... FROM "app_blog"
# WHERE COALESCE((app_blog.i18n->>'title_nl'), "app_blog"."title") = 'Valk'
Model objects containing translated fields get virtual fields for each field/
language combination plus a field which always returns the active language.
In the example, we have configured 3 translation languages: ('nl', 'de', 'fr')
resulting in 4 virtual fields for each original field:
b = Blog.objects.create(title='Falcon', title_nl='Valk', title_de='Falk')
b._meta.get_fields()
(<django.db.models.fields.AutoField: id>,
<django.db.models.fields.CharField: title>,
<django.db.models.fields.TextField: body>,
<django.db.models.fields.related.ForeignKey: category>,
<modeltrans.fields.TranslationField: i18n>,
<modeltrans.fields.TranslatedCharField: title_i18n>,
<modeltrans.fields.TranslatedCharField: title_en>,
<modeltrans.fields.TranslatedCharField: title_nl>,
<modeltrans.fields.TranslatedCharField: title_de>,
<modeltrans.fields.TranslatedCharField: title_fr>,
<modeltrans.fields.TranslatedTextField: body_i18n>,
<modeltrans.fields.TranslatedTextField: body_en>,
<modeltrans.fields.TranslatedTextField: body_nl>,
<modeltrans.fields.TranslatedTextField: body_de>,
<modeltrans.fields.TranslatedTextField: body_fr>)
Each virtual field for an explicit language will only return a value if that language is defined:
print(b.title_nl, b.title_fr)
# 'Valk', None
The virtual field <field>_i18n
returns the translated value for the current
active language and falls back to the language in LANGUAGE_CODE
:
with override("nl"):
print(b.title_i18n)
# 'Valk'
with override("de"):
print(b.title_i18n)
# 'Falk'
with override("fr"):
print(b.title_i18n)
# 'Falcon' (no french translation available, falls back to LANGUAGE_CODE)
Django-modeltrans also allows ordering on translated values. Ordering on
<field>_i18n
probably makes most sense, as it more likely that there is a
value to order by:
with override("de"):
qs = Blog.objects.order_by("title_i18n")
# SELECT ...,
# FROM "app_blog"
# ORDER BY COALESCE((app_blog.i18n->>'title_de'), "app_blog"."title") ASC
Results in the following ordering:
title_i18n title_en title_nl title_de
------------ ------------ ------------ ------------
Crayfish Crayfish
Delfine Dolphin Dolfijn Delfine
Dragonfly Dragonfly Libellen
Duck Duck Eend
Falk Falcon Valk Falk
Frog Frog Kikker
Kabeljau Cod Kabeljau
Toad Toad Pad
As you can see, although the german translations are not complete, ordering on
title_i18n
still results in a useful ordering.
Note
These examples assume the default setting for MODELTRANS_FALLBACK
.
If you customize that setting, it can get slightly more complex, resulting
in more than 2 arguments to the COALESCE
function.
Known issues¶
We use django-modeltrans in production, but some aspects of it’s API might be a bit surprising. This page lists the issue we are aware of. Some might get fixed at some point, some are just the result of database or Django implementations.
Reading the explanation of the Inner workings might also help to understand some of these issues.
Unsupported QuerySet methods¶
Using translated fields in QuerySet
/Manager
methods
.distinct()
, .extra()
, .aggregate()
, .update()
is not supported.
Fields supported¶
Behavior is tested using CharField()
and TextField()
, as these make most sense for translated values.
Additional fields could make sense, and will likely work, but need extra test coverage.
Ordering defined in Model.Meta.ordering
¶
Any ordering using translated fields defined in Model.Meta.ordering
is only supported with
Django 2.0 and later (django/django#8473 is required).
Context of ‘current language’¶
Lookups (<field>_i18n
) are translated when the line they are defined on is executed:
class Foo():
qs = Blog.objects.filter(title_i18n__contains="foo")
def get_blogs(self):
return self.qs
When Foo.get_blogs()
will be called in the request cycle, one might expect the current language
for that request to define the title_i18n__contains
filter.
But instead, the language active while creating the class Foo
will be used.
For example the queryset
argument to ModelChoiceField()
.
See github issue #34
Management commands¶
The packages adds a management command to create relevant migrations
not automatically created by the ./manage.py makemigrations
command.
Data migration to migrate from django-modeltranslation¶
Syntax: ./manage.py i18n_makemigrations <apps>
Only to migrate data from the fields managed by django-modeltranslation to the JSON field managed by django-modeltrans.
Explained in more detail in Migrating from django-modeltranslation
API Reference¶
Public API¶
modeltrans.admin
¶
modeltrans.fields
¶
-
class
modeltrans.fields.
TranslatedVirtualField
(original_field, language=None, *args, **kwargs)[source]¶ A field representing a single field translated to a specific language.
Parameters: - original_field – The original field to be translated
- language – The language to translate to, or
None
to track the current active Django language.
-
class
modeltrans.fields.
TranslationField
(fields=None, required_languages=None, virtual_fields=True, fallback_language_field=None, *args, **kwargs)[source]¶ This model field is used to store the translations in the translated model.
Parameters: - fields (iterable) – List of model field names to make translatable.
- required_languages (iterable or dict) – List of languages required for the model. If a dict is supplied, the keys must be translated field names with the value containing a list of required languages for that specific field.
- virtual_fields (bool) – If
False
, do not add virtual fields to access translated values with. Set toTrue
during migration from django-modeltranslation to prevent collisions with it’s database fields while having thei18n
field available. - fallback_language_field – If not None, this should be the name of the field containing a language code to use as the first language in any fallback chain. For example: if you have a model instance with ‘nl’ as language_code, and set fallback_language_field=’language_code’, ‘nl’ will always be tried after the current language before any other language.
modeltrans.manager
¶
-
class
modeltrans.manager.
MultilingualManager
[source]¶ When adding the
modeltrans.fields.TranslationField
to a model, MultilingualManager is automatically mixed in to the manager class of that model.If you want to use translated fields when building the query from a related model, you need to add
objects = MultilingualManager()
to the model you want to build the query from.For example,
Category
needsobjects = MultilingualManager()
in order to allowCategory.objects.filter(blog__title_i18n__icontains="django")
:class Category(models.Model): title = models.CharField(max_length=255) objects = MultilingualManager() # required to use translated fields of Blog. class Blog(models.Model): title = models.CharField(max_length=255) body = models.TextField(null=True) category = models.ForeignKey(Category, null=True, blank=True, on_delete=models.CASCADE) i18n = TranslationField(fields=("title", "body"))
-
class
modeltrans.manager.
MultilingualQuerySet
(model=None, query=None, using=None, hints=None)[source]¶ Extends
~django.db.models.query.QuerySet
and makes the translated versions of fields accessible through the normal QuerySet methods, analogous to the virtual fields added to a translated model:<field>
allow getting/setting the default language<field>_<lang>
(for example,<field>_de
) allows getting/setting a specific language. Note that ifLANGUAGE_CODE == "en"
,<field>_en
is mapped to<field>
.<field>_i18n
follows the currently active translation in Django, and falls back to the default language.
When adding the
modeltrans.fields.TranslationField
to a model, MultilingualManager is automatically mixed in to the manager class of that model.
Settings Reference¶
django-modeltrans allows some configuration to define its behavior. By default, it tries to use sensible defaults derived from the default django settings.
MODELTRANS_AVAILABLE_LANGUAGES
¶
A list of language codes to allow model fields to be translated in. By default, the language codes extracted from django’s LANGUAGES setting.
- Note that
- the default language, defined in django’s LANGUAGE_CODE setting, should not be added to this list (will be ignored).
- order is not important
A custom definition might be:
MODELTRANS_AVAILABLE_LANGUAGES = ('de', 'fr')
MODELTRANS_FALLBACK
¶
A dict of fallback chains as lists of languages. By default, it falls back to the language defined in django setting LANGUAGE_CODE
.
- For example, django-modeltrans will fall back to:
- english when the active language is ‘nl’
- fist dutch and finally english with active language is ‘fy’
If configured like this:
LANGUAGE_CODE = 'en'
MODELTRANS_AVAILABLE_LANGUAGES = ('nl', 'fy')
MODELTRANS_FALLBACK = {
'default': (LANGUAGE_CODE, ),
'fy': ('nl', 'en')
}
Note that a custom fallback language can be configured on a model instance if the i18n
field is configured like this:
class Model(models.Model):
title = models.CharField(max_length=100)
fallback_language = models.CharField(max_length=2)
i18n = TranslationField(fields=("title",), fallback_language_field="fallback_language")
in which fallback_language_field
refers to the model field that contains the language code.
This topic is explained in Custom fallback language.
MODELTRANS_ADD_FIELD_HELP_TEXT
¶
If True
, the <name>_i18n
fields with empty help_text``s will get a ``help_text
like:
current language: en
True
by default.
Migrating from django-modeltranslation¶
This is how to migrate from django-modeltranslation (version 0.12.1) to
django-modeltrans
:
Make sure you have a recent backup of your data available!
Add
modeltrans
to yourINSTALLED_APPS
Make sure the default language for django-modeltranslation is equal to the language in
LANGUAGE_CODE
, which django-modeltrans will use.Copy the setting
AVAILABLE_LANGUAGES
toMODELTRANS_AVAILABLE_LANGUAGES
.Add the TranslationField to the models you want to translate and keep the registrations for now. In order to prevent field name collisions, disable the virtual fields in django-modeltrans for now (
virtual_fields=False
):# models.py from django.contrib.postgres.indexes import GinIndex from django.db import models from modeltrans.fields import TranslationField class Blog(models.Model): title = models.CharField(max_length=255) body = models.TextField(null=True) # add this field, containing the TranslationOptions attributes as arguments: i18n = TranslationField(fields=('title', 'body'), virtual_fields=False) # add the GinIndex class Meta: indexes = [GinIndex(fields=["i18n"])] # translation.py from modeltranslation.translator import translator, TranslationOptions from .models import Blog class BlogTranslationOptions(TranslationOptions): fields = ('name', 'title', ) translator.register(Blog, BlogTranslationOptions)
Run
./manage.py makemigrations <apps>
. This will create the migration adding thei18n
-fields required by django-modeltrans. Apply them with./manage.py migrate
We need to create a migration to copy the values of the translated fields into the newly created
i18n
-field. django-modeltrans provides a management command to do that./manage.py i18n_makemigrations <apps>
.Run
./manage.py migrate
to apply the generated data migrations. Your models with translated fields should have a populatedi18n
field after these migrations.Now, to remove django-modeltranslation:
- Remove
modeltranslation
fromINSTALLED_APPS
. - Remove django-modeltranslation settings (
DEFAULT_LANGUAGE
,AVAILABLE_LANGUAGES
) from yoursettings.py
’s - Remove all
translation.py
files from your apps. - Remove the use of
modeltranslation.admin.TranslationAdmin
in youradmin.py
’s
- Remove
Run
./manage.py makemigrations <apps>
. This will create migrations that remove the fields generated by django-modeltranslation from your registered models.Run
./manage.py migrate
to actually apply the generated migrations. This will remove the django-modeltranslation fields and their content from your database.Update your code:
Remove
virtual_fields=False
from eachTranslationField
.Use the
<field>_i18n
field in places where you would use<field>
with django-modeltranslation. Less magic, but explicit is better than implicit!Use
<field>_<language>
for the translated fields, just like your are used to.If you use lookups containing translated fields from non-translated models, you should add
MultilingualManager()
to your models as a manager:from django.contrib.postgres.indexes import GinIndex from django.db import models from modeltrans.fields TranslationField from modeltrans.manager import MultilingualManager class Site(models.Model): title = models.CharField(max_length=100) # adding manager allows queries like Site.objects.filter(blog__title_i18n__contains='modeltrans') objects = MultilingualManager() class Blog(models.Model): title = models.CharField(max_length=100) body = models.TextField() i18n = TranslationField(fields=('title', 'body')) site = models.ForeignKey(Site) class Meta: indexes = [GinIndex(fields=["i18n"]]
django-modeltrans change log¶
0.7.5 (2023-08-03)¶
- Fix crash with
limit_choices_to
(#100) fixes: #94
0.7.4 (2023-04-03)¶
- Add support for Django 4.2
0.7.3 (2022-05-25)¶
- Revert: Use
TranslationModelForm
to implementActiveLanguageMixin
(#86)
0.7.2 (2021-10-21)¶
- Use
TranslationModelForm
to implementActiveLanguageMixin
(#80) - Add support for django 4.0 (#78)
0.7.1 (2021-07-21)¶
- Add translations for NL, DE and FR
- Remove translation strings in example and tests
0.7.0 (2021-06-30)¶
- Add
TranslationModelForm
0.6.0 (2021-04-07)¶
- Add support for Django==3.2
0.5.2 (and 0.5.1) (2021-01-12)¶
- Adjust imports to remove deprecation warnings with django==3.1.* #65
0.5.0 (2020-11-23)¶
- Add per-record fallback feature #63
0.4.0 (2019-11-22)¶
- Drop python 2, Django 1.11 and Django 2.0 support #56
- Add option for
i18n
model fields inheritance #51 - Fix patching managers of models inheriting from an abstract models #50
0.3.4 (2019-01-15)¶
- Fix exception on nullable i18n field #49
0.3.3 (2018-07-19)¶
- Add instruction to remove
virtual_fields=True
to migration guide, fixes #45 - Use
AppConfig
to compute path to app dir, fixes #46 - Do not copy empty fields into i18n field, fixes #47
0.3.2 (2018-07-18)¶
- Removed
encoding
kwarg toopen()
insetup.py
to fix python 2.7 install.
0.3.1 (2018-07-16)¶
- Added
long_description
tosetup.py
, no functional changes.
0.3.0 (2018-07-15)¶
- Adopted black code style.
- Removed auto-adding indexes, as it was unpredictable. You must add the
GinIndex
manually like described in the documentation on performance. - Support dict for
required_languages
argument toTranslationField
, to allow more fine-grained mapping of field names to required languages. ActiveLanguageMixin
does not use the<field>_i18n
version of the field, but rather the virtual field with the current active language. This makes sure no fallback values are accidentally saved for another language.
0.2.2 (2018-03-13)¶
- Hide original field with
ActiveLanguageMixin
. - Raise an
ValueError
on accessing translated fields on a model fetched with.defer('i18n')
. - do not accidentally add
i18n
to dict in Model.create - Improve handling of explicit PK's and expression rewriting code.
- Add help_text to virtual fields with language=None.
0.2.1 (2018-01-24)¶
- Dropped support for Django 1.9 and 1.10.
- Used
ugettext_lazy
rather thanugettext
to fix admin header translation #32 - Removed default value
False
forField.editable
, to allow using the translated version of a field in aModelForm
.
0.2.0 (2017-11-13)¶
- No annotations are made while ordering anymore, instead, expressions are passed onto the original
order_by()
method. - Any translated fields used in
Model.Meta.ordering
is transformed into the correct expression with django 2.0 and later (fixes #25). django.contrib.postgres.GinIndex
is added to thei18n
column if it's supported by the django version used (1.11 and later). It can be disabled with the settingMODELTRANS_CREATE_GIN
.- The migration generated from
./manage.py i18n_makemigrations <app>
used to move the data and add a GIN index. This is split into two commands:./manage.py i18n_makemigrations
and./manage.py i18n_make_indexes
. - Added support for `values(**expressions)`` with references to translated fields.
- Added support for translated values in
annotate()
0.1.2 (2017-10-23)¶
- Ensure a dynamic mixed
MultilingualQuerySet
can be pickled. - Add basic support for
Func
inorder_by()
0.1.1 (2017-10-23)¶
- Allow adding
MultilingualManager()
as a manager to objects without translations to allow lookups of translated content through those managers.
0.1.0 (2017-10-23)¶
- Use proper alias in subqueries, fixes #23.
- Support lookups on and ordering by related translated fields (
.filter(category__name_nl='Vogels')
), fixes #13. - Use
KeyTextTransform()
rather thanRawSQL()
to access keys in theJSONField
. For Django 1.9 and 1.10 the Django 1.11 version is used.
0.0.8 (2017-10-19)¶
- Check if
MODELTRANS_AVAILABLE_LANGUAGES
only contains strings. - Make sure
settings.LANGUAGE_CODE
is never returned fromconf.get_available_languages()
0.0.7 (2017-09-04)¶
- Cleaned up the settings used by django-modeltrans #19.
This might be a breaking change, depending on your configuration.
AVAILABLE_LANGUAGES
is now renamed toMODELTRANS_AVAILABLE_LANGUAGES
and defaults to the language codes in the djangoLANGUAGES
setting.DEFAULT_LANGUAGE
is removed, instead, django-modeltrans uses the djangoLANGUAGE_CODE
setting.
- Added per-language configurable fallback using the
MODELTRANS_FALLBACK
setting.
0.0.6 (2017-08-29)¶
- Also fall back to
DEFAULT_LANGUAGE
if the value for a key in the translations dict is falsy.
0.0.5 (2017-07-26)¶
- Removed registration in favor of adding the
TranslationField
to a model you need to translated. - Created documentation.
0.0.4 (2017-05-19)¶
- Improve robustness of rewriting lookups in QuerySets
0.0.3 (2017-05-18)¶
- Add the
gin
index in the data migration. - Added tests for the migration procedure.