Converted documentation to shpinx and added an initial setup (work-in-progress).

This commit is contained in:
Dirk Eschler 2012-10-17 11:18:36 +02:00
parent 38f65c340a
commit 2f869bf723
8 changed files with 701 additions and 2423 deletions

View file

@ -0,0 +1,153 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-modeltranslation.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-modeltranslation.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/django-modeltranslation"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-modeltranslation"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

View file

@ -0,0 +1,285 @@
# -*- coding: utf-8 -*-
#
# django-modeltranslation documentation build configuration file, created by
# sphinx-quickstart on Wed Oct 17 10:26:58 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'django-modeltranslation'
copyright = u'2009-2012, Peter Eschler, Dirk Eschler'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.4'
# The full version, including alpha/beta/rc tags.
release = '0.4.0-alpha1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'django-modeltranslationdoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'django-modeltranslation.tex', u'django-modeltranslation Documentation',
u'Dirk Eschler', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'django-modeltranslation', u'django-modeltranslation Documentation',
[u'Dirk Eschler'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'django-modeltranslation', u'django-modeltranslation Documentation',
u'Dirk Eschler', 'django-modeltranslation', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = u'django-modeltranslation'
epub_author = u'Dirk Eschler'
epub_publisher = u'Dirk Eschler'
epub_copyright = u'2009-2012, Peter Eschler, Dirk Eschler'
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# A tuple containing the cover image and cover page html template filenames.
#epub_cover = ()
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True

View file

@ -1,3 +1,5 @@
.. django-modeltranslation documentation master file
.. _ref-topics-modeltranslation:
================
@ -15,12 +17,14 @@ 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*
**Authors**
- Peter Eschler <peschler@googlemail.com> (http://www.nmy.de)
- Dirk Eschler <eschler@gmail.com> (http://www.nmy.de)
- Peter Eschler <peschler@gmail.com>
- Dirk Eschler <eschler@gmail.com>
- and many more contributors
.. contents::
.. toctree::
:maxdepth: 2
Features
@ -62,11 +66,14 @@ in detail in the following sections:
Configure the project's ``settings.py``
---------------------------------------
Required settings
*****************
The following variables have to be added to or edited in the project's
``settings.py``:
**settings.INSTALLED_APPS**
``INSTALLED_APPS``
^^^^^^^^^^^^^^^^^^
Make sure that the ``modeltranslation`` app is listed in your
``INSTALLED_APPS`` variable:
@ -78,18 +85,18 @@ Make sure that the ``modeltranslation`` app is listed in your
....
)
Also make sure that the app can be found on a path contained in your
``PYTHONPATH`` environment variable.
.. note:: 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
``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):
and ``en`` in your project, set the ``LANGUAGES`` variable like this (where
``de`` is the default language):
::
@ -104,17 +111,21 @@ 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**
Advanced settings
*****************
Modeltranslation also has some advanced settings to customize its behaviour:
*New in 0.3*
``MODELTRANSLATION_DEFAULT_LANGUAGE``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 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_FILES**
*New in 0.4*
``MODELTRANSLATION_TRANSLATION_FILES``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 0.4
Modeltranslation uses an autoregister feature similiar to the one in Django's
admin. The autoregistration process will look for a ``translation.py``
@ -135,13 +146,13 @@ the modules that are taken into account. It uses the following syntax:
automatically added to ``MODELTRANSLATION_TRANSLATION_FILES``. A
DeprecationWarning is issued in this case.
**settings.MODELTRANSLATION_CUSTOM_FIELDS**
*New in 0.3*
``MODELTRANSLATION_CUSTOM_FIELDS``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 0.3
``Modeltranslation`` officially supports ``CharField`` and ``TextField``.
*New in 0.4*
.. versionadded:: 0.4
Support for ``FileField`` and ``ImageField``.
@ -178,7 +189,7 @@ steps:
3. Register the model and the translation option class at the
``modeltranslation.translator.translator``
The ``modeltranslation`` application reads the ``translation.py`` file in your
The modeltranslation application reads the ``translation.py`` file in your
app directory thereby triggering the registration of the translation
options found in the file.
@ -198,7 +209,7 @@ 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
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:
@ -269,7 +280,7 @@ 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
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.
@ -286,7 +297,7 @@ 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 the modeltranslation app the model looks
like this - note the additional fields automatically added by the app::
class News(models.Model):
@ -332,7 +343,7 @@ at the same time, the default translation field wins.
Examples for translated field access
------------------------------------
Because the whole point of using the ``modeltranslation`` app is translating
Because 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
@ -378,7 +389,7 @@ Tweaks applied to the admin
---------------------------
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
@ -390,7 +401,7 @@ method does the following:
get_form and get_fieldsets
~~~~~~~~~~~~~~~~~~~~~~~~~~
**************************
The ``TranslationBaseModelAdmin`` class overrides ``get_form``,
``get_fieldsets`` and ``_declared_fieldsets`` to make the options ``fields``,
``exclude`` and ``fieldsets`` work in a transparent way. It basically does:
@ -475,23 +486,23 @@ custom admin class and that's done in the example above. After that the
Inlines
-------
*New in 0.2*
.. versionadded:: 0.2
Support for tabular and stacked inlines, common and generic ones.
A translated inline must derive from one of the following classes:
* `modeltranslation.admin.TranslationTabularInline`
* `modeltranslation.admin.TranslationStackedInline`
* `modeltranslation.admin.TranslationGenericTabularInline`
* `modeltranslation.admin.TranslationGenericStackedInline`
* ``modeltranslation.admin.TranslationTabularInline``
* ``modeltranslation.admin.TranslationStackedInline``
* ``modeltranslation.admin.TranslationGenericTabularInline``
* ``modeltranslation.admin.TranslationGenericStackedInline``
Just like `TranslationAdmin` these classes implement a special method
`def formfield_for_dbfield(self, db_field, **kwargs)` which does all the
patching.
Just like ``TranslationAdmin`` these classes implement a special method
``formfield_for_dbfield`` 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:
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:
::
@ -507,15 +518,16 @@ definition is left out for simplicity. Our News model inlines the new model:
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.
.. 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`__.:
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`__.:
__ translationadmin_in_combination_with_other_admin_classes_
@ -540,11 +552,11 @@ __ translationadmin_in_combination_with_other_admin_classes_
Using tabbed translation fields
-------------------------------
*New in 0.3*
.. versionadded:: 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:
The proposed way to include it is through the inner ``Media`` class of a
``TranslationAdmin`` class like this:
::
@ -559,8 +571,8 @@ The proposed way to include it is through the inner `Media` class of a
'screen': ('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
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.
@ -596,7 +608,7 @@ populated with initial data.
The ``sync_translation_fields`` command
=======================================
*New in 0.4*
.. versionadded:: 0.4
TODO
@ -700,3 +712,11 @@ from being finished.
.. _django-transdb: http://code.google.com/p/transdb/
.. _i18ndynamic: http://code.google.com/p/i18ndynamic/
.. _django-pluggable-model-i18n: http://code.google.com/p/django-pluggable-model-i18n/
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -0,0 +1,190 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-modeltranslation.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-modeltranslation.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

View file

@ -1,479 +0,0 @@
.. _ref-topics-modeltranslation:
===================
Model translation
===================
.. admonition:: About this document
This document provides an introduction to the modeltranslation application.
.. currentmodule:: modeltranslation.models
.. moduleauthor:: P. Eschler <peschler@googlemail.com>
The 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.
.. contents::
Features
========
- Unlimited number of target languages
- Add translations without changing existing models
- Django admin support
- ?Supports inherited models
Installation
============
To install 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
project's ``settings.py``.
2. Configure your languages in the ``settings.py``.
3. Create a ``translation.py`` in your project directory and register
``TranslationOptions`` for every model you want to translate.
4. Configure the ``TRANSLATION_REGISTRY`` variable in your ``settings.py``.
5. Sync the database using ``manage.py syncdb`` (note that this only applies
if the models registered in the ``translations.py`` did not have been
synced to the database before. If they did - read further down what to do
in that case.
Configure the project's ``settings.py``
---------------------------------------
The 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.TRANSLATION_REGISTRY**
In order to be able to import the project's ``translation.py`` registration
file the ``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 ``TRANSLATION_REGISTRY`` must be set like this::
TRANSLATION_REGISTRY = "myproject.translation"
Registering models and their fields for translation
---------------------------------------------------
The ``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:
1. Create a ``translation.py`` in your project directory.
2. Create a translation option class for every model to translate.
3. Register the model and the translation option class at the
``modeltranslation.translator.translator``
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 class
------------------------------------------------
After 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.
.. _set_language: http://docs.djangoproject.com/en/dev/topics/i18n/#the-set-language-redirect-view
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 fields
===========================================
The ``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 field
The 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 access
---------------------------------
So 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 access
------------------------------------
Because 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 integration
================================
In 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 admin
---------------------------
The ``TranslationAdmin`` class does only implement one special method which is
``def formfield_for_dbfield(self, db_field, **kwargs)``. This method does the
following:
1. Removes the original field from every admin form by setting it
``editable=False``.
2. Copies the widget of the original field to each of it's translation fields.
3. Checks if the - now removed - original field was required and if so makes the
default translation field required instead.
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 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, but
it has this nice admin class::
class NewsAdmin(model.Admin):
def formfield_for_dbfield(self, db_field, **kwargs):
# does some funky stuff with the formfield here
So a first attempt might be to create your own admin class which subclasses
``NewsAdmin`` and ``TranslationAdmin`` to combine stuff like so::
class MyTranslatedNewsAdmin(NewsAdmin, TranslationAdmin):
pass
Unfortunately this won't work 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 field
This 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.
The ``update_translation_fields`` command
=========================================
In 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.
Caveats
=======
Consider 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 views
-----------------------------------------
Since 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.
.. _How Django discovers language preference: http://docs.djangoproject.com/en/dev/topics/i18n/#id2
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 projects
================
`django-multilingual`_
----------------------
A library providing support for multilingual content in Django models.
It is not possible to reuse existing models without modifying them.
`django-multilingual-model`_
----------------------------
A much simpler version of the above `django-multilingual`.
It works very similiar to the `django-multilingual` approach.
`transdb`_
----------
Django'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.
`i18ndynamic`_
--------------
This approach is not developed any more.
`django-pluggable-model-i18n`_
------------------------------
This 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.
.. _django-multilingual: http://code.google.com/p/django-multilingual/
.. _django-multilingual-model: http://code.google.com/p/django-multilingual-model/
.. _django-transdb: http://code.google.com/p/transdb/
.. _i18ndynamic: http://code.google.com/p/i18ndynamic/
.. _django-pluggable-model-i18n: http://code.google.com/p/django-pluggable-model-i18n/

View file

@ -1,545 +0,0 @@
.. _ref-topics-modeltranslation:
===================
Model translation
===================
.. admonition:: About this document
This document provides an introduction to the modeltranslation application.
.. currentmodule:: modeltranslation.models
.. moduleauthor:: Peter Eschler <peschler@googlemail.com>,
Dirk Eschler <eschler@gmail.com>
The 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.
.. contents::
Features
========
- Unlimited number of target languages
- Add translations without changing existing models
- Django admin support
- Supports inherited models
Installation
============
To install 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
project's ``settings.py``.
2. Configure your languages in the ``settings.py``.
3. Create a ``translation.py`` in your project directory and register
``TranslationOptions`` for every model you want to translate.
4. Configure the ``TRANSLATION_REGISTRY`` variable in your ``settings.py``.
5. Sync the database using ``manage.py syncdb`` (note that this only applies
if the models registered in the ``translations.py`` did not have been
synced to the database before. If they did - read further down what to do
in that case.
Configure the project's ``settings.py``
---------------------------------------
The 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.TRANSLATION_REGISTRY**
In order to be able to import the project's ``translation.py`` registration
file the ``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 ``TRANSLATION_REGISTRY`` must be set like this::
TRANSLATION_REGISTRY = "myproject.translation"
Registering models and their fields for translation
---------------------------------------------------
The ``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:
1. Create a ``translation.py`` in your project directory.
2. Create a translation option class for every model to translate.
3. Register the model and the translation option class at the
``modeltranslation.translator.translator``
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 class
------------------------------------------------
After 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.
.. _set_language: http://docs.djangoproject.com/en/dev/topics/i18n/#the-set-language-redirect-view
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 fields
===========================================
The ``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 field
The 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 access
---------------------------------
So 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 access
------------------------------------
Because 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 integration
================================
In 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 admin
---------------------------
The ``TranslationAdmin`` class does only implement one special method which is
``def formfield_for_dbfield(self, db_field, **kwargs)``. This method does the
following:
1. Removes the original field from every admin form by setting it
``editable=False``.
2. Copies the widget of the original field to each of it's translation fields.
3. Checks if the - now removed - original field was required and if so makes the
default translation field required instead.
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 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,
but it has this nice admin class::
class NewsAdmin(model.Admin):
def formfield_for_dbfield(self, db_field, **kwargs):
# does some funky stuff with the formfield here
So a first attempt might be to create your own admin class which subclasses
``NewsAdmin`` and ``TranslationAdmin`` to combine stuff like so::
class MyTranslatedNewsAdmin(NewsAdmin, TranslationAdmin):
pass
Unfortunately this won't work 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 field
This 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.
Inlines
-------
*New in development version*
Support for tabular and stacked inlines, common and generic ones.
A translated inline must derive from one of the following classes:
* `modeltranslation.admin.TranslationTabularInline`
* `modeltranslation.admin.TranslationStackedInline`
* `modeltranslation.admin.TranslationGenericTabularInline`
* `modeltranslation.admin.TranslationGenericStackedInline`
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 [InstallationAndUsage#TranslationAdmin_in_combination_with_other_admin_classes 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)
}}}
The ``update_translation_fields`` command
=========================================
In 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.
Caveats
=======
Consider 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 views
-----------------------------------------
Since 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.
.. _How Django discovers language preference: http://docs.djangoproject.com/en/dev/topics/i18n/#id2
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 projects
================
`django-multilingual`_
----------------------
A library providing support for multilingual content in Django models.
It is not possible to reuse existing models without modifying them.
`django-multilingual-model`_
----------------------------
A much simpler version of the above `django-multilingual`.
It works very similiar to the `django-multilingual` approach.
`transdb`_
----------
Django'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.
`i18ndynamic`_
--------------
This approach is not developed any more.
`django-pluggable-model-i18n`_
------------------------------
This 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.
.. _django-multilingual: http://code.google.com/p/django-multilingual/
.. _django-multilingual-model: http://code.google.com/p/django-multilingual-model/
.. _django-transdb: http://code.google.com/p/transdb/
.. _i18ndynamic: http://code.google.com/p/i18ndynamic/
.. _django-pluggable-model-i18n: http://code.google.com/p/django-pluggable-model-i18n/

View file

@ -1,644 +0,0 @@
.. _ref-topics-modeltranslation:
================
Modeltranslation
================
The 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*
- Peter Eschler <peschler@googlemail.com> (http://www.nmy.de)
- Dirk Eschler <eschler@gmail.com> (http://www.nmy.de)
.. contents::
Features
========
- Unlimited number of target languages
- Add translations without changing existing models
- Django admin support
- Supports inherited models
Installation
============
To install 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
project's ``settings.py``.
2. Configure your languages in the ``settings.py``.
3. Create a ``translation.py`` in your project directory and register
``TranslationOptions`` for every model you want to translate.
4. Configure the ``MODELTRANSLATION_TRANSLATION_REGISTRY`` variable in your
``settings.py``.
5. Sync the database using ``manage.py syncdb`` (note that this only applies
if the models registered in the ``translations.py`` did not have been
synced to the database before. If they did - read further down what to do
in that case.
Configure the project's ``settings.py``
---------------------------------------
The 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 translation
---------------------------------------------------
The ``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:
1. Create a ``translation.py`` in your project directory.
2. Create a translation option class for every model to translate.
3. Register the model and the translation option class at the
``modeltranslation.translator.translator``
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 class
------------------------------------------------
After 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.
.. _set_language: http://docs.djangoproject.com/en/dev/topics/i18n/#the-set-language-redirect-view
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 fields
===========================================
The ``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 field
The 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 access
---------------------------------
So 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 access
------------------------------------
Because 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 integration
================================
In 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 admin
---------------------------
The ``TranslationAdmin`` class does only implement one special method which is
``def formfield_for_dbfield(self, db_field, **kwargs)``. This method does the
following:
1. Removes the original field from every admin form by setting it
``editable=False``.
2. Copies the widget of the original field to each of it's translation fields.
3. Checks if the - now removed - original field was required and if so makes
the default translation field required instead.
.. _translationadmin_in_combination_with_other_admin_classes:
!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 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):
pass
In 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 here
Unfortunately 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 field
This 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.
Inlines
-------
*New in 0.2*
Support for tabular and stacked inlines, common and generic ones.
A translated inline must derive from one of the following classes:
* `modeltranslation.admin.TranslationTabularInline`
* `modeltranslation.admin.TranslationStackedInline`
* `modeltranslation.admin.TranslationGenericTabularInline`
* `modeltranslation.admin.TranslationGenericStackedInline`
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`__.:
__ 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 fields
-------------------------------
*New 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`` command
=========================================
In 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.
Caveats
=======
Consider 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 views
-----------------------------------------
Since 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.
.. _How Django discovers language preference: http://docs.djangoproject.com/en/dev/topics/i18n/#id2
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 projects
================
`django-multilingual`_
----------------------
A library providing support for multilingual content in Django models.
It is not possible to reuse existing models without modifying them.
`django-multilingual-model`_
----------------------------
A much simpler version of the above `django-multilingual`.
It works very similiar to the `django-multilingual` approach.
`transdb`_
----------
Django'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.
`i18ndynamic`_
--------------
This approach is not developed any more.
`django-pluggable-model-i18n`_
------------------------------
This 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.
.. _django-multilingual: http://code.google.com/p/django-multilingual/
.. _django-multilingual-model: http://code.google.com/p/django-multilingual-model/
.. _django-transdb: http://code.google.com/p/transdb/
.. _i18ndynamic: http://code.google.com/p/i18ndynamic/
.. _django-pluggable-model-i18n: http://code.google.com/p/django-pluggable-model-i18n/

View file

@ -1,702 +0,0 @@
.. _ref-topics-modeltranslation:
================
Modeltranslation
================
The 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*
- Peter Eschler <peschler@googlemail.com> (http://www.nmy.de)
- Dirk Eschler <eschler@gmail.com> (http://www.nmy.de)
.. contents::
Features
========
- Unlimited number of target languages
- Add translations without changing existing models
- Django admin support
- Supports inherited models
Installation
============
::
pip install django-modeltranslation
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
project's ``settings.py``.
2. Configure your ``LANGUAGES`` in ``settings.py``.
3. Create a ``translation.py`` in your app directory and register
``TranslationOptions`` for every model you want to translate.
4. Sync the database using ``manage.py syncdb`` (note that this only applies
if the models registered in the ``translations.py`` did not have been
synced to the database before. If they did - read further down what to do
in that case.
Configure the project's ``settings.py``
---------------------------------------
The 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_FILES**
*New in 0.4*
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``.
A setting ``MODELTRANSLATION_TRANSLATION_FILES`` is provided to limit or extend
the modules that are taken into account. It uses the following syntax:
::
('<APP1_MODULE>.translation',
'<APP2_MODULE>.translation',)
.. note:: Modeltranslation up to version 0.3 used a single project wide
registration file which was defined through
``MODELTRANSLATION_TRANSLATION_REGISTRY = '<PROJECT_MODULE>.translation'``.
For backwards compatibiliy the module defined through this setting is
automatically added to ``MODELTRANSLATION_TRANSLATION_FILES``. A
DeprecationWarning is issued in this case.
**settings.MODELTRANSLATION_CUSTOM_FIELDS**
*New in 0.3*
``Modeltranslation`` officially supports ``CharField`` and ``TextField``.
*New in 0.4*
Support for ``FileField`` and ``ImageField``.
In most cases subclasses of the supported 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 translation
---------------------------------------------------
The ``modeltranslation`` app can translate ``CharField`` and ``TextField``
based fields (as well as ``FileField`` and ``ImageField`` as of version 0.4)
of any model class. For each model to translate a translation option class
containing the fields to translate is registered with the ``modeltranslation``
app.
Registering models and their fields for translation requires the following
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
``modeltranslation.translator.translator``
The ``modeltranslation`` application reads the ``translation.py`` file in your
app directory thereby triggering the registration of the translation
options found in the file.
A translation option is a class that declares which fields of a model to
translate. The class must derive from ``modeltranslation.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.
To illustrate this let's have a look at a simple example using a ``News``
model. The news in this example only contains a ``title`` and a ``text`` field.
Instead of a news, this could be any Django model class:
::
class News(models.Model):
title = models.CharField(max_length=255)
text = models.TextField()
In order to tell the ``modeltranslation`` app to translate the ``title`` and
``text`` field, create a ``translation.py`` file in your news app directory and
add the following:
::
from modeltranslation.translator import translator, TranslationOptions
from news.models import News
class NewsTranslationOptions(TranslationOptions):
fields = ('title', 'text',)
translator.register(News, NewsTranslationOptions)
Note that this does not require to change the ``News`` model in any way, it's
only imported. The ``NewsTranslationOptions`` derives from
``TranslationOptions`` and provides the ``fields`` attribute. Finally the model
and its translation options are registered at the ``translator`` object.
At this point you are mostly done and the model classes registered for
translation will have been added some auto-magical fields. The next section
explains how things are working under the hood.
Changes automatically applied to the model class
------------------------------------------------
After registering the ``News`` model for translation 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.
.. _set_language: http://docs.djangoproject.com/en/dev/topics/i18n/#the-set-language-redirect-view
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 fields
===========================================
The ``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 field
The 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 access
---------------------------------
So 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 access
------------------------------------
Because 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 integration
================================
In 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 admin
---------------------------
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 does the following:
1. Copies the widget of the original field to each of it's translation fields.
2. Checks if the original field was required and if so makes
the default translation field required instead.
get_form and get_fieldsets
~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``TranslationBaseModelAdmin`` class overrides ``get_form``,
``get_fieldsets`` and ``_declared_fieldsets`` to make the options ``fields``,
``exclude`` and ``fieldsets`` work in a transparent way. It basically does:
1. Removes the original field from every admin form by adding it to
``exclude`` under the hood.
2. Replaces the - now removed - orginal fields with their corresponding
translation fields.
Taken the ``fieldsets`` option as an example, where the ``title`` field is
registered for translation but not the ``news`` field:
::
class NewsAdmin(TranslationAdmin):
fieldsets = [
(u'News', {'fields': ('title', 'news',)})
]
In this case ``get_fieldsets`` will return a patched fieldset which contains
the translation fields of ``title``, but not the original field:
::
>>> a = NewsAdmin(NewsModel, site)
>>> a.get_fieldsets(request)
[(u'News', {'fields': ('title_de', 'title_en', 'news',)})]
.. _translationadmin_in_combination_with_other_admin_classes:
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 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):
pass
In a more complex setup the NewsAdmin itself might override
formfield_for_dbfield:
::
class NewsAdmin(model.Admin):
def formfield_for_dbfield(self, db_field, **kwargs):
# does some funky stuff with the formfield here
Unfortunately 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 field
This 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.
Inlines
-------
*New in 0.2*
Support for tabular and stacked inlines, common and generic ones.
A translated inline must derive from one of the following classes:
* `modeltranslation.admin.TranslationTabularInline`
* `modeltranslation.admin.TranslationStackedInline`
* `modeltranslation.admin.TranslationGenericTabularInline`
* `modeltranslation.admin.TranslationGenericStackedInline`
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`__.:
__ 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 fields
-------------------------------
*New 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 = (
'modeltranslation/js/force_jquery.js',
'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js',
'modeltranslation/js/tabbed_translation_fields.js',
)
css = {
'screen': ('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`` command
=========================================
In 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.
The ``sync_translation_fields`` command
=======================================
*New in 0.4*
TODO
Caveats
=======
Consider the following example (assuming the default language 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 views
-----------------------------------------
Since 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.
.. _How Django discovers language preference: http://docs.djangoproject.com/en/dev/topics/i18n/#id2
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 projects
================
`django-multilingual`_
----------------------
A library providing support for multilingual content in Django models.
It is not possible to reuse existing models without modifying them.
`django-multilingual-model`_
----------------------------
A much simpler version of the above `django-multilingual`.
It works very similiar to the `django-multilingual` approach.
`transdb`_
----------
Django'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.
`i18ndynamic`_
--------------
This approach is not developed any more.
`django-pluggable-model-i18n`_
------------------------------
This 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.
.. _django-multilingual: http://code.google.com/p/django-multilingual/
.. _django-multilingual-model: http://code.google.com/p/django-multilingual-model/
.. _django-transdb: http://code.google.com/p/transdb/
.. _i18ndynamic: http://code.google.com/p/i18ndynamic/
.. _django-pluggable-model-i18n: http://code.google.com/p/django-pluggable-model-i18n/