From 67e6e0fcf35d93f88bfb1a4cbf5c6008d99dd923 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 22 Oct 2011 17:17:57 +0000 Subject: Fixed #17087 -- Re-organized the i18n docs to reduce confusion between USE_I18N/USE_L10N and the concepts of internationalization/localisation. Re moved some duplicate content. git-svn-id: http://code.djangoproject.com/svn/django/trunk@17026 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/howto/i18n.txt | 121 -- docs/howto/index.txt | 1 - docs/internals/contributing/index.txt | 2 +- docs/internals/contributing/localizing.txt | 68 ++ docs/internals/contributing/translations.txt | 68 -- docs/ref/contrib/localflavor.txt | 2 +- docs/ref/settings.txt | 16 +- docs/ref/utils.txt | 2 +- docs/topics/cache.txt | 9 +- docs/topics/i18n/deployment.txt | 217 ---- docs/topics/i18n/formatting.txt | 182 +++ docs/topics/i18n/index.txt | 96 +- docs/topics/i18n/internationalization.txt | 1021 ----------------- docs/topics/i18n/localization.txt | 409 ------- docs/topics/i18n/translation.txt | 1561 ++++++++++++++++++++++++++ 15 files changed, 1872 insertions(+), 1903 deletions(-) delete mode 100644 docs/howto/i18n.txt create mode 100644 docs/internals/contributing/localizing.txt delete mode 100644 docs/internals/contributing/translations.txt delete mode 100644 docs/topics/i18n/deployment.txt create mode 100644 docs/topics/i18n/formatting.txt delete mode 100644 docs/topics/i18n/internationalization.txt delete mode 100644 docs/topics/i18n/localization.txt create mode 100644 docs/topics/i18n/translation.txt (limited to 'docs') diff --git a/docs/howto/i18n.txt b/docs/howto/i18n.txt deleted file mode 100644 index 7310e52591..0000000000 --- a/docs/howto/i18n.txt +++ /dev/null @@ -1,121 +0,0 @@ -.. _using-translations-in-your-own-projects: - -=============================================== -Using internationalization in your own projects -=============================================== - -At runtime, Django builds an in-memory unified catalog of literals-translations. -To achieve this it looks for translations by following this algorithm regarding -the order in which it examines the different file paths to load the compiled -:term:`message files ` (``.mo``) and the precedence of multiple -translations for the same literal: - -1. The directories listed in :setting:`LOCALE_PATHS` have the highest - precedence, with the ones appearing first having higher precedence than - the ones appearing later. -2. Then, it looks for and uses if it exists a ``locale`` directory in each - of the installed apps listed in :setting:`INSTALLED_APPS`. The ones - appearing first have higher precedence than the ones appearing later. -3. Then, it looks for a ``locale`` directory in the project directory, or - more accurately, in the directory containing your settings file. -4. Finally, the Django-provided base translation in ``django/conf/locale`` - is used as a fallback. - -.. deprecated:: 1.3 - Lookup in the ``locale`` subdirectory of the directory containing your - settings file (item 3 above) is deprecated since the 1.3 release and will be - removed in Django 1.5. You can use the :setting:`LOCALE_PATHS` setting - instead, by listing the absolute filesystem path of such ``locale`` - directory in the setting value. - -.. seealso:: - - The translations for literals included in JavaScript assets are looked up - following a similar but not identical algorithm. See the - :ref:`javascript_catalog view documentation ` for - more details. - -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 common -message file specific to the project you are composing. 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: - -* All paths listed in :setting:`LOCALE_PATHS` in your settings file are - searched for ``/LC_MESSAGES/django.(po|mo)`` -* ``$PROJECTPATH/locale//LC_MESSAGES/django.(po|mo)`` -- - deprecated, see above. -* ``$APPPATH/locale//LC_MESSAGES/django.(po|mo)`` -* ``$PYTHONPATH/django/conf/locale//LC_MESSAGES/django.(po|mo)`` - -To create message files, you use the :djadmin:`django-admin.py 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 ` -to produce the binary ``.mo`` files that are used by ``gettext``. Read the -:doc:`/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. - -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-specific translations could -produce weird problems with ``makemessages``: It will traverse all directories -below the current path and so might put message IDs into a unified, common -message file for the current project 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``, when ran on a project level will only extract -strings that are connected to your explicit project and not strings that are -distributed independently. - -Using translations outside views and templates -============================================== - -While Django provides a rich set of i18n tools for use in views and templates, -it does not restrict the usage to Django-specific code. The Django translation -mechanisms can be used to translate arbitrary texts to any language that is -supported by Django (as long as an appropriate translation catalog exists, of -course). You can load a translation catalog, activate it and translate text to -language of your choice, but remember to switch back to original language, as -activating a translation catalog is done on per-thread basis and such change -will affect code running in the same thread. - -For example:: - - from django.utils import translation - def welcome_translated(language): - cur_language = translation.get_language() - try: - translation.activate(language) - text = translation.ugettext('welcome') - finally: - translation.activate(cur_language) - return text - -Calling this function with the value 'de' will give you ``"Willkommen"``, -regardless of :setting:`LANGUAGE_CODE` and language set by middleware. - -Functions of particular interest are ``django.utils.translation.get_language()`` -which returns the language used in the current thread, -``django.utils.translation.activate()`` which activates a translation catalog -for the current thread, and ``django.utils.translation.check_for_language()`` -which checks if the given language is supported by Django. diff --git a/docs/howto/index.txt b/docs/howto/index.txt index 49d0644034..3616f7ae16 100644 --- a/docs/howto/index.txt +++ b/docs/howto/index.txt @@ -18,7 +18,6 @@ you quickly accomplish common tasks. deployment/index error-reporting initial-data - i18n jython legacy-databases outputting-csv diff --git a/docs/internals/contributing/index.txt b/docs/internals/contributing/index.txt index 77af3d611c..7e6de0d491 100644 --- a/docs/internals/contributing/index.txt +++ b/docs/internals/contributing/index.txt @@ -56,7 +56,7 @@ Browse the following sections to find out how: triaging-tickets writing-code/index writing-documentation - translations + localizing committing-code .. _django-users: http://groups.google.com/group/django-users diff --git a/docs/internals/contributing/localizing.txt b/docs/internals/contributing/localizing.txt new file mode 100644 index 0000000000..90a07c1e2d --- /dev/null +++ b/docs/internals/contributing/localizing.txt @@ -0,0 +1,68 @@ +================= +Localizing Django +================= + +Various parts of Django, such as the admin site and validation error messages, +are internationalized. This means they display differently depending on each +user's language or country. For this, Django uses the same internationalization +and localization infrastructure available to Django applications, described in +the :doc:`i18n documentation `. + +Translations +------------ + +Translations are contributed by Django users worldwide. The translation work is +coordinated at `Transifex`_. + +If you find an incorrect translation or want to discuss specific translations, +go to the `translation team`_ page for that language. If you would like to help +out with translating or add a language that isn't yet 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`. + +* Signup at `Transifex`_ and visit the `Django project page`_. + +* On the `translation teams`_ page, choose the language team you want + to work with, **or** -- in case the language team doesn't exist yet -- + request a new team by clicking on the "Request a new team" button + and select the appropriate language. + +* Then, click the "Join this Team" button to become a member of this team. + Every team has at least one coordinator who is responsible to review + your membership request. You can of course also contact the team + coordinator to clarify procedural problems and handle the actual + translation process. + +* Once you are a member of a team choose the translation resource you + want to update on the team page. For example the "core" resource refers + to the translation catalogue that contains all non-contrib translations. + Each of the contrib apps also have a resource (prefixed with "contrib"). + + .. note:: + For more information about how to use Transifex, read the + `Transifex User Guide`_. + +Formats +------- + +You can also review ``conf/locale//formats.py``. This file describes +the date, time and numbers formatting particularities of your locale. See +:ref:`format-localization` for details. + +The format files aren't managed by the use of Transifex. To change them, you +must :doc:`create a patch` against the Django source tree, as for any code change: + +* 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. + +.. _Transifex: http://www.transifex.net/ +.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/ +.. _Django project page: http://www.transifex.net/projects/p/django/ +.. _translation team: http://www.transifex.net/projects/p/django/teams/ +.. _translation teams: http://www.transifex.net/projects/p/django/teams/ +.. _Transifex User Guide: http://help.transifex.net/ diff --git a/docs/internals/contributing/translations.txt b/docs/internals/contributing/translations.txt deleted file mode 100644 index 343839540f..0000000000 --- a/docs/internals/contributing/translations.txt +++ /dev/null @@ -1,68 +0,0 @@ -======================================= -Submitting and maintaining translations -======================================= - -Various parts of Django, such as the admin site and validation error messages, -are internationalized. This means they display different text depending on each -user's language setting. For this, Django uses the same internationalization and -localization infrastructure available to Django applications, described in the -:doc:`i18n documentation`. - -Internationalization --------------------- - -Translations are contributed by Django users worldwide. The translation work is -coordinated at `Transifex`_. - -If you find an incorrect translation or want to discuss specific translations, -go to the `translation team`_ page for that language. If you would like to help -out with translating or add a language that isn't yet 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`. - -* Signup at `Transifex`_ and visit the `Django project page`_. - -* On the `translation teams`_ page, choose the language team you want - to work with, **or** -- in case the language team doesn't exist yet -- - request a new team by clicking on the "Request a new team" button - and select the appropriate language. - -* Then, click the "Join this Team" button to become a member of this team. - Every team has at least one coordinator who is responsible to review - your membership request. You can of course also contact the team - coordinator to clarify procedural problems and handle the actual - translation process. - -* Once you are a member of a team choose the translation resource you - want to update on the team page. For example the "core" resource refers - to the translation catalogue that contains all non-contrib translations. - Each of the contrib apps also have a resource (prefixed with "contrib"). - - .. note:: - For more information about how to use Transifex, read the - `Transifex User Guide`_. - -Localization ------------- - -You can also review ``conf/locale//formats.py``. This file describes -the date, time and numbers formatting particularities of your locale. See -:ref:`format-localization` for details. - -The format files aren't managed by the use of Transifex. To change them, you -must :doc:`create a patch` against the Django source tree, as for any code change: - -* 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. - -.. _Transifex: http://www.transifex.net/ -.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/ -.. _Django project page: http://www.transifex.net/projects/p/django/ -.. _translation team: http://www.transifex.net/projects/p/django/teams/ -.. _translation teams: http://www.transifex.net/projects/p/django/teams/ -.. _Transifex User Guide: http://help.transifex.net/ diff --git a/docs/ref/contrib/localflavor.txt b/docs/ref/contrib/localflavor.txt index 0a39c31b7a..4671906fa1 100644 --- a/docs/ref/contrib/localflavor.txt +++ b/docs/ref/contrib/localflavor.txt @@ -146,7 +146,7 @@ Django's general catalog in ``django/conf/locale``. If you want localflavor's texts to be translated, like form fields error messages, you must include :mod:`django.contrib.localflavor` in the :setting:`INSTALLED_APPS` setting, so the internationalization system can find the catalog, as explained in -:ref:`using-translations-in-your-own-projects`. +:ref:`how-django-discovers-translations`. Adding flavors ============== diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index c3539192f3..999e2bad00 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1243,7 +1243,7 @@ LOCALE_PATHS Default: ``()`` (Empty tuple) A tuple of directories where Django looks for translation files. -See :ref:`using-translations-in-your-own-projects`. +See :ref:`how-django-discovers-translations`. Example:: @@ -2038,10 +2038,10 @@ USE_I18N Default: ``True`` -A boolean that specifies whether Django's internationalization system should be -enabled. This provides an easy way to turn it off, for performance. If this is -set to ``False``, Django will make some optimizations so as not to load the -internationalization machinery. +A boolean that specifies whether Django's translation system should be enabled. +This provides an easy way to turn it off, for performance. If this is set to +``False``, Django will make some optimizations so as not to load the +translation machinery. See also :setting:`USE_L10N` @@ -2054,9 +2054,9 @@ USE_L10N Default: ``False`` -A boolean that specifies if data will be localized by default or not. If this -is set to ``True``, e.g. Django will display numbers and dates using the -format of the current locale. +A boolean that specifies if localized formatting of data will be enabled by +default or not. If this is set to ``True``, e.g. Django will display numbers and +dates using the format of the current locale. See also :setting:`USE_I18N` and :setting:`LANGUAGE_CODE` diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index e4648bd1b9..bce2f59cd3 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -455,7 +455,7 @@ appropriate entities. :synopsis: Internationalization support. For a complete discussion on the usage of the following see the -:doc:`Internationalization documentation `. +:doc:`translation documentation `. .. function:: gettext(message) diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt index 768bd245b8..1d85683d95 100644 --- a/docs/topics/cache.txt +++ b/docs/topics/cache.txt @@ -494,12 +494,9 @@ more on these decorators. .. versionadded:: 1.2 If :setting:`USE_I18N` is set to ``True`` then the generated cache key will -include the name of the active :term:`language`. -This allows you to easily cache multilingual sites without having to create -the cache key yourself. - -See :doc:`/topics/i18n/deployment` for more on how Django discovers the active -language. +include the name of the active :term:`language` -- see also +:ref:`how-django-discovers-language-preference`). This allows you to easily +cache multilingual sites without having to create the cache key yourself. __ `Controlling cache: Using other headers`_ diff --git a/docs/topics/i18n/deployment.txt b/docs/topics/i18n/deployment.txt deleted file mode 100644 index eed5c96af7..0000000000 --- a/docs/topics/i18n/deployment.txt +++ /dev/null @@ -1,217 +0,0 @@ -========================== -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 ` 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 :setting:`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 :setting:`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 :setting:`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. And it should come before ``CommonMiddleware`` - because ``CommonMiddleware`` needs an activated language in order - to resolve the requested URL. -* If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it. - -For example, your :setting:`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 :doc:`middleware documentation -`.) - -``LocaleMiddleware`` tries to determine the user's language preference by -following this algorithm: - -.. versionchanged:: 1.4 - -* First, it looks for the language prefix in the requested URL. This is - only performed when you are using the ``i18n_patterns`` function in your - root URLconf. See :ref:`url-internationalization` for more information - about the language prefix and how to internationalize URL patterns. - -* Failing that, it looks for a ``django_language`` key in the current - user's session. - -* Failing that, it looks for a cookie. - - The name of the cookie used is set by the :setting:`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 :setting:`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`, 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 :setting:`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). - -* If you define a custom :setting:`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 :setting:`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` - 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`, Django looks for -translations by following this algorithm regarding the order in which it -examines the different file paths to load the compiled :term:`message files -` (``.mo``) and the precedence of multiple translations for the -same literal: - -1. The directories listed in :setting:`LOCALE_PATHS` have the highest - precedence, with the ones appearing first having higher precedence than - the ones appearing later. -2. Then, it looks for and uses if it exists a ``locale`` directory in each - of the installed apps listed in :setting:`INSTALLED_APPS`. The ones - appearing first have higher precedence than the ones appearing later. -3. Then, it looks for a ``locale`` directory in the project directory, or - more accurately, in the directory containing your settings file. -4. Finally, the Django-provided base translation in ``django/conf/locale`` - is used as a fallback. - -.. deprecated:: 1.3 - Lookup in the ``locale`` subdirectory of the directory containing your - settings file (item 3 above) is deprecated since the 1.3 release and will be - removed in Django 1.5. You can use the :setting:`LOCALE_PATHS` setting - instead, by listing the absolute filesystem path of such ``locale`` - directory in the setting value. - -.. seealso:: - - The translations for literals included in JavaScript assets are looked up - following a similar but not identical algorithm. See the - :ref:`javascript_catalog view documentation ` for - more details. - -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. diff --git a/docs/topics/i18n/formatting.txt b/docs/topics/i18n/formatting.txt new file mode 100644 index 0000000000..ffd0bcdd22 --- /dev/null +++ b/docs/topics/i18n/formatting.txt @@ -0,0 +1,182 @@ +=================== +Format localization +=================== + +.. _format-localization: + +.. versionadded:: 1.2 + +Overview +======== + +Django's formatting system is capable to display dates, times and numbers in templates using the format specified for the current :term:`locale `. It also handles localized input in forms. + +When it's enabled, two users accessing the same content may see dates, times and +numbers formatted in different ways, depending on the formats for their current +locale. + +The formatting system is disabled by default. To enable it, it's +necessary to set :setting:`USE_L10N = True ` in your settings file. + +.. note:: + + The default :file:`settings.py` file created by :djadmin:`django-admin.py + startproject ` includes :setting:`USE_L10N = True ` + for convenience. + +.. note:: + + There is also an independent but related :setting:`USE_I18N` setting that + controls if Django should activate translation. See + :doc:`/topics/i18n/translation` for more details. + +Locale aware input in forms +=========================== + +When formatting is enabled, Django can use localized formats when parsing dates, +times and numbers in forms. That means it tries different formats for different +locales when guessing the format used by the user when inputting data on forms. + +.. note:: + Django uses different formats for displaying data to those it uses for + parsing data. Most notably, the formats for parsing dates can't use the + ``%a`` (abbreviated weekday name), ``%A`` (full weekday name), + ``%b`` (abbreviated month name), ``%B`` (full month name), + or ``%p`` (AM/PM). + +To enable a form field to localize input and output data simply use its +``localize`` argument:: + + class CashRegisterForm(forms.Form): + product = forms.CharField() + revenue = forms.DecimalField(max_digits=4, decimal_places=2, localize=True) + +.. _topic-l10n-templates: + +Controlling localization in templates +===================================== + +When you have enabled formatting with :setting:`USE_L10N`, Django +will try to use a locale specific format whenever it outputs a value +in a template. + +However, it may not always be appropriate to use localized values -- +for example, if you're outputting Javascript or XML that is designed +to be machine-readable, you will always want unlocalized values. You +may also want to use localization in selected templates, rather than +using localization everywhere. + +To allow for fine control over the use of localization, Django +provides the ``l10n`` template library that contains the following +tags and filters. + +Template tags +------------- + +.. templatetag:: localize + +localize +~~~~~~~~ + +.. versionadded:: 1.3 + +Enables or disables localization of template variables in the +contained block. + +This tag allows a more fine grained control of localization than +:setting:`USE_L10N`. + +To activate or deactivate localization for a template block, use:: + + {% load l10n %} + + {% localize on %} + {{ value }} + {% endlocalize %} + + {% localize off %} + {{ value }} + {% endlocalize %} + +.. note:: + + The value of :setting:`USE_L10N` isn't respected inside of a + ``{% localize %}`` block. + +See :tfilter:`localize` and :tfilter:`unlocalize` for template filters that will +do the same job on a per-variable basis. + +Template filters +---------------- + +.. templatefilter:: localize + +localize +~~~~~~~~ + +.. versionadded:: 1.3 + +Forces localization of a single value. + +For example:: + + {% load l10n %} + + {{ value|localize }} + +To disable localization on a single value, use :tfilter:`unlocalize`. To control +localization over a large section of a template, use the :ttag:`localize` template +tag. + + +.. templatefilter:: unlocalize + +unlocalize +~~~~~~~~~~ + +.. versionadded:: 1.3 + +Forces a single value to be printed without localization. + +For example:: + + {% load l10n %} + + {{ value|unlocalize }} + +To force localization of a single value, use :tfilter:`localize`. To +control localization over a large section of a template, use the +:ttag:`localize` template tag. + +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, specify the path where you'll place format files first. +To do that, just set your :setting:`FORMAT_MODULE_PATH` setting to the package +where format files will exist, for instance:: + + FORMAT_MODULE_PATH = 'mysite.formats' + +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. diff --git a/docs/topics/i18n/index.txt b/docs/topics/i18n/index.txt index e499fecb0f..4e2c9c345c 100644 --- a/docs/topics/i18n/index.txt +++ b/docs/topics/i18n/index.txt @@ -2,64 +2,77 @@ Internationalization and localization ===================================== +.. toctree:: + :hidden: + :maxdepth: 1 + + translation + formatting + 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. +The goal of internationalization and localization is to allow a single Web +application to offer its content in languages and formats tailored to the +audience. + +Django has full support for :doc:`translation of text +` and :doc:`formatting of dates, times and numbers +`. 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. +* It allows developers and template authors to specify which parts of their apps + should be translated or formatted for local languages and cultures. +* It uses these hooks to localize Web apps for particular users according to + their 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 responsibilities -associated with each of these tasks (although it's perfectly normal if you -find yourself performing more than one of these roles): +Obviously, translation depends on the target language. Formatting usually +depends on the target country. -* For application authors wishing to make sure their Django apps can be - used in different locales: :doc:`/topics/i18n/internationalization`. -* For translators wanting to translate Django apps: :doc:`/topics/i18n/localization`. -* For system administrators/final users setting up internationalized apps or - developers integrating third party apps: :doc:`/topics/i18n/deployment`. +Definitions +=========== -.. toctree:: - :hidden: - :maxdepth: 1 +The words "internationalization" and "localization" often cause confusion; +here's a simplified definition: + +.. glossary:: - internationalization - localization - deployment + internationalization + Preparing the software for localization. Usually done by developers. -.. _ seealso:: + localization + Writing the translations and local formats. Usually done by translators. -For more general information about the topic, see the `GNU gettext documentation`_ -and the `Wikipedia article`_. +More details can be found in the `W3C Web Internationalization FAQ`_, the `Wikipedia article`_ or the `GNU gettext documentation`_. +.. _W3C Web Internationalization FAQ: http://www.w3.org/International/questions/qa-i18n .. _GNU gettext documentation: http://www.gnu.org/software/gettext/manual/gettext.html#Concepts .. _Wikipedia article: http://en.wikipedia.org/wiki/Internationalization_and_localization -Glossary -======== +.. warning:: -First lets define some terms that will help us to handle a common language: + Translation and formatting are controlled by :setting:`USE_I18N` and + :setting:`USE_L10N` settings respectively. However, both features involve + internationalization and localization. The names of the settings are an + unfortunate result of Django's history. + +Here are some other 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. + Examples: ``it``, ``de_AT``, ``es``, ``pt_BR``. The language part is + always is lower case and the country part in upper case. The separator + is an underscore. 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. + format. Examples: ``it``, ``de-at``, ``es``, ``pt-br``. Both the language + and the country parts are in lower case. The separator is a dash. message file A message file is a plain-text file, representing a single language, @@ -70,21 +83,6 @@ First lets define some terms that will help us to handle a common language: 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. + format file + A format file is a Python module that defines the data formats for a given + locale. \ No newline at end of file diff --git a/docs/topics/i18n/internationalization.txt b/docs/topics/i18n/internationalization.txt deleted file mode 100644 index c8611acff8..0000000000 --- a/docs/topics/i18n/internationalization.txt +++ /dev/null @@ -1,1021 +0,0 @@ -==================== -Internationalization -==================== - -.. module:: django.utils.translation - -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 -:func:`~django.utils.translation.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, - :func:`~django.utils.translation.ugettext` is more useful than - ``gettext()``. Sometimes, you should be using - :func:`~django.utils.translation.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 -the month and the day placeholders 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. - -.. _translator-comments: - -Comments for translators ------------------------- - -.. versionadded:: 1.3 - -If you would like to give translators hints about a translatable string, you -can add a comment prefixed with the ``Translators`` keyword on the line -preceding the string, e.g.:: - - def my_view(request): - # Translators: This message appears on the home page only - output = ugettext("Welcome to my site.") - -This also works in templates with the :ttag:`comment` tag: - -.. code-block:: html+django - - {% comment %}Translators: This is a text of the base template {% endcomment %} - -The comment will then appear in the resulting .po file and should also be -displayed by most translation tools. - -Marking strings as no-op ------------------------- - -Use the function :func:`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 :func:`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 -`_ is -greater than the two forms used in English ('object' for the singular and -'objects' for all the cases where ``count`` is different from one, 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``. - -.. _contextual-markers: - -Contextual markers ------------------- - -.. versionadded:: 1.3 - -Sometimes words have several meanings, such as ``"May"`` in English, which -refers to a month name and to a verb. To enable translators to translate -these words correctly in different contexts, you can use the -:func:`django.utils.translation.pgettext()` function, or the -:func:`django.utils.translation.npgettext()` function if the string needs -pluralization. Both take a context string as the first variable. - -In the resulting .po file, the string will then appear as often as there are -different contextual markers for the same string (the context will appear on -the ``msgctxt`` line), allowing the translator to give a different translation -for each of them. - -For example:: - - from django.utils.translation import pgettext - - month = pgettext("month name", "May") - -or:: - - from django.utils.translation import pgettext_lazy - - class MyThing(models.Model): - name = models.CharField(help_text=pgettext_lazy( - 'help text for MyThing model', 'This is the help text')) - -will appear in the .po file as: - -.. code-block:: po - - msgctxt "month name" - msgid "May" - msgstr "" - -.. versionadded:: 1.4 - -Contextual markers are also supported by the :ttag:`trans` and -:ttag:`blocktrans` template tags. - -.. _lazy-translations: - -Lazy translation ----------------- - -Use the function :func:`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 -"``, 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 :doc:`Django 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') - -Notes on model classes translation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Your model classes may not only contain normal fields: you may have relations -(with a ``ForeignKey`` field) or additional model methods you may use for -columns in the Django admin site. - -If you have models with foreign keys and you use the Django admin site, you can -provide translations for the relation itself by using the ``verbose_name`` -parameter on the ``ForeignKey`` object:: - - class MyThing(models.Model): - kind = models.ForeignKey(ThingKind, related_name='kinds', - verbose_name=_('kind')) - -As you would do for the ``verbose_name`` and ``verbose_name_plural`` settings of -a model Meta class, you should provide a lowercase verbose name text for the -relation as Django will automatically titlecase it when required. - -For model methods, you can provide translations to Django and the admin site -with the ``short_description`` parameter set on the corresponding method:: - - class MyThing(models.Model): - kind = models.ForeignKey(ThingKind, related_name='kinds', - verbose_name=_('kind')) - - def is_mouse(self): - return self.kind.type == MOUSE_TYPE - is_mouse.short_description = _('Is it a mouse?') - -As always with model classes translations, don't forget to use the lazy -translation method! - -Working with lazy translation objects -------------------------------------- - -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 -:func:`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). - -Localized names of languages -============================ - -.. function:: get_language_info - -.. versionadded:: 1.3 - -The ``get_language_info()`` function provides detailed information about -languages:: - - >>> from django.utils.translation import get_language_info - >>> li = get_language_info('de') - >>> print li['name'], li['name_local'], li['bidi'] - German Deutsch False - -The ``name`` and ``name_local`` attributes of the dictionary contain the name of -the language in English and in the language itself, respectively. The ``bidi`` -attribute is True only for bi-directional languages. - -The source of the language information is the ``django.conf.locale`` module. -Similar access to this information is available for template code. See below. - -.. _specifying-translation-strings-in-template-code: - -Specifying translation strings: In template code -================================================ - -.. highlightlang:: html+django - -Translations in :doc:`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. - -.. templatetag:: trans - -``trans`` template tag ----------------------- - -The ``{% trans %}`` template tag translates either a constant string -(enclosed in single or double quotes) or variable content:: - - {% trans "This is the title." %} - {% trans myvar %} - -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:: - - {% trans "myvar" noop %} - -Internally, inline translations use an -:func:`~django.utils.translation.ugettext` call. - -In case a template var (``myvar`` above) is passed to the tag, the tag will -first resolve such variable to a string at run-time and then look up that -string in the message catalogs. - -It's not possible to mix a template variable inside a string within ``{% trans -%}``. If your translations require strings with variables (placeholders), use -``{% blocktrans %}`` instead. - -.. versionadded:: 1.4 - -If you'd like to retrieve a translated string without displaying it, you can -use the following syntax:: - - {% trans "This is the title" as the_title %} - - {{ the_title }} - - -In practice you'll use this to get strings that are used in multiple places -or should be used as arguments for other template tags or filters:: - - {% trans "starting point" as start %} - {% trans "end point" as end %} - {% trans "La Grande Boucle" as race %} - -

- {{ race }} -

-

- {% for stage in tour_stages %} - {% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}
{% else %}, {% endif %} - {% endfor %} -

- -.. versionadded:: 1.4 - -``{% trans %}`` also supports :ref:`contextual markers` -using the ``context`` keyword: - -.. code-block:: html+django - - {% trans "May" context "month name" %} - -.. templatetag:: blocktrans - -``blocktrans`` template tag ---------------------------- - -.. versionchanged:: 1.3 - New keyword argument format. - -Contrarily to the :ttag:`trans` tag, the ``blocktrans`` tag allows you to mark -complex sentences consisting of literals and variable content for translation -by making use of placeholders:: - - {% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %} - -To translate a template expression -- say, accessing object attributes or -using template filters -- you need to bind the expression to a local variable -for use within the translation block. Examples:: - - {% blocktrans with amount=article.price %} - That will cost $ {{ amount }}. - {% endblocktrans %} - - {% blocktrans with myvar=value|filter %} - This will have {{ myvar }} inside. - {% endblocktrans %} - -You can use multiple expressions inside a single ``blocktrans`` tag:: - - {% blocktrans with book_t=book|title author_t=author|title %} - This is {{ book_t }} by {{ author_t }} - {% endblocktrans %} - -.. note:: The previous more verbose format is still supported: - ``{% blocktrans with book|title as book_t and author|title as author_t %}`` - -.. versionchanged:: 1.4 - -If resolving one of the block arguments fails, blocktrans will fall back to -the default language by deactivating the currently active language -temporarily with the :func:`~django.utils.translation.deactivate_all` -function. - -This tag also provides for pluralization. To use it: - -* Designate and bind a counter value with the name ``count``. This value will - be the one used to select the right plural form. - -* Specify both the singular and plural forms separating them with the - ``{% plural %}`` tag within the ``{% blocktrans %}`` and - ``{% endblocktrans %}`` tags. - -An example:: - - {% blocktrans count counter=list|length %} - There is only one {{ name }} object. - {% plural %} - There are {{ counter }} {{ name }} objects. - {% endblocktrans %} - -A more complex example:: - - {% blocktrans with amount=article.price count years=i.length %} - That will cost $ {{ amount }} per year. - {% plural %} - That will cost $ {{ amount }} per {{ years }} years. - {% endblocktrans %} - -When you use both the pluralization feature and bind values to local variables -in addition to the counter value, keep in mind that the ``blocktrans`` -construct is internally converted to an ``ungettext`` call. This means the -same :ref:`notes regarding ungettext variables ` -apply. - -Reverse URL lookups cannot be carried out within the ``blocktrans`` and should -be retrieved (and stored) beforehand:: - - {% url path.to.view arg arg2 as the_url %} - {% blocktrans %} - This is a URL: {{ the_url }} - {% endblocktrans %} - -.. versionadded:: 1.4 - -``{% blocktrans %}`` also supports :ref:`contextual -markers` using the ``context`` keyword: - -.. code-block:: html+django - - {% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %} - -.. _template-translation-vars: - -Other tags ----------- - -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). - -.. versionadded:: 1.3 - -You can also retrieve information about any of the available languages using -provided template tags and filters. To get information about a single language, -use the ``{% get_language_info %}`` tag:: - - {% get_language_info for LANGUAGE_CODE as lang %} - {% get_language_info for "pl" as lang %} - -You can then access the information:: - - Language code: {{ lang.code }}
- Name of language: {{ lang.name_local }}
- Name in English: {{ lang.name }}
- Bi-directional: {{ lang.bidi }} - -You can also use the ``{% get_language_info_list %}`` template tag to retrieve -information for a list of languages (e.g. active languages as specified in -:setting:`LANGUAGES`). See :ref:`the section about the set_language redirect -view ` for an example of how to display a language -selector using ``{% get_language_info_list %}``. - -In addition to :setting:`LANGUAGES` style nested tuples, -``{% get_language_info_list %}`` supports simple lists of language codes. -If you do this in your view: - -.. code-block:: python - - return render_to_response('mytemplate.html', { - 'available_languages': ['en', 'es', 'fr'], - }, RequestContext(request)) - -you can iterate over those languages in the template:: - - {% get_language_info_list for available_languages as langs %} - {% for lang in langs %} ... {% endfor %} - -There are also simple filters available for convenience: - -* ``{{ LANGUAGE_CODE|language_name }}`` ("German") -* ``{{ LANGUAGE_CODE|language_name_local }}`` ("Deutsch") -* ``{{ LANGUAGE_CODE|bidi }}`` (False) - -.. _Django templates: ../templates_python/ - -Specifying translation strings: In JavaScript code -================================================== - -.. highlightlang:: python - -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. - -.. _javascript_catalog-view: - -The ``javascript_catalog`` view -------------------------------- - -.. module:: django.views.i18n - -.. function:: javascript_catalog(request, domain='djangojs', packages=None) - -The main solution to these problems is the :meth:`django.views.i18n.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 applications or Django core, according to what you -specify in either the info_dict or the URL. Paths listed in -:setting:`LOCALE_PATHS` are also included. - -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 :setting:`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. - -The precedence of translations is such that the packages appearing later in the -``packages`` argument have higher precedence than the ones appearing at the -beginning, this is important in the case of clashing translations for the same -literal. - -By default, the view uses the ``djangojs`` gettext domain. This can be -changed by altering the ``domain`` argument. - -You can make the view dynamic by putting the packages into the URL pattern:: - - urlpatterns = patterns('', - (r'^jsi18n/(?P\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 :setting:`INSTALLED_APPS` setting. - -The JavaScript translations found in the paths listed in the -:setting:`LOCALE_PATHS` setting are also always included. To keep consistency -with the translations lookup order algorithm used for Python and templates, the -directories listed in :setting:`LOCALE_PATHS` have the highest precedence with -the ones appearing first having higher precedence than the ones appearing -later. - -.. versionchanged:: 1.3 - Directories listed in :setting:`LOCALE_PATHS` weren't included in the - lookup algorithm until version 1.3. - -Using the JavaScript translation catalog ----------------------------------------- - -.. highlightlang:: javascript - -To use the catalog, just pull in the dynamically generated script like this: - -.. code-block:: html+django - - - -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). - -.. _url-internationalization: - -Specifying translation strings: In URL patterns -=============================================== - -.. versionadded:: 1.4 - -.. module:: django.conf.urls.i18n - -Django provides two mechanisms to internationalize URL patterns: - -* Adding the language prefix to the root of the URL patterns to make it - possible for :class:`~django.middleware.locale.LocaleMiddleware` to detect - the language to activate from the requested URL. - -* Making URL patterns themselves translatable via the - :func:`django.utils.translation.ugettext_lazy()` function. - -.. warning:: - - Using either one of these features requires that an active language be set - for each request; in other words, you need to have - :class:`django.middleware.locale.LocaleMiddleware` in your - :setting:`MIDDLEWARE_CLASSES` setting. - -Language prefix in URL patterns -------------------------------- - -.. function:: i18n_patterns(prefix, pattern_description, ...) - -This function can be used in your root URLconf as a replacement for the normal -:func:`django.conf.urls.patterns` function. Django will automatically -prepend the current active language code to all url patterns defined within -:func:`~django.conf.urls.i18n.i18n_patterns`. Example URL patterns:: - - from django.conf.urls import patterns, include, url - from django.conf.urls.i18n import i18n_patterns - - urlpatterns = patterns('' - url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'), - ) - - news_patterns = patterns('' - url(r'^$', 'news.views.index', name='index'), - url(r'^category/(?P[\w-]+)/$', 'news.views.category', name='category'), - url(r'^(?P[\w-]+)/$', 'news.views.details', name='detail'), - ) - - urlpatterns += i18n_patterns('', - url(r'^about/$', 'about.view', name='about'), - url(r'^news/$', include(news_patterns, namespace='news')), - ) - - -After defining these URL patterns, Django will automatically add the -language prefix to the URL patterns that were added by the ``i18n_patterns`` -function. Example:: - - from django.core.urlresolvers import reverse - from django.utils.translation import activate - - >>> activate('en') - >>> reverse('sitemap_xml') - '/sitemap.xml' - >>> reverse('news:index') - '/en/news/' - - >>> activate('nl') - >>> reverse('news:detail', kwargs={'slug': 'news-slug'}) - '/nl/news/news-slug/' - -.. warning:: - - :func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in your root - URLconf. Using it within an included URLconf will throw an - :exc:`ImproperlyConfigured` exception. - -.. warning:: - - Ensure that you don't have non-prefixed URL patterns that might collide - with an automatically-added language prefix. - - -Translating URL patterns ------------------------- - -URL patterns can also be marked translatable using the -:func:`~django.utils.translation.ugettext_lazy` function. Example:: - - from django.conf.urls import patterns, include, url - from django.conf.urls.i18n import i18n_patterns - from django.utils.translation import ugettext_lazy as _ - - urlpatterns = patterns('' - url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'), - ) - - news_patterns = patterns('' - url(r'^$', 'news.views.index', name='index'), - url(_(r'^category/(?P[\w-]+)/$'), 'news.views.category', name='category'), - url(r'^(?P[\w-]+)/$', 'news.views.details', name='detail'), - ) - - urlpatterns += i18n_patterns('', - url(_(r'^about/$'), 'about.view', name='about'), - url(_(r'^news/$'), include(news_patterns, namespace='news')), - ) - - -After you've created the translations (see :doc:`localization` for more -information), the :func:`~django.core.urlresolvers.reverse` function will -return the URL in the active language. Example:: - - from django.core.urlresolvers import reverse - from django.utils.translation import activate - - >>> activate('en') - >>> reverse('news:category', kwargs={'slug': 'recent'}) - '/en/news/category/recent/' - - >>> activate('nl') - >>> reverse('news:category', kwargs={'slug': 'recent'}) - '/nl/nieuws/categorie/recent/' - -.. warning:: - - In most cases, it's best to use translated URLs only within a - language-code-prefixed block of patterns (using - :func:`~django.conf.urls.i18n.i18n_patterns`), to avoid the possibility - that a carelessly translated URL causes a collision with a non-translated - URL pattern. - -.. _reversing_in_templates: - -.. templatetag:: language - -Reversing in templates ----------------------- - -If localized URLs get reversed in templates they always use the current -language. To link to a URL in another language use the ``language`` -template tag. It enables the given language in the enclosed template section: - -.. code-block:: html+django - - {% load i18n %} - - {% get_available_languages as languages %} - - {% trans "View this category in:" %} - {% for lang_code, lang_name in languages %} - {% language lang_code %} - {{ lang_name }} - {% endlanguage %} - {% endfor %} - -The :ttag:`language` tag expects the language code as the only argument. - -.. _set_language-redirect-view: - -The ``set_language`` redirect view -================================== - -.. highlightlang:: python - -.. function:: set_language(request) - -As a convenience, Django comes with a view, :func:`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 :setting:`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 - -
- {% csrf_token %} - - - -
diff --git a/docs/topics/i18n/localization.txt b/docs/topics/i18n/localization.txt deleted file mode 100644 index 13a970f02f..0000000000 --- a/docs/topics/i18n/localization.txt +++ /dev/null @@ -1,409 +0,0 @@ -============ -Localization -============ - -This document covers three localization-related topics: `Creating language -files`_ , `locale aware date, time and numbers input/output in forms`_, -and `controlling localization in templates`_. - -.. _`Creating language files`: how-to-create-language-files_ -.. _`locale aware date, time and numbers input/output in forms`: format-localization_ -.. _`controlling localization in templates`: topic-l10n-templates_ - -.. seealso:: - - The :doc:`/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 `. - -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:: Gettext utilities - - The ``makemessages`` command (and ``compilemessages`` discussed later) use - commands from the GNU gettext toolset: ``xgettext``, ``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`. 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. - -The 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`` or ``.txt`` 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 -` 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:: 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. - -.. admonition:: .po files: Encoding and BOM usage. - - Django only supports ``.po`` files encoded in UTF-8 and without any BOM - (Byte Order Mark) so if your text editor adds such marks to the beginning of - files by default then you will need to reconfigure it. - -.. _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 explicitly specify what in gettext parlance is known -as a domain in this case the ``djangojs`` domain, by providing 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. 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 -=================== - -.. versionadded:: 1.2 - -Django's formatting system is disabled by default. To enable it, it's -necessary to set :setting:`USE_L10N = True ` in your settings file. - -.. note:: - - The default :file:`settings.py` file created by :djadmin:`django-admin.py - startproject ` includes :setting:`USE_L10N = True ` - for convenience. - -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:: - Django uses different formats for displaying data to those it uses for - parsing data. Most notably, the formats for parsing dates can't use the - ``%a`` (abbreviated weekday name), ``%A`` (full weekday name), - ``%b`` (abbreviated month name), ``%B`` (full month name), - or ``%p`` (AM/PM). - -To enable a form field to localize input and output data simply use its -``localize`` argument:: - - class CashRegisterForm(forms.Form): - product = forms.CharField() - revenue = forms.DecimalField(max_digits=4, decimal_places=2, localize=True) - -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 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. - -.. _topic-l10n-templates: - -Controlling localization in templates -===================================== - -When you have enabled localization using :setting:`USE_L10N`, Django -will try to use a locale specific format whenever it outputs a value -in a template. - -However, it may not always be appropriate to use localized values -- -for example, if you're outputting Javascript or XML that is designed -to be machine-readable, you will always want unlocalized values. You -may also want to use localization in selected templates, rather than -using localization everywhere. - -To allow for fine control over the use of localization, Django -provides the ``l10n`` template library that contains the following -tags and filters. - -Template tags -------------- - -.. templatetag:: localize - -localize -~~~~~~~~ - -.. versionadded:: 1.3 - -Enables or disables localization of template variables in the -contained block. - -This tag allows a more fine grained control of localization than -:setting:`USE_L10N`. - -To activate or deactivate localization for a template block, use:: - - {% load l10n %} - - {% localize on %} - {{ value }} - {% endlocalize %} - - {% localize off %} - {{ value }} - {% endlocalize %} - -.. note:: - - The value of :setting:`USE_L10N` is not respected inside of a - `{% localize %}` block. - -See :tfilter:`localized` and :tfilter:`unlocalized` for a template filter that will -do the same job on a per-variable basis. - -Template filters ----------------- - -.. templatefilter:: localize - -localize -~~~~~~~~ - -.. versionadded:: 1.3 - -Forces localization of a single value. - -For example:: - - {% load l10n %} - - {{ value|localize }} - -To disable localization on a single value, use :tfilter:`unlocalize`. To control -localization over a large section of a template, use the :ttag:`localize` template -tag. - - -.. templatefilter:: unlocalize - -unlocalize -~~~~~~~~~~ - -.. versionadded:: 1.3 - -Forces a single value to be printed without localization. - -For example:: - - {% load l10n %} - - {{ value|unlocalize }} - -To force localization of a single value, use :tfilter:`localize`. To -control localization over a large section of a template, use the -:ttag:`localize` template tag. diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt new file mode 100644 index 0000000000..bef052e52d --- /dev/null +++ b/docs/topics/i18n/translation.txt @@ -0,0 +1,1561 @@ +=========== +Translation +=========== + +.. module:: django.utils.translation + +Overview +======== + +In order to make a Django project translatable, you have to add a minimal amount +of hooks to your Python code and templates. These hooks are called +:term:`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 then provides utilities to extract the translation strings into a +:term:`message file`. This file is a convenient way for translators to provide +the equivalent of the translation strings in the target language. Once the +translators have filled in the message file, it must be compiled. This process +relies on the GNU gettext toolset. + +Once this is done, Django takes care of translating Web apps on the fly in each +available language, according to users' language preferences. + +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 ` in your settings file. 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 :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting. + +.. note:: + + There is also an independent but related :setting:`USE_L10N` setting that + controls if Django should implement format localization. See + :doc:`/topics/i18n/formatting` for more details. + +Internationalization: in Python code +==================================== + +Standard translation +-------------------- + +Specify a translation string by using the function +:func:`~django.utils.translation.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, + :func:`~django.utils.translation.ugettext` is more useful than + ``gettext()``. Sometimes, you should be using + :func:`~django.utils.translation.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, +:djadmin:`django-admin.py makemessages `, won't be able to find +these strings. More on :djadmin:`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 +the month and the day placeholders 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. + +.. _translator-comments: + +Comments for translators +------------------------ + +.. versionadded:: 1.3 + +If you would like to give translators hints about a translatable string, you +can add a comment prefixed with the ``Translators`` keyword on the line +preceding the string, e.g.:: + + def my_view(request): + # Translators: This message appears on the home page only + output = ugettext("Welcome to my site.") + +This also works in templates with the :ttag:`comment` tag: + +.. code-block:: html+django + + {% comment %}Translators: This is a text of the base template {% endcomment %} + +The comment will then appear in the resulting ``.po`` file and should also be +displayed by most translation tools. + +Marking strings as no-op +------------------------ + +Use the function :func:`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 :func:`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 +`_ is +greater than the two forms used in English ('object' for the singular and +'objects' for all the cases where ``count`` is different from one, 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 an error when running :djadmin:`django-admin.py + compilemessages `:: + + a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid' + +.. _contextual-markers: + +Contextual markers +------------------ + +.. versionadded:: 1.3 + +Sometimes words have several meanings, such as ``"May"`` in English, which +refers to a month name and to a verb. To enable translators to translate +these words correctly in different contexts, you can use the +:func:`django.utils.translation.pgettext()` function, or the +:func:`django.utils.translation.npgettext()` function if the string needs +pluralization. Both take a context string as the first variable. + +In the resulting ``.po`` file, the string will then appear as often as there are +different contextual markers for the same string (the context will appear on the +``msgctxt`` line), allowing the translator to give a different translation for +each of them. + +For example:: + + from django.utils.translation import pgettext + + month = pgettext("month name", "May") + +or:: + + from django.utils.translation import pgettext_lazy + + class MyThing(models.Model): + name = models.CharField(help_text=pgettext_lazy( + 'help text for MyThing model', 'This is the help text')) + +will appear in the ``.po`` file as: + +.. code-block:: po + + msgctxt "month name" + msgid "May" + msgstr "" + +.. versionadded:: 1.4 + +Contextual markers are also supported by the :ttag:`trans` and +:ttag:`blocktrans` template tags. + +.. _lazy-translations: + +Lazy translation +---------------- + +Use the function :func:`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 +"``, 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 :doc:`Django 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 = _('my things') + +Notes on model classes translation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Your model classes may not only contain normal fields: you may have relations +(with a ``ForeignKey`` field) or additional model methods you may use for +columns in the Django admin site. + +If you have models with foreign keys and you use the Django admin site, you can +provide translations for the relation itself by using the ``verbose_name`` +parameter on the ``ForeignKey`` object:: + + class MyThing(models.Model): + kind = models.ForeignKey(ThingKind, related_name='kinds', + verbose_name=_('kind')) + +As you would do for the ``verbose_name`` and ``verbose_name_plural`` settings of +a model Meta class, you should provide a lowercase verbose name text for the +relation as Django will automatically titlecase it when required. + +For model methods, you can provide translations to Django and the admin site +with the ``short_description`` parameter set on the corresponding method:: + + class MyThing(models.Model): + kind = models.ForeignKey(ThingKind, related_name='kinds', + verbose_name=_('kind')) + + def is_mouse(self): + return self.kind.type == MOUSE_TYPE + is_mouse.short_description = _('Is it a mouse?') + +As always with model classes translations, don't forget to use the lazy +translation method! + +Working with lazy translation objects +------------------------------------- + +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 +:func:`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). + +Localized names of languages +---------------------------- + +.. function:: get_language_info + +.. versionadded:: 1.3 + +The ``get_language_info()`` function provides detailed information about +languages:: + + >>> from django.utils.translation import get_language_info + >>> li = get_language_info('de') + >>> print li['name'], li['name_local'], li['bidi'] + German Deutsch False + +The ``name`` and ``name_local`` attributes of the dictionary contain the name of +the language in English and in the language itself, respectively. The ``bidi`` +attribute is True only for bi-directional languages. + +The source of the language information is the ``django.conf.locale`` module. +Similar access to this information is available for template code. See below. + +.. _specifying-translation-strings-in-template-code: + +Internationalization: in template code +====================================== + +.. highlightlang:: html+django + +Translations in :doc:`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. + +.. templatetag:: trans + +``trans`` template tag +---------------------- + +The ``{% trans %}`` template tag translates either a constant string +(enclosed in single or double quotes) or variable content:: + + {% trans "This is the title." %} + {% trans myvar %} + +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:: + + {% trans "myvar" noop %} + +Internally, inline translations use an +:func:`~django.utils.translation.ugettext` call. + +In case a template var (``myvar`` above) is passed to the tag, the tag will +first resolve such variable to a string at run-time and then look up that +string in the message catalogs. + +It's not possible to mix a template variable inside a string within ``{% trans +%}``. If your translations require strings with variables (placeholders), use +``{% blocktrans %}`` instead. + +.. versionadded:: 1.4 + +If you'd like to retrieve a translated string without displaying it, you can +use the following syntax:: + + {% trans "This is the title" as the_title %} + + {{ the_title }} + + +In practice you'll use this to get strings that are used in multiple places +or should be used as arguments for other template tags or filters:: + + {% trans "starting point" as start %} + {% trans "end point" as end %} + {% trans "La Grande Boucle" as race %} + +

+ {{ race }} +

+

+ {% for stage in tour_stages %} + {% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}
{% else %}, {% endif %} + {% endfor %} +

+ +.. versionadded:: 1.4 + +``{% trans %}`` also supports :ref:`contextual markers` +using the ``context`` keyword: + +.. code-block:: html+django + + {% trans "May" context "month name" %} + +.. templatetag:: blocktrans + +``blocktrans`` template tag +--------------------------- + +.. versionchanged:: 1.3 + New keyword argument format. + +Contrarily to the :ttag:`trans` tag, the ``blocktrans`` tag allows you to mark +complex sentences consisting of literals and variable content for translation +by making use of placeholders:: + + {% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %} + +To translate a template expression -- say, accessing object attributes or +using template filters -- you need to bind the expression to a local variable +for use within the translation block. Examples:: + + {% blocktrans with amount=article.price %} + That will cost $ {{ amount }}. + {% endblocktrans %} + + {% blocktrans with myvar=value|filter %} + This will have {{ myvar }} inside. + {% endblocktrans %} + +You can use multiple expressions inside a single ``blocktrans`` tag:: + + {% blocktrans with book_t=book|title author_t=author|title %} + This is {{ book_t }} by {{ author_t }} + {% endblocktrans %} + +.. note:: The previous more verbose format is still supported: + ``{% blocktrans with book|title as book_t and author|title as author_t %}`` + +.. versionchanged:: 1.4 + +If resolving one of the block arguments fails, blocktrans will fall back to +the default language by deactivating the currently active language +temporarily with the :func:`~django.utils.translation.deactivate_all` +function. + +This tag also provides for pluralization. To use it: + +* Designate and bind a counter value with the name ``count``. This value will + be the one used to select the right plural form. + +* Specify both the singular and plural forms separating them with the + ``{% plural %}`` tag within the ``{% blocktrans %}`` and + ``{% endblocktrans %}`` tags. + +An example:: + + {% blocktrans count counter=list|length %} + There is only one {{ name }} object. + {% plural %} + There are {{ counter }} {{ name }} objects. + {% endblocktrans %} + +A more complex example:: + + {% blocktrans with amount=article.price count years=i.length %} + That will cost $ {{ amount }} per year. + {% plural %} + That will cost $ {{ amount }} per {{ years }} years. + {% endblocktrans %} + +When you use both the pluralization feature and bind values to local variables +in addition to the counter value, keep in mind that the ``blocktrans`` +construct is internally converted to an ``ungettext`` call. This means the +same :ref:`notes regarding ungettext variables ` +apply. + +Reverse URL lookups cannot be carried out within the ``blocktrans`` and should +be retrieved (and stored) beforehand:: + + {% url path.to.view arg arg2 as the_url %} + {% blocktrans %} + This is a URL: {{ the_url }} + {% endblocktrans %} + +.. versionadded:: 1.4 + +``{% blocktrans %}`` also supports :ref:`contextual +markers` using the ``context`` keyword: + +.. code-block:: html+django + + {% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %} + +.. _template-translation-vars: + +Other tags +---------- + +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). + +.. versionadded:: 1.3 + +You can also retrieve information about any of the available languages using +provided template tags and filters. To get information about a single language, +use the ``{% get_language_info %}`` tag:: + + {% get_language_info for LANGUAGE_CODE as lang %} + {% get_language_info for "pl" as lang %} + +You can then access the information:: + + Language code: {{ lang.code }}
+ Name of language: {{ lang.name_local }}
+ Name in English: {{ lang.name }}
+ Bi-directional: {{ lang.bidi }} + +You can also use the ``{% get_language_info_list %}`` template tag to retrieve +information for a list of languages (e.g. active languages as specified in +:setting:`LANGUAGES`). See :ref:`the section about the set_language redirect +view ` for an example of how to display a language +selector using ``{% get_language_info_list %}``. + +In addition to :setting:`LANGUAGES` style nested tuples, +``{% get_language_info_list %}`` supports simple lists of language codes. +If you do this in your view: + +.. code-block:: python + + return render_to_response('mytemplate.html', { + 'available_languages': ['en', 'es', 'fr'], + }, RequestContext(request)) + +you can iterate over those languages in the template:: + + {% get_language_info_list for available_languages as langs %} + {% for lang in langs %} ... {% endfor %} + +There are also simple filters available for convenience: + +* ``{{ LANGUAGE_CODE|language_name }}`` ("German") +* ``{{ LANGUAGE_CODE|language_name_local }}`` ("Deutsch") +* ``{{ LANGUAGE_CODE|bidi }}`` (False) + +.. _Django templates: ../templates_python/ + +Internationalization: in JavaScript code +======================================== + +.. highlightlang:: python + +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. + +.. _javascript_catalog-view: + +The ``javascript_catalog`` view +------------------------------- + +.. module:: django.views.i18n + +.. function:: javascript_catalog(request, domain='djangojs', packages=None) + +The main solution to these problems is the +:meth:`django.views.i18n.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 applications or +Django core, according to what you specify in either the ``info_dict`` or the +URL. Paths listed in :setting:`LOCALE_PATHS` are also included. + +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 :setting:`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. + +The precedence of translations is such that the packages appearing later in the +``packages`` argument have higher precedence than the ones appearing at the +beginning, this is important in the case of clashing translations for the same +literal. + +By default, the view uses the ``djangojs`` gettext domain. This can be +changed by altering the ``domain`` argument. + +You can make the view dynamic by putting the packages into the URL pattern:: + + urlpatterns = patterns('', + (r'^jsi18n/(?P\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 :setting:`INSTALLED_APPS` setting. + +The JavaScript translations found in the paths listed in the +:setting:`LOCALE_PATHS` setting are also always included. To keep consistency +with the translations lookup order algorithm used for Python and templates, the +directories listed in :setting:`LOCALE_PATHS` have the highest precedence with +the ones appearing first having higher precedence than the ones appearing +later. + +.. versionchanged:: 1.3 + Directories listed in :setting:`LOCALE_PATHS` weren't included in the + lookup algorithm until version 1.3. + +Using the JavaScript translation catalog +---------------------------------------- + +.. highlightlang:: javascript + +To use the catalog, just pull in the dynamically generated script like this: + +.. code-block:: html+django + + + +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). + +.. _url-internationalization: + +Internationalization: in URL patterns +===================================== + +.. versionadded:: 1.4 + +.. module:: django.conf.urls.i18n + +Django provides two mechanisms to internationalize URL patterns: + +* Adding the language prefix to the root of the URL patterns to make it + possible for :class:`~django.middleware.locale.LocaleMiddleware` to detect + the language to activate from the requested URL. + +* Making URL patterns themselves translatable via the + :func:`django.utils.translation.ugettext_lazy()` function. + +.. warning:: + + Using either one of these features requires that an active language be set + for each request; in other words, you need to have + :class:`django.middleware.locale.LocaleMiddleware` in your + :setting:`MIDDLEWARE_CLASSES` setting. + +Language prefix in URL patterns +------------------------------- + +.. function:: i18n_patterns(prefix, pattern_description, ...) + +This function can be used in your root URLconf as a replacement for the normal +:func:`django.conf.urls.patterns` function. Django will automatically +prepend the current active language code to all url patterns defined within +:func:`~django.conf.urls.i18n.i18n_patterns`. Example URL patterns:: + + from django.conf.urls import patterns, include, url + from django.conf.urls.i18n import i18n_patterns + + urlpatterns = patterns('' + url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'), + ) + + news_patterns = patterns('' + url(r'^$', 'news.views.index', name='index'), + url(r'^category/(?P[\w-]+)/$', 'news.views.category', name='category'), + url(r'^(?P[\w-]+)/$', 'news.views.details', name='detail'), + ) + + urlpatterns += i18n_patterns('', + url(r'^about/$', 'about.view', name='about'), + url(r'^news/$', include(news_patterns, namespace='news')), + ) + + +After defining these URL patterns, Django will automatically add the +language prefix to the URL patterns that were added by the ``i18n_patterns`` +function. Example:: + + from django.core.urlresolvers import reverse + from django.utils.translation import activate + + >>> activate('en') + >>> reverse('sitemap_xml') + '/sitemap.xml' + >>> reverse('news:index') + '/en/news/' + + >>> activate('nl') + >>> reverse('news:detail', kwargs={'slug': 'news-slug'}) + '/nl/news/news-slug/' + +.. warning:: + + :func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in your root + URLconf. Using it within an included URLconf will throw an + :exc:`ImproperlyConfigured` exception. + +.. warning:: + + Ensure that you don't have non-prefixed URL patterns that might collide + with an automatically-added language prefix. + + +Translating URL patterns +------------------------ + +URL patterns can also be marked translatable using the +:func:`~django.utils.translation.ugettext_lazy` function. Example:: + + from django.conf.urls import patterns, include, url + from django.conf.urls.i18n import i18n_patterns + from django.utils.translation import ugettext_lazy as _ + + urlpatterns = patterns('' + url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'), + ) + + news_patterns = patterns('' + url(r'^$', 'news.views.index', name='index'), + url(_(r'^category/(?P[\w-]+)/$'), 'news.views.category', name='category'), + url(r'^(?P[\w-]+)/$', 'news.views.details', name='detail'), + ) + + urlpatterns += i18n_patterns('', + url(_(r'^about/$'), 'about.view', name='about'), + url(_(r'^news/$'), include(news_patterns, namespace='news')), + ) + + +After you've created the translations, the +:func:`~django.core.urlresolvers.reverse` function will return the URL in the +active language. Example:: + + from django.core.urlresolvers import reverse + from django.utils.translation import activate + + >>> activate('en') + >>> reverse('news:category', kwargs={'slug': 'recent'}) + '/en/news/category/recent/' + + >>> activate('nl') + >>> reverse('news:category', kwargs={'slug': 'recent'}) + '/nl/nieuws/categorie/recent/' + +.. warning:: + + In most cases, it's best to use translated URLs only within a + language-code-prefixed block of patterns (using + :func:`~django.conf.urls.i18n.i18n_patterns`), to avoid the possibility + that a carelessly translated URL causes a collision with a non-translated + URL pattern. + +.. _reversing_in_templates: + +.. templatetag:: language + +Reversing in templates +---------------------- + +If localized URLs get reversed in templates they always use the current +language. To link to a URL in another language use the ``language`` +template tag. It enables the given language in the enclosed template section: + +.. code-block:: html+django + + {% load i18n %} + + {% get_available_languages as languages %} + + {% trans "View this category in:" %} + {% for lang_code, lang_name in languages %} + {% language lang_code %} + {{ lang_name }} + {% endlanguage %} + {% endfor %} + +The :ttag:`language` tag expects the language code as the only argument. + +.. _how-to-create-language-files: + +Localization: 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 `. + +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, :djadmin:`django-admin.py makemessages +`, that automates the creation and upkeep of these files. + +.. admonition:: Gettext utilities + + The ``makemessages`` command (and ``compilemessages`` discussed later) use + commands from the GNU gettext toolset: ``xgettext``, ``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`. 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. + +The 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 :djadmin:`django-admin.py makemessages ` examines every +file that has the ``.html`` or ``.txt`` 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 + +.. warning:: + + When :ref:`creating message files from JavaScript source code + ` you need to use the special + 'djangojs' domain, **not** ``-e js``. + +.. admonition:: No gettext? + + If you don't have the ``gettext`` utilities installed, + :djadmin:`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 + :djadmin:`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 :djadmin:`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 :djadmin:`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 :djadmin:`django-admin.py makemessages `, run :djadmin:`django-admin.py compilemessages ` like this:: + + django-admin.py compilemessages + +That's it. Your translations are ready for use. + +.. admonition:: Working on Windows? + + If you're using Windows and need to install the GNU gettext utilities so + :djadmin:`django-admin.py compilemessages ` works see + :ref:`gettext_on_windows` for more information. + +.. admonition:: .po files: Encoding and BOM usage. + + Django only supports ``.po`` files encoded in UTF-8 and without any BOM + (Byte Order Mark) so if your text editor adds such marks to the beginning of + files by default then you will need to reconfigure it. + +.. _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 :djadmin:`django-admin.py makemessages ` tool. +The only difference is you need to explicitly specify what in gettext parlance +is known as a domain in this case the ``djangojs`` domain, by providing 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 :djadmin:`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. 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". + + +Miscellaneous +============= + +.. _set_language-redirect-view: + +The ``set_language`` redirect view +---------------------------------- + +.. highlightlang:: python + +.. function:: set_language(request) + +As a convenience, Django comes with a view, :func:`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 :setting:`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 + +
+ {% csrf_token %} + + + +
+ +Using translations outside views and templates +---------------------------------------------- + +While Django provides a rich set of i18n tools for use in views and templates, +it does not restrict the usage to Django-specific code. The Django translation +mechanisms can be used to translate arbitrary texts to any language that is +supported by Django (as long as an appropriate translation catalog exists, of +course). You can load a translation catalog, activate it and translate text to +language of your choice, but remember to switch back to original language, as +activating a translation catalog is done on per-thread basis and such change +will affect code running in the same thread. + +For example:: + + from django.utils import translation + def welcome_translated(language): + cur_language = translation.get_language() + try: + translation.activate(language) + text = translation.ugettext('welcome') + finally: + translation.activate(cur_language) + return text + +Calling this function with the value 'de' will give you ``"Willkommen"``, +regardless of :setting:`LANGUAGE_CODE` and language set by middleware. + +Functions of particular interest are ``django.utils.translation.get_language()`` +which returns the language used in the current thread, +``django.utils.translation.activate()`` which activates a translation catalog +for the current thread, and ``django.utils.translation.check_for_language()`` +which checks if the given language is supported by Django. + +Implementation notes +==================== + +.. _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. + +.. _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 :setting:`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 :setting:`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. And it should come before ``CommonMiddleware`` + because ``CommonMiddleware`` needs an activated language in order + to resolve the requested URL. +* If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it. + +For example, your :setting:`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 :doc:`middleware documentation +`.) + +``LocaleMiddleware`` tries to determine the user's language preference by +following this algorithm: + +.. versionchanged:: 1.4 + +* First, it looks for the language prefix in the requested URL. This is + only performed when you are using the ``i18n_patterns`` function in your + root URLconf. See :ref:`url-internationalization` for more information + about the language prefix and how to internationalize URL patterns. + +* Failing that, it looks for a ``django_language`` key in the current + user's session. + +* Failing that, it looks for a cookie. + + The name of the cookie used is set by the :setting:`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 :setting:`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`, 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 :setting:`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). + +* If you define a custom :setting:`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, :djadmin:`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 :setting:`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` + 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: + +How Django discovers translations +--------------------------------- + +At runtime, Django builds an in-memory unified catalog of literals-translations. +To achieve this it looks for translations by following this algorithm regarding +the order in which it examines the different file paths to load the compiled +:term:`message files ` (``.mo``) and the precedence of multiple +translations for the same literal: + +1. The directories listed in :setting:`LOCALE_PATHS` have the highest + precedence, with the ones appearing first having higher precedence than + the ones appearing later. +2. Then, it looks for and uses if it exists a ``locale`` directory in each + of the installed apps listed in :setting:`INSTALLED_APPS`. The ones + appearing first have higher precedence than the ones appearing later. +3. Then, it looks for a ``locale`` directory in the project directory, or + more accurately, in the directory containing your settings file. +4. Finally, the Django-provided base translation in ``django/conf/locale`` + is used as a fallback. + +.. deprecated:: 1.3 + + Lookup in the ``locale`` subdirectory of the directory containing your + settings file (item 3 above) is deprecated since the 1.3 release and will be + removed in Django 1.5. You can use the :setting:`LOCALE_PATHS` setting + instead, by listing the absolute filesystem path of such ``locale`` + directory in the setting value. + +.. seealso:: + + The translations for literals included in JavaScript assets are looked up + following a similar but not identical algorithm. See the + :ref:`javascript_catalog view documentation ` for + more details. + +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 common +message file specific to the project you are composing. 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: + +* All paths listed in :setting:`LOCALE_PATHS` in your settings file are + searched for ``/LC_MESSAGES/django.(po|mo)`` +* ``$PROJECTPATH/locale//LC_MESSAGES/django.(po|mo)`` -- + deprecated, see above. +* ``$APPPATH/locale//LC_MESSAGES/django.(po|mo)`` +* ``$PYTHONPATH/django/conf/locale//LC_MESSAGES/django.(po|mo)`` + +To create message files, you use the :djadmin:`django-admin.py 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 ` +to produce the binary ``.mo`` files that are used by ``gettext``. + +You can also run :djadmin:`django-admin.py compilemessages +--settings=path.to.settings ` to make the compiler process all +the directories in your :setting:`LOCALE_PATHS` setting. + +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-specific translations could produce weird +problems with :djadmin:`makemessages`: it will traverse all directories below +the current path and so might put message IDs into a unified, common message +file for the current project 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, +:djadmin:`django-admin.py makemessages `, when ran on a project +level will only extract strings that are connected to your explicit project and +not strings that are distributed independently. -- cgit v1.3