Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
221 lines (173 sloc) 9.17 KB

Multi-Lingual Sites

Mezzanine provides optional support for django-modeltranslation which enables content editors to provide multi-lingual content to support sites in multiple languages.

Note

Mezzanine only provides the integration of django-modeltranslation. For dedicated assistance, please check out the documentation for django-modeltranslation: documentation or its code.

Translation Fields in Mezzanine

In order to enable translation fields for Mezzanine content, you will need to install django-modeltranslation and activate the app in your settings.py. Once you have installed django-modeltranslation, you can enable support for it by setting the USE_MODELTRANSLATION setting to True in your project's settings.py module, and also defining at least two entries in the :django:setting:`LANGUAGES` setting.

For new projects, manage.py createdb will take care of creating extra columns in the database for each language. For current or older projects, you can catch up by running manage.py sync_translation_fields and then manage.py update_translation_fields.

Note

A django-modeltranslation setting that can help managing the transition for content partially in several languages is MODELTRANSLATION_FALLBACK_LANGUAGES. This setting allows you to avoid having empty content when the translation is not provided for a specific language. Please consult django-modeltranslation's documentation for more detail.

Translation Fields for Custom Applications

For models that don't inherit from Mezzanine's models, again please consult django-modeltranslation's documentation. To start with, you'll need to provide a translation.py module, containing classes describing which of your model fields you wish to translate, as well as registering your models using the modeltranslation.translator.translator.register method.

For models that extends Mezzanine capabilities, there are two rules:

Firstly, the app in which your model is defined must be listed after the app it is extending from in your :django:setting:`INSTALLED_APPS` setting. For example, :mod:`mezzanine.forms` extends models from :mod:`mezzanine.pages` and should appear after it.

Note

If your app defines both models that need to be translated and static content or templates that override default ones from Mezzanine, you'll need to split your app to distinguish between presentation and content. This is due to conflicting ideas with translated model inheritance, and template or static file overriding, in regard to the order of :django:setting:`INSTALLED_APPS``

Secondly, for an external app, create a translation.py module at the root of your app. The content of this file might benefit from :mod:`mezzanine.core.translation` depending on what you are extending from. For example, to improve the model from :doc:`content-architecture` and provide translatable fields:

from django.db import models
from mezzanine.pages.models import Page

class Author(Page):
    dob = models.DateField("Date of birth")
    trivia = models.TextField("Trivia")

class Book(models.Model):
    author = models.ForeignKey("Author")
    cover = models.ImageFiled(upload_to="authors")
    title = models.CharField("Title", max_length=200)

A corresponding translation.py module in this app would look like:

from modeltranslation.translator import translator, TranslationOptions
from .models import Author, Book

class TranslatedAuthor(TranslationOptions):
    fields = ('trivia',)

class TranslatedBook(TranslationOptions):
    fields = ('title',)

translator.register(Author, TranslatedAuthor)
translator.register(Book, TranslatedBook)

In this case, please note :class:`mezzanine.pages.translation.TranslatedPage` is not referenced in any way. This is due to the fact that :class:`mezzanine.pages.models.Page` is not abstract, and thus has its own table in the database. The fields have already been registered for translation and django-modeltranslation will happily handle it for you.

If you want to extend an abstract model, such as :class:`mezzanine.core.models.Slugged` or :class:`mezzanine.core.models.Displayable`, you will need to subclass their translation registration. An example of this is the :mod:`mezzanine.blog` app in its :mod:`.translation.py` module:

from modeltranslation.translator import translator
from mezzanine.core.translation import (TranslatedSlugged,
                                        TranslatedDisplayable,
                                        TranslatedRichText)
from mezzanine.blog.models import BlogCategory, BlogPost

class TranslatedBlogPost(TranslatedDisplayable, TranslatedRichText):
    fields = ()

class TranslatedBlogCategory(TranslatedSlugged):
    fields = ()

translator.register(BlogPost, TranslatedBlogPost)
translator.register(BlogCategory, TranslatedBlogCategory)

You don't add translatable fields in your model beside those already defined inside Mezzanine's models. You need to extend from :mod:`mezzanine.core.translation` classes, so django-modeltranslation is aware of the abstract fields it will have to manage.

After that, you can manage.py createdb for a new project or manage.py sync_translation_fields and then manage.py update_translation_fields for an existing one.

Translation Fields and Migrations

Mezzanine is shipped with its own migration files but these do not take translation fields into account. These fields are created by every project's :django:setting:`LANGUAGES` setting and thus can't be provided by default. If you want to both manage migrations for your project and enable translation fields, there are two possibilities.

Either you disable translation fields while managing your migrations as usual and then catch up by adding the missing fields if any:

# edit settings.py to set USE_MODELTRANSLATION = False
$ python manage.py makemigrations
$ python manage.py migrate
# edit settings.py to set USE_MODELTRANSLATION back to True
$ python manage.py sync_translation_fields

This way, your migration files will never contains references to your specific :django:setting:`LANGUAGES` setting.

Or you create migration files including all the translation fields for your project. This way you won't need to rely on the manage.py sync_translation_fields command anymore. You will need to define a custom :django:setting:`MIGRATION_MODULES` and then run:

$ python manage.py makemigrations

Have a look at :ref:`field-injection-caveats` for a better introduction to :django:setting:`MIGRATION_MODULES``

Translation for Injected Fields

If you added fields in Mezzanine's models through :ref:`EXTRA_MODEL_FIELDS` and want to add translations, you will need to create a custom app that will hold the necessary translation.py module. Adding a translation field to all of Mezzanine's content type would look like:

EXTRA_MODEL_FIELDS = (
    (
        "mezzanine.pages.models.Page.quote",
        "TextField",
        ("Page's Quote",),
        {"blank": True},
    ),
)

The app containing the corresponding translation.py module should be defined after :mod:`mezzanine.pages` in :django:setting:`INSTALLED_APPS` but before any app that contains models that subclass :class:`mezzanine.pages.models.Page` (such as :mod:`mezzanine.forms`, :mod:`mezzanine.galleries` or cartridge.shop). The translation.py file itself would be:

from modeltranslation.translator import translator
from mezzanine.pages.translation import TranslatedPage
from mezzanine.pages.models import Page

class TranslatedInjectedPage(TranslatedPage):
    fields = ('quote',),

translator.unregister(Page)
translator.register(Page, TranslatedInjectedPage)

Redistributable Applications for Mezzanine

If you want to provide translation support for your Mezzanine app, make sure it works with both :ref:`USE_MODELTRANSLATION` set to True or False. Mezzanine enforces the value to False if django-modeltranslation is not installed.

The :ref:`USE_MODELTRANSLATION` setting can therefore be used to check against, when extra steps are required (such as saving an instance of a model in every language). In the case of a project with :ref:`USE_MODELTRANSLATION` set to False, the translation.py module will just be ignored.

The :ref:`USE_MODELTRANSLATION` setting is also available in the template's settings variable. Have a look at the includes/language_selector.html template in :mod:`mezzanine.core` for a working example.