|
InstallationAndUsage
Release: development version ModeltranslationThe modeltranslation application can be used to translate dynamic content of existing models to an arbitrary number of languages without having to change the original model classes. It uses a registration approach (comparable to Django's admin app) to be able to add translations to existing or new projects and is fully integrated into the Django admin backend. The advantage of a registration approach is the ability to add translations to models on a per-project basis. You can use the same app in different projects, may they use translations or not, and you never have to touch the original model class. Authors
Contents
Features
InstallationTo install the application please follow these steps. Each step is described in detail in the following sections:
Configure the project's settings.pyThe following variables have to be added to or edited in the project's settings.py: settings.INSTALLED_APPS Make sure that the modeltranslation app is listed in your INSTALLED_APPS variable: INSTALLED_APPS = (
...
'modeltranslation',
....
)Also make sure that the app can be found on a path contained in your PYTHONPATH environment variable. settings.LANGUAGES The LANGUAGES variable must contain all languages used for translation. The first language is treated as the default language. The modeltranslation application uses the list of languages to add localized fields to the models registered for translation. To use the languages de and en in your project, set the settings.LANGUAGES variable like this (where de is the default language): gettext = lambda s: s
LANGUAGES = (
('de', gettext('German')),
('en', gettext('English')),
)Note that the gettext lambda function is not a feature of the modeltranslation app, but rather required for Django to be able to (statically) translate the verbose names of the languages using the standard i18n solution. settings.MODELTRANSLATION_DEFAULT_LANGUAGE New in 0.3 To override the default language as described in settings.LANGUAGES, define MODELTRANSLATION_DEFAULT_LANGUAGE. Note that the value has to be in settings.LANGUAGES, otherwise an exception will be raised. settings.MODELTRANSLATION_TRANSLATION_REGISTRY In order to be able to import the project's translation.py registration file the MODELTRANSLATION_TRANSLATION_REGISTRY must be set to a value in the form <PROJECT_MODULE>.translation. E.g. if your project is located in a folder named myproject the MODELTRANSLATION_TRANSLATION_REGISTRY must be set like this: MODELTRANSLATION_TRANSLATION_REGISTRY = "myproject.translation" settings.MODELTRANSLATION_CUSTOM_FIELDS New in 0.3 Modeltranslation officially supports CharField and TextField. In most cases subclasses of these fields will work fine, too. Other fields aren't supported and will throw an ImproperlyConfigured exception. The list of supported fields can be extended. Just define a tuple of field names in your settings.py like this: MODELTRANSLATION_CUSTOM_FIELDS = ('MyField', 'MyOtherField',)Note: This just prevents modeltranslation from throwing an ImproperlyConfigured exception. Any non text-like field will most likely fail in one way or another. The feature is considered experimental and might be replaced by a more sophisticated mechanism in future versions. Registering models and their fields for translationThe modeltranslation app can translate CharField and TextField based fields of any model class. For each model to translate a translation option class containg the fields to translate is registered with the modeltranslation app. Registering models and their fields for translation requires the following steps:
The modeltranslation application reads the translation.py file in your project 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.ModelTranslation and it must provide a fields attribute storing the list of fieldnames. The option class must be registered with the modeltranslation.translator.translator instance. Note: In contrast to the Django admin application which looks for admin.py files in the project and application directories, the modeltranslation app looks only for one translation.py file in the project directory. 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 project directory and add the following: from modeltranslation.translator import translator, TranslationOptions
from some.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 it's 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. Changes automatically applied to the model classAfter registering the News model for transaltion an 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,
)
;
ALTER TABLE `news_news` ADD CONSTRAINT page_id_refs_id_3edd1f0d FOREIGN KEY (`page_id`) REFERENCES `page_page` (`id`);
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. If you are starting a fresh project and have considered your translation needs in the beginning then simply sync your database 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. Note that all added fields are declared null=True not matter if the original field is required. In other words - all translations are optional. To populate the default translation fields added by the modeltranslation application you can use the update_translation_fields command below. See the The update_translation_fields command section for more infos on this. Accessing translated and translation fieldsThe modeltranslation app changes the behaviour of the translated fields. To explain this consider the News example 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 the modeltranslation app 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 fieldThe 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 for translated field accessSo 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 also updates the value in the associated default translation field. Rule 3 Assigning a value to the default translation field also updates the original field - note that the value of the original field will not be updated until the model instance is saved. Rule 4 If both fields - the original and the default translation field - are updated at the same time, the default translation field wins. Examples for translated field accessBecause 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. Django admin backend integrationIn order to be able to edit the translations via the admin backend you need to register a special admin class for the translated models. The admin class must derive from modeltranslation.admin.TranslationAdmin which does some funky patching on all your models registered for translation: from django.contrib import admin
from modeltranslation.admin import TranslationAdmin
class NewsAdmin(TranslationAdmin):
list_display = ('title',)
admin.site.register(News, NewsAdmin)Tweaks applied to the adminThe TranslationAdmin class does only implement one special method which is def formfield_for_dbfield(self, db_field, **kwargs). This method does the following:
TranslationAdmin in combination with other admin classesIf there already exists a custom admin class for a translated model and you don't want or can't edit that class directly there is another solution. Taken the News example let's say there is a NewsAdmin class defined by the News app itself. This app is not yours or you don't want to touch it at all. In the most common case you simply make use of Python's support for multiple inheritance like this: class MyTranslatedNewsAdmin(NewsAdmin, TranslationAdmin):
passIn a more complex setup the NewsAdmin might define its own class: class NewsAdmin(model.Admin):
def formfield_for_dbfield(self, db_field, **kwargs):
# does some funky stuff with the formfield hereUnfortunately the first example won't work anymore because Python can only execute one of the formfield_for_dbfield methods. Since both admin class implement this method Python must make a decision and it chooses the first class NewsAdmin. The functionality from TranslationAdmin will not be executed and translation in the admin will not work for this class. But don't panic, here's a solution: class MyTranslatedNewsAdmin(NewsAdmin, TranslationAdmin):
def formfield_for_dbfield(self, db_field, **kwargs):
field = super(MyTranslatedNewsAdmin, self).formfield_for_dbfield(db_field, **kwargs)
self.patch_translation_field(db_field, field, **kwargs)
return fieldThis implements the formfield_for_dbfield such that both functionalities will be executed. The first line calls the superclass method which in this case will be the one of NewsAdmin because it is the first class inherited from. The TranslationAdmin capsulates all it's functionality in the patch_translation_field(db_field, field, **kwargs) method and the formfield_for_dbfield implementation of the TranslationAdmin class simply calls it. You can copy this behaviour by calling it from a custom admin class and that's done in the example above. After that the field is fully patched for translation and finally returned. InlinesNew in 0.2 Support for tabular and stacked inlines, common and generic ones. A translated inline must derive from one of the following classes:
Just like TranslationAdmin these classes implement a special method def formfield_for_dbfield(self, db_field, **kwargs) which does all the patching. For our example we assume that there is new model called Image. It's definition is left out for simplicity. Our News model inlines the new model: from django.contrib import admin
from modeltranslation.admin import TranslationTabularInline
class ImageInline(TranslationTabularInline):
model = Image
class NewsAdmin(admin.ModelAdmin):
list_display = ('title',)
inlines = [ImageInline,]
admin.site.register(News, NewsAdmin)Note: In this example only the Image model is registered in translation.py. It's not a requirement that NewsAdmin derives from TranslationAdmin in order to inline a model which is registered for translation. In this more complex example we assume that the News and Image models are registered in translation.py. The News model has an own custom admin class and the Image model an own generic stacked inline class. It uses the technique described in TranslationAdmin in combination with other admin classes.: from django.contrib import admin
from modeltranslation.admin import TranslationAdmin, TranslationGenericStackedInline
class TranslatedImageInline(ImageInline, TranslationGenericStackedInline):
model = Image
class TranslatedNewsAdmin(NewsAdmin, TranslationAdmin):
def formfield_for_dbfield(self, db_field, **kwargs):
field = super(TranslatedNewsAdmin, self).formfield_for_dbfield(db_field, **kwargs)
self.patch_translation_field(db_field, field, **kwargs)
return field
inlines = [TranslatedImageInline,]
admin.site.register(News, NewsAdmin)Using tabbed translation fieldsNew in 0.3 Modeltranslation supports separation of translation fields via jquery-ui tabs. The proposed way to include it is through the inner Media class of a TranslationAdmin class like this: class NewsAdmin(TranslationAdmin):
class Media:
js = (
'/static/modeltranslation/js/force_jquery.js',
'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js',
'/static/modeltranslation/js/tabbed_translation_fields.js',
)
css = {
'screen': ('/static/modeltranslation/css/tabbed_translation_fields.css',),
}The force_jquery.js script is necessary when using Django's built-in django.jQuery object. This and the static urls used are just an example and might have to be adopted to your setup of serving static files. Standard jquery-ui theming can be used to customize the look of tabs, the provided css file is supposed to work well with a default Django admin. The update_translation_fields commandIn case the modeltranslation app was installed on an existing project and you have specified to translate fields of models which are already synced to the database, you have to update your database schema manually. Unfortunately the newly added translation fields on the model will be empty then, and your templates will show the translated value of the fields (see Rule 1 below) which will be empty in this case. To correctly initialize the default translation field you can use the update_translation_fields command: manage.py update_translation_fields Taken the News example from above this command will copy the value from the news object's title field to the default translation field title_de. It only does so if the default translation field is empty otherwise nothing is copied. Note: The command will examine your settings.LANGUAGES variable and the first language declared there will be used as the default language. All translated models (as specified in the project's translation.py will be populated with initial data. CaveatsConsider the following example (assuming the default lanuage is de): >>> n = News.objects.create(title="foo") >>> n.title 'foo' >>> n.title_de >>> Because the original field title was specified in the constructor it is directly passed into the instance's __dict__ and the descriptor which normally updates the associated default translation field (title_de) is not called. Therefor the call to n.title_de returns an empty value. Now assign the title, which triggers the descriptor and the default translation field is updated: >>> n.title = 'foo' >>> n.title_de 'foo' >>> Accessing translated fields outside viewsSince the modeltranslation mechanism relies on the current language as it is returned by the get_language function care must be taken when accessing translated fields outside a view function. Within a view function the language is set by Django based on a flexible model described at How Django discovers language preference which is normally used only by Django's static translation system. When a translated field is accessed in a view function or in a template, it uses the django.utils.translation.get_language function to determine the current language and return the appropriate value. Outside a view (or a template), i.e. in normal Python code, a call to the get_language function still returns a value, but it might not what you expect. Since no request is involved, Django's machinery for discovering the user's preferred language is not activated. todo: explain more The unittests in tests.py use the django.utils.translation.trans_real functions to activate and deactive a specific language outside a view function. Related projectsdjango-multilingualA library providing support for multilingual content in Django models. It is not possible to reuse existing models without modifying them. django-multilingual-modelA much simpler version of the above django-multilingual. It works very similiar to the django-multilingual approach. transdbDjango's field that stores labels in more than one language in database. This approach uses a specialized Field class, which means one has to change existing models. i18ndynamicThis approach is not developed any more. django-pluggable-model-i18nThis app utilizes a new approach to multilingual models based on the same concept the new admin interface uses. A translation for an existing model can be added by registering a translation class for that model. This is more or less what modeltranslation does, unfortunately it is far from being finished. |
Are you planning to add support for inherited models soon? What are you other feature plans?
In fact I am already using it with inherited models and it works fine. Do you have an example/use case where it does not work with inherited models?
I do not have plans for more features at the moment but of course I'm open to suggestions :)
Maybe I really missed something:
Why this app needs to duplicate default language translations in the original column?
Is this not a waste of DB space?
Additionally, I think the backend users will be confused to see the same content twice (for default language and original field) in the edit form.
@peschler I think wawrzyniak might have meant support for adding TranslationOptions? for the abstract base class only, instead of adding all classes that inherit from it.
Personally I would die for that feature :)
Putting a url like django-modeltranslation 0.2 must allow automatic instalation as dependency without pypi registration.
You could install adding this lines in your setup.py:
setup(
) Pypi registration is good too.Note for my last comment.
This works, but only if you use setuptools or distribute.
I have tested the example code and it works. But (...) is a elipsis, nothing to put before dependency_links. Sorry for the formating (and for my bad english).
Hi
thanks for great module.
i would ask how to solve situation if for example: active language is fr, but title_fr is empty (not filled) so i want to get default language text for expample title_en.
in simple, if translation isn't filled, how to get default lang value?
thank for ideas
note to my previous post:
for column named title it works :o) (active lang is fr, but if title_fr is empty im getting title_en value) but i have other field, named content (content = models.TextField?(('content'))) and if content_fr is empty i dont get content_en value.
any hints?
:o) solved... wysiwig set <br> instead of empty text ...
thanks again for module
Hi,
It should be admin.site.register(News, TranslatedNewsAdmin?) insteat admin.site.register(News, NewsAdmin?) in example http://code.google.com/p/django-modeltranslation/wiki/InstallationAndUsage#translationadmin-in-combination-with-other-admin-classes, is it?