mirror of
https://github.com/Hopiu/django-modeltranslation.git
synced 2026-05-04 19:44:43 +00:00
Converted documentation to shpinx and added an initial setup (work-in-progress).
This commit is contained in:
parent
38f65c340a
commit
2f869bf723
8 changed files with 701 additions and 2423 deletions
153
docs/modeltranslation/Makefile
Normal file
153
docs/modeltranslation/Makefile
Normal 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."
|
||||
285
docs/modeltranslation/conf.py
Normal file
285
docs/modeltranslation/conf.py
Normal 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
|
||||
|
|
@ -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`
|
||||
190
docs/modeltranslation/make.bat
Normal file
190
docs/modeltranslation/make.bat
Normal 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
|
||||
|
|
@ -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/
|
||||
|
|
@ -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/
|
||||
|
|
@ -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/
|
||||
|
|
@ -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/
|
||||
Loading…
Reference in a new issue