| 1 |
==================== |
|---|
| 2 |
Internationalization |
|---|
| 3 |
==================== |
|---|
| 4 |
|
|---|
| 5 |
Django has full support for internationalization of text in code and templates. |
|---|
| 6 |
Here's how it works. |
|---|
| 7 |
|
|---|
| 8 |
Overview |
|---|
| 9 |
======== |
|---|
| 10 |
|
|---|
| 11 |
The goal of internationalization is to allow a single Web application to offer |
|---|
| 12 |
its content and functionality in multiple languages. |
|---|
| 13 |
|
|---|
| 14 |
You, the Django developer, can accomplish this goal by adding a minimal amount |
|---|
| 15 |
of hooks to your Python code and templates. These hooks are called |
|---|
| 16 |
**translation strings**. They tell Django: "This text should be translated into |
|---|
| 17 |
the end user's language, if a translation for this text is available in that |
|---|
| 18 |
language." |
|---|
| 19 |
|
|---|
| 20 |
Django takes care of using these hooks to translate Web apps, on the fly, |
|---|
| 21 |
according to users' language preferences. |
|---|
| 22 |
|
|---|
| 23 |
Essentially, Django does two things: |
|---|
| 24 |
|
|---|
| 25 |
* It lets developers and template authors specify which parts of their apps |
|---|
| 26 |
should be translatable. |
|---|
| 27 |
* It uses these hooks to translate Web apps for particular users according |
|---|
| 28 |
to their language preferences. |
|---|
| 29 |
|
|---|
| 30 |
How to internationalize your app: in three steps |
|---|
| 31 |
------------------------------------------------ |
|---|
| 32 |
|
|---|
| 33 |
1. Embed translation strings in your Python code and templates. |
|---|
| 34 |
2. Get translations for those strings, in whichever languages you want to |
|---|
| 35 |
support. |
|---|
| 36 |
3. Activate the locale middleware in your Django settings. |
|---|
| 37 |
|
|---|
| 38 |
.. admonition:: Behind the scenes |
|---|
| 39 |
|
|---|
| 40 |
Django's translation machinery uses the standard ``gettext`` module that |
|---|
| 41 |
comes with Python. |
|---|
| 42 |
|
|---|
| 43 |
If you don't need internationalization |
|---|
| 44 |
====================================== |
|---|
| 45 |
|
|---|
| 46 |
Django's internationalization hooks are on by default, and that means there's a |
|---|
| 47 |
bit of i18n-related overhead in certain places of the framework. If you don't |
|---|
| 48 |
use internationalization, you should take the two seconds to set |
|---|
| 49 |
``USE_I18N = False`` in your settings file. If ``USE_I18N`` is set to |
|---|
| 50 |
``False``, then Django will make some optimizations so as not to load the |
|---|
| 51 |
internationalization machinery. |
|---|
| 52 |
|
|---|
| 53 |
See the `documentation for USE_I18N`_. |
|---|
| 54 |
|
|---|
| 55 |
.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n |
|---|
| 56 |
|
|---|
| 57 |
How to specify translation strings |
|---|
| 58 |
================================== |
|---|
| 59 |
|
|---|
| 60 |
Translation strings specify "This text should be translated." These strings can |
|---|
| 61 |
appear in your Python code and templates. It's your responsibility to mark |
|---|
| 62 |
translatable strings; the system can only translate strings it knows about. |
|---|
| 63 |
|
|---|
| 64 |
In Python code |
|---|
| 65 |
-------------- |
|---|
| 66 |
|
|---|
| 67 |
Standard translation |
|---|
| 68 |
~~~~~~~~~~~~~~~~~~~~ |
|---|
| 69 |
|
|---|
| 70 |
Specify a translation string by using the function ``_()``. (Yes, the name of |
|---|
| 71 |
the function is the "underscore" character.) This function is available |
|---|
| 72 |
globally in any Python module; you don't have to import it. |
|---|
| 73 |
|
|---|
| 74 |
In this example, the text ``"Welcome to my site."`` is marked as a translation |
|---|
| 75 |
string:: |
|---|
| 76 |
|
|---|
| 77 |
def my_view(request): |
|---|
| 78 |
output = _("Welcome to my site.") |
|---|
| 79 |
return HttpResponse(output) |
|---|
| 80 |
|
|---|
| 81 |
The function ``django.utils.translation.gettext()`` is identical to ``_()``. |
|---|
| 82 |
This example is identical to the previous one:: |
|---|
| 83 |
|
|---|
| 84 |
from django.utils.translation import gettext |
|---|
| 85 |
def my_view(request): |
|---|
| 86 |
output = gettext("Welcome to my site.") |
|---|
| 87 |
return HttpResponse(output) |
|---|
| 88 |
|
|---|
| 89 |
Translation works on computed values. This example is identical to the previous |
|---|
| 90 |
two:: |
|---|
| 91 |
|
|---|
| 92 |
def my_view(request): |
|---|
| 93 |
words = ['Welcome', 'to', 'my', 'site.'] |
|---|
| 94 |
output = _(' '.join(words)) |
|---|
| 95 |
return HttpResponse(output) |
|---|
| 96 |
|
|---|
| 97 |
Translation works on variables. Again, here's an identical example:: |
|---|
| 98 |
|
|---|
| 99 |
def my_view(request): |
|---|
| 100 |
sentence = 'Welcome to my site.' |
|---|
| 101 |
output = _(sentence) |
|---|
| 102 |
return HttpResponse(output) |
|---|
| 103 |
|
|---|
| 104 |
(The caveat with using variables or computed values, as in the previous two |
|---|
| 105 |
examples, is that Django's translation-string-detecting utility, |
|---|
| 106 |
``make-messages.py``, won't be able to find these strings. More on |
|---|
| 107 |
``make-messages`` later.) |
|---|
| 108 |
|
|---|
| 109 |
The strings you pass to ``_()`` or ``gettext()`` can take placeholders, |
|---|
| 110 |
specified with Python's standard named-string interpolation syntax. Example:: |
|---|
| 111 |
|
|---|
| 112 |
def my_view(request, n): |
|---|
| 113 |
output = _('%(name)s is my name.') % {'name': n} |
|---|
| 114 |
return HttpResponse(output) |
|---|
| 115 |
|
|---|
| 116 |
This technique lets language-specific translations reorder the placeholder |
|---|
| 117 |
text. For example, an English translation may be ``"Adrian is my name."``, |
|---|
| 118 |
while a Spanish translation may be ``"Me llamo Adrian."`` -- with the |
|---|
| 119 |
placeholder (the name) placed after the translated text instead of before it. |
|---|
| 120 |
|
|---|
| 121 |
For this reason, you should use named-string interpolation (e.g., ``%(name)s``) |
|---|
| 122 |
instead of positional interpolation (e.g., ``%s`` or ``%d``). If you used |
|---|
| 123 |
positional interpolation, translations wouldn't be able to reorder placeholder |
|---|
| 124 |
text. |
|---|
| 125 |
|
|---|
| 126 |
Marking strings as no-op |
|---|
| 127 |
~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 128 |
|
|---|
| 129 |
Use the function ``django.utils.translation.gettext_noop()`` to mark a string |
|---|
| 130 |
as a translation string without translating it. The string is later translated |
|---|
| 131 |
from a variable. |
|---|
| 132 |
|
|---|
| 133 |
Use this if you have constant strings that should be stored in the source |
|---|
| 134 |
language because they are exchanged over systems or users -- such as strings in |
|---|
| 135 |
a database -- but should be translated at the last possible point in time, such |
|---|
| 136 |
as when the string is presented to the user. |
|---|
| 137 |
|
|---|
| 138 |
Lazy translation |
|---|
| 139 |
~~~~~~~~~~~~~~~~ |
|---|
| 140 |
|
|---|
| 141 |
Use the function ``django.utils.translation.gettext_lazy()`` to translate |
|---|
| 142 |
strings lazily -- when the value is accessed rather than when the |
|---|
| 143 |
``gettext_lazy()`` function is called. |
|---|
| 144 |
|
|---|
| 145 |
For example, to translate a model's ``help_text``, do the following:: |
|---|
| 146 |
|
|---|
| 147 |
from django.utils.translation import gettext_lazy |
|---|
| 148 |
|
|---|
| 149 |
class MyThing(models.Model): |
|---|
| 150 |
name = models.CharField(help_text=gettext_lazy('This is the help text')) |
|---|
| 151 |
|
|---|
| 152 |
In this example, ``gettext_lazy()`` stores a lazy reference to the string -- |
|---|
| 153 |
not the actual translation. The translation itself will be done when the string |
|---|
| 154 |
is used in a string context, such as template rendering on the Django admin site. |
|---|
| 155 |
|
|---|
| 156 |
If you don't like the verbose name ``gettext_lazy``, you can just alias it as |
|---|
| 157 |
``_`` (underscore), like so:: |
|---|
| 158 |
|
|---|
| 159 |
from django.utils.translation import gettext_lazy as _ |
|---|
| 160 |
|
|---|
| 161 |
class MyThing(models.Model): |
|---|
| 162 |
name = models.CharField(help_text=_('This is the help text')) |
|---|
| 163 |
|
|---|
| 164 |
Always use lazy translations in `Django models`_. And it's a good idea to add |
|---|
| 165 |
translations for the field names and table names, too. This means writing |
|---|
| 166 |
explicit ``verbose_name`` and ``verbose_name_plural`` options in the ``Meta`` |
|---|
| 167 |
class, though:: |
|---|
| 168 |
|
|---|
| 169 |
from django.utils.translation import gettext_lazy as _ |
|---|
| 170 |
|
|---|
| 171 |
class MyThing(models.Model): |
|---|
| 172 |
name = models.CharField(_('name'), help_text=_('This is the help text')) |
|---|
| 173 |
class Meta: |
|---|
| 174 |
verbose_name = _('my thing') |
|---|
| 175 |
verbose_name_plural = _('mythings') |
|---|
| 176 |
|
|---|
| 177 |
.. _Django models: http://www.djangoproject.com/documentation/model_api/ |
|---|
| 178 |
|
|---|
| 179 |
Pluralization |
|---|
| 180 |
~~~~~~~~~~~~~ |
|---|
| 181 |
|
|---|
| 182 |
Use the function ``django.utils.translation.ngettext()`` to specify pluralized |
|---|
| 183 |
messages. Example:: |
|---|
| 184 |
|
|---|
| 185 |
from django.utils.translation import ngettext |
|---|
| 186 |
def hello_world(request, count): |
|---|
| 187 |
page = ngettext('there is %(count)d object', 'there are %(count)d objects', count) % { |
|---|
| 188 |
'count': count, |
|---|
| 189 |
} |
|---|
| 190 |
return HttpResponse(page) |
|---|
| 191 |
|
|---|
| 192 |
``ngettext`` takes three arguments: the singular translation string, the plural |
|---|
| 193 |
translation string and the number of objects (which is passed to the |
|---|
| 194 |
translation languages as the ``count`` variable). |
|---|
| 195 |
|
|---|
| 196 |
In template code |
|---|
| 197 |
---------------- |
|---|
| 198 |
|
|---|
| 199 |
Using translations in `Django templates`_ uses two template tags and a slightly |
|---|
| 200 |
different syntax than in Python code. To give your template access to these |
|---|
| 201 |
tags, put ``{% load i18n %}`` toward the top of your template. |
|---|
| 202 |
|
|---|
| 203 |
The ``{% trans %}`` template tag translates a constant string or a variable |
|---|
| 204 |
content:: |
|---|
| 205 |
|
|---|
| 206 |
<title>{% trans "This is the title." %}</title> |
|---|
| 207 |
|
|---|
| 208 |
If you only want to mark a value for translation, but translate it later from a |
|---|
| 209 |
variable, use the ``noop`` option:: |
|---|
| 210 |
|
|---|
| 211 |
<title>{% trans "value" noop %}</title> |
|---|
| 212 |
|
|---|
| 213 |
It's not possible to use template variables in ``{% trans %}`` -- only constant |
|---|
| 214 |
strings, in single or double quotes, are allowed. If your translations require |
|---|
| 215 |
variables (placeholders), use ``{% blocktrans %}``. Example:: |
|---|
| 216 |
|
|---|
| 217 |
{% blocktrans %}This will have {{ value }} inside.{% endblocktrans %} |
|---|
| 218 |
|
|---|
| 219 |
To translate a template expression -- say, using template filters -- you need |
|---|
| 220 |
to bind the expression to a local variable for use within the translation |
|---|
| 221 |
block:: |
|---|
| 222 |
|
|---|
| 223 |
{% blocktrans with value|filter as myvar %} |
|---|
| 224 |
This will have {{ myvar }} inside. |
|---|
| 225 |
{% endblocktrans %} |
|---|
| 226 |
|
|---|
| 227 |
If you need to bind more than one expression inside a ``blocktrans`` tag, |
|---|
| 228 |
separate the pieces with ``and``:: |
|---|
| 229 |
|
|---|
| 230 |
{% blocktrans with book|title as book_t and author|title as author_t %} |
|---|
| 231 |
This is {{ book_t }} by {{ author_t }} |
|---|
| 232 |
{% endblocktrans %} |
|---|
| 233 |
|
|---|
| 234 |
To pluralize, specify both the singular and plural forms with the |
|---|
| 235 |
``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and |
|---|
| 236 |
``{% endblocktrans %}``. Example:: |
|---|
| 237 |
|
|---|
| 238 |
{% blocktrans count list|count as counter %} |
|---|
| 239 |
There is only one {{ name }} object. |
|---|
| 240 |
{% plural %} |
|---|
| 241 |
There are {{ counter }} {{ name }} objects. |
|---|
| 242 |
{% endblocktrans %} |
|---|
| 243 |
|
|---|
| 244 |
Internally, all block and inline translations use the appropriate |
|---|
| 245 |
``gettext`` / ``ngettext`` call. |
|---|
| 246 |
|
|---|
| 247 |
Each ``RequestContext`` has access to two translation-specific variables: |
|---|
| 248 |
|
|---|
| 249 |
* ``LANGUAGES`` is a list of tuples in which the first element is the |
|---|
| 250 |
language code and the second is the language name (in that language). |
|---|
| 251 |
* ``LANGUAGE_CODE`` is the current user's preferred language, as a string. |
|---|
| 252 |
Example: ``en-us``. (See "How language preference is discovered", below.) |
|---|
| 253 |
* ``LANGUAGE_BIDI`` is the current language's direction. If True, it's a |
|---|
| 254 |
right-to-left language, e.g: Hebrew, Arabic. If False it's a |
|---|
| 255 |
left-to-right language, e.g: English, French, German etc. |
|---|
| 256 |
|
|---|
| 257 |
|
|---|
| 258 |
If you don't use the ``RequestContext`` extension, you can get those values with |
|---|
| 259 |
three tags:: |
|---|
| 260 |
|
|---|
| 261 |
{% get_current_language as LANGUAGE_CODE %} |
|---|
| 262 |
{% get_available_languages as LANGUAGES %} |
|---|
| 263 |
{% get_current_language_bidi as LANGUAGE_BIDI %} |
|---|
| 264 |
|
|---|
| 265 |
These tags also require a ``{% load i18n %}``. |
|---|
| 266 |
|
|---|
| 267 |
Translation hooks are also available within any template block tag that accepts |
|---|
| 268 |
constant strings. In those cases, just use ``_()`` syntax to specify a |
|---|
| 269 |
translation string. Example:: |
|---|
| 270 |
|
|---|
| 271 |
{% some_special_tag _("Page not found") value|yesno:_("yes,no") %} |
|---|
| 272 |
|
|---|
| 273 |
In this case, both the tag and the filter will see the already-translated |
|---|
| 274 |
string, so they don't need to be aware of translations. |
|---|
| 275 |
|
|---|
| 276 |
.. _Django templates: http://www.djangoproject.com/documentation/templates_python/ |
|---|
| 277 |
|
|---|
| 278 |
How to create language files |
|---|
| 279 |
============================ |
|---|
| 280 |
|
|---|
| 281 |
Once you've tagged your strings for later translation, you need to write (or |
|---|
| 282 |
obtain) the language translations themselves. Here's how that works. |
|---|
| 283 |
|
|---|
| 284 |
Message files |
|---|
| 285 |
------------- |
|---|
| 286 |
|
|---|
| 287 |
The first step is to create a **message file** for a new language. A message |
|---|
| 288 |
file is a plain-text file, representing a single language, that contains all |
|---|
| 289 |
available translation strings and how they should be represented in the given |
|---|
| 290 |
language. Message files have a ``.po`` file extension. |
|---|
| 291 |
|
|---|
| 292 |
Django comes with a tool, ``bin/make-messages.py``, that automates the creation |
|---|
| 293 |
and upkeep of these files. |
|---|
| 294 |
|
|---|
| 295 |
To create or update a message file, run this command:: |
|---|
| 296 |
|
|---|
| 297 |
bin/make-messages.py -l de |
|---|
| 298 |
|
|---|
| 299 |
...where ``de`` is the language code for the message file you want to create. |
|---|
| 300 |
The language code, in this case, is in locale format. For example, it's |
|---|
| 301 |
``pt_BR`` for Brazilian and ``de_AT`` for Austrian German. |
|---|
| 302 |
|
|---|
| 303 |
The script should be run from one of three places: |
|---|
| 304 |
|
|---|
| 305 |
* The root ``django`` directory (not a Subversion checkout, but the one |
|---|
| 306 |
that is linked-to via ``$PYTHONPATH`` or is located somewhere on that |
|---|
| 307 |
path). |
|---|
| 308 |
* The root directory of your Django project. |
|---|
| 309 |
* The root directory of your Django app. |
|---|
| 310 |
|
|---|
| 311 |
The script runs over the entire Django source tree and pulls out all strings |
|---|
| 312 |
marked for translation. It creates (or updates) a message file in the directory |
|---|
| 313 |
``conf/locale``. In the ``de`` example, the file will be |
|---|
| 314 |
``conf/locale/de/LC_MESSAGES/django.po``. |
|---|
| 315 |
|
|---|
| 316 |
If run over your project source tree or your application source tree, it will |
|---|
| 317 |
do the same, but the location of the locale directory is ``locale/LANG/LC_MESSAGES`` |
|---|
| 318 |
(note the missing ``conf`` prefix). |
|---|
| 319 |
|
|---|
| 320 |
.. admonition:: No gettext? |
|---|
| 321 |
|
|---|
| 322 |
If you don't have the ``gettext`` utilities installed, ``make-messages.py`` |
|---|
| 323 |
will create empty files. If that's the case, either install the ``gettext`` |
|---|
| 324 |
utilities or just copy the English message file |
|---|
| 325 |
(``conf/locale/en/LC_MESSAGES/django.po``) and use it as a starting point; |
|---|
| 326 |
it's just an empty translation file. |
|---|
| 327 |
|
|---|
| 328 |
The format of ``.po`` files is straightforward. Each ``.po`` file contains a |
|---|
| 329 |
small bit of metadata, such as the translation maintainer's contact |
|---|
| 330 |
information, but the bulk of the file is a list of **messages** -- simple |
|---|
| 331 |
mappings between translation strings and the actual translated text for the |
|---|
| 332 |
particular language. |
|---|
| 333 |
|
|---|
| 334 |
For example, if your Django app contained a translation string for the text |
|---|
| 335 |
``"Welcome to my site."``, like so:: |
|---|
| 336 |
|
|---|
| 337 |
_("Welcome to my site.") |
|---|
| 338 |
|
|---|
| 339 |
...then ``make-messages.py`` will have created a ``.po`` file containing the |
|---|
| 340 |
following snippet -- a message:: |
|---|
| 341 |
|
|---|
| 342 |
#: path/to/python/module.py:23 |
|---|
| 343 |
msgid "Welcome to my site." |
|---|
| 344 |
msgstr "" |
|---|
| 345 |
|
|---|
| 346 |
A quick explanation: |
|---|
| 347 |
|
|---|
| 348 |
* ``msgid`` is the translation string, which appears in the source. Don't |
|---|
| 349 |
change it. |
|---|
| 350 |
* ``msgstr`` is where you put the language-specific translation. It starts |
|---|
| 351 |
out empty, so it's your responsibility to change it. Make sure you keep |
|---|
| 352 |
the quotes around your translation. |
|---|
| 353 |
* As a convenience, each message includes the filename and line number |
|---|
| 354 |
from which the translation string was gleaned. |
|---|
| 355 |
|
|---|
| 356 |
Long messages are a special case. There, the first string directly after the |
|---|
| 357 |
``msgstr`` (or ``msgid``) is an empty string. Then the content itself will be |
|---|
| 358 |
written over the next few lines as one string per line. Those strings are |
|---|
| 359 |
directly concatenated. Don't forget trailing spaces within the strings; |
|---|
| 360 |
otherwise, they'll be tacked together without whitespace! |
|---|
| 361 |
|
|---|
| 362 |
.. admonition:: Mind your charset |
|---|
| 363 |
|
|---|
| 364 |
When creating a ``.po`` file with your favorite text editor, first edit |
|---|
| 365 |
the charset line (search for ``"CHARSET"``) and set it to the charset |
|---|
| 366 |
you'll be using to edit the content. Generally, utf-8 should work for most |
|---|
| 367 |
languages, but ``gettext`` should handle any charset you throw at it. |
|---|
| 368 |
|
|---|
| 369 |
To reexamine all source code and templates for new translation strings and |
|---|
| 370 |
update all message files for **all** languages, run this:: |
|---|
| 371 |
|
|---|
| 372 |
make-messages.py -a |
|---|
| 373 |
|
|---|
| 374 |
Compiling message files |
|---|
| 375 |
----------------------- |
|---|
| 376 |
|
|---|
| 377 |
After you create your message file -- and each time you make changes to it -- |
|---|
| 378 |
you'll need to compile it into a more efficient form, for use by ``gettext``. |
|---|
| 379 |
Do this with the ``bin/compile-messages.py`` utility. |
|---|
| 380 |
|
|---|
| 381 |
This tool runs over all available ``.po`` files and creates ``.mo`` files, |
|---|
| 382 |
which are binary files optimized for use by ``gettext``. In the same directory |
|---|
| 383 |
from which you ran ``make-messages.py``, run ``compile-messages.py`` like |
|---|
| 384 |
this:: |
|---|
| 385 |
|
|---|
| 386 |
bin/compile-messages.py |
|---|
| 387 |
|
|---|
| 388 |
That's it. Your translations are ready for use. |
|---|
| 389 |
|
|---|
| 390 |
.. admonition:: A note to translators |
|---|
| 391 |
|
|---|
| 392 |
If you've created a translation in a language Django doesn't yet support, |
|---|
| 393 |
please let us know! See `Submitting and maintaining translations`_ for |
|---|
| 394 |
the steps to take. |
|---|
| 395 |
|
|---|
| 396 |
.. _Submitting and maintaining translations: http://www.djangoproject.com/documentation/contributing/ |
|---|
| 397 |
|
|---|
| 398 |
How Django discovers language preference |
|---|
| 399 |
======================================== |
|---|
| 400 |
|
|---|
| 401 |
Once you've prepared your translations -- or, if you just want to use the |
|---|
| 402 |
translations that come with Django -- you'll just need to activate translation |
|---|
| 403 |
for your app. |
|---|
| 404 |
|
|---|
| 405 |
Behind the scenes, Django has a very flexible model of deciding which language |
|---|
| 406 |
should be used -- installation-wide, for a particular user, or both. |
|---|
| 407 |
|
|---|
| 408 |
To set an installation-wide language preference, set ``LANGUAGE_CODE`` in your |
|---|
| 409 |
`settings file`_. Django uses this language as the default translation -- the |
|---|
| 410 |
final attempt if no other translator finds a translation. |
|---|
| 411 |
|
|---|
| 412 |
If all you want to do is run Django with your native language, and a language |
|---|
| 413 |
file is available for your language, all you need to do is set |
|---|
| 414 |
``LANGUAGE_CODE``. |
|---|
| 415 |
|
|---|
| 416 |
If you want to let each individual user specify which language he or she |
|---|
| 417 |
prefers, use ``LocaleMiddleware``. ``LocaleMiddleware`` enables language |
|---|
| 418 |
selection based on data from the request. It customizes content for each user. |
|---|
| 419 |
|
|---|
| 420 |
To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'`` |
|---|
| 421 |
to your ``MIDDLEWARE_CLASSES`` setting. Because middleware order matters, you |
|---|
| 422 |
should follow these guidelines: |
|---|
| 423 |
|
|---|
| 424 |
* Make sure it's one of the first middlewares installed. |
|---|
| 425 |
* It should come after ``SessionMiddleware``, because ``LocaleMiddleware`` |
|---|
| 426 |
makes use of session data. |
|---|
| 427 |
* If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it. |
|---|
| 428 |
|
|---|
| 429 |
For example, your ``MIDDLEWARE_CLASSES`` might look like this:: |
|---|
| 430 |
|
|---|
| 431 |
MIDDLEWARE_CLASSES = ( |
|---|
| 432 |
'django.contrib.sessions.middleware.SessionMiddleware', |
|---|
| 433 |
'django.middleware.locale.LocaleMiddleware', |
|---|
| 434 |
'django.middleware.common.CommonMiddleware', |
|---|
| 435 |
) |
|---|
| 436 |
|
|---|
| 437 |
(For more on middleware, see the `middleware documentation`_.) |
|---|
| 438 |
|
|---|
| 439 |
``LocaleMiddleware`` tries to determine the user's language preference by |
|---|
| 440 |
following this algorithm: |
|---|
| 441 |
|
|---|
| 442 |
* First, it looks for a ``django_language`` key in the the current user's |
|---|
| 443 |
`session`_. |
|---|
| 444 |
* Failing that, it looks for a cookie called ``django_language``. |
|---|
| 445 |
* Failing that, it looks at the ``Accept-Language`` HTTP header. This |
|---|
| 446 |
header is sent by your browser and tells the server which language(s) you |
|---|
| 447 |
prefer, in order by priority. Django tries each language in the header |
|---|
| 448 |
until it finds one with available translations. |
|---|
| 449 |
* Failing that, it uses the global ``LANGUAGE_CODE`` setting. |
|---|
| 450 |
|
|---|
| 451 |
Notes: |
|---|
| 452 |
|
|---|
| 453 |
* In each of these places, the language preference is expected to be in the |
|---|
| 454 |
standard language format, as a string. For example, Brazilian is |
|---|
| 455 |
``pt-br``. |
|---|
| 456 |
* If a base language is available but the sublanguage specified is not, |
|---|
| 457 |
Django uses the base language. For example, if a user specifies ``de-at`` |
|---|
| 458 |
(Austrian German) but Django only has ``de`` available, Django uses |
|---|
| 459 |
``de``. |
|---|
| 460 |
* Only languages listed in the `LANGUAGES setting`_ can be selected. If |
|---|
| 461 |
you want to restrict the language selection to a subset of provided |
|---|
| 462 |
languages (because your application doesn't provide all those languages), |
|---|
| 463 |
set ``LANGUAGES`` to a list of languages. For example:: |
|---|
| 464 |
|
|---|
| 465 |
LANGUAGES = ( |
|---|
| 466 |
('de', _('German')), |
|---|
| 467 |
('en', _('English')), |
|---|
| 468 |
) |
|---|
| 469 |
|
|---|
| 470 |
This example restricts languages that are available for automatic |
|---|
| 471 |
selection to German and English (and any sublanguage, like de-ch or |
|---|
| 472 |
en-us). |
|---|
| 473 |
|
|---|
| 474 |
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages |
|---|
| 475 |
|
|---|
| 476 |
* If you define a custom ``LANGUAGES`` setting, as explained in the |
|---|
| 477 |
previous bullet, it's OK to mark the languages as translation strings |
|---|
| 478 |
-- but use a "dummy" ``gettext()`` function, not the one in |
|---|
| 479 |
``django.utils.translation``. You should *never* import |
|---|
| 480 |
``django.utils.translation`` from within your settings file, because that |
|---|
| 481 |
module in itself depends on the settings, and that would cause a circular |
|---|
| 482 |
import. |
|---|
| 483 |
|
|---|
| 484 |
The solution is to use a "dummy" ``gettext()`` function. Here's a sample |
|---|
| 485 |
settings file:: |
|---|
| 486 |
|
|---|
| 487 |
gettext = lambda s: s |
|---|
| 488 |
|
|---|
| 489 |
LANGUAGES = ( |
|---|
| 490 |
('de', gettext('German')), |
|---|
| 491 |
('en', gettext('English')), |
|---|
| 492 |
) |
|---|
| 493 |
|
|---|
| 494 |
With this arrangement, ``make-messages.py`` will still find and mark |
|---|
| 495 |
these strings for translation, but the translation won't happen at |
|---|
| 496 |
runtime -- so you'll have to remember to wrap the languages in the *real* |
|---|
| 497 |
``gettext()`` in any code that uses ``LANGUAGES`` at runtime. |
|---|
| 498 |
|
|---|
| 499 |
* The ``LocaleMiddleware`` can only select languages for which there is a |
|---|
| 500 |
Django-provided base translation. If you want to provide translations |
|---|
| 501 |
for your application that aren't already in the set of translations |
|---|
| 502 |
in Django's source tree, you'll want to provide at least basic |
|---|
| 503 |
translations for that language. For example, Django uses technical |
|---|
| 504 |
message IDs to translate date formats and time formats -- so you will |
|---|
| 505 |
need at least those translations for the system to work correctly. |
|---|
| 506 |
|
|---|
| 507 |
A good starting point is to copy the English ``.po`` file and to |
|---|
| 508 |
translate at least the technical messages -- maybe the validator |
|---|
| 509 |
messages, too. |
|---|
| 510 |
|
|---|
| 511 |
Technical message IDs are easily recognized; they're all upper case. You |
|---|
| 512 |
don't translate the message ID as with other messages, you provide the |
|---|
| 513 |
correct local variant on the provided English value. For example, with |
|---|
| 514 |
``DATETIME_FORMAT`` (or ``DATE_FORMAT`` or ``TIME_FORMAT``), this would |
|---|
| 515 |
be the format string that you want to use in your language. The format |
|---|
| 516 |
is identical to the format strings used by the ``now`` template tag. |
|---|
| 517 |
|
|---|
| 518 |
Once ``LocaleMiddleware`` determines the user's preference, it makes this |
|---|
| 519 |
preference available as ``request.LANGUAGE_CODE`` for each `request object`_. |
|---|
| 520 |
Feel free to read this value in your view code. Here's a simple example:: |
|---|
| 521 |
|
|---|
| 522 |
def hello_world(request, count): |
|---|
| 523 |
if request.LANGUAGE_CODE == 'de-at': |
|---|
| 524 |
return HttpResponse("You prefer to read Austrian German.") |
|---|
| 525 |
else: |
|---|
| 526 |
return HttpResponse("You prefer to read another language.") |
|---|
| 527 |
|
|---|
| 528 |
Note that, with static (middleware-less) translation, the language is in |
|---|
| 529 |
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's |
|---|
| 530 |
in ``request.LANGUAGE_CODE``. |
|---|
| 531 |
|
|---|
| 532 |
.. _settings file: http://www.djangoproject.com/documentation/settings/ |
|---|
| 533 |
.. _middleware documentation: http://www.djangoproject.com/documentation/middleware/ |
|---|
| 534 |
.. _session: http://www.djangoproject.com/documentation/sessions/ |
|---|
| 535 |
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects |
|---|
| 536 |
|
|---|
| 537 |
The ``set_language`` redirect view |
|---|
| 538 |
================================== |
|---|
| 539 |
|
|---|
| 540 |
As a convenience, Django comes with a view, ``django.views.i18n.set_language``, |
|---|
| 541 |
that sets a user's language preference and redirects back to the previous page. |
|---|
| 542 |
|
|---|
| 543 |
Activate this view by adding the following line to your URLconf:: |
|---|
| 544 |
|
|---|
| 545 |
(r'^i18n/', include('django.conf.urls.i18n')), |
|---|
| 546 |
|
|---|
| 547 |
(Note that this example makes the view available at ``/i18n/setlang/``.) |
|---|
| 548 |
|
|---|
| 549 |
The view expects to be called via the ``GET`` method, with a ``language`` |
|---|
| 550 |
parameter set in the query string. If session support is enabled, the view |
|---|
| 551 |
saves the language choice in the user's session. Otherwise, it saves the |
|---|
| 552 |
language choice in a ``django_language`` cookie. |
|---|
| 553 |
|
|---|
| 554 |
After setting the language choice, Django redirects the user, following this |
|---|
| 555 |
algorithm: |
|---|
| 556 |
|
|---|
| 557 |
* Django looks for a ``next`` parameter in the query string. |
|---|
| 558 |
* If that doesn't exist, or is empty, Django tries the URL in the |
|---|
| 559 |
``Referer`` header. |
|---|
| 560 |
* If that's empty -- say, if a user's browser suppresses that header -- |
|---|
| 561 |
then the user will be redirected to ``/`` (the site root) as a fallback. |
|---|
| 562 |
|
|---|
| 563 |
Here's example HTML template code:: |
|---|
| 564 |
|
|---|
| 565 |
<form action="/i18n/setlang/" method="get"> |
|---|
| 566 |
<input name="next" type="hidden" value="/next/page/" /> |
|---|
| 567 |
<select name="language"> |
|---|
| 568 |
{% for lang in LANGUAGES %} |
|---|
| 569 |
<option value="{{ lang.0 }}">{{ lang.1 }}</option> |
|---|
| 570 |
{% endfor %} |
|---|
| 571 |
</select> |
|---|
| 572 |
<input type="submit" value="Go" /> |
|---|
| 573 |
</form> |
|---|
| 574 |
|
|---|
| 575 |
Using translations in your own projects |
|---|
| 576 |
======================================= |
|---|
| 577 |
|
|---|
| 578 |
Django looks for translations by following this algorithm: |
|---|
| 579 |
|
|---|
| 580 |
* First, it looks for a ``locale`` directory in the application directory |
|---|
| 581 |
of the view that's being called. If it finds a translation for the |
|---|
| 582 |
selected language, the translation will be installed. |
|---|
| 583 |
* Next, it looks for a ``locale`` directory in the project directory. If it |
|---|
| 584 |
finds a translation, the translation will be installed. |
|---|
| 585 |
* Finally, it checks the base translation in ``django/conf/locale``. |
|---|
| 586 |
|
|---|
| 587 |
This way, you can write applications that include their own translations, and |
|---|
| 588 |
you can override base translations in your project path. Or, you can just build |
|---|
| 589 |
a big project out of several apps and put all translations into one big project |
|---|
| 590 |
message file. The choice is yours. |
|---|
| 591 |
|
|---|
| 592 |
.. note:: |
|---|
| 593 |
|
|---|
| 594 |
If you're using manually configured settings, as described in the |
|---|
| 595 |
`settings documentation`_, the ``locale`` directory in the project |
|---|
| 596 |
directory will not be examined, since Django loses the ability to work out |
|---|
| 597 |
the location of the project directory. (Django normally uses the location |
|---|
| 598 |
of the settings file to determine this, and a settings file doesn't exist |
|---|
| 599 |
if you're manually configuring your settings.) |
|---|
| 600 |
|
|---|
| 601 |
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable |
|---|
| 602 |
|
|---|
| 603 |
All message file repositories are structured the same way. They are: |
|---|
| 604 |
|
|---|
| 605 |
* ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)`` |
|---|
| 606 |
* ``$PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)`` |
|---|
| 607 |
* All paths listed in ``LOCALE_PATHS`` in your settings file are |
|---|
| 608 |
searched in that order for ``<language>/LC_MESSAGES/django.(po|mo)`` |
|---|
| 609 |
* ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)`` |
|---|
| 610 |
|
|---|
| 611 |
To create message files, you use the same ``make-messages.py`` tool as with the |
|---|
| 612 |
Django message files. You only need to be in the right place -- in the directory |
|---|
| 613 |
where either the ``conf/locale`` (in case of the source tree) or the ``locale/`` |
|---|
| 614 |
(in case of app messages or project messages) directory are located. And you |
|---|
| 615 |
use the same ``compile-messages.py`` to produce the binary ``django.mo`` files that |
|---|
| 616 |
are used by ``gettext``. |
|---|
| 617 |
|
|---|
| 618 |
Application message files are a bit complicated to discover -- they need the |
|---|
| 619 |
``LocaleMiddleware``. If you don't use the middleware, only the Django message |
|---|
| 620 |
files and project message files will be processed. |
|---|
| 621 |
|
|---|
| 622 |
Finally, you should give some thought to the structure of your translation |
|---|
| 623 |
files. If your applications need to be delivered to other users and will |
|---|
| 624 |
be used in other projects, you might want to use app-specific translations. |
|---|
| 625 |
But using app-specific translations and project translations could produce |
|---|
| 626 |
weird problems with ``make-messages``: ``make-messages`` will traverse all |
|---|
| 627 |
directories below the current path and so might put message IDs into the |
|---|
| 628 |
project message file that are already in application message files. |
|---|
| 629 |
|
|---|
| 630 |
The easiest way out is to store applications that are not part of the project |
|---|
| 631 |
(and so carry their own translations) outside the project tree. That way, |
|---|
| 632 |
``make-messages`` on the project level will only translate strings that are |
|---|
| 633 |
connected to your explicit project and not strings that are distributed |
|---|
| 634 |
independently. |
|---|
| 635 |
|
|---|
| 636 |
Translations and JavaScript |
|---|
| 637 |
=========================== |
|---|
| 638 |
|
|---|
| 639 |
Adding translations to JavaScript poses some problems: |
|---|
| 640 |
|
|---|
| 641 |
* JavaScript code doesn't have access to a ``gettext`` implementation. |
|---|
| 642 |
|
|---|
| 643 |
* JavaScript code doesn't have access to .po or .mo files; they need to be |
|---|
| 644 |
delivered by the server. |
|---|
| 645 |
|
|---|
| 646 |
* The translation catalogs for JavaScript should be kept as small as |
|---|
| 647 |
possible. |
|---|
| 648 |
|
|---|
| 649 |
Django provides an integrated solution for these problems: It passes the |
|---|
| 650 |
translations into JavaScript, so you can call ``gettext``, etc., from within |
|---|
| 651 |
JavaScript. |
|---|
| 652 |
|
|---|
| 653 |
The ``javascript_catalog`` view |
|---|
| 654 |
------------------------------- |
|---|
| 655 |
|
|---|
| 656 |
The main solution to these problems is the ``javascript_catalog`` view, which |
|---|
| 657 |
sends out a JavaScript code library with functions that mimic the ``gettext`` |
|---|
| 658 |
interface, plus an array of translation strings. Those translation strings are |
|---|
| 659 |
taken from the application, project or Django core, according to what you |
|---|
| 660 |
specify in either the {{{info_dict}}} or the URL. |
|---|
| 661 |
|
|---|
| 662 |
You hook it up like this:: |
|---|
| 663 |
|
|---|
| 664 |
js_info_dict = { |
|---|
| 665 |
'packages': ('your.app.package',), |
|---|
| 666 |
} |
|---|
| 667 |
|
|---|
| 668 |
urlpatterns = patterns('', |
|---|
| 669 |
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), |
|---|
| 670 |
) |
|---|
| 671 |
|
|---|
| 672 |
Each string in ``packages`` should be in Python dotted-package syntax (the |
|---|
| 673 |
same format as the strings in ``INSTALLED_APPS``) and should refer to a package |
|---|
| 674 |
that contains a ``locale`` directory. If you specify multiple packages, all |
|---|
| 675 |
those catalogs are merged into one catalog. This is useful if you have |
|---|
| 676 |
JavaScript that uses strings from different applications. |
|---|
| 677 |
|
|---|
| 678 |
You can make the view dynamic by putting the packages into the URL pattern:: |
|---|
| 679 |
|
|---|
| 680 |
urlpatterns = patterns('', |
|---|
| 681 |
(r'^jsi18n/(?P<packages>\S+?)/$, 'django.views.i18n.javascript_catalog'), |
|---|
| 682 |
) |
|---|
| 683 |
|
|---|
| 684 |
With this, you specify the packages as a list of package names delimited by '+' |
|---|
| 685 |
signs in the URL. This is especially useful if your pages use code from |
|---|
| 686 |
different apps and this changes often and you don't want to pull in one big |
|---|
| 687 |
catalog file. As a security measure, these values can only be either |
|---|
| 688 |
``django.conf`` or any package from the ``INSTALLED_APPS`` setting. |
|---|
| 689 |
|
|---|
| 690 |
Using the JavaScript translation catalog |
|---|
| 691 |
---------------------------------------- |
|---|
| 692 |
|
|---|
| 693 |
To use the catalog, just pull in the dynamically generated script like this:: |
|---|
| 694 |
|
|---|
| 695 |
<script type="text/javascript" src="/path/to/jsi18n/"></script> |
|---|
| 696 |
|
|---|
| 697 |
This is how the admin fetches the translation catalog from the server. When the |
|---|
| 698 |
catalog is loaded, your JavaScript code can use the standard ``gettext`` |
|---|
| 699 |
interface to access it:: |
|---|
| 700 |
|
|---|
| 701 |
document.write(gettext('this is to be translated')); |
|---|
| 702 |
|
|---|
| 703 |
There even is a ``ngettext`` interface and a string interpolation function:: |
|---|
| 704 |
|
|---|
| 705 |
d = { |
|---|
| 706 |
count: 10 |
|---|
| 707 |
}; |
|---|
| 708 |
s = interpolate(ngettext('this is %(count)s object', 'this are %(count)s objects', d.count), d); |
|---|
| 709 |
|
|---|
| 710 |
The ``interpolate`` function supports both positional interpolation and named |
|---|
| 711 |
interpolation. So the above could have been written as:: |
|---|
| 712 |
|
|---|
| 713 |
s = interpolate(ngettext('this is %s object', 'this are %s objects', 11), [11]); |
|---|
| 714 |
|
|---|
| 715 |
The interpolation syntax is borrowed from Python. You shouldn't go over the top |
|---|
| 716 |
with string interpolation, though: this is still JavaScript, so the code will |
|---|
| 717 |
have to do repeated regular-expression substitutions. This isn't as fast as |
|---|
| 718 |
string interpolation in Python, so keep it to those cases where you really |
|---|
| 719 |
need it (for example, in conjunction with ``ngettext`` to produce proper |
|---|
| 720 |
pluralizations). |
|---|
| 721 |
|
|---|
| 722 |
Creating JavaScript translation catalogs |
|---|
| 723 |
---------------------------------------- |
|---|
| 724 |
|
|---|
| 725 |
You create and update the translation catalogs the same way as the other Django |
|---|
| 726 |
translation catalogs -- with the {{{make-messages.py}}} tool. The only |
|---|
| 727 |
difference is you need to provide a ``-d djangojs`` parameter, like this:: |
|---|
| 728 |
|
|---|
| 729 |
make-messages.py -d djangojs -l de |
|---|
| 730 |
|
|---|
| 731 |
This would create or update the translation catalog for JavaScript for German. |
|---|
| 732 |
After updating translation catalogs, just run ``compile-messages.py`` the same |
|---|
| 733 |
way as you do with normal Django translation catalogs. |
|---|
| 734 |
|
|---|
| 735 |
Specialities of Django translation |
|---|
| 736 |
================================== |
|---|
| 737 |
|
|---|
| 738 |
If you know ``gettext``, you might note these specialities in the way Django |
|---|
| 739 |
does translation: |
|---|
| 740 |
|
|---|
| 741 |
* The string domain is ``django`` or ``djangojs``. The string domain is used to |
|---|
| 742 |
differentiate between different programs that store their data in a |
|---|
| 743 |
common message-file library (usually ``/usr/share/locale/``). The ``django`` |
|---|
| 744 |
domain is used for python and template translation strings and is loaded into |
|---|
| 745 |
the global translation catalogs. The ``djangojs`` domain is only used for |
|---|
| 746 |
JavaScript translation catalogs to make sure that those are as small as |
|---|
| 747 |
possible. |
|---|
| 748 |
* Django only uses ``gettext`` and ``gettext_noop``. That's because Django |
|---|
| 749 |
always uses ``DEFAULT_CHARSET`` strings internally. There isn't much use |
|---|
| 750 |
in using ``ugettext``, because you'll always need to produce utf-8 |
|---|
| 751 |
anyway. |
|---|
| 752 |
* Django doesn't use ``xgettext`` alone. It uses Python wrappers around |
|---|
| 753 |
``xgettext`` and ``msgfmt``. That's mostly for convenience. |
|---|