diff --git a/README.rst b/README.rst index e2f5734..1141288 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Modeltranslation ================ -The modeltranslation application can be used to translate dynamic content of +The modeltranslation application is used to translate dynamic content of existing Django 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 @@ -21,11 +21,12 @@ model class. Features ======== -- Add translations without changing existing models -- Fast, because translation fields are stored in the same table -- Supports inherited models -- Django admin support -- Unlimited number of target languages +- 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 +- Django admin integration +- Flexible fallbacks, auto-population and more! Project Home diff --git a/docs/modeltranslation/admin.rst b/docs/modeltranslation/admin.rst index b7f6c4b..99423b8 100644 --- a/docs/modeltranslation/admin.rst +++ b/docs/modeltranslation/admin.rst @@ -30,7 +30,7 @@ formfield_for_dbfield The ``TranslationBaseModelAdmin`` class, which ``TranslationAdmin`` and all inline related classes in modeltranslation derive from, implements a special -method which is ``def formfield_for_dbfield(self, db_field, **kwargs)``. This +method which is ``formfield_for_dbfield(self, db_field, **kwargs)``. This method does the following: 1. Copies the widget of the original field to each of its translation fields. @@ -78,7 +78,7 @@ TranslationAdmin in Combination with Other Admin Classes If 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 a (fictional) reusable blog app which defines a model ``Entry`` and a +Taken a reusable blog app which defines a model ``Entry`` and a corresponding admin class called ``EntryAdmin``. This app is not yours and you don't want to touch it at all. diff --git a/docs/modeltranslation/commands.rst b/docs/modeltranslation/commands.rst index fc1a06e..fe76dec 100644 --- a/docs/modeltranslation/commands.rst +++ b/docs/modeltranslation/commands.rst @@ -8,19 +8,19 @@ Management Commands The ``update_translation_fields`` Command ----------------------------------------- -In case the modeltranslation app was installed on an existing project and you +In case modeltranslation was installed in 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 (see :ref:`db-fields`). 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) which will be empty in this case. To correctly initialize the -default translation field you can use the ``update_translation_fields`` +:ref:`Rule 1 `) which will be empty in this case. To correctly initialize +the default translation field you can use the ``update_translation_fields`` command: .. code-block:: console - $ ./manage.py update_translation_fields + $ python manage.py update_translation_fields Taken the news example used throughout the documentation this command will copy the value from the news object's ``title`` field to the default translation @@ -47,11 +47,12 @@ The ``sync_translation_fields`` Command .. code-block:: console - $ ./manage.py sync_translation_fields + $ python manage.py sync_translation_fields This command compares the database and translated models definitions (finding new translation fields) and provides SQL statements to alter tables. You should run this command after adding -new language or deciding to translate new field in a ``TranslationOptions``. +a new language to your ``settings.LANGUAGES`` or a new field to the ``TranslationOptions`` of +a registered model. However, if you are using South in your project, in most cases it's recommended to use migration instead of ``sync_translation_fields``. See :ref:`db-fields` for detailed info and use cases. @@ -62,28 +63,29 @@ The ``loaddata`` Command .. versionadded:: 0.7 -It is just extension to original ``loaddata`` command which adds an optional ``populate`` keyword. -If specified, then normal loading command will be run under selected auto-population modes. +An extended version of Django's original ``loaddata`` command which adds an optional +``populate`` keyword. If the keyword is specified, the normal loading command will be +run under the selected auto-population modes. By default no auto-population is performed. .. code-block:: console - $ ./manage.py loaddata --populate=all fixtures.json + $ python manage.py loaddata --populate=all fixtures.json Allowed modes are listed :ref:`here `. To choose ``False`` (turn off auto-population) specify ``'0'`` or ``'false'``: .. code-block:: console - $ ./manage.py loaddata --populate=false fixtures.json - $ ./manage.py loaddata --populate=0 fixtures.json + $ python manage.py loaddata --populate=false fixtures.json + $ python manage.py loaddata --populate=0 fixtures.json .. note:: - If ``populate`` is not specified, then current auto-population mode is used. *Current* means + If ``populate`` is not specified, the current auto-population mode is used. *Current* means the one set by :ref:`settings `. Moreover, this ``loaddata`` command version can override the nasty habit of changing locale to -`en-us`. By default, it will retain proper locale. To get back to old behaviour, set +`en-us`. By default, it will retain the proper locale. To get the old behaviour back, set :ref:`settings-modeltranslation_loaddata_retain_locale` to ``False``. diff --git a/docs/modeltranslation/forms.rst b/docs/modeltranslation/forms.rst index 6d98190..4578749 100644 --- a/docs/modeltranslation/forms.rst +++ b/docs/modeltranslation/forms.rst @@ -4,7 +4,8 @@ ModelForms ========== ``ModelForms`` for multilanguage models are defined and handled as typical ``ModelForms``. -Please note, however, that they shouldn't be defined next to models (see :ref:`a note `). +Please note, however, that they shouldn't be defined next to models +(see :ref:`a note `). Editing multilanguage models with all translation fields in the admin backend is quite sensible. However, presenting all model fields to the user on the frontend may be not the right way. @@ -37,18 +38,18 @@ In most cases formfields for translation fields behave as expected. However, the problem with ``models.CharField`` - probably the most commonly translated field type. The problem is that default formfield for ``CharField`` stores empty values as empty strings -(``''``), even if field is nullable +(``''``), even if the field is nullable (see django `ticket #9590 `_). -Thus formfields for translation fields are patched by `MT`. Following rules apply: +Thus formfields for translation fields are patched by modeltranslation. The following rules apply: .. _formfield_rules: -- If original field is not nullable, empty value would be saved as ``''``; -- If original field is nullable, empty value would be saved as ``None``. +- If the original field is not nullable, an empty value is saved as ``''``; +- If the original field is nullable, an empty value is saved as ``None``. To deal with complex cases, these rules can be overridden per model or even per field -(using ``TranslationOptions``):: +using ``TranslationOptions``:: class NewsTranslationOptions(TranslationOptions): fields = ('title', 'text',) @@ -68,9 +69,9 @@ This configuration is especially useful for fields with unique constraints:: slug = models.SlugField(max_length=30, unique=True) Because the ``slug`` field is not nullable, its translation fields would store empty values as -``''`` and that would result in error when 2 or more ``Categories`` are saved with +``''`` and that would result in an error when two or more ``Categories`` are saved with ``slug_en`` empty - unique constraints wouldn't be satisfied. Instead, ``None`` should be stored, -as several ``None`` values in database don't violate uniqueness:: +as several ``None`` values in the database don't violate uniqueness:: class CategoryTranslationOptions(TranslationOptions): fields = ('name', 'slug') @@ -82,15 +83,15 @@ as several ``None`` values in database don't violate uniqueness:: None-checkbox widget ******************** -Maybe there is a situation when somebody want to store in a field both empty strings and ``None`` -values. For such a scenario there is third configuration value: ``'both'``:: +Maybe there is a situation where you want to store both - empty strings and ``None`` +values - in a field. For such a scenario there is a third configuration value: ``'both'``:: class NewsTranslationOptions(TranslationOptions): fields = ('title', 'text',) empty_values = {'title': None, 'text': 'both'} -It results in special widget with a None-checkbox to null a field. It's not recommended in frontend -as users may be confused what this `None` is. Probably only useful place for this widget is admin -backend; see :ref:`admin-formfield`. +It results in a special widget with a None-checkbox to null a field. It's not recommended in +frontend as users may be confused what this `None` is. The only useful place for this widget might +be the admin backend; see :ref:`admin-formfield`. -To sum up, only valid ``empty_values`` values are: ``None``, ``''`` and ``'both'``. +To sum it up, the valid values for ``empty_values`` are: ``None``, ``''`` and ``'both'``. diff --git a/docs/modeltranslation/installation.rst b/docs/modeltranslation/installation.rst index d061bbc..4b8ffea 100644 --- a/docs/modeltranslation/installation.rst +++ b/docs/modeltranslation/installation.rst @@ -59,20 +59,20 @@ Setup To setup the application please follow these steps. Each step is described in detail in the following sections: -1. Add the ``modeltranslation`` app to the ``INSTALLED_APPS`` variable of your +1. Add ``modeltranslation`` to the ``INSTALLED_APPS`` variable of your project's ``settings.py``. -#. Set ``USE_I18N = True`` in ``settings.py``. +2. Set ``USE_I18N = True`` in ``settings.py``. -#. Configure your ``LANGUAGES`` in ``settings.py``. +3. Configure your ``LANGUAGES`` in ``settings.py``. -#. Create a ``translation.py`` in your app directory and register +4. Create a ``translation.py`` in your app directory and register ``TranslationOptions`` for every model you want to translate. -#. Sync the database using ``./manage.py syncdb`` (note that this only applies - if the models registered in the ``translation.py`` did not have been - synced to the database before. If they did - read :ref:`further down ` what to do - in that case. +5. Sync the database using ``python manage.py syncdb``. + + .. note:: This only applies if the models registered in ``translation.py`` haven't been + synced to the database before. If they have, please read :ref:`db-fields`. Configuration @@ -94,6 +94,7 @@ Make sure that the ``modeltranslation`` app is listed in your INSTALLED_APPS = ( ... 'modeltranslation', + 'django.contrib.admin', # optional .... ) @@ -102,9 +103,9 @@ Make sure that the ``modeltranslation`` app is listed in your before ``django.contrib.admin``. .. important:: - If you want to use the ``django-debug-toolbar`` together with `MT`, put ``debug_toolbar`` - as first entry in ``INSTALLED_APPS`` or use - `explicit setup + If you want to use the ``django-debug-toolbar`` together with + modeltranslation, put ``debug_toolbar`` as first entry in + ``INSTALLED_APPS`` or use `explicit setup `_. .. _settings-languages: @@ -115,10 +116,10 @@ Make sure that the ``modeltranslation`` app is listed in your 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 ``LANGUAGES`` variable like this (where -``de`` is the default language):: +Modeltranslation 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 ``LANGUAGES`` variable like this (where ``de`` is the +default language):: gettext = lambda s: s LANGUAGES = ( @@ -144,7 +145,7 @@ and ``en`` in your project, set the ``LANGUAGES`` variable like this (where in your project. When it isn't present (and neither is ``MODELTRANSLATION_LANGUAGES``), it defaults to Django's `global LANGUAGES setting `_ - instead, and that are quite a number of languages! + instead, and that are quite a few languages! Advanced Settings @@ -379,4 +380,4 @@ 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. +under ``en-us`` locale. diff --git a/docs/modeltranslation/registration.rst b/docs/modeltranslation/registration.rst index 7db585b..ba10c07 100644 --- a/docs/modeltranslation/registration.rst +++ b/docs/modeltranslation/registration.rst @@ -12,7 +12,7 @@ steps: 1. Create a ``translation.py`` in your app directory. 2. Create a translation option class for every model to translate. -3. Register the model and the translation option class at the +3. Register the model and the translation option class at ``modeltranslation.translator.translator``. The modeltranslation application reads the ``translation.py`` file in your @@ -33,9 +33,9 @@ Instead of a news, this could be any Django model class:: 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:: +In order to tell modeltranslation to translate the ``title`` and ``text`` fields, +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 @@ -86,7 +86,7 @@ say it in code:: Of course multiple inheritance and inheritance chains (A > B > C) also work as expected. -.. note:: When upgrading from a previous modeltranslation version, please +.. note:: When upgrading from a previous modeltranslation version (<0.5), 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). @@ -137,17 +137,18 @@ 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 `MT` code executes. In normal circumstances it shouldn't -affect anything - as long as ``models.py`` contain only models' related code. +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 when a ``ModelForm`` is declared in ``models.py`` just after +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 ``ModelForm`` will be created before -`MT` would add new fields to the model (``ModelForm`` gather 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). +old fields list (without translation fields). That's because the ``ModelForm`` will be created +before modeltranslation would add new fields to the model (``ModelForm`` gather 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 `MT` (and as sensible design, anyway), +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: @@ -168,22 +169,22 @@ fields) and apply it. If not, you can use a little helper: :ref:`commands-sync_translation_fields` 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, +If you are adding translation fields to a third-party app that is using South, +things get more complicated. In order to be able to update the app in the 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 by default -declared ``blank=True`` and ``null=True`` no matter if the original field is -required or not. In other words - all translations are optional, unless an explicit option -is provided - see below. +Note that all added fields are by default declared ``blank=True`` and +``null=True`` no matter if the original field is required or not. In other +words - all translations are optional, unless an explicit option is +provided - see below. -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 +To populate the default translation fields added by modeltranslation with +values from existing database fields, you can use the +``update_translation_fields`` command below. See :ref:`commands-update_translation_fields` for more info on this. @@ -194,14 +195,14 @@ Required fields .. versionadded:: 0.8 -By default, all translation fields are optional (not required). It can be changed using special -attribute on ``TranslationOptions``, though:: +By default, all translation fields are optional (not required). This can be +changed using a special attribute on ``TranslationOptions``:: class NewsTranslationOptions(TranslationOptions): fields = ('title', 'text',) required_languages = ('en', 'de') -It quite self-explanatory: for German and English, all translation fields are required. For other +It's quite self-explanatory: for German and English, all translation fields are required. For other languages - optional. A more fine-grained control is available:: @@ -216,7 +217,7 @@ For German, all fields (both ``title`` and ``text``) are required; for all other .. 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 - ``full_clean()`` model method. + the ``full_clean()`` model method. The required fields are still ``null=True``, though. diff --git a/docs/modeltranslation/usage.rst b/docs/modeltranslation/usage.rst index 1ad3f88..cb3df69 100644 --- a/docs/modeltranslation/usage.rst +++ b/docs/modeltranslation/usage.rst @@ -3,7 +3,7 @@ Accessing Translated and Translation Fields =========================================== -The modeltranslation app changes the behaviour of the translated fields. To +Modeltranslation changes the behaviour of the translated fields. To explain this consider the news example from the :ref:`registration` chapter again. The original ``News`` model looked like this:: @@ -11,7 +11,7 @@ again. The original ``News`` model looked like this:: title = models.CharField(max_length=255) text = models.TextField() -Now that it is registered with the modeltranslation app the model looks +Now that it is registered with modeltranslation the model looks like this - note the additional fields automatically added by the app:: class News(models.Model): @@ -160,21 +160,22 @@ Moreover, some fields can be explicitly assigned different values:: It will result in ``title_de == 'enigma'`` and other ``title_?? == '-- no translation yet --'``. -There is another way of altering the current population status, an ``auto_populate`` context manager:: +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 = News.objects.create(title='bar') -Auto-population tooks place also in model constructor, what is extremely useful when loading +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 + call_command('loaddata', 'fixture.json') # Some fixture loading - z = News(title='bar') - print z.title_en, z.title_de # prints 'bar bar' + z = News(title='bar') + print z.title_en, z.title_de # prints 'bar bar' There is a more convenient way than calling ``populate`` manager method or entering ``auto_populate`` manager context all the time: @@ -186,7 +187,7 @@ It controls the default population behaviour. Auto-population modes ^^^^^^^^^^^^^^^^^^^^^ -There are 4 different population modes: +There are four different population modes: ``False`` [set by default] @@ -212,17 +213,17 @@ There are 4 different population modes: Falling back ------------ -Modeltranslation provides mechanism to control behaviour of data access in case of empty +Modeltranslation provides a mechanism to control behaviour of data access in case of empty translation values. This mechanism affects field access. -Consider ``News`` example: a creator of some news hasn't specified it's german title and content, -but only english ones. Then if a german visitor is viewing site, we would rather show him english -title/content of the news than display empty strings. This is called *fallback*. :: +Consider the ``News`` example: a creator of some news hasn't specified its German title and +content, but only English ones. Then if a German visitor is viewing the site, we would rather show +him English title/content of the news than display empty strings. This is called *fallback*. :: News.title_en = 'English title' News.title_de = '' print News.title - # If current active language is german, it should display title_de field value (''). + # If current active language is German, it should display the title_de field value (''). # But if fallback is enabled, it would display 'English title' instead. There are several ways of controlling fallback, described below. @@ -234,17 +235,17 @@ Fallback languages .. versionadded:: 0.5 -:ref:`settings-modeltranslation_fallback_languages` setting allows to set order of *fallback -languages*. By default it is only ``DEFAULT_LANGUAGE``. +:ref:`settings-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', 'de', 'fr') -means: if current active language field value is unset, try english value. If it is also unset, -try german, and so on - until some language yield non-empty value of the field. +means: if current active language field value is unset, try English value. If it is also unset, +try German, and so on - until some language yields a non-empty value of the field. -There is also option to define fallback by language, using dict syntax:: +There is also an option to define a fallback by language, using dict syntax:: MODELTRANSLATION_FALLBACK_LANGUAGES = { 'default': ('en', 'de', 'fr'), @@ -255,11 +256,11 @@ There is also option to define fallback by language, using dict syntax:: The ``default`` key is required and its value denote languages which are always tried at the end. With such a setting: -- for `uk` (Ukrainian) order of fallback languages is: ``('ru', 'en', 'de', 'fr')`` -- for `fr` order of fallback languages is: ``('de', 'en')`` - `fr` obviously is not fallback, since - it's active language; and `de` would be tried before `en` -- for `en` and `de` fallback order is ``('de', 'fr')`` and ``('en', 'fr')``, respectively -- for any other language order of fallback languages is just ``('en', 'de', 'fr')`` +- 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``:: @@ -286,7 +287,7 @@ Fallback values .. versionadded:: 0.4 But what if current language and all fallback languages yield no field value? Then modeltranslation -will use field's *fallback value*, if one was defined. +will use the field's *fallback value*, if one was defined. Fallback values are defined in ``TranslationOptions``, for example:: @@ -308,7 +309,7 @@ Fallback values can be also customized per model field:: } If current language and all fallback languages yield no field value, and no fallback values are -defined, then modeltranslation will use field's default value. +defined, then modeltranslation will use the field's default value. .. _fallback_undef: @@ -319,13 +320,13 @@ 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 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 fallback on a different value or your default is expensive to calculate, provide +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):