mirror of https://github.com/django/django.git
Fixed #10260 - Refactored internationalization documentation. Thanks, Ramiro Morales.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12440 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
9b630a0857
commit
f93f056c32
|
@ -306,7 +306,7 @@ YEAR_MONTH_FORMAT = 'F Y'
|
|||
# http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
|
||||
MONTH_DAY_FORMAT = 'F j'
|
||||
|
||||
# Default shortformatting for date objects. See all available format strings here:
|
||||
# Default short formatting for date objects. See all available format strings here:
|
||||
# http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
|
||||
SHORT_DATE_FORMAT = 'm/d/Y'
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
.. _howto-i18n:
|
||||
|
||||
.. _using-translations-in-your-own-projects:
|
||||
|
||||
===============================================
|
||||
Using internationalization in your own projects
|
||||
===============================================
|
||||
|
||||
At runtime, 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 Django-provided base translation in
|
||||
``django/conf/locale``.
|
||||
|
||||
In all cases the name of the directory containing the translation is expected to
|
||||
be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``,
|
||||
etc.
|
||||
|
||||
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
|
||||
:ref:`settings-without-django-settings-module`, 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.)
|
||||
|
||||
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 :djadmin:`django-admin.py makemessages <makemessages>`
|
||||
tool. You only need to be in the same directory where the ``locale/`` directory
|
||||
is located. And you use :djadmin:`django-admin.py compilemessages <compilemessages>`
|
||||
to produce the binary ``.mo`` files that are used by ``gettext``. Read the
|
||||
:ref:`topics-i18n-localization` document for more details.
|
||||
|
||||
You can also run ``django-admin.py compilemessages --settings=path.to.settings``
|
||||
to make the compiler process all the directories in your :setting:`LOCALE_PATHS`
|
||||
setting.
|
||||
|
||||
Application message files are a bit complicated to discover -- they need the
|
||||
:class:`~django.middleware.locale.LocaleMiddleware`. If you don't use the
|
||||
middleware, only the Django message files and project message files will be
|
||||
installed and available at runtime.
|
||||
|
||||
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 ``makemessages``: It 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,
|
||||
``django-admin.py makemessages`` on the project level will only translate
|
||||
strings that are connected to your explicit project and not strings that are
|
||||
distributed independently.
|
|
@ -20,6 +20,7 @@ you quickly accomplish common tasks.
|
|||
deployment/index
|
||||
error-reporting
|
||||
initial-data
|
||||
i18n
|
||||
jython
|
||||
legacy-databases
|
||||
outputting-csv
|
||||
|
|
|
@ -402,15 +402,18 @@ translated, here's what to do:
|
|||
|
||||
* Join the `Django i18n mailing list`_ and introduce yourself.
|
||||
|
||||
* Make sure you read the notes about :ref:`specialties-of-django-i18n`.
|
||||
|
||||
* Create translations using the methods described in the
|
||||
:ref:`i18n documentation <topics-i18n>`. For this you will use the
|
||||
``django-admin.py makemessages`` tool. In this particular case it should
|
||||
be run from the top-level ``django`` directory of the Django source tree.
|
||||
:ref:`localization documentation <topics-i18n-localization>`. For this
|
||||
you will use the ``django-admin.py makemessages`` tool. In this
|
||||
particular case it should be run from the top-level ``django`` directory
|
||||
of the Django source tree.
|
||||
|
||||
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`` (for example for ``pt-BR``, the file will be
|
||||
``conf/locale/pt-br/LC_MESSAGES/django.po``).
|
||||
the directory ``conf/locale`` (for example for ``pt_BR``, the file will be
|
||||
``conf/locale/pt_BR/LC_MESSAGES/django.po``).
|
||||
|
||||
* Make sure that ``django-admin.py compilemessages -l <lang>`` runs without
|
||||
producing any warnings.
|
||||
|
@ -419,7 +422,11 @@ translated, here's what to do:
|
|||
``-d djangojs`` command line option to the ``django-admin.py``
|
||||
invocations).
|
||||
|
||||
* Create a diff of the ``.po`` file(s) against the current Subversion trunk.
|
||||
* Optionally, review and update the ``conf/locale/<locale>/formats.py``
|
||||
file to describe the date, time and numbers formatting particularities of
|
||||
your locale. See :ref:`format-localization` for details.
|
||||
|
||||
* Create a diff against the current Subversion trunk.
|
||||
|
||||
* Open a ticket in Django's ticket system, set its ``Component`` field to
|
||||
``Translations``, and attach the patch to it.
|
||||
|
|
|
@ -883,8 +883,8 @@ LANGUAGE_CODE
|
|||
Default: ``'en-us'``
|
||||
|
||||
A string representing the language code for this installation. This should be in
|
||||
standard language format. For example, U.S. English is ``"en-us"``. See
|
||||
:ref:`topics-i18n`.
|
||||
standard :term:`language format<language code>`. For example, U.S. English is
|
||||
``"en-us"``. See :ref:`topics-i18n`.
|
||||
|
||||
.. setting:: LANGUAGE_COOKIE_NAME
|
||||
|
||||
|
@ -911,9 +911,11 @@ see the current list of translated languages by looking in
|
|||
|
||||
.. _online source: http://code.djangoproject.com/browser/django/trunk/django/conf/global_settings.py
|
||||
|
||||
The list is a tuple of two-tuples in the format (language code, language
|
||||
name) -- for example, ``('ja', 'Japanese')``. This specifies which languages
|
||||
are available for language selection. See :ref:`topics-i18n`.
|
||||
The list is a tuple of two-tuples in the format ``(language code, language
|
||||
name)``, the ``language code`` part should be a
|
||||
:term:`language name<language code>` -- for example, ``('ja', 'Japanese')``.
|
||||
This specifies which languages are available for language selection. See
|
||||
:ref:`topics-i18n`.
|
||||
|
||||
Generally, the default value should suffice. Only set this setting if you want
|
||||
to restrict language selection to a subset of the Django-provided languages.
|
||||
|
@ -948,7 +950,7 @@ LOCALE_PATHS
|
|||
Default: ``()`` (Empty tuple)
|
||||
|
||||
A tuple of directories where Django looks for translation files.
|
||||
See :ref:`translations-in-your-own-projects`.
|
||||
See :ref:`using-translations-in-your-own-projects`.
|
||||
|
||||
.. setting:: LOGIN_REDIRECT_URL
|
||||
|
||||
|
|
|
@ -516,6 +516,19 @@ documentation <ref-contrib-syndication>`.
|
|||
|
||||
.. _RSS best practices: http://www.rssboard.org/rss-profile
|
||||
|
||||
Technical message IDs
|
||||
---------------------
|
||||
|
||||
Up to version 1.1 Django used :ref:`technical message IDs<technical-messages>`
|
||||
to provide localizers the possibility to translate date and time formats. They
|
||||
were translatable :term:`translation strings <translation string>` that could
|
||||
be recognized because they were all upper case (for example
|
||||
``DATETIME_FORMAT``, ``DATE_FORMAT``, ``TIME_FORMAT``). They have been
|
||||
deprecated in favor of the new :ref:`Format localization
|
||||
<format-localization>` infrastructure that allows localizers to specify that
|
||||
information in a ``formats.py`` file in the corresponding
|
||||
``django/conf/locale/<locale name>/`` directory.
|
||||
|
||||
What's new in Django 1.2
|
||||
========================
|
||||
|
||||
|
@ -577,7 +590,7 @@ added support for comparison operators. No longer will you have to type:
|
|||
.. code-block:: html+django
|
||||
|
||||
{% ifnotequal a b %}
|
||||
...
|
||||
...
|
||||
{% endifnotequal %}
|
||||
|
||||
You can now do this:
|
||||
|
@ -585,7 +598,7 @@ You can now do this:
|
|||
.. code-block:: html+django
|
||||
|
||||
{% if a != b %}
|
||||
...
|
||||
...
|
||||
{% endif %}
|
||||
|
||||
There's really no reason to use ``{% ifequal %}`` or ``{% ifnotequal %}``
|
||||
|
|
1144
docs/topics/i18n.txt
1144
docs/topics/i18n.txt
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,195 @@
|
|||
.. _topics-i18n-deployment:
|
||||
|
||||
==========================
|
||||
Deployment of translations
|
||||
==========================
|
||||
|
||||
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
|
||||
:setting:`USE_I18N = False <USE_I18N>` in your settings file. If
|
||||
:setting:`USE_I18N` is set to ``False``, then Django will make some
|
||||
optimizations so as not to load the internationalization machinery.
|
||||
|
||||
You'll probably also want to remove ``'django.core.context_processors.i18n'``
|
||||
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
|
||||
|
||||
.. note::
|
||||
|
||||
There is also an independent but related :setting:`USE_L10N` setting that
|
||||
controls if Django should implement format localization.
|
||||
|
||||
If :setting:`USE_L10N` is set to ``True``, Django will handle numbers times,
|
||||
and dates in the format of the current locale. That includes representation
|
||||
of these field types on templates and allowed input formats for dates,
|
||||
times on model forms.
|
||||
|
||||
See :ref:`format-localization` for more details.
|
||||
|
||||
If you do need internationalization
|
||||
===================================
|
||||
|
||||
.. _how-django-discovers-language-preference:
|
||||
|
||||
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 :setting:`LANGUAGE_CODE`.
|
||||
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 it, 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 :ref:`middleware documentation
|
||||
<topics-http-middleware>`.)
|
||||
|
||||
``LocaleMiddleware`` tries to determine the user's language preference by
|
||||
following this algorithm:
|
||||
|
||||
* First, it looks for a ``django_language`` key in the current user's
|
||||
session.
|
||||
|
||||
* Failing that, it looks for a cookie.
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
|
||||
In Django version 0.96 and before, the cookie's name is hard-coded to
|
||||
``django_language``. In Django 1,0, The cookie name is set by the
|
||||
``LANGUAGE_COOKIE_NAME`` setting. (The default name is
|
||||
``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.
|
||||
|
||||
.. _locale-middleware-notes:
|
||||
|
||||
Notes:
|
||||
|
||||
* In each of these places, the language preference is expected to be in the
|
||||
standard :term:`language format<language code>`, as a string. For example,
|
||||
Brazilian Portuguese 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 :setting:`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: ../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" ``ugettext()`` 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" ``ugettext()`` function. Here's a sample
|
||||
settings file::
|
||||
|
||||
ugettext = lambda s: s
|
||||
|
||||
LANGUAGES = (
|
||||
('de', ugettext('German')),
|
||||
('en', ugettext('English')),
|
||||
)
|
||||
|
||||
With this arrangement, ``django-admin.py makemessages`` 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* ``ugettext()`` 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 a basic
|
||||
one as described in the :ref:`Locale restrictions<locale-restrictions>`
|
||||
note.
|
||||
|
||||
Once ``LocaleMiddleware`` determines the user's preference, it makes this
|
||||
preference available as ``request.LANGUAGE_CODE`` for each
|
||||
:class:`~django.http.HttpRequest`. 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: ../settings/
|
||||
.. _middleware documentation: ../middleware/
|
||||
.. _session: ../sessions/
|
||||
.. _request object: ../request_response/#httprequest-objects
|
||||
|
||||
How Django discovers translations
|
||||
---------------------------------
|
||||
|
||||
As described in :ref:`using-translations-in-your-own-projects`,
|
||||
at runtime, 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 Django-provided base translation in
|
||||
``django/conf/locale``.
|
||||
|
||||
In all cases the name of the directory containing the translation is expected to
|
||||
be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``,
|
||||
etc.
|
|
@ -0,0 +1,115 @@
|
|||
.. _topics-i18n:
|
||||
|
||||
=====================================
|
||||
Internationalization and localization
|
||||
=====================================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Django has full support for internationalization of text in code and
|
||||
templates, and format localization of dates and numbers. Here's how it works.
|
||||
|
||||
Essentially, Django does two things:
|
||||
|
||||
* It allows developers and template authors to 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.
|
||||
|
||||
The complete process can be seen as divided in three stages. It is also possible
|
||||
to identify an identical number of roles with very well defined responsabilities
|
||||
associated with each of these tasks (although it's perfectly normal if you
|
||||
find yourself performing more than one of these roles):
|
||||
|
||||
* For applicacion authors wishing to make sure their Django apps can be
|
||||
used in different locales: :ref:`topics-i18n-internationalization`.
|
||||
* For translators wanting to translate Django apps: :ref:`topics-i18n-localization`.
|
||||
* For system administrators/final users setting up internationalized apps or
|
||||
developers integrating third party apps: :ref:`topics-i18n-deployment`.
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:maxdepth: 1
|
||||
|
||||
internationalization
|
||||
localization
|
||||
deployment
|
||||
|
||||
.. _ seealso::
|
||||
|
||||
For more general information about the topic, see the `GNU gettext documentation`_
|
||||
and the `Wikipedia article`_.
|
||||
|
||||
.. _GNU gettext documentation: http://www.gnu.org/software/gettext/manual/gettext.html#Concepts
|
||||
.. _Wikipedia article: http://en.wikipedia.org/wiki/Internationalization_and_localization
|
||||
|
||||
Glossary
|
||||
========
|
||||
|
||||
First lets define some terms that will help us to handle a common language:
|
||||
|
||||
.. glossary::
|
||||
|
||||
locale name
|
||||
A locale name, either a language specification of the form ``ll`` or a
|
||||
combined language and country specification of the form ``ll_CC``.
|
||||
Examples: ``it``, ``de_AT``, ``es``, ``pt_BR``. Note the underscore in
|
||||
some of them and the case of the part located to its right.
|
||||
|
||||
language code
|
||||
Represents the name of a language. Browsers send the names of the
|
||||
languages they accept in the ``Accept-Language`` HTTP header using this
|
||||
format. Examples: ``it``, ``de-at``, ``es``, ``pt-br``. Note the ``-``
|
||||
separator.
|
||||
|
||||
message file
|
||||
A message file is a plain-text file, representing a single language,
|
||||
that contains all available :term:`translation strings
|
||||
<translation string>` and how they should be represented in the given
|
||||
language. Message files have a ``.po`` file extension.
|
||||
|
||||
translation string
|
||||
A literal that can be translated.
|
||||
|
||||
.. _specialties-of-django-i18n:
|
||||
|
||||
Specialties of Django translation
|
||||
=================================
|
||||
|
||||
Django's translation machinery uses the standard ``gettext`` module that comes
|
||||
with Python. If you know ``gettext``, you might note these specialties in the
|
||||
way Django does translation:
|
||||
|
||||
* The string domain is ``django`` or ``djangojs``. This 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 doesn't use ``xgettext`` alone. It uses Python wrappers around
|
||||
``xgettext`` and ``msgfmt``. This is mostly for convenience.
|
||||
|
||||
.. _technical-messages:
|
||||
|
||||
Django technical message IDs
|
||||
----------------------------
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
Starting with Django 1.2, technical message IDs are being replaced by :ref:`format-localization`
|
||||
|
||||
Django uses technical message IDs to translate date formats and time formats.
|
||||
Technical message IDs are :term:`translation strings <translation string>` and
|
||||
can be easily recognized; they're all upper case. You don't translate the
|
||||
message ID as with other translation strings, you provide the correct local
|
||||
variant on the provided English value. The format is identical to the format
|
||||
strings used by the ``now`` template tag.
|
||||
|
||||
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. A Django
|
||||
contributor localizing it to Spanish probably would provide a ``"j N Y P"``
|
||||
"translation" for it in the relevant ``django.po`` file::
|
||||
|
||||
msgid "DATETIME_FORMAT"
|
||||
msgstr "j N Y P"
|
|
@ -0,0 +1,578 @@
|
|||
.. _topics-i18n-internationalization:
|
||||
|
||||
====================
|
||||
Internationalization
|
||||
====================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
The goal of internationalization is to allow a single Web application to offer
|
||||
its content and functionality in multiple languages and locales.
|
||||
|
||||
For text translations, you, the Django developer, can accomplish this goal by
|
||||
adding a minimal amount of hooks to your Python 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." It's your responsibility to mark translatable
|
||||
strings; the system can only translate strings it knows about.
|
||||
|
||||
Django takes care of using these hooks to translate Web apps, on the fly,
|
||||
according to users' language preferences.
|
||||
|
||||
Specifying translation strings: In Python code
|
||||
==============================================
|
||||
|
||||
Standard translation
|
||||
--------------------
|
||||
|
||||
Specify a translation string by using the function ``ugettext()``. It's
|
||||
convention to import this as a shorter alias, ``_``, to save typing.
|
||||
|
||||
.. note::
|
||||
Python's standard library ``gettext`` module installs ``_()`` into the
|
||||
global namespace, as an alias for ``gettext()``. In Django, we have chosen
|
||||
not to follow this practice, for a couple of reasons:
|
||||
|
||||
1. For international character set (Unicode) support, ``ugettext()`` is
|
||||
more useful than ``gettext()``. Sometimes, you should be using
|
||||
``ugettext_lazy()`` as the default translation method for a particular
|
||||
file. Without ``_()`` in the global namespace, the developer has to
|
||||
think about which is the most appropriate translation function.
|
||||
|
||||
2. The underscore character (``_``) is used to represent "the previous
|
||||
result" in Python's interactive shell and doctest tests. Installing a
|
||||
global ``_()`` function causes interference. Explicitly importing
|
||||
``ugettext()`` as ``_()`` avoids this problem.
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
In this example, the text ``"Welcome to my site."`` is marked as a translation
|
||||
string::
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
def my_view(request):
|
||||
output = _("Welcome to my site.")
|
||||
return HttpResponse(output)
|
||||
|
||||
Obviously, you could code this without using the alias. This example is
|
||||
identical to the previous one::
|
||||
|
||||
from django.utils.translation import ugettext
|
||||
|
||||
def my_view(request):
|
||||
output = ugettext("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,
|
||||
``django-admin.py makemessages``, won't be able to find these strings. More on
|
||||
``makemessages`` later.)
|
||||
|
||||
The strings you pass to ``_()`` or ``ugettext()`` can take placeholders,
|
||||
specified with Python's standard named-string interpolation syntax. Example::
|
||||
|
||||
def my_view(request, m, d):
|
||||
output = _('Today is %(month)s, %(day)s.') % {'month': m, 'day': d}
|
||||
return HttpResponse(output)
|
||||
|
||||
This technique lets language-specific translations reorder the placeholder
|
||||
text. For example, an English translation may be ``"Today is November, 26."``,
|
||||
while a Spanish translation may be ``"Hoy es 26 de Noviembre."`` -- with the
|
||||
placeholders (the month and the day) with their positions swapped.
|
||||
|
||||
For this reason, you should use named-string interpolation (e.g., ``%(day)s``)
|
||||
instead of positional interpolation (e.g., ``%s`` or ``%d``) whenever you
|
||||
have more than a single parameter. 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.ugettext_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.
|
||||
|
||||
Pluralization
|
||||
-------------
|
||||
|
||||
Use the function ``django.utils.translation.ungettext()`` to specify pluralized
|
||||
messages.
|
||||
|
||||
``ungettext`` takes three arguments: the singular translation string, the plural
|
||||
translation string and the number of objects.
|
||||
|
||||
This function is useful when you need your Django application to be localizable
|
||||
to languages where the number and complexity of `plural forms
|
||||
<http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms>`_ is
|
||||
greater than the two forms used in English ('object' for the singular and
|
||||
'objects' for all the cases where ``count`` is different from zero, irrespective
|
||||
of its value.)
|
||||
|
||||
For example::
|
||||
|
||||
from django.utils.translation import ungettext
|
||||
def hello_world(request, count):
|
||||
page = ungettext('there is %(count)d object', 'there are %(count)d objects', count) % {
|
||||
'count': count,
|
||||
}
|
||||
return HttpResponse(page)
|
||||
|
||||
In this example the number of objects is passed to the translation languages as
|
||||
the ``count`` variable.
|
||||
|
||||
Lets see a slightly more complex usage example::
|
||||
|
||||
from django.utils.translation import ungettext
|
||||
|
||||
count = Report.objects.count()
|
||||
if count == 1:
|
||||
name = Report._meta.verbose_name
|
||||
else:
|
||||
name = Report._meta.verbose_name_plural
|
||||
|
||||
text = ungettext(
|
||||
'There is %(count)d %(name)s available.',
|
||||
'There are %(count)d %(name)s available.',
|
||||
count
|
||||
) % {
|
||||
'count': count,
|
||||
'name': name
|
||||
}
|
||||
|
||||
Here we reuse localizable, hopefully already translated literals (contained in
|
||||
the ``verbose_name`` and ``verbose_name_plural`` model ``Meta`` options) for
|
||||
other parts of the sentence so all of it is consistently based on the
|
||||
cardinality of the elements at play.
|
||||
|
||||
.. _pluralization-var-notes:
|
||||
|
||||
.. note::
|
||||
|
||||
When using this technique, make sure you use a single name for every
|
||||
extrapolated variable included in the literal. In the example above note how
|
||||
we used the ``name`` Python variable in both translation strings. This
|
||||
example would fail::
|
||||
|
||||
from django.utils.translation import ungettext
|
||||
from myapp.models import Report
|
||||
|
||||
count = Report.objects.count()
|
||||
d = {
|
||||
'count': count,
|
||||
'name': Report._meta.verbose_name
|
||||
'plural_name': Report._meta.verbose_name_plural
|
||||
}
|
||||
text = ungettext(
|
||||
'There is %(count)d %(name)s available.',
|
||||
'There are %(count)d %(plural_name)s available.',
|
||||
count
|
||||
) % d
|
||||
|
||||
You would get a ``a format specification for argument 'name', as in
|
||||
'msgstr[0]', doesn't exist in 'msgid'`` error when running
|
||||
``django-admin.py compilemessages`` or a ``KeyError`` Python exception at
|
||||
runtime.
|
||||
|
||||
.. _lazy-translations:
|
||||
|
||||
Lazy translation
|
||||
----------------
|
||||
|
||||
Use the function ``django.utils.translation.ugettext_lazy()`` to translate
|
||||
strings lazily -- when the value is accessed rather than when the
|
||||
``ugettext_lazy()`` function is called.
|
||||
|
||||
For example, to translate a model's ``help_text``, do the following::
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
class MyThing(models.Model):
|
||||
name = models.CharField(help_text=ugettext_lazy('This is the help text'))
|
||||
|
||||
In this example, ``ugettext_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.
|
||||
|
||||
The result of a ``ugettext_lazy()`` call can be used wherever you would use a
|
||||
unicode string (an object with type ``unicode``) in Python. If you try to use
|
||||
it where a bytestring (a ``str`` object) is expected, things will not work as
|
||||
expected, since a ``ugettext_lazy()`` object doesn't know how to convert
|
||||
itself to a bytestring. You can't use a unicode string inside a bytestring,
|
||||
either, so this is consistent with normal Python behavior. For example::
|
||||
|
||||
# This is fine: putting a unicode proxy into a unicode string.
|
||||
u"Hello %s" % ugettext_lazy("people")
|
||||
|
||||
# This will not work, since you cannot insert a unicode object
|
||||
# into a bytestring (nor can you insert our unicode proxy there)
|
||||
"Hello %s" % ugettext_lazy("people")
|
||||
|
||||
If you ever see output that looks like ``"hello
|
||||
<django.utils.functional...>"``, you have tried to insert the result of
|
||||
``ugettext_lazy()`` into a bytestring. That's a bug in your code.
|
||||
|
||||
If you don't like the verbose name ``ugettext_lazy``, you can just alias it as
|
||||
``_`` (underscore), like so::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
class MyThing(models.Model):
|
||||
name = models.CharField(help_text=_('This is the help text'))
|
||||
|
||||
Always use lazy translations in :ref:`Django models <topics-db-models>`.
|
||||
Field names and table names should be marked for translation (otherwise, they
|
||||
won't be translated in the admin interface). This means writing explicit
|
||||
``verbose_name`` and ``verbose_name_plural`` options in the ``Meta`` class,
|
||||
though, rather than relying on Django's default determination of
|
||||
``verbose_name`` and ``verbose_name_plural`` by looking at the model's class
|
||||
name::
|
||||
|
||||
from django.utils.translation import ugettext_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')
|
||||
|
||||
Working with lazy translation objects
|
||||
-------------------------------------
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
Using ``ugettext_lazy()`` and ``ungettext_lazy()`` to mark strings in models
|
||||
and utility functions is a common operation. When you're working with these
|
||||
objects elsewhere in your code, you should ensure that you don't accidentally
|
||||
convert them to strings, because they should be converted as late as possible
|
||||
(so that the correct locale is in effect). This necessitates the use of a
|
||||
couple of helper functions.
|
||||
|
||||
Joining strings: string_concat()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Standard Python string joins (``''.join([...])``) will not work on lists
|
||||
containing lazy translation objects. Instead, you can use
|
||||
``django.utils.translation.string_concat()``, which creates a lazy object that
|
||||
concatenates its contents *and* converts them to strings only when the result
|
||||
is included in a string. For example::
|
||||
|
||||
from django.utils.translation import string_concat
|
||||
...
|
||||
name = ugettext_lazy(u'John Lennon')
|
||||
instrument = ugettext_lazy(u'guitar')
|
||||
result = string_concat([name, ': ', instrument])
|
||||
|
||||
In this case, the lazy translations in ``result`` will only be converted to
|
||||
strings when ``result`` itself is used in a string (usually at template
|
||||
rendering time).
|
||||
|
||||
The allow_lazy() decorator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Django offers many utility functions (particularly in ``django.utils``) that
|
||||
take a string as their first argument and do something to that string. These
|
||||
functions are used by template filters as well as directly in other code.
|
||||
|
||||
If you write your own similar functions and deal with translations, you'll
|
||||
face the problem of what to do when the first argument is a lazy translation
|
||||
object. You don't want to convert it to a string immediately, because you might
|
||||
be using this function outside of a view (and hence the current thread's locale
|
||||
setting will not be correct).
|
||||
|
||||
For cases like this, use the ``django.utils.functional.allow_lazy()``
|
||||
decorator. It modifies the function so that *if* it's called with a lazy
|
||||
translation as the first argument, the function evaluation is delayed until it
|
||||
needs to be converted to a string.
|
||||
|
||||
For example::
|
||||
|
||||
from django.utils.functional import allow_lazy
|
||||
|
||||
def fancy_utility_function(s, ...):
|
||||
# Do some conversion on string 's'
|
||||
...
|
||||
fancy_utility_function = allow_lazy(fancy_utility_function, unicode)
|
||||
|
||||
The ``allow_lazy()`` decorator takes, in addition to the function to decorate,
|
||||
a number of extra arguments (``*args``) specifying the type(s) that the
|
||||
original function can return. Usually, it's enough to include ``unicode`` here
|
||||
and ensure that your function returns only Unicode strings.
|
||||
|
||||
Using this decorator means you can write your function and assume that the
|
||||
input is a proper string, then add support for lazy translation objects at the
|
||||
end.
|
||||
|
||||
Specifying translation strings: In template code
|
||||
================================================
|
||||
|
||||
.. highlightlang:: html+django
|
||||
|
||||
Translations in :ref:`Django templates <topics-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 either a constant string
|
||||
(enclosed in single or double quotes) or variable content::
|
||||
|
||||
<title>{% trans "This is the title." %}</title>
|
||||
<title>{% trans myvar %}</title>
|
||||
|
||||
If the ``noop`` option is present, variable lookup still takes place but the
|
||||
translation is skipped. This is useful when "stubbing out" content that will
|
||||
require translation in the future::
|
||||
|
||||
<title>{% trans "myvar" noop %}</title>
|
||||
|
||||
Internally, inline translations use an ``ugettext`` call.
|
||||
|
||||
It's not possible to mix a template variable inside a string within ``{% trans
|
||||
%}``. If your translations require strings with variables (placeholders), use
|
||||
``{% blocktrans %}``::
|
||||
|
||||
{% blocktrans %}This string 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 %}
|
||||
|
||||
If you need to bind more than one expression inside a ``blocktrans`` tag,
|
||||
separate the pieces with ``and``::
|
||||
|
||||
{% blocktrans with book|title as book_t and author|title as author_t %}
|
||||
This is {{ book_t }} by {{ author_t }}
|
||||
{% endblocktrans %}
|
||||
|
||||
To pluralize, specify both the singular and plural forms with the
|
||||
``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and
|
||||
``{% endblocktrans %}``. Example::
|
||||
|
||||
{% blocktrans count list|length as counter %}
|
||||
There is only one {{ name }} object.
|
||||
{% plural %}
|
||||
There are {{ counter }} {{ name }} objects.
|
||||
{% endblocktrans %}
|
||||
|
||||
When you use the pluralization feature and bind additional values to local
|
||||
variables apart from the counter value that selects the translated literal to be
|
||||
used, have in mind that the ``blocktrans`` construct is internally converted
|
||||
to an ``ungettext`` call. This means the same :ref:`notes regarding ungettext
|
||||
variables <pluralization-var-notes>` apply.
|
||||
|
||||
Each ``RequestContext`` has access to three translation-specific variables:
|
||||
|
||||
* ``LANGUAGES`` is a list of tuples in which the first element is the
|
||||
:term:`language code` and the second is the language name (translated into
|
||||
the currently active locale).
|
||||
|
||||
* ``LANGUAGE_CODE`` is the current user's preferred language, as a string.
|
||||
Example: ``en-us``. (See :ref:`how-django-discovers-language-preference`.)
|
||||
|
||||
* ``LANGUAGE_BIDI`` is the current locale'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::
|
||||
|
||||
{% 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.
|
||||
|
||||
.. note::
|
||||
In this example, the translation infrastructure will be passed the string
|
||||
``"yes,no"``, not the individual strings ``"yes"`` and ``"no"``. The
|
||||
translated string will need to contain the comma so that the filter
|
||||
parsing code knows how to split up the arguments. For example, a German
|
||||
translator might translate the string ``"yes,no"`` as ``"ja,nein"``
|
||||
(keeping the comma intact).
|
||||
|
||||
.. _Django templates: ../templates_python/
|
||||
|
||||
Specifying translation strings: In JavaScript code
|
||||
==================================================
|
||||
|
||||
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 mimic 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 are merged 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={% url django.views.i18n.javascript_catalog %}"></script>
|
||||
|
||||
This uses reverse URL lookup to find the URL of the JavaScript catalog view.
|
||||
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 is also an ``ngettext`` interface::
|
||||
|
||||
var object_cnt = 1 // or 0, or 2, or 3, ...
|
||||
s = ngettext('literal for the singular case',
|
||||
'literal for the plural case', object_cnt);
|
||||
|
||||
and even a string interpolation function::
|
||||
|
||||
function interpolate(fmt, obj, named);
|
||||
|
||||
The interpolation syntax is borrowed from Python, so the ``interpolate``
|
||||
function supports both positional and named interpolation:
|
||||
|
||||
* Positional interpolation: ``obj`` contains a JavaScript Array object
|
||||
whose elements values are then sequentially interpolated in their
|
||||
corresponding ``fmt`` placeholders in the same order they appear.
|
||||
For example::
|
||||
|
||||
fmts = ngettext('There is %s object. Remaining: %s',
|
||||
'There are %s objects. Remaining: %s', 11);
|
||||
s = interpolate(fmts, [11, 20]);
|
||||
// s is 'There are 11 objects. Remaining: 20'
|
||||
|
||||
* Named interpolation: This mode is selected by passing the optional
|
||||
boolean ``named`` parameter as true. ``obj`` contains a JavaScript
|
||||
object or associative array. For example::
|
||||
|
||||
d = {
|
||||
count: 10
|
||||
total: 50
|
||||
};
|
||||
|
||||
fmts = ngettext('Total: %(total)s, there is %(count)s object',
|
||||
'there are %(count)s of a total of %(total)s objects', d.count);
|
||||
s = interpolate(fmts, d, true);
|
||||
|
||||
You shouldn't go over the top with string interpolation, though: this is still
|
||||
JavaScript, so the code has to make 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).
|
||||
|
||||
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 ``POST`` method, with a ``language``
|
||||
parameter set in request. If session support is enabled, the view
|
||||
saves the language choice in the user's session. Otherwise, it saves the
|
||||
language choice in a cookie that is by default named ``django_language``.
|
||||
(The name can be changed through the ``LANGUAGE_COOKIE_NAME`` setting.)
|
||||
|
||||
After setting the language choice, Django redirects the user, following this
|
||||
algorithm:
|
||||
|
||||
* Django looks for a ``next`` parameter in the ``POST`` data.
|
||||
* If that doesn't exist, or is empty, Django tries the URL in the
|
||||
``Referrer`` 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:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<form action="/i18n/setlang/" method="post">
|
||||
<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>
|
|
@ -0,0 +1,294 @@
|
|||
.. _topics-i18n-localization:
|
||||
|
||||
============
|
||||
Localization
|
||||
============
|
||||
|
||||
This document covers two localization-related topics: `Creating language
|
||||
files`_ and `locale aware date, time and numbers input/output in forms`_
|
||||
|
||||
.. _`Creating language files`: how-to-create-language-files_
|
||||
.. _`locale aware date, time and numbers input/output in forms`: format-localization_
|
||||
|
||||
.. seealso::
|
||||
|
||||
The :ref:`howto-i18n` document included with the Django HOW-TO documents collection.
|
||||
|
||||
.. _how-to-create-language-files:
|
||||
|
||||
How to create language files
|
||||
============================
|
||||
|
||||
Once the string literals of an application have been tagged for later
|
||||
translation, the translation themselves need to be written (or obtained). Here's
|
||||
how that works.
|
||||
|
||||
.. _locale-restrictions:
|
||||
|
||||
.. admonition:: Locale restrictions
|
||||
|
||||
Django does not support localizing your application into a locale for which
|
||||
Django itself has not been translated. In this case, it will ignore your
|
||||
translation files. If you were to try this and Django supported it, you
|
||||
would inevitably see a mixture of translated strings (from your application)
|
||||
and English strings (from Django itself). If you want to support a locale
|
||||
for your application that is not already part of Django, you'll need to make
|
||||
at least a minimal translation of the Django core.
|
||||
|
||||
A good starting point is to copy the Django English ``.po`` file and to
|
||||
translate at least some :term:`translation strings <translation string>`.
|
||||
|
||||
Message files
|
||||
-------------
|
||||
|
||||
The first step is to create a :term:`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, ``django-admin.py makemessages``, that automates the
|
||||
creation and upkeep of these files.
|
||||
|
||||
.. admonition:: A note to Django veterans
|
||||
|
||||
The old tool ``bin/make-messages.py`` has been moved to the command
|
||||
``django-admin.py makemessages`` to provide consistency throughout Django.
|
||||
|
||||
.. admonition:: Gettext utilities
|
||||
|
||||
The ``makemessages`` command (and ``compilemessages`` discussed later) use
|
||||
commands from the GNU gettext toolset: ``xgetetxt``, ``msgfmt``,
|
||||
``msgmerge`` and ``msguniq``.
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
|
||||
The minimum version of the ``gettext`` utilities supported is 0.15.
|
||||
|
||||
To create or update a message file, run this command::
|
||||
|
||||
django-admin.py makemessages -l de
|
||||
|
||||
...where ``de`` is the language code for the message file you want to create.
|
||||
The language code, in this case, is in :term:`locale format<locale name>`. For
|
||||
example, it's ``pt_BR`` for Brazilian Portuguese and ``de_AT`` for Austrian
|
||||
German.
|
||||
|
||||
The script should be run from one of two places:
|
||||
|
||||
* The root directory of your Django project.
|
||||
* The root directory of your Django app.
|
||||
|
||||
Th script runs over your project source tree or your application source tree and
|
||||
pulls out all strings marked for translation. It creates (or updates) a message
|
||||
file in the directory ``locale/LANG/LC_MESSAGES``. In the ``de`` example, the
|
||||
file will be ``locale/de/LC_MESSAGES/django.po``.
|
||||
|
||||
By default ``django-admin.py makemessages`` examines every file that has the
|
||||
``.html`` file extension. In case you want to override that default, use the
|
||||
``--extension`` or ``-e`` option to specify the file extensions to examine::
|
||||
|
||||
django-admin.py makemessages -l de -e txt
|
||||
|
||||
Separate multiple extensions with commas and/or use ``-e`` or ``--extension``
|
||||
multiple times::
|
||||
|
||||
django-admin.py makemessages -l de -e html,txt -e xml
|
||||
|
||||
When :ref:`creating message files from JavaScript source code
|
||||
<creating-message-files-from-js-code>` you need to use the special 'djangojs'
|
||||
domain, **not** ``-e js``.
|
||||
|
||||
.. admonition:: No gettext?
|
||||
|
||||
If you don't have the ``gettext`` utilities installed, ``django-admin.py
|
||||
makemessages`` will create empty files. If that's the case, either install
|
||||
the ``gettext`` utilities or just copy the English message file
|
||||
(``locale/en/LC_MESSAGES/django.po``) if available and use it as a starting
|
||||
point; it's just an empty translation file.
|
||||
|
||||
.. admonition:: Working on Windows?
|
||||
|
||||
If you're using Windows and need to install the GNU gettext utilities so
|
||||
``django-admin makemessages`` works see :ref:`gettext_on_windows` for more
|
||||
information.
|
||||
|
||||
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 ``django-admin.py makemessages`` 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, in the form of a comment line
|
||||
prefixed with ``#`` and located above the ``msgid`` line, 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
|
||||
directly concatenated. 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. Due to the way the ``gettext`` tools
|
||||
work internally and because we want to allow non-ASCII source strings in
|
||||
Django's core and your applications, you **must** use UTF-8 as the encoding
|
||||
for your PO file. This means that everybody will be using the same
|
||||
encoding, which is important when Django processes the PO files.
|
||||
|
||||
To reexamine all source code and templates for new translation strings and
|
||||
update all message files for **all** languages, run this::
|
||||
|
||||
django-admin.py makemessages -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 ``django-admin.py compilemessages`` 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 ``django-admin.py makemessages``, run ``django-admin.py
|
||||
compilemessages`` like this::
|
||||
|
||||
django-admin.py compilemessages
|
||||
|
||||
That's it. Your translations are ready for use.
|
||||
|
||||
.. admonition:: A note to Django veterans
|
||||
|
||||
The old tool ``bin/compile-messages.py`` has been moved to the command
|
||||
``django-admin.py compilemessages`` to provide consistency throughout
|
||||
Django.
|
||||
|
||||
.. admonition:: Working on Windows?
|
||||
|
||||
If you're using Windows and need to install the GNU gettext utilities so
|
||||
``django-admin compilemessages`` works see :ref:`gettext_on_windows` for more
|
||||
information.
|
||||
|
||||
.. _creating-message-files-from-js-code:
|
||||
|
||||
Creating message files from JavaScript source code
|
||||
==================================================
|
||||
|
||||
You create and update the message files the same way as the other Django message
|
||||
files -- with the ``django-admin.py makemessages`` tool. The only difference is
|
||||
you need to provide a ``-d djangojs`` parameter, like this::
|
||||
|
||||
django-admin.py makemessages -d djangojs -l de
|
||||
|
||||
This would create or update the message file for JavaScript for German.
|
||||
After updating message files, just run ``django-admin.py compilemessages``
|
||||
the same way as you do with normal Django message files.
|
||||
|
||||
.. _gettext_on_windows:
|
||||
|
||||
``gettext`` on Windows
|
||||
======================
|
||||
|
||||
This is only needed for people who either want to extract message IDs or compile
|
||||
message files (``.po``). Translation work itself just involves editing existing
|
||||
files of this type, but if you want to create your own message files, or want to
|
||||
test or compile a changed message file, you will need the ``gettext`` utilities:
|
||||
|
||||
* Download the following zip files from the GNOME servers
|
||||
http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/ or from one
|
||||
of its mirrors_
|
||||
|
||||
* ``gettext-runtime-X.zip``
|
||||
* ``gettext-tools-X.zip``
|
||||
|
||||
``X`` is the version number, we are requiring ``0.15`` or higher.
|
||||
|
||||
* Extract the contents of the ``bin\`` directories in both files to the
|
||||
same folder on your system (i.e. ``C:\Program Files\gettext-utils``)
|
||||
|
||||
* Update the system PATH:
|
||||
|
||||
* ``Control Panel > System > Advanced > Environment Variables``.
|
||||
* In the ``System variables`` list, click ``Path``, click ``Edit``.
|
||||
* Add ``;C:\Program Files\gettext-utils\bin`` at the end of the
|
||||
``Variable value`` field.
|
||||
|
||||
.. _mirrors: http://ftp.gnome.org/pub/GNOME/MIRRORS
|
||||
|
||||
You may also use ``gettext`` binaries you have obtained elsewhere, so long as
|
||||
the ``xgettext --version`` command works properly. Some version 0.14.4 binaries
|
||||
have been found to not support this command. Do not attempt to use Django
|
||||
translation utilities with a ``gettext`` package if the command ``xgettext
|
||||
--version`` entered at a Windows command prompt causes a popup window saying
|
||||
"xgettext.exe has generated errors and will be closed by Windows".
|
||||
|
||||
.. _format-localization:
|
||||
|
||||
Format localization
|
||||
===================
|
||||
|
||||
Django's formatting system is disabled by default. To enable it, it's necessay
|
||||
to set :setting:`USE_L10N = True <USE_L10N>` in your settings file.
|
||||
|
||||
When using Django's formatting system, dates and numbers on templates will be
|
||||
displayed using the format specified for the current locale. Two users
|
||||
accessing the same content, but in different language, will see date and
|
||||
number fields formatted in different ways, depending on the format for their
|
||||
current locale.
|
||||
|
||||
Django will also use localized formats when parsing data in forms. That means
|
||||
Django uses different formats for different locales when guessing the format
|
||||
used by the user when inputting data on forms. Note that Django uses different
|
||||
formats for displaying data, and for parsing it.
|
||||
|
||||
Creating custom format files
|
||||
----------------------------
|
||||
|
||||
Django provides format definitions for many locales, but sometimes you might
|
||||
want to create your own, because a format files doesn't exist for your locale,
|
||||
or because you want to overwrite some of the values.
|
||||
|
||||
To use custom formats, first thing to do, is to specify the path where you'll
|
||||
place format files. To do that, just set your :setting:`FORMAT_MODULE_PATH`
|
||||
setting to the the path (in the format ``'foo.bar.baz``) where format files
|
||||
will exists.
|
||||
|
||||
Files are not placed directly in this directory, but in a directory named as
|
||||
the locale, and must be named ``formats.py``.
|
||||
|
||||
To customize the English formats, a structure like this would be needed::
|
||||
|
||||
mysite/
|
||||
formats/
|
||||
__init__.py
|
||||
en/
|
||||
__init__.py
|
||||
formats.py
|
||||
|
||||
where :file:`formats.py` contains custom format definitions. For example::
|
||||
|
||||
THOUSAND_SEPARATOR = ' '
|
||||
|
||||
to use a space as a thousand separator, instead of the default for English,
|
||||
a comma.
|
|
@ -21,7 +21,7 @@ Introductions to all the key parts of Django you'll need to know:
|
|||
cache
|
||||
conditional-view-processing
|
||||
email
|
||||
i18n
|
||||
i18n/index
|
||||
pagination
|
||||
serialization
|
||||
settings
|
||||
|
|
Loading…
Reference in New Issue