Merge pull request #29 from agelinas/master

wagtail-modeltranslation documentation
This commit is contained in:
Rui Martins 2015-09-05 16:51:34 +01:00
commit 96b70e5fe6
11 changed files with 1113 additions and 3 deletions

View file

@ -13,5 +13,9 @@ Core Committers
Contributors
------------
.. _django-modeltranslation: https://github.com/deschler/django-modeltranslation
.. _django-linguo: https://github.com/zmathew/django-linguo
* `Django-modeltranslation`_
* `Django-linguo`_
.. _Django-modeltranslation: https://github.com/deschler/django-modeltranslation
.. _Django-linguo: https://github.com/zmathew/django-linguo

View file

@ -97,4 +97,4 @@ https://github.com/infoportugal/wagtail-modeltranslation
Documentation
-------------
Coming soon
http://wagtail-modeltranslation-docs.readthedocs.org/en/latest/#

20
docs/AUTHORS.rst Normal file
View file

@ -0,0 +1,20 @@
InfoPortugal, S.A. - https://github.com/infoportugal
Authors
=======
Core Committers
---------------
* Diogo Marques
* Rui Martins
* Eduardo Nogueira
Contributors
------------
* Django-modeltranslation_
* Django-linguo_
.. _django-modeltranslation: https://github.com/deschler/django-modeltranslation
.. _django-linguo: https://github.com/zmathew/django-linguo

213
docs/Advanced Settings.rst Normal file
View file

@ -0,0 +1,213 @@
*****************
Advanced Settings
*****************
Modeltranslation has some advanced settings to customize its behaviour.
.. _settings-modeltranslation_default_language:
Default language
----------------
``MODELTRANSLATION_DEFAULT_LANGUAGE``
Default: ``None``
To override the default language as described in :ref:`Configuration settings<language_settings>`, you can define a language in
``MODELTRANSLATION_DEFAULT_LANGUAGE``. Note that the value has to be in ``settings.LANGUAGES``, otherwise an
``ImproperlyConfigured`` exception will be raised.
Example::
MODELTRANSLATION_DEFAULT_LANGUAGE = 'pt'
Default languages
-----------------
``MODELTRANSLATION_LANGUAGES``
Default: same as LANGUAGES
Allows to set the languages the content will be translated into. If not set, by default all languages listed in LANGUAGES
will be used.
Example::
LANGUAGES = (
('pt', 'Portuguese'),
('es', 'Spanish'),
('fr', 'French'),
)
MODELTRANSLATION_LANGUAGES = ('pt', 'es')
.. note::
This setting may become useful if your users will be producing content for a restricted set of languages, while your
application is translated into a greater number of locales.
.. _MODELTRANSLATION_FALLBACK_LANGUAGES:
Fallback languages
------------------
``MODELTRANSLATION_FALLBACK_LANGUAGES``
Default: ``(DEFAULT_LANGUAGE)``
By default modeltranslation will fallback to the computed value of the DEFAULT_LANGUAGE. This is either the first language
found in the LANGUAGES setting or the value defined through MODELTRANSLATION_DEFAULT_LANGUAGE which acts as an override.
This setting allows for a more fine grained tuning of the fallback behaviour by taking additional languages into account.
The language order is defined as a tuple or list of language codes.
Example::
MODELTRANSLATION_FALLBACK_LANGUAGES = ('pt', 'es')
Using a dict syntax it is also possible to define fallbacks by language. A default key is required in this case to define
the default behaviour of unlisted languages.
Example::
MODELTRANSLATION_FALLBACK_LANGUAGES = {'default': ('pt', 'es'), 'fr': ('es',)}
.. note::
Each language has to be in the LANGUAGES setting, otherwise an ``Improperly Configured`` exception is raised.
.. _settings-modeltranslation_prepopulate_language:
Prepopulate language
--------------------
``MODELTRANSLATION_PREPOPULATE_LANGUAGE``
Default: ``the current active language``
By default modeltranslation will use the current request language for prepopulating admin fields specified in the
``prepopulated_fields`` admin property. This is often used to automatically fill slug fields.
This setting allows you to pin this functionality to a specific language.
Example::
MODELTRANSLATION_PREPOPULATE_LANGUAGE = 'fr'
.. note::
The language has to be in the ``LANGUAGES`` setting, otherwise an ``ImproperlyConfigured`` exception is raised.
Translation files
-----------------
``MODELTRANSLATION_TRANSLATION_FILES``
Default: ``()`` (empty tuple)
Modeltranslation uses an autoregister feature similiar to the one in Django's admin. The autoregistration process will look
for a ``translation.py`` file in the root directory of each application that is in INSTALLED_APPS.
The setting ``MODELTRANSLATION_TRANSLATION_FILES`` is provided to extend the modules that are taken into account.
Syntax::
MODELTRANSLATION_TRANSLATION_FILES = (
'<APP1_MODULE>.translation',
'<APP2_MODULE>.translation',
)
Example::
MODELTRANSLATION_TRANSLATION_FILES = (
'news.translation',
'projects.translation',
)
Custom fields
-------------
``MODELTRANSLATION_CUSTOM_FIELDS``
Default: ``()`` (empty tuple)
Modeltranslation supports the fields listed in the `Matrix of supported_fields`_. In most cases subclasses of the supported
fields will work fine, too. Unsupported fields will throw an ``Improperly Configured`` exception.
The list of supported fields can be extended by defining a tuple of field names in your ``settings file``.
Example::
MODELTRANSLATION_CUSTOM_FIELDS = ('MyField', 'MyOtherField',)
.. warning::
This just prevents modeltranslation from throwing an ``Improperly Configured`` exception. Any unsupported 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.
.. _settings-modeltranslation_auto_populate:
Auto populate
-------------
``MODELTRANSLATION_AUTO_POPULATE``
Default: ``False``
This setting controls if the `multilingual_manager` should automatically populate language field values in its ``create``
and ``get_or_create`` method, and in model constructors, so that these two blocks of statements can be considered equivalent::
foo.objects.populate(True).create(title='-- no translation yet --')
with auto_populate(True):
q = foo(title='-- no translation yet --')
# same effect with MODELTRANSLATION_AUTO_POPULATE == True:
foo.objects.create(title='-- no translation yet --')
q = foo(title='-- no translation yet --')
Debug
-----
``MODELTRANSLATION_DEBUG``
Default: ``False``
Used for modeltranslation related debug output. Currently setting it to ``False`` will just prevent Django's development
server from printing the ``Registered xx models for translation`` message to stdout.
Fallbacks
---------
``MODELTRANSLATION_ENABLE_FALLBACKS``
Default: ``True``
Controls if fallback (both language and value) will occur.
.. _settings-modeltranslation_loaddata_retain_locale:
Retain locale
-------------
``MODELTRANSLATION_LOADDATA_RETAIN_LOCALE``
Default: ``True``
Control if the ``loaddata`` command should leave the settings-defined locale alone. Setting it to ``False`` will result in
previous behaviour of loaddata: inserting fixtures to database under en-us locale.

77
docs/CHANGELOG.rst Normal file
View file

@ -0,0 +1,77 @@
-----------------------------------
Change Log
-----------------------------------
v0.2.2:
- Added duplicate content buttons to translated StreamFieldPanels;
v0.2.1:
- Fixed missing templatetags folder on pypi package;
v0.2:
- Support for StreamFields;
- No more django-modeltranslation dependency;
v0.1.5
- Fixed required fields related bug
v0.1.4
- Support for wagtailsearch and wagtailsnippets
v0.1.3
- Support for translated inline panels #8: https://github.com/infoportugal/wagtail-modeltranslation/issues/8
v0.1.2
- Fixed Problem updating field with null value #6: https://github.com/infoportugal/wagtail-modeltranslation/issues/6
v0.1.1
- Fixed url_path issue caused by a browser with language different from settings.LANGUAGE_CODE
v0.1
- Minor release working but lacks full test coverage.
- Last version had required fields validation problems, now fixed.
v0.0.9
- Fixed issue that causes duplicated translation fields, preventing auto-slug from working properly.
v0.0.8
- Fixed issue related to deleting a non existing key on PAGE_EDIT_HANDLER dict
v0.0.7
- Fixed set_url_path() translatable model method
v0.0.6
- Fixed js issue related to auto-generating slugs
v0.0.5
- Now using django-modeltranslation 0.9.1;
- Fixed problem related to slug field fallbacks;
v0.0.4
** IMPORTANT: ** make sure that TranslationMixin comes before Page class on model inheritance
- Fix enhancement #1: url_path translation field
- Now includes a template tag that returns current page url to corresponding translated url
- New management command to update url_path translation fields - **set\_translation\_url\_paths**
v0.0.3
- New methods;
- Now supports required fields;
- Fixed issue related to browser locale;
v0.0.2
- Prepopulated fields now works for translated fields (title and slug)

118
docs/Installation.rst Normal file
View file

@ -0,0 +1,118 @@
.. _installation:
************
Installation
************
Requirements
============
* Django >= 1.7
* Wagtail >= 1.0
Installing using Pip
--------------------
.. code-block:: console
$ pip install wagtail-modeltranslation >= 0.2.2
Installing using the source
---------------------------
* From github: **git clone** https://github.com/infoportugal/wagtail-modeltranslation.git
* Copy wagtail_modeltranslation folder in project tree
OR
* Download ZIP file on Github.com from infoportugal/wagtail-modeltranslation
* Unzip and copy wagtail_modeltranslation folder in project tree
Setup
=====
To setup the application please follow these steps:
1. In the settings/base.py file:
- Add wagtail_modeltranslation to the INSTALLED_APPS
.. code-block:: console
INSTALLED_APPS = (
...
'wagtail_modeltranslation',
)
- Add django.middleware.locale.LocaleMiddleware to MIDDLEWARE_CLASSES.
.. code-block:: console
MIDDLEWARE_CLASSES = (
...
'django.middleware.locale.LocaleMiddleware',
)
- Set ``USE_I18N = True``
.. _language_settings:
- Configure your LANGUAGES.
The LANGUAGES variable must contain all languages you will use for translation. The first language is treated as the
*default language*.
Modeltranslation uses the list of languages to add localized fields to the models registered for translation.
For example, to use the languages Portuguese, Spanish and French in your project, set the LANGUAGES variable like this
(where ``pt`` is the default language).
.. code-block:: console
LANGUAGES = (
('pt', u'Portugese'),
('es', u'Spanish'),
('fr', u'French'),
)
.. warning::
When the LANGUAGES setting isn't present in ``settings/base.py`` (and neither is ``MODELTRANSLATION_LANGUAGES``), it defaults to Django's global LANGUAGES setting instead, and there are quite a few languages in the default!
2. Create a ``translation.py`` file in your app directory and register ``TranslationOptions`` for every model you want to translate.
.. code-block:: console
from .models import foo
from wagtail_modeltranslation.translator import TranslationOptions
from wagtail_modeltranslation.decorators import register
@register(foo)
class FooTR(TranslationOptions):
fields = (
'body',
)
3. Add ``TranslationMixin`` to your translatable model:.
.. code-block:: console
# .models foo
...
from wagtail_modeltranslation.models import TranslationMixin
class FooModel(TranslationMixin, Page):
body = StreamField(...)
4. Run ``python manage.py makemigrations`` followed by ``python manage.py migrate``. This will add extra fields in the database.

52
docs/Introduction.rst Normal file
View file

@ -0,0 +1,52 @@
*************
Introduction
*************
Creating multilingual sites
===========================
**I18n**
Django and Wagtail CMS have implemented Internationalisation (I18n) in their frameworks. Hooks are provided for translating
strings such as literals. Furthermore, **locale language files** are included. This is where the translated text of the
frameworks is stored.
When writing your own apps, it is recommended that you use Il18n. If you need guidance, you can read the `Django Internalization
Documentation <https://docs.djangoproject.com/en/1.8/topics/i18n/translation/>`_.
**Wagtail-modeltranslation**
Another important component in the translation equation is the content stored in database fields. This is where
wagtail-modeltranslation comes into play.
Wagtail-modeltranslation is a fork of django-modeltranslation designed to define the fields that need to be translated.
In Wagtail, translation fields are displayed and edited together on the same page in the Wagtail admin interface. Translated
fields can be used in your templates and as you would use any other field.
Some of the advantages of wagtail-modeltranslation
--------------------------------------------------
* The same template is used for multiple languages
* The document tree is simpler with no need to have a separate branch for each language
* Languages can be added without changing existing models or views
* Translation fields are stored in the same table (no expensive joins)
* Can handle more than just text fields
* Wagtail admin integration
* Flexible fallbacks, auto-population and more!
* Default Page model has translatable fields by default
* StreamFields are supported
* Easy to implement
Examples used in this document
------------------------------
We will be using a fictitious model ``foo`` in the coding examples.
Wagtail-modeltranslation and Modeltranslation
---------------------------------------------
This document is for the most part an adaptation of the django-modeltranslation documentation, so we will refer to
``wagtail-modeltranslation`` when the material discussed is specific to Wagtail CMS and ``modeltranslation`` when it
is applicable to both wagtail-modeltranslation and django-modeltranslaton. You don't need to distinguish between the
two since wagtail-modeltranslation includes all the functionalities of django-modeltranslation.

View file

@ -0,0 +1,252 @@
********************
Multilingual Manager
********************
Every model registered for translation is patched so that all its managers become subclasses of ``MultilingualManager`` (of course, if a custom manager is defined on the model, its functions is retained). ``MultilingualManager`` simplifies language-aware queries, especially on third-party apps, by rewriting query field names.
Every model manager is patched, not only ``objects`` but also managers inherited from abstract base classes.
For example::
# Assuming the current language is "pt",
# these queries returns the same objects
Foo1 = Foo.objects.filter(introduction__contains='Welcome')
Foo2 = Foo.objects.filter(introduction_pt__contains='Welcome')
assert Foo1 == Foo2
It works this way: the translation field name is used, it is changed into the current language field name, based on the current language. Any language-suffixed fields are left untouched, so ``title_es`` 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
---------------
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 = Foo.objects.populate(True).create(title='bar')
is equivalent of::
x = Foo.objects.create(title_pt='bar', title_es='bar', title_fr='bar') ## title_?? for every language
Moreover, some fields can be explicitly assigned different values::
x = Foo.objects.populate(True).create(title='-- no translation yet --', title_es='hay traducción todavía')
It will result in ``title_es == 'hay traducción todavía'`` 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 = Foo.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 = Foo(title='bar')
print z.title_pt, z.title_es, z.title_fr # prints 'bar 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
---------------------
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.
Here is an example: a writer of some news hasn't specified a French title and content, but only the Spanish and Portuguese ones. Then if a French visitor is viewing the site, we would rather show
him English title/content of the news than having empty strings displayed. This is called *fallback*. ::
news.title_en = 'English title'
news.title_fr = ''
print news.title
# If current active language is French, 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 French and fallback to English is enabled,
# it would display 'English title'.
There are several ways of controlling fallback, described below.
.. _fallback_lang:
Fallback languages
------------------
:ref:`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', 'es')
means: if current active language field value is unset, try English value. If it is also unset,
try Portuguese, 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': ('pt', 'es', 'en'),
'fr': ('es',),
'uk': ('fr',)
}
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.
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
---------------
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
------------------
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
-------------------------------
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.
.. 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.

214
docs/Registering Models.rst Normal file
View file

@ -0,0 +1,214 @@
.. _Registering Models:
Registering models for translation
==================================
``Modeltranslation`` can translate model fields of any model class.
In wagtail-modeltranslation a **TranslationMixin** is used with the Page model:
.. code-block::console
from wagtail_modeltranslation.models import TranslationMixin
class FooModel(TranslationMixin, Page):
body = StreamField(...)
Registering models and their fields used for translation requires the following steps:
1. Create **translation.py** in your app directory.
2. Define the models you want to use, import wagtail-modeltranslation's **TranslationOptions** and the **register** decorator
3. Create a translation option class for every model you want to translate and precede the class with the **@register** decorator.
The wagtail-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 model fields are needed for translation. The class must derive from
**wagtail_modeltranslation.translator.TranslationOptions** and it must provide a **field** attribute storing the list of
field names. The option class must be registered with the **wagtail_modeltranslation.decorators.register** instance.
To illustrate this let's have a look at a simple example using a **Foo** model. The example only contains an **introduction**
and a **body** field.
Instead of a **Foo** model, this could be any Wagtail model class:
.. code-block:: console
from .models import Foo
from wagtail_modeltranslation.translator import TranslationOptions
from wagtail_modeltranslation.decorators import register
@register(Foo)
class FooTR(TranslationOptions):
fields = (
'introduction',
'body',
)
In the above example, the **introduction** and **body** language fields will be be added for each language defined in
LANGUAGES in the settings file ,**base.py**, when the database is updated with **./manage.py makemigrations** and
**./manage.py migrate.**
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 class
----------------------------------------------------
After registering the **Foo** model for translation a SQL dump of the Foo app will look like this:
.. code-block:: console
$ ./manage.py sqlall news
BEGIN;
CREATE TABLE `news_Foo` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`introduction` varchar(255) NOT NULL,
`introduction_pt` varchar(255) NULL,
`introduction_es` varchar(255) NULL,
`introduction_fr` varchar(255) NULL,
`body` varchar(255) NOT NULL,
`body_pt` varchar(255) NULL,
`body_es` varchar(255) NULL,
`body_fr` varchar(255) NULL,
)
;
CREATE INDEX `news_Foo_page_id` ON `news_Foo` (`page_id`);
COMMIT;
Note the ``introduction_pt``, ``introduction_es``, ``introduction_fr``, ``body_pt``, ``body_es`` and ``body_fr`` fields
which are not declared in the original ``Foo`` model class 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.
.. _register-precautions:
Precautions regarding registration approach
-------------------------------------------
Be aware that registration approach (as opposed to base-class approach) to models translation has a few caveats, though
(despite many pros).
First important thing to note is the fact that translatable models are being patched - that means their fields list is not
final until the modeltranslation code executes. In normal circumstances it shouldn't affect anything - as long as
``models.py`` contain only models' related code.
For example: consider a project where a ``ModelForm`` is declared in ``models.py`` just after its model. When the file is
executed, the form gets prepared - but it will be frozen with old fields list (without translation fields). That's because the
``ModelForm`` will be created before modeltranslation would add new fields to the model (``ModelForm`` gathers fields info at
class creation time, not instantiation time). Proper solution is to define the form in ``forms.py``, which wouldn't be imported
alongside with **models.py** (and rather imported from views file or urlconf).
Generally, for seamless integration with modeltranslation (and as sensible design anyway), the models.py`` should contain
only bare models and model related logic.
.. _db-fields:
Committing fields to database
-----------------------------
.. _migrations:
Modeltranslation supports the migration system introduced by Django 1.7. Besides the normal workflow as described in Django's
`Migration Docs <https://docs.djangoproject.com/en/1.8/topics/migrations/>`__, you should do a migration whenever one of the following changes have been made to your project:
- Added or removed a language through ``settings.LANGUAGES`` or ``settings.MODELTRANSLATION LANGUAGES``.
- Registered or unregistered a field through ``TranslationOptions``.
It doesn't matter if you are starting a fresh project or change an existing one, it's always:
1. ``python manage.py makemigration`` to create a new migration with
the added or removed fields.
2. ``python manage.py migrate`` to apply the changes.
.. _required_langs:
Required fields
---------------
By default, all translation fields are optional (not required). This can be changed using a special attribute on
``TranslationOptions``::
class NewsTranslationOptions(TranslationOptions):
fields = ('introduction', 'body',)
required_languages = ('pt', 'es')
It's quite self-explanatory: for Portuguese and Spanish, the ``introduction`` and ``body`` translation fields are required. For other
languages, they are optional.
A more fine-grained control is available::
class NewsTranslationOptions(TranslationOptions):
fields = ('introduction', 'body',)
required_languages = {'pt': ('introduction', 'body'), 'default': ('introduction',)}
For Portuguese, all fields (both ``introduction`` and ``body``) are required; for all other languages, only
``introduction`` is required. The ``default`` is optional.
.. note::
Requirement is enforced by ``blank=False``. Please remember that it will trigger validation only
in modelforms and admin (as always in Django). Manual model validation can be performed via
the ``full_clean()`` model method.
The required fields are still ``null=True``, though.
.. _supported_field_matrix:
Matrix of supported fields
--------------------------
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 Wagtail and gives an overview about their
current support status.
=============================== ===============
Model Field Implemented
=============================== ===============
**AutoField** |n|
**BigIntegerField** |i|
**BooleanField** |y|
**CharField** |y|
**CommaSeparatedIntegerField** |y|
**DateField** |y|
**DateTimeField** |y|
**DecimalField** |y|
**EmailField** |i|
**FileField** |y|
**FilePathField** |i|
**FloatField** |y|
**ImageField** |y|
**IntegerField** |y|
**IPAddressField** |y|
**GenericIPAddressField** |y|
**NullBooleanField** |y|
**PositiveIntegerField** |i|
**PositiveSmallIntegerField** |i|
**SlugField** |i|
**SmallIntegerField** |i|
**StreamField** |y|
**TextField** |y|
**TimeField** |y|
**URLField** |i|
**ForeignKey** |y|
**OneToOneField** |y|
**ManyToManyField** |n|
=============================== ===============
.. |y| replace:: Yes
.. |i| replace:: Yes\*
.. |n| replace:: No
.. |u| replace:: ?
\* Implicitly supported (as subclass of a supported field)

103
docs/Usage.rst Normal file
View file

@ -0,0 +1,103 @@
Accessing translated fields
===========================
Modeltranslation changes the behaviour of the translated fields. To explain this consider the Foo
example from the :ref:`Registering Models` chapter again. The original ``Foo`` model:
.. code-block:: console
class FooModel(TranslationMixin, Page):
introduction = models.CharField(max_length=255)
body = RichtextField(blank=True)
Now that it is registered with wagtail-modeltranslation, the model looks like this - note the additional fields automatically added by the app:
.. code-block:: console
class FooModel(TranslationMixin, Page):
introduction = models.CharField(max_length=255) # original/translated field
introduction_pt = models.CharField(null=True, blank=True, max_length=255) # default translation field
introduction_es = models.CharField(null=True, blank=True, max_length=255) # translation field
introduction_fr = models.CharField(null=True, blank=True, max_length=255) # translation field
body = RichTextField(blank=True) # original/translated field
body_pt = RichTextField(null=True, blank=True) # default translation field
body_es = RichTextField(null=True, blank=True) # translation field
body_fr = RichTextField(null=True, blank=True) # translation field
The example above assumes that the default language is ``pt``, therefore the ``introduction_pt`` and ``body_pt`` fields are marked as the *default translation fields*. If the default language were ``fr``, then ``introduction_fr`` and ``body_fr`` would be the *default translation fields*.
.. warning::
The ``title`` field is inherited from the Page model; if you try to create a field called ``title`` in one of your ``models`` you will get a warning messaqe. You can include the ``title field`` in ``translation.py`` and in the ``content_panels`` since it is inherited.
.. code-block:: console
# Indicate fields to include in Wagtail admin panel(s)
FooModel.content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('introduction', classname="full"),
FieldPanel('body', classname="full"),
]
.. code-block:: console
# translation.py
from .models import Foo
from wagtail_modeltranslation.translator import TranslationOptions
from wagtail_modeltranslation.decorators import register
@register(Foo)
class FooTR(TranslationOptions):
fields = (
'title',
'introduction',
'body',
)
.. _rules:
Rules for Translated Field Access
---------------------------------
The following rules apply to setting and getting the value of the original and the translation fields:
**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 of translated field access
------------------------------------
Because the whole point of using the wagtail-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** refers to the Django ``set_language`` view and the corresponding ``get_lang``
function.
Assuming the current language is ``pt`` in the Foo example above, the translated ``introduction`` field will return the value from the ``introduction_pt`` field::
# Assuming the current language is "pt"
n = News.objects.all()[0]
t = n.introduction # returns the Portuguese translation
# Assuming the current language is "pt"
t = n.introduction # returns the Portuguese 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.

57
docs/index.rst Normal file
View file

@ -0,0 +1,57 @@
.. Wagtail ModelTranslation Documentation documentation master file, created by
sphinx-quickstart on Tue Aug 18 11:42:59 2015.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
========================
Wagtail Modeltranslation
========================
This app is based on django-modeltranslation: https://github.com/deschler/django-modeltranslation
It's an alternative approach for i18n support on Wagtail CMS websites.
The modeltranslation application is used to translate dynamic content of
existing Wagtail 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 add translations to existing or new projects and is
fully integrated into the Wagtail admin UI.
The advantage of a registration approach is the ability to add translations to
models on a per-app basis. You can use the same app in different projects,
whether or not they use translations, and without touching the original
model class.
.. image:: https://github.com/infoportugal/wagtail-modeltranslation/blob/master/screenshot.png?raw=true
:target: https://github.com/infoportugal/wagtail-modeltranslation/blob/master/screenshot.png?raw=true
Features
========
- Add translations without changing existing models or views
- Translation fields are stored in the same table (no expensive joins)
- Supports inherited models (abstract and multi-table inheritance)
- Handle more than just text fields
- Wagtail admin integration
- Flexible fallbacks, auto-population and more!
- Default Page model fields has translatable fields by default
- StreamFields are now supported!
Contents
========
.. toctree::
:maxdepth: 2
Introduction
Installation
Advanced Settings
Registering Models
Usage
Multilingual Manager
AUTHORS
CHANGELOG