Registering Models for Translation¶
Modeltranslation can translate model fields of any model class. For each model
to translate a translation option class containing the fields to translate is
registered with a special object called the translator
.
Registering models and their fields for translation requires the following steps:
- Create a
translation.py
in your app directory. - Create a translation option class for every model to translate.
- Register the model and the translation option class at the
modeltranslation.translator.translator
.
The modeltranslation application reads the translation.py
file in your
app directory thereby triggering the registration of the translation
options found in the file.
A translation option is a class that declares which fields of a model to
translate. The class must derive from
modeltranslation.translator.TranslationOptions
and it must provide a
fields
attribute storing the list of fieldnames. The option class must be
registered with the modeltranslation.translator.translator
instance.
To illustrate this let’s have a look at a simple example using a News
model. The news in this example only contains a title
and a text
field.
Instead of a news, this could be any Django model class:
class News(models.Model):
title = models.CharField(max_length=255)
text = models.TextField()
In order to tell the modeltranslation app to translate the title
and
text
field, create a translation.py
file in your news app directory and
add the following:
from modeltranslation.translator import translator, TranslationOptions
from news.models import News
class NewsTranslationOptions(TranslationOptions):
fields = ('title', 'text',)
translator.register(News, NewsTranslationOptions)
Note that this does not require to change the News
model in any way, it’s
only imported. The NewsTranslationOptions
derives from
TranslationOptions
and provides the fields
attribute. Finally the model
and its translation options are registered at the translator
object.
At this point you are mostly done and the model classes registered for translation will have been added some auto-magical fields. The next section explains how things are working under the hood.
TranslationOptions
fields inheritance¶
New in version 0.5.
A subclass of any TranslationOptions
will inherit fields from its bases
(similar to the way Django models inherit fields, but with a different syntax).
from modeltranslation.translator import translator, TranslationOptions
from news.models import News, NewsWithImage
class NewsTranslationOptions(TranslationOptions):
fields = ('title', 'text',)
class NewsWithImageTranslationOptions(NewsTranslationOptions):
fields = ('image',)
translator.register(News, NewsTranslationOptions)
translator.register(NewsWithImage, NewsWithImageTranslationOptions)
The above example adds the fields title
and text
from the
NewsTranslationOptions
class to NewsWithImageTranslationOptions
, or to
say it in code:
assert NewsWithImageTranslationOptions.fields == ('title', 'text', 'image')
Of course multiple inheritance and inheritance chains (A > B > C) also work as expected.
Note
When upgrading from a previous modeltranslation version, please
review your TranslationOptions
classes and see if introducing fields
inheritance broke the project (if you had always subclassed
TranslationOptions
only, there is no risk).
Changes Automatically Applied to the Model Class¶
After registering the News
model for translation a SQL dump of the news
app will look like this:
$ ./manage.py sqlall news
BEGIN;
CREATE TABLE `news_news` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`title` varchar(255) NOT NULL,
`title_de` varchar(255) NULL,
`title_en` varchar(255) NULL,
`text` longtext NULL,
`text_de` longtext NULL,
`text_en` longtext NULL,
)
;
CREATE INDEX `news_news_page_id` ON `news_news` (`page_id`);
COMMIT;
Note the title_de
, title_en
, text_de
and text_en
fields which
are not declared in the original News
model class but rather have been
added by the modeltranslation app. These are called translation fields. There
will be one for every language in your project’s settings.py
.
The name of these additional fields is build using the original name of the
translated field and appending one of the language identifiers found in the
settings.LANGUAGES
.
As these fields are added to the registered model class as fully valid Django model fields, they will appear in the db schema for the model although it has not been specified on the model explicitly.
Committing fields to database¶
If you are starting a fresh project and have considered your translation needs
in the beginning then simply sync your database (./manage.py syncdb
or
./manage.py schemamigration myapp --initial
if using South)
and you are ready to use the translated models.
In case you are translating an existing project and your models have already been synced to the database you will need to alter the tables in your database and add these additional translation fields. If you are using South, you’re done: simply create a new migration (South will detect newly added translation fields) and apply it. If not, you can use a little helper: The sync_translation_fields Command which can execute schema-ALTERing SQL to add new fields. Use either of these two solutions, not both.
If you are adding translation fields to third-party app that is using South,
things get more complicated. In order to be able to update the app in future,
and to feel comfortable, you should use the sync_translation_fields
command.
Although it’s possible to introduce new fields in a migration, it’s nasty and
involves copying migration files, using SOUTH_MIGRATION_MODULES
setting,
and passing --delete-ghost-migrations
flag, so we don’t recommend it.
Invoking sync_translation_fields
is plain easier.
Note that all added fields are
declared blank=True
and null=True
no matter if the original field is
required or not. In other words - all translations are optional. To populate
the default translation fields added by the modeltranslation application
with values from existing database fields, you
can use the update_translation_fields
command below. See
The update_translation_fields Command for more info on this.
TranslationOptions
attributes reference¶
Quick cheatsheet with links to proper docs sections and examples showing expected syntax.
Classes inheriting from TranslationOptions
can have following attributes defined:
-
TranslationOptions.
fields
(required)¶ List of translatable model fields. See Registering Models for Translation.
Some fields can be implicitly added through inheritance, see TranslationOptions fields inheritance.
-
TranslationOptions.
fallback_languages
¶ Control order of languages for fallback purposes. See Fallback languages.
fallback_languages = {'default': ('en', 'de', 'fr'), 'uk': ('ru',)}
-
TranslationOptions.
fallback_values
¶ Set the value that should be used if no fallback language yielded a value. See Fallback values.
fallback_values = _('-- sorry, no translation provided --') fallback_values = {'title': _('Object not translated'), 'text': '---'}
-
TranslationOptions.
fallback_undefined
¶ Set what value should be considered “no value”. See Fallback undefined.
fallback_undefined = None fallback_undefined = {'title': 'no title', 'text': None}
-
TranslationOptions.
empty_values
¶ Override the value that should be saved in forms on empty fields. See Formfields and nullability.
empty_values = '' empty_values = {'title': '', 'slug': None, 'desc': 'both'}
Supported Fields Matrix¶
While the main purpose of modeltranslation is to translate text-like fields, translating other fields can be useful in several situations. The table lists all model fields available in Django and gives an overview about their current support status:
Model Field | 0.4 | 0.5 | 0.7 |
---|---|---|---|
AutoField |
No | No | No |
BigIntegerField |
No | Yes* | Yes* |
BooleanField |
No | Yes | Yes |
CharField |
Yes | Yes | Yes |
CommaSeparatedIntegerField |
No | Yes | Yes |
DateField |
No | Yes | Yes |
DateTimeField |
No | Yes | Yes |
DecimalField |
No | Yes | Yes |
EmailField |
Yes* | Yes* | Yes* |
FileField |
Yes | Yes | Yes |
FilePathField |
Yes* | Yes* | Yes* |
FloatField |
No | Yes | Yes |
ImageField |
Yes | Yes | Yes |
IntegerField |
No | Yes | Yes |
IPAddressField |
No | Yes | Yes |
GenericIPAddressField |
No | Yes | Yes |
NullBooleanField |
No | Yes | Yes |
PositiveIntegerField |
No | Yes* | Yes* |
PositiveSmallIntegerField |
No | Yes* | Yes* |
SlugField |
Yes* | Yes* | Yes* |
SmallIntegerField |
No | Yes* | Yes* |
TextField |
Yes | Yes | Yes |
TimeField |
No | Yes | Yes |
URLField |
Yes* | Yes* | Yes* |
ForeignKey |
No | No | Yes |
OneToOneField |
No | No | Yes |
ManyToManyField |
No | No | No |
* Implicitly supported (as subclass of a supported field)