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