747 lines
31 KiB
Plaintext
747 lines
31 KiB
Plaintext
====================
|
|
Internationalization
|
|
====================
|
|
|
|
Django has full support for internationalization of text in code and templates.
|
|
Here's how it works.
|
|
|
|
Overview
|
|
========
|
|
|
|
The goal of internationalization is to allow a single Web application to offer
|
|
its content and functionality in multiple languages.
|
|
|
|
You, the Django developer, can accomplish this goal by adding a minimal amount
|
|
of hooks to your Python code and templates. These hooks are called
|
|
**translation strings**. They tell Django: "This text should be translated into
|
|
the end user's language, if a translation for this text is available in that
|
|
language."
|
|
|
|
Django takes care of using these hooks to translate Web apps, on the fly,
|
|
according to users' language preferences.
|
|
|
|
Essentially, Django does two things:
|
|
|
|
* It lets developers and template authors specify which parts of their apps
|
|
should be translatable.
|
|
* It uses these hooks to translate Web apps for particular users according
|
|
to their language preferences.
|
|
|
|
How to internationalize your app: in three steps
|
|
------------------------------------------------
|
|
|
|
1. Embed translation strings in your Python code and templates.
|
|
2. Get translations for those strings, in whichever languages you want to
|
|
support.
|
|
3. Activate the locale middleware in your Django settings.
|
|
|
|
.. admonition:: Behind the scenes
|
|
|
|
Django's translation machinery uses the standard ``gettext`` module that
|
|
comes with Python.
|
|
|
|
If you don't need internationalization
|
|
======================================
|
|
|
|
Django's internationalization hooks are on by default, and that means there's a
|
|
bit of i18n-related overhead in certain places of the framework. If you don't
|
|
use internationalization, you should take the two seconds to set
|
|
``USE_I18N = False`` in your settings file. If ``USE_I18N`` is set to
|
|
``False``, then Django will make some optimizations so as not to load the
|
|
internationalization machinery.
|
|
|
|
See the `documentation for USE_I18N`_.
|
|
|
|
.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n
|
|
|
|
How to specify translation strings
|
|
==================================
|
|
|
|
Translation strings specify "This text should be translated." These strings can
|
|
appear in your Python code and templates. It's your responsibility to mark
|
|
translatable strings; the system can only translate strings it knows about.
|
|
|
|
In Python code
|
|
--------------
|
|
|
|
Standard translation
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Specify a translation string by using the function ``_()``. (Yes, the name of
|
|
the function is the "underscore" character.) This function is available
|
|
globally in any Python module; you don't have to import it.
|
|
|
|
In this example, the text ``"Welcome to my site."`` is marked as a translation
|
|
string::
|
|
|
|
def my_view(request):
|
|
output = _("Welcome to my site.")
|
|
return HttpResponse(output)
|
|
|
|
The function ``django.utils.translation.gettext()`` is identical to ``_()``.
|
|
This example is identical to the previous one::
|
|
|
|
from django.utils.translation import gettext
|
|
def my_view(request):
|
|
output = gettext("Welcome to my site.")
|
|
return HttpResponse(output)
|
|
|
|
Translation works on computed values. This example is identical to the previous
|
|
two::
|
|
|
|
def my_view(request):
|
|
words = ['Welcome', 'to', 'my', 'site.']
|
|
output = _(' '.join(words))
|
|
return HttpResponse(output)
|
|
|
|
Translation works on variables. Again, here's an identical example::
|
|
|
|
def my_view(request):
|
|
sentence = 'Welcome to my site.'
|
|
output = _(sentence)
|
|
return HttpResponse(output)
|
|
|
|
(The caveat with using variables or computed values, as in the previous two
|
|
examples, is that Django's translation-string-detecting utility,
|
|
``make-messages.py``, won't be able to find these strings. More on
|
|
``make-messages`` later.)
|
|
|
|
The strings you pass to ``_()`` or ``gettext()`` can take placeholders,
|
|
specified with Python's standard named-string interpolation syntax. Example::
|
|
|
|
def my_view(request, n):
|
|
output = _('%(name)s is my name.') % {'name': n}
|
|
return HttpResponse(output)
|
|
|
|
This technique lets language-specific translations reorder the placeholder
|
|
text. For example, an English translation may be ``"Adrian is my name."``,
|
|
while a Spanish translation may be ``"Me llamo Adrian."`` -- with the
|
|
placeholder (the name) placed after the translated text instead of before it.
|
|
|
|
For this reason, you should use named-string interpolation (e.g., ``%(name)s``)
|
|
instead of positional interpolation (e.g., ``%s`` or ``%d``). If you used
|
|
positional interpolation, translations wouldn't be able to reorder placeholder
|
|
text.
|
|
|
|
Marking strings as no-op
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Use the function ``django.utils.translation.gettext_noop()`` to mark a string
|
|
as a translation string without translating it. The string is later translated
|
|
from a variable.
|
|
|
|
Use this if you have constant strings that should be stored in the source
|
|
language because they are exchanged over systems or users -- such as strings in
|
|
a database -- but should be translated at the last possible point in time, such
|
|
as when the string is presented to the user.
|
|
|
|
Lazy translation
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
Use the function ``django.utils.translation.gettext_lazy()`` to translate
|
|
strings lazily -- when the value is accessed rather than when the
|
|
``gettext_lazy()`` function is called.
|
|
|
|
For example, to translate a model's ``help_text``, do the following::
|
|
|
|
from django.utils.translation import gettext_lazy
|
|
|
|
class MyThing(models.Model):
|
|
name = models.CharField(help_text=gettext_lazy('This is the help text'))
|
|
|
|
In this example, ``gettext_lazy()`` stores a lazy reference to the string --
|
|
not the actual translation. The translation itself will be done when the string
|
|
is used in a string context, such as template rendering on the Django admin site.
|
|
|
|
If you don't like the verbose name ``gettext_lazy``, you can just alias it as
|
|
``_`` (underscore), like so::
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
class MyThing(models.Model):
|
|
name = models.CharField(help_text=_('This is the help text'))
|
|
|
|
Always use lazy translations in `Django models`_. And it's a good idea to add
|
|
translations for the field names and table names, too. This means writing
|
|
explicit ``verbose_name`` and ``verbose_name_plural`` options in the ``Meta``
|
|
class, though::
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
class MyThing(models.Model):
|
|
name = models.CharField(_('name'), help_text=_('This is the help text'))
|
|
class Meta:
|
|
verbose_name = _('my thing')
|
|
verbose_name_plural = _('mythings')
|
|
|
|
.. _Django models: http://www.djangoproject.com/documentation/model_api/
|
|
|
|
Pluralization
|
|
~~~~~~~~~~~~~
|
|
|
|
Use the function ``django.utils.translation.ngettext()`` to specify pluralized
|
|
messages. Example::
|
|
|
|
from django.utils.translation import ngettext
|
|
def hello_world(request, count):
|
|
page = ngettext('there is %(count)d object', 'there are %(count)d objects', count) % {
|
|
'count': count,
|
|
}
|
|
return HttpResponse(page)
|
|
|
|
``ngettext`` takes three arguments: the singular translation string, the plural
|
|
translation string and the number of objects (which is passed to the
|
|
translation languages as the ``count`` variable).
|
|
|
|
In template code
|
|
----------------
|
|
|
|
Using translations in `Django templates`_ uses two template tags and a slightly
|
|
different syntax than in Python code. To give your template access to these
|
|
tags, put ``{% load i18n %}`` toward the top of your template.
|
|
|
|
The ``{% trans %}`` template tag translates a constant string or a variable
|
|
content::
|
|
|
|
<title>{% trans "This is the title." %}</title>
|
|
|
|
If you only want to mark a value for translation, but translate it later from a
|
|
variable, use the ``noop`` option::
|
|
|
|
<title>{% trans "value" noop %}</title>
|
|
|
|
It's not possible to use template variables in ``{% trans %}`` -- only constant
|
|
strings, in single or double quotes, are allowed. If your translations require
|
|
variables (placeholders), use ``{% blocktrans %}``. Example::
|
|
|
|
{% blocktrans %}This will have {{ value }} inside.{% endblocktrans %}
|
|
|
|
To translate a template expression -- say, using template filters -- you need
|
|
to bind the expression to a local variable for use within the translation
|
|
block::
|
|
|
|
{% blocktrans with value|filter as myvar %}
|
|
This will have {{ myvar }} inside.
|
|
{% endblocktrans %}
|
|
|
|
To pluralize, specify both the singular and plural forms with the
|
|
``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and
|
|
``{% endblocktrans %}``. Example::
|
|
|
|
{% blocktrans count list|counted as counter %}
|
|
There is only one {{ name }} object.
|
|
{% plural %}
|
|
There are {{ counter }} {{ name }} objects.
|
|
{% endblocktrans %}
|
|
|
|
Internally, all block and inline translations use the appropriate
|
|
``gettext`` / ``ngettext`` call.
|
|
|
|
Each ``RequestContext`` has access to two translation-specific variables:
|
|
|
|
* ``LANGUAGES`` is a list of tuples in which the first element is the
|
|
language code and the second is the language name (in that language).
|
|
* ``LANGUAGE_CODE`` is the current user's preferred language, as a string.
|
|
Example: ``en-us``. (See "How language preference is discovered", below.)
|
|
* ``LANGUAGE_BIDI`` is the current language's direction. If True, it's a
|
|
right-to-left language, e.g: Hebrew, Arabic. If False it's a
|
|
left-to-right language, e.g: English, French, German etc.
|
|
|
|
|
|
If you don't use the ``RequestContext`` extension, you can get those values with
|
|
three tags::
|
|
|
|
{% get_current_language as LANGUAGE_CODE %}
|
|
{% get_available_languages as LANGUAGES %}
|
|
{% get_current_language_bidi as LANGUAGE_BIDI %}
|
|
|
|
These tags also require a ``{% load i18n %}``.
|
|
|
|
Translation hooks are also available within any template block tag that accepts
|
|
constant strings. In those cases, just use ``_()`` syntax to specify a
|
|
translation string. Example::
|
|
|
|
{% some_special_tag _("Page not found") value|yesno:_("yes,no") %}
|
|
|
|
In this case, both the tag and the filter will see the already-translated
|
|
string, so they don't need to be aware of translations.
|
|
|
|
.. _Django templates: http://www.djangoproject.com/documentation/templates_python/
|
|
|
|
How to create language files
|
|
============================
|
|
|
|
Once you've tagged your strings for later translation, you need to write (or
|
|
obtain) the language translations themselves. Here's how that works.
|
|
|
|
Message files
|
|
-------------
|
|
|
|
The first step is to create a **message file** for a new language. A message
|
|
file is a plain-text file, representing a single language, that contains all
|
|
available translation strings and how they should be represented in the given
|
|
language. Message files have a ``.po`` file extension.
|
|
|
|
Django comes with a tool, ``bin/make-messages.py``, that automates the creation
|
|
and upkeep of these files.
|
|
|
|
To create or update a message file, run this command::
|
|
|
|
bin/make-messages.py -l de
|
|
|
|
...where ``de`` is the language code for the message file you want to create.
|
|
The language code, in this case, is in locale format. For example, it's
|
|
``pt_BR`` for Brazilian and ``de_AT`` for Austrian German.
|
|
|
|
The script should be run from one of three places:
|
|
|
|
* The root ``django`` directory (not a Subversion checkout, but the one
|
|
that is linked-to via ``$PYTHONPATH`` or is located somewhere on that
|
|
path).
|
|
* The root directory of your Django project.
|
|
* The root directory of your Django app.
|
|
|
|
The script runs over the entire Django source tree and pulls out all strings
|
|
marked for translation. It creates (or updates) a message file in the directory
|
|
``conf/locale``. In the ``de`` example, the file will be
|
|
``conf/locale/de/LC_MESSAGES/django.po``.
|
|
|
|
If run over your project source tree or your appliation source tree, it will
|
|
do the same, but the location of the locale directory is ``locale/LANG/LC_MESSAGES``
|
|
(note the missing ``conf`` prefix).
|
|
|
|
.. admonition:: No gettext?
|
|
|
|
If you don't have the ``gettext`` utilities installed, ``make-messages.py``
|
|
will create empty files. If that's the case, either install the ``gettext``
|
|
utilities or just copy the English message file
|
|
(``conf/locale/en/LC_MESSAGES/django.po``) and use it as a starting point;
|
|
it's just an empty translation file.
|
|
|
|
The format of ``.po`` files is straightforward. Each ``.po`` file contains a
|
|
small bit of metadata, such as the translation maintainer's contact
|
|
information, but the bulk of the file is a list of **messages** -- simple
|
|
mappings between translation strings and the actual translated text for the
|
|
particular language.
|
|
|
|
For example, if your Django app contained a translation string for the text
|
|
``"Welcome to my site."``, like so::
|
|
|
|
_("Welcome to my site.")
|
|
|
|
...then ``make-messages.py`` will have created a ``.po`` file containing the
|
|
following snippet -- a message::
|
|
|
|
#: path/to/python/module.py:23
|
|
msgid "Welcome to my site."
|
|
msgstr ""
|
|
|
|
A quick explanation:
|
|
|
|
* ``msgid`` is the translation string, which appears in the source. Don't
|
|
change it.
|
|
* ``msgstr`` is where you put the language-specific translation. It starts
|
|
out empty, so it's your responsibility to change it. Make sure you keep
|
|
the quotes around your translation.
|
|
* As a convenience, each message includes the filename and line number
|
|
from which the translation string was gleaned.
|
|
|
|
Long messages are a special case. There, the first string directly after the
|
|
``msgstr`` (or ``msgid``) is an empty string. Then the content itself will be
|
|
written over the next few lines as one string per line. Those strings are
|
|
directlyconcatenated. Don't forget trailing spaces within the strings;
|
|
otherwise, they'll be tacked together without whitespace!
|
|
|
|
.. admonition:: Mind your charset
|
|
|
|
When creating a ``.po`` file with your favorite text editor, first edit
|
|
the charset line (search for ``"CHARSET"``) and set it to the charset
|
|
you'll be using to edit the content. Generally, utf-8 should work for most
|
|
languages, but ``gettext`` should handle any charset you throw at it.
|
|
|
|
To reexamine all source code and templates for new translation strings and
|
|
update all message files for **all** languages, run this::
|
|
|
|
make-messages.py -a
|
|
|
|
Compiling message files
|
|
-----------------------
|
|
|
|
After you create your message file -- and each time you make changes to it --
|
|
you'll need to compile it into a more efficient form, for use by ``gettext``.
|
|
Do this with the ``bin/compile-messages.py`` utility.
|
|
|
|
This tool runs over all available ``.po`` files and creates ``.mo`` files,
|
|
which are binary files optimized for use by ``gettext``. In the same directory
|
|
from which you ran ``make-messages.py``, run ``compile-messages.py`` like
|
|
this::
|
|
|
|
bin/compile-messages.py
|
|
|
|
That's it. Your translations are ready for use.
|
|
|
|
.. admonition:: A note to translators
|
|
|
|
If you've created a translation in a language Django doesn't yet support,
|
|
please let us know! See `Submitting and maintaining translations`_ for
|
|
the steps to take.
|
|
|
|
.. _Submitting and maintaining translations: http://www.djangoproject.com/documentation/contributing/
|
|
|
|
How Django discovers language preference
|
|
========================================
|
|
|
|
Once you've prepared your translations -- or, if you just want to use the
|
|
translations that come with Django -- you'll just need to activate translation
|
|
for your app.
|
|
|
|
Behind the scenes, Django has a very flexible model of deciding which language
|
|
should be used -- installation-wide, for a particular user, or both.
|
|
|
|
To set an installation-wide language preference, set ``LANGUAGE_CODE`` in your
|
|
`settings file`_. Django uses this language as the default translation -- the
|
|
final attempt if no other translator finds a translation.
|
|
|
|
If all you want to do is run Django with your native language, and a language
|
|
file is available for your language, all you need to do is set
|
|
``LANGUAGE_CODE``.
|
|
|
|
If you want to let each individual user specify which language he or she
|
|
prefers, use ``LocaleMiddleware``. ``LocaleMiddleware`` enables language
|
|
selection based on data from the request. It customizes content for each user.
|
|
|
|
To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'``
|
|
to your ``MIDDLEWARE_CLASSES`` setting. Because middleware order matters, you
|
|
should follow these guidelines:
|
|
|
|
* Make sure it's one of the first middlewares installed.
|
|
* It should come after ``SessionMiddleware``, because ``LocaleMiddleware``
|
|
makes use of session data.
|
|
* If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it.
|
|
|
|
For example, your ``MIDDLEWARE_CLASSES`` might look like this::
|
|
|
|
MIDDLEWARE_CLASSES = (
|
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
'django.middleware.locale.LocaleMiddleware',
|
|
'django.middleware.common.CommonMiddleware',
|
|
)
|
|
|
|
(For more on middleware, see the `middleware documentation`_.)
|
|
|
|
``LocaleMiddleware`` tries to determine the user's language preference by
|
|
following this algorithm:
|
|
|
|
* First, it looks for a ``django_language`` key in the the current user's
|
|
`session`_.
|
|
* Failing that, it looks for a cookie called ``django_language``.
|
|
* Failing that, it looks at the ``Accept-Language`` HTTP header. This
|
|
header is sent by your browser and tells the server which language(s) you
|
|
prefer, in order by priority. Django tries each language in the header
|
|
until it finds one with available translations.
|
|
* Failing that, it uses the global ``LANGUAGE_CODE`` setting.
|
|
|
|
Notes:
|
|
|
|
* In each of these places, the language preference is expected to be in the
|
|
standard language format, as a string. For example, Brazilian is
|
|
``pt-br``.
|
|
* If a base language is available but the sublanguage specified is not,
|
|
Django uses the base language. For example, if a user specifies ``de-at``
|
|
(Austrian German) but Django only has ``de`` available, Django uses
|
|
``de``.
|
|
* Only languages listed in the `LANGUAGES setting`_ can be selected. If
|
|
you want to restrict the language selection to a subset of provided
|
|
languages (because your application doesn't provide all those languages),
|
|
set ``LANGUAGES`` to a list of languages. For example::
|
|
|
|
LANGUAGES = (
|
|
('de', _('German')),
|
|
('en', _('English')),
|
|
)
|
|
|
|
This example restricts languages that are available for automatic
|
|
selection to German and English (and any sublanguage, like de-ch or
|
|
en-us).
|
|
|
|
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
|
|
|
* If you define a custom ``LANGUAGES`` setting, as explained in the
|
|
previous bullet, it's OK to mark the languages as translation strings
|
|
-- but use a "dummy" ``gettext()`` function, not the one in
|
|
``django.utils.translation``. You should *never* import
|
|
``django.utils.translation`` from within your settings file, because that
|
|
module in itself depends on the settings, and that would cause a circular
|
|
import.
|
|
|
|
The solution is to use a "dummy" ``gettext()`` function. Here's a sample
|
|
settings file::
|
|
|
|
gettext = lambda s: s
|
|
|
|
LANGUAGES = (
|
|
('de', gettext('German')),
|
|
('en', gettext('English')),
|
|
)
|
|
|
|
With this arrangement, ``make-messages.py`` will still find and mark
|
|
these strings for translation, but the translation won't happen at
|
|
runtime -- so you'll have to remember to wrap the languages in the *real*
|
|
``gettext()`` in any code that uses ``LANGUAGES`` at runtime.
|
|
|
|
* The ``LocaleMiddleware`` can only select languages for which there is a
|
|
Django-provided base translation. If you want to provide translations
|
|
for your application that aren't already in the set of translations
|
|
in Django's source tree, you'll want to provide at least basic
|
|
translations for that language. For example, Django uses technical
|
|
message IDs to translate date formats and time formats -- so you will
|
|
need at least those translations for the system to work correctly.
|
|
|
|
A good starting point is to copy the English ``.po`` file and to
|
|
translate at least the technical messages -- maybe the validator
|
|
messages, too.
|
|
|
|
Technical message IDs are easily recognized; they're all upper case. You
|
|
don't translate the message ID as with other messages, you provide the
|
|
correct local variant on the provided English value. For example, with
|
|
``DATETIME_FORMAT`` (or ``DATE_FORMAT`` or ``TIME_FORMAT``), this would
|
|
be the format string that you want to use in your language. The format
|
|
is identical to the format strings used by the ``now`` template tag.
|
|
|
|
Once ``LocaleMiddleware`` determines the user's preference, it makes this
|
|
preference available as ``request.LANGUAGE_CODE`` for each `request object`_.
|
|
Feel free to read this value in your view code. Here's a simple example::
|
|
|
|
def hello_world(request, count):
|
|
if request.LANGUAGE_CODE == 'de-at':
|
|
return HttpResponse("You prefer to read Austrian German.")
|
|
else:
|
|
return HttpResponse("You prefer to read another language.")
|
|
|
|
Note that, with static (middleware-less) translation, the language is in
|
|
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
|
|
in ``request.LANGUAGE_CODE``.
|
|
|
|
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
|
.. _middleware documentation: http://www.djangoproject.com/documentation/middleware/
|
|
.. _session: http://www.djangoproject.com/documentation/sessions/
|
|
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
|
|
|
The ``set_language`` redirect view
|
|
==================================
|
|
|
|
As a convenience, Django comes with a view, ``django.views.i18n.set_language``,
|
|
that sets a user's language preference and redirects back to the previous page.
|
|
|
|
Activate this view by adding the following line to your URLconf::
|
|
|
|
(r'^i18n/', include('django.conf.urls.i18n')),
|
|
|
|
(Note that this example makes the view available at ``/i18n/setlang/``.)
|
|
|
|
The view expects to be called via the ``GET`` method, with a ``language``
|
|
parameter set in the query string. If session support is enabled, the view
|
|
saves the language choice in the user's session. Otherwise, it saves the
|
|
language choice in a ``django_language`` cookie.
|
|
|
|
After setting the language choice, Django redirects the user, following this
|
|
algorithm:
|
|
|
|
* Django looks for a ``next`` parameter in the query string.
|
|
* If that doesn't exist, or is empty, Django tries the URL in the
|
|
``Referer`` header.
|
|
* If that's empty -- say, if a user's browser suppresses that header --
|
|
then the user will be redirected to ``/`` (the site root) as a fallback.
|
|
|
|
Here's example HTML template code::
|
|
|
|
<form action="/i18n/setlang/" method="get">
|
|
<input name="next" type="hidden" value="/next/page/" />
|
|
<select name="language">
|
|
{% for lang in LANGUAGES %}
|
|
<option value="{{ lang.0 }}">{{ lang.1 }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<input type="submit" value="Go" />
|
|
</form>
|
|
|
|
Using translations in your own projects
|
|
=======================================
|
|
|
|
Django looks for translations by following this algorithm:
|
|
|
|
* First, it looks for a ``locale`` directory in the application directory
|
|
of the view that's being called. If it finds a translation for the
|
|
selected language, the translation will be installed.
|
|
* Next, it looks for a ``locale`` directory in the project directory. If it
|
|
finds a translation, the translation will be installed.
|
|
* Finally, it checks the base translation in ``django/conf/locale``.
|
|
|
|
This way, you can write applications that include their own translations, and
|
|
you can override base translations in your project path. Or, you can just build
|
|
a big project out of several apps and put all translations into one big project
|
|
message file. The choice is yours.
|
|
|
|
.. note::
|
|
|
|
If you're using manually configured settings, as described in the
|
|
`settings documentation`_, the ``locale`` directory in the project
|
|
directory will not be examined, since Django loses the ability to work out
|
|
the location of the project directory. (Django normally uses the location
|
|
of the settings file to determine this, and a settings file doesn't exist
|
|
if you're manually configuring your settings.)
|
|
|
|
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
|
|
|
All message file repositories are structured the same way. They are:
|
|
|
|
* ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)``
|
|
* ``$PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)``
|
|
* All paths listed in ``LOCALE_PATHS`` in your settings file are
|
|
searched in that order for ``<language>/LC_MESSAGES/django.(po|mo)``
|
|
* ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)``
|
|
|
|
To create message files, you use the same ``make-messages.py`` tool as with the
|
|
Django message files. You only need to be in the right place -- in the directory
|
|
where either the ``conf/locale`` (in case of the source tree) or the ``locale/``
|
|
(in case of app messages or project messages) directory are located. And you
|
|
use the same ``compile-messages.py`` to produce the binary ``django.mo`` files that
|
|
are used by ``gettext``.
|
|
|
|
Application message files are a bit complicated to discover -- they need the
|
|
``LocaleMiddleware``. If you don't use the middleware, only the Django message
|
|
files and project message files will be processed.
|
|
|
|
Finally, you should give some thought to the structure of your translation
|
|
files. If your applications need to be delivered to other users and will
|
|
be used in other projects, you might want to use app-specific translations.
|
|
But using app-specific translations and project translations could produce
|
|
weird problems with ``make-messages``: ``make-messages`` will traverse all
|
|
directories below the current path and so might put message IDs into the
|
|
project message file that are already in application message files.
|
|
|
|
The easiest way out is to store applications that are not part of the project
|
|
(and so carry their own translations) outside the project tree. That way,
|
|
``make-messages`` on the project level will only translate strings that are
|
|
connected to your explicit project and not strings that are distributed
|
|
independently.
|
|
|
|
Translations and JavaScript
|
|
===========================
|
|
|
|
Adding translations to JavaScript poses some problems:
|
|
|
|
* JavaScript code doesn't have access to a ``gettext`` implementation.
|
|
|
|
* JavaScript code doesn't have access to .po or .mo files; they need to be
|
|
delivered by the server.
|
|
|
|
* The translation catalogs for JavaScript should be kept as small as
|
|
possible.
|
|
|
|
Django provides an integrated solution for these problems: It passes the
|
|
translations into JavaScript, so you can call ``gettext``, etc., from within
|
|
JavaScript.
|
|
|
|
The ``javascript_catalog`` view
|
|
-------------------------------
|
|
|
|
The main solution to these problems is the ``javascript_catalog`` view, which
|
|
sends out a JavaScript code library with functions that mimick the ``gettext``
|
|
interface, plus an array of translation strings. Those translation strings are
|
|
taken from the application, project or Django core, according to what you
|
|
specify in either the {{{info_dict}}} or the URL.
|
|
|
|
You hook it up like this::
|
|
|
|
js_info_dict = {
|
|
'packages': ('your.app.package',),
|
|
}
|
|
|
|
urlpatterns = patterns('',
|
|
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
|
|
)
|
|
|
|
Each string in ``packages`` should be in Python dotted-package syntax (the
|
|
same format as the strings in ``INSTALLED_APPS``) and should refer to a package
|
|
that contains a ``locale`` directory. If you specify multiple packages, all
|
|
those catalogs aremerged into one catalog. This is useful if you have
|
|
JavaScript that uses strings from different applications.
|
|
|
|
You can make the view dynamic by putting the packages into the URL pattern::
|
|
|
|
urlpatterns = patterns('',
|
|
(r'^jsi18n/(?P<packages>\S+?)/$, 'django.views.i18n.javascript_catalog'),
|
|
)
|
|
|
|
With this, you specify the packages as a list of package names delimited by '+'
|
|
signs in the URL. This is especially useful if your pages use code from
|
|
different apps and this changes often and you don't want to pull in one big
|
|
catalog file. As a security measure, these values can only be either
|
|
``django.conf`` or any package from the ``INSTALLED_APPS`` setting.
|
|
|
|
Using the JavaScript translation catalog
|
|
----------------------------------------
|
|
|
|
To use the catalog, just pull in the dynamically generated script like this::
|
|
|
|
<script type="text/javascript" src="/path/to/jsi18n/"></script>
|
|
|
|
This is how the admin fetches the translation catalog from the server. When the
|
|
catalog is loaded, your JavaScript code can use the standard ``gettext``
|
|
interface to access it::
|
|
|
|
document.write(gettext('this is to be translated'));
|
|
|
|
There even is a ``ngettext`` interface and a string interpolation function::
|
|
|
|
d = {
|
|
count: 10
|
|
};
|
|
s = interpolate(ngettext('this is %(count)s object', 'this are %(count)s objects', d.count), d);
|
|
|
|
The ``interpolate`` function supports both positional interpolation and named
|
|
interpolation. So the above could have been written as::
|
|
|
|
s = interpolate(ngettext('this is %s object', 'this are %s objects', 11), [11]);
|
|
|
|
The interpolation syntax is borrowed from Python. You shouldn't go over the top
|
|
with string interpolation, though: this is still JavaScript, so the code will
|
|
have to do repeated regular-expression substitutions. This isn't as fast as
|
|
string interpolation in Python, so keep it to those cases where you really
|
|
need it (for example, in conjunction with ``ngettext`` to produce proper
|
|
pluralizations).
|
|
|
|
Creating JavaScript translation catalogs
|
|
----------------------------------------
|
|
|
|
You create and update the translation catalogs the same way as the other Django
|
|
translation catalogs -- with the {{{make-messages.py}}} tool. The only
|
|
difference is you need to provide a ``-d djangojs`` parameter, like this::
|
|
|
|
make-messages.py -d djangojs -l de
|
|
|
|
This would create or update the translation catalog for JavaScript for German.
|
|
After updating translation catalogs, just run ``compile-messages.py`` the same
|
|
way as you do with normal Django translation catalogs.
|
|
|
|
Specialities of Django translation
|
|
==================================
|
|
|
|
If you know ``gettext``, you might note these specialities in the way Django
|
|
does translation:
|
|
|
|
* The string domain is ``django`` or ``djangojs``. The string domain is used to
|
|
differentiate between different programs that store their data in a
|
|
common message-file library (usually ``/usr/share/locale/``). The ``django``
|
|
domain is used for python and template translation strings and is loaded into
|
|
the global translation catalogs. The ``djangojs`` domain is only used for
|
|
JavaScript translation catalogs to make sure that those are as small as
|
|
possible.
|
|
* Django only uses ``gettext`` and ``gettext_noop``. That's because Django
|
|
always uses ``DEFAULT_CHARSET`` strings internally. There isn't much use
|
|
in using ``ugettext``, because you'll always need to produce utf-8
|
|
anyway.
|
|
* Django doesn't use ``xgettext`` alone. It uses Python wrappers around
|
|
``xgettext`` and ``msgfmt``. That's mostly for convenience.
|