From f93f056c32f614c9a130ec66824d94ec20526cdf Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Tue, 16 Feb 2010 12:12:53 +0000 Subject: [PATCH] Fixed #10260 - Refactored internationalization documentation. Thanks, Ramiro Morales. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12440 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/conf/global_settings.py | 2 +- docs/howto/i18n.txt | 72 ++ docs/howto/index.txt | 1 + docs/internals/contributing.txt | 19 +- docs/ref/settings.txt | 14 +- docs/releases/1.2.txt | 17 +- docs/topics/i18n.txt | 1144 --------------------- docs/topics/i18n/deployment.txt | 195 ++++ docs/topics/i18n/index.txt | 115 +++ docs/topics/i18n/internationalization.txt | 578 +++++++++++ docs/topics/i18n/localization.txt | 294 ++++++ docs/topics/index.txt | 2 +- 12 files changed, 1293 insertions(+), 1160 deletions(-) create mode 100644 docs/howto/i18n.txt delete mode 100644 docs/topics/i18n.txt create mode 100644 docs/topics/i18n/deployment.txt create mode 100644 docs/topics/i18n/index.txt create mode 100644 docs/topics/i18n/internationalization.txt create mode 100644 docs/topics/i18n/localization.txt diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 5d5742218c..59d50fbde9 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -306,7 +306,7 @@ YEAR_MONTH_FORMAT = 'F Y' # http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now MONTH_DAY_FORMAT = 'F j' -# Default shortformatting for date objects. See all available format strings here: +# Default short formatting for date objects. See all available format strings here: # http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now SHORT_DATE_FORMAT = 'm/d/Y' diff --git a/docs/howto/i18n.txt b/docs/howto/i18n.txt new file mode 100644 index 0000000000..722ab7ba90 --- /dev/null +++ b/docs/howto/i18n.txt @@ -0,0 +1,72 @@ +.. _howto-i18n: + +.. _using-translations-in-your-own-projects: + +=============================================== +Using internationalization in your own projects +=============================================== + +At runtime, Django looks for translations by following this algorithm: + + * First, it looks for a ``locale`` directory in the application directory + of the view that's being called. If it finds a translation for the + selected language, the translation will be installed. + * Next, it looks for a ``locale`` directory in the project directory. If it + finds a translation, the translation will be installed. + * Finally, it checks the Django-provided base translation in + ``django/conf/locale``. + +In all cases the name of the directory containing the translation is expected to +be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``, +etc. + +This way, you can write applications that include their own translations, and +you can override base translations in your project path. Or, you can just build +a big project out of several apps and put all translations into one big project +message file. The choice is yours. + +.. note:: + + If you're using manually configured settings, as described in + :ref:`settings-without-django-settings-module`, the ``locale`` directory in + the project directory will not be examined, since Django loses the ability + to work out the location of the project directory. (Django normally uses the + location of the settings file to determine this, and a settings file doesn't + exist if you're manually configuring your settings.) + +All message file repositories are structured the same way. They are: + + * ``$APPPATH/locale//LC_MESSAGES/django.(po|mo)`` + * ``$PROJECTPATH/locale//LC_MESSAGES/django.(po|mo)`` + * All paths listed in ``LOCALE_PATHS`` in your settings file are + searched in that order for ``/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 +:ref:`topics-i18n-localization` document for more details. + +You can also run ``django-admin.py compilemessages --settings=path.to.settings`` +to make the compiler process all the directories in your :setting:`LOCALE_PATHS` +setting. + +Application message files are a bit complicated to discover -- they need the +:class:`~django.middleware.locale.LocaleMiddleware`. If you don't use the +middleware, only the Django message files and project message files will be +installed and available at runtime. + +Finally, you should give some thought to the structure of your translation +files. If your applications need to be delivered to other users and will +be used in other projects, you might want to use app-specific translations. +But using app-specific translations and project translations could produce +weird problems with ``makemessages``: It will traverse all directories below +the current path and so might put message IDs into the project message file +that are already in application message files. + +The easiest way out is to store applications that are not part of the project +(and so carry their own translations) outside the project tree. That way, +``django-admin.py makemessages`` on the project level will only translate +strings that are connected to your explicit project and not strings that are +distributed independently. diff --git a/docs/howto/index.txt b/docs/howto/index.txt index 1a27a2ebac..c582c8ed17 100644 --- a/docs/howto/index.txt +++ b/docs/howto/index.txt @@ -20,6 +20,7 @@ you quickly accomplish common tasks. deployment/index error-reporting initial-data + i18n jython legacy-databases outputting-csv diff --git a/docs/internals/contributing.txt b/docs/internals/contributing.txt index 43909e3828..a257e679a3 100644 --- a/docs/internals/contributing.txt +++ b/docs/internals/contributing.txt @@ -402,15 +402,18 @@ translated, here's what to do: * Join the `Django i18n mailing list`_ and introduce yourself. + * Make sure you read the notes about :ref:`specialties-of-django-i18n`. + * Create translations using the methods described in the - :ref:`i18n documentation `. For this you will use the - ``django-admin.py makemessages`` tool. In this particular case it should - be run from the top-level ``django`` directory of the Django source tree. + :ref:`localization documentation `. For this + you will use the ``django-admin.py makemessages`` tool. In this + particular case it should be run from the top-level ``django`` directory + of the Django source tree. The script runs over the entire Django source tree and pulls out all strings marked for translation. It creates (or updates) a message file in - the directory ``conf/locale`` (for example for ``pt-BR``, the file will be - ``conf/locale/pt-br/LC_MESSAGES/django.po``). + the directory ``conf/locale`` (for example for ``pt_BR``, the file will be + ``conf/locale/pt_BR/LC_MESSAGES/django.po``). * Make sure that ``django-admin.py compilemessages -l `` runs without producing any warnings. @@ -419,7 +422,11 @@ translated, here's what to do: ``-d djangojs`` command line option to the ``django-admin.py`` invocations). - * Create a diff of the ``.po`` file(s) against the current Subversion trunk. + * Optionally, review and update the ``conf/locale//formats.py`` + file to describe the date, time and numbers formatting particularities of + your locale. See :ref:`format-localization` for details. + + * Create a diff against the current Subversion trunk. * Open a ticket in Django's ticket system, set its ``Component`` field to ``Translations``, and attach the patch to it. diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index f411a8bad5..1137b354ff 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -883,8 +883,8 @@ LANGUAGE_CODE Default: ``'en-us'`` A string representing the language code for this installation. This should be in -standard language format. For example, U.S. English is ``"en-us"``. See -:ref:`topics-i18n`. +standard :term:`language format`. For example, U.S. English is +``"en-us"``. See :ref:`topics-i18n`. .. setting:: LANGUAGE_COOKIE_NAME @@ -911,9 +911,11 @@ see the current list of translated languages by looking in .. _online source: http://code.djangoproject.com/browser/django/trunk/django/conf/global_settings.py -The list is a tuple of two-tuples in the format (language code, language -name) -- for example, ``('ja', 'Japanese')``. This specifies which languages -are available for language selection. See :ref:`topics-i18n`. +The list is a tuple of two-tuples in the format ``(language code, language +name)``, the ``language code`` part should be a +:term:`language name` -- for example, ``('ja', 'Japanese')``. +This specifies which languages are available for language selection. See +:ref:`topics-i18n`. Generally, the default value should suffice. Only set this setting if you want to restrict language selection to a subset of the Django-provided languages. @@ -948,7 +950,7 @@ LOCALE_PATHS Default: ``()`` (Empty tuple) A tuple of directories where Django looks for translation files. -See :ref:`translations-in-your-own-projects`. +See :ref:`using-translations-in-your-own-projects`. .. setting:: LOGIN_REDIRECT_URL diff --git a/docs/releases/1.2.txt b/docs/releases/1.2.txt index 069e16b567..1098917c95 100644 --- a/docs/releases/1.2.txt +++ b/docs/releases/1.2.txt @@ -516,6 +516,19 @@ documentation `. .. _RSS best practices: http://www.rssboard.org/rss-profile +Technical message IDs +--------------------- + +Up to version 1.1 Django used :ref:`technical message IDs` +to provide localizers the possibility to translate date and time formats. They +were translatable :term:`translation strings ` that could +be recognized because they were all upper case (for example +``DATETIME_FORMAT``, ``DATE_FORMAT``, ``TIME_FORMAT``). They have been +deprecated in favor of the new :ref:`Format localization +` infrastructure that allows localizers to specify that +information in a ``formats.py`` file in the corresponding +``django/conf/locale//`` directory. + What's new in Django 1.2 ======================== @@ -577,7 +590,7 @@ added support for comparison operators. No longer will you have to type: .. code-block:: html+django {% ifnotequal a b %} - ... + ... {% endifnotequal %} You can now do this: @@ -585,7 +598,7 @@ You can now do this: .. code-block:: html+django {% if a != b %} - ... + ... {% endif %} There's really no reason to use ``{% ifequal %}`` or ``{% ifnotequal %}`` diff --git a/docs/topics/i18n.txt b/docs/topics/i18n.txt deleted file mode 100644 index 2a1b1107be..0000000000 --- a/docs/topics/i18n.txt +++ /dev/null @@ -1,1144 +0,0 @@ -.. _topics-i18n: - -==================== -Internationalization -==================== - -Django has full support for internationalization of text in code and -templates, and format localization of dates and numbers. Here's how it works. - -Overview -======== - -The goal of internationalization is to allow a single Web application to offer -its content and functionality in multiple languages and locales. - -For text translation, you, the Django developer, can accomplish this goal by -adding a minimal amount of hooks to your Python code and templates. These hooks -are called **translation strings**. They tell Django: "This text should be -translated into the end user's language, if a translation for this text is -available in that language." - -Django takes care of using these hooks to translate Web apps, on the fly, -according to users' language preferences. - -Essentially, Django does two things: - - * It lets developers and template authors specify which parts of their apps - should be translatable. - * It uses these hooks to translate Web apps for particular users according - to their language preferences. - -For format localization, it's just necessary to set -:setting:`USE_L10N = True ` in your settings file. If -:setting:`USE_L10N` is set to ``True``, Django will display -numbers and dates in the format of the current locale. That includes field -representation on templates, and allowed input formats on the admin. - -If you don't need internationalization in your app -================================================== - -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 ``TEMPLATE_CONTEXT_PROCESSORS`` setting. - -If you do need internationalization: three steps -================================================ - - 1. Embed translation strings in your Python code and templates. - 2. Get translations for those strings, in whichever languages you want to - support. - 3. Activate the locale middleware in your Django settings. - -.. admonition:: Behind the scenes - - Django's translation machinery uses the standard ``gettext`` module that - comes with Python. - -1. How to specify translation strings -===================================== - -Translation strings specify "This text should be translated." These strings can -appear in your Python code and templates. It's your responsibility to mark -translatable strings; the system can only translate strings it knows about. - -In Python code --------------- - -Standard translation -~~~~~~~~~~~~~~~~~~~~ - -Specify a translation string by using the function ``ugettext()``. It's -convention to import this as a shorter alias, ``_``, to save typing. - -.. note:: - Python's standard library ``gettext`` module installs ``_()`` into the - global namespace, as an alias for ``gettext()``. In Django, we have chosen - not to follow this practice, for a couple of reasons: - - 1. For international character set (Unicode) support, ``ugettext()`` is - more useful than ``gettext()``. Sometimes, you should be using - ``ugettext_lazy()`` as the default translation method for a particular - file. Without ``_()`` in the global namespace, the developer has to - think about which is the most appropriate translation function. - - 2. The underscore character (``_``) is used to represent "the previous - result" in Python's interactive shell and doctest tests. Installing a - global ``_()`` function causes interference. Explicitly importing - ``ugettext()`` as ``_()`` avoids this problem. - -.. highlightlang:: python - -In this example, the text ``"Welcome to my site."`` is marked as a translation -string:: - - from django.utils.translation import ugettext as _ - - def my_view(request): - output = _("Welcome to my site.") - return HttpResponse(output) - -Obviously, you could code this without using the alias. This example is -identical to the previous one:: - - from django.utils.translation import ugettext - - def my_view(request): - output = ugettext("Welcome to my site.") - return HttpResponse(output) - -Translation works on computed values. This example is identical to the previous -two:: - - def my_view(request): - words = ['Welcome', 'to', 'my', 'site.'] - output = _(' '.join(words)) - return HttpResponse(output) - -Translation works on variables. Again, here's an identical example:: - - def my_view(request): - sentence = 'Welcome to my site.' - output = _(sentence) - return HttpResponse(output) - -(The caveat with using variables or computed values, as in the previous two -examples, is that Django's translation-string-detecting utility, -``django-admin.py makemessages``, won't be able to find these strings. More on -``makemessages`` later.) - -The strings you pass to ``_()`` or ``ugettext()`` can take placeholders, -specified with Python's standard named-string interpolation syntax. Example:: - - def my_view(request, m, d): - output = _('Today is %(month)s, %(day)s.') % {'month': m, 'day': d} - return HttpResponse(output) - -This technique lets language-specific translations reorder the placeholder -text. For example, an English translation may be ``"Today is November, 26."``, -while a Spanish translation may be ``"Hoy es 26 de Noviembre."`` -- with the -placeholders (the month and the day) with their positions swapped. - -For this reason, you should use named-string interpolation (e.g., ``%(day)s``) -instead of positional interpolation (e.g., ``%s`` or ``%d``) whenever you -have more than a single parameter. If you used positional interpolation, -translations wouldn't be able to reorder placeholder text. - -Marking strings as no-op -~~~~~~~~~~~~~~~~~~~~~~~~ - -Use the function ``django.utils.translation.ugettext_noop()`` to mark a string -as a translation string without translating it. The string is later translated -from a variable. - -Use this if you have constant strings that should be stored in the source -language because they are exchanged over systems or users -- such as strings in -a database -- but should be translated at the last possible point in time, such -as when the string is presented to the user. - -.. _lazy-translations: - -Lazy translation -~~~~~~~~~~~~~~~~ - -Use the function ``django.utils.translation.ugettext_lazy()`` to translate -strings lazily -- when the value is accessed rather than when the -``ugettext_lazy()`` function is called. - -For example, to translate a model's ``help_text``, do the following:: - - from django.utils.translation import ugettext_lazy - - class MyThing(models.Model): - name = models.CharField(help_text=ugettext_lazy('This is the help text')) - -In this example, ``ugettext_lazy()`` stores a lazy reference to the string -- -not the actual translation. The translation itself will be done when the string -is used in a string context, such as template rendering on the Django admin -site. - -The result of a ``ugettext_lazy()`` call can be used wherever you would use a -unicode string (an object with type ``unicode``) in Python. If you try to use -it where a bytestring (a ``str`` object) is expected, things will not work as -expected, since a ``ugettext_lazy()`` object doesn't know how to convert -itself to a bytestring. You can't use a unicode string inside a bytestring, -either, so this is consistent with normal Python behavior. For example:: - - # This is fine: putting a unicode proxy into a unicode string. - u"Hello %s" % ugettext_lazy("people") - - # This will not work, since you cannot insert a unicode object - # into a bytestring (nor can you insert our unicode proxy there) - "Hello %s" % ugettext_lazy("people") - -If you ever see output that looks like ``"hello -"``, you have tried to insert the result of -``ugettext_lazy()`` into a bytestring. That's a bug in your code. - -If you don't like the verbose name ``ugettext_lazy``, you can just alias it as -``_`` (underscore), like so:: - - from django.utils.translation import ugettext_lazy as _ - - class MyThing(models.Model): - name = models.CharField(help_text=_('This is the help text')) - -Always use lazy translations in :ref:`Django models `. -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') - -Pluralization -~~~~~~~~~~~~~ - -Use the function ``django.utils.translation.ungettext()`` to specify pluralized -messages. - -``ungettext`` takes three arguments: the singular translation string, the -plural translation string and the number of objects. - -This function is useful when you need your Django application to be localizable -to languages where the number and complexity of `plural forms -`_ is -greater than the two forms used in English ('object' for the singular and -'objects' for all the cases where ``count`` is different from zero, -irrespective of its value). - -For example:: - - from django.utils.translation import ungettext - def hello_world(request, count): - page = ungettext('there is %(count)d object', 'there are %(count)d objects', count) % { - 'count': count, - } - return HttpResponse(page) - -In this example the number of objects is passed to the translation languages as -the ``count`` variable. - -Lets see a slightly more complex usage example:: - - from django.utils.translation import ungettext - - count = Report.objects.count() - if count == 1: - name = Report._meta.verbose_name - else: - name = Report._meta.verbose_name_plural - - text = ungettext( - 'There is %(count)d %(name)s available.', - 'There are %(count)d %(name)s available.', - count - ) % { - 'count': count, - 'name': name - } - -Here we reuse localizable, hopefully already translated literals (contained in -the ``verbose_name`` and ``verbose_name_plural`` model ``Meta`` options) for -other parts of the sentence so all of it is consistently based on the -cardinality of the elements at play. - -.. _pluralization-var-notes: - -.. note:: - - When using this technique, make sure you use a single name for every - extrapolated variable included in the literal. In the example above note - how we used the ``name`` Python variable in both translation strings. This - example would fail:: - - from django.utils.translation import ungettext - from myapp.models import Report - - count = Report.objects.count() - d = { - 'count': count, - 'name': Report._meta.verbose_name - 'plural_name': Report._meta.verbose_name_plural - } - text = ungettext( - 'There is %(count)d %(name)s available.', - 'There are %(count)d %(plural_name)s available.', - count - ) % d - - You would get a ``a format specification for argument 'name', as in - 'msgstr[0]', doesn't exist in 'msgid'`` error when running - ``django-admin.py compilemessages`` or a ``KeyError`` Python exception at - runtime. - -In template code ----------------- - -.. highlightlang:: html+django - -Translations in :ref:`Django templates ` uses two template -tags and a slightly different syntax than in Python code. To give your template -access to these tags, put ``{% load i18n %}`` toward the top of your template. - -The ``{% trans %}`` template tag translates 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 -original text will be returned unchanged. This is useful when "stubbing out" -content that will require translation in the future:: - - {% trans "myvar" noop %} - -Internally, inline translations use an ``ugettext`` call. - -It's not possible to mix a template variable inside a string within ``{% trans -%}``. If your translations require strings with variables (placeholders), use -``{% blocktrans %}``:: - - {% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %} - -To translate a template expression -- say, using template filters -- you need -to bind the expression to a local variable for use within the translation -block:: - - {% blocktrans with value|filter as myvar %} - This will have {{ myvar }} inside. - {% endblocktrans %} - -If you need to bind more than one expression inside a ``blocktrans`` tag, -separate the pieces with ``and``:: - - {% blocktrans with book|title as book_t and author|title as author_t %} - This is {{ book_t }} by {{ author_t }} - {% endblocktrans %} - -To pluralize, specify both the singular and plural forms with the -``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and -``{% endblocktrans %}``. Example:: - - {% blocktrans count list|length as counter %} - There is only one {{ name }} object. - {% plural %} - There are {{ counter }} {{ name }} objects. - {% endblocktrans %} - -When you use the pluralization feature and bind additional values to local -variables apart from the counter value that selects the translated literal to -be used, have in mind that the ``blocktrans`` construct is internally converted -to an ``ungettext`` call. This means the same :ref:`notes regarding ungettext -variables ` apply. - -Each ``RequestContext`` has access to three translation-specific variables: - - * ``LANGUAGES`` is a list of tuples in which the first element is the - language code and the second is the language name (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`, - below.) - - * ``LANGUAGE_BIDI`` is the current locale's direction. If True, it's a - right-to-left language, e.g.: Hebrew, Arabic. If False it's a - left-to-right language, e.g.: English, French, German etc. - - -If you don't use the ``RequestContext`` extension, you can get those values -with three tags:: - - {% get_current_language as LANGUAGE_CODE %} - {% get_available_languages as LANGUAGES %} - {% get_current_language_bidi as LANGUAGE_BIDI %} - -These tags also require a ``{% load i18n %}``. - -Translation hooks are also available within any template block tag that accepts -constant strings. In those cases, just use ``_()`` syntax to specify a -translation string:: - - {% some_special_tag _("Page not found") value|yesno:_("yes,no") %} - -In this case, both the tag and the filter will see the already-translated -string, so they don't need to be aware of translations. - -.. note:: - In this example, the translation infrastructure will be passed the string - ``"yes,no"``, not the individual strings ``"yes"`` and ``"no"``. The - translated string will need to contain the comma so that the filter - parsing code knows how to split up the arguments. For example, a German - translator might translate the string ``"yes,no"`` as ``"ja,nein"`` - (keeping the comma intact). - -.. _Django templates: ../templates_python/ - -Working with lazy translation objects -------------------------------------- - -.. highlightlang:: python - -Using ``ugettext_lazy()`` and ``ungettext_lazy()`` to mark strings in models -and utility functions is a common operation. When you're working with these -objects elsewhere in your code, you should ensure that you don't accidentally -convert them to strings, because they should be converted as late as possible -(so that the correct locale is in effect). This necessitates the use of a -couple of helper functions. - -Joining strings: string_concat() -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Standard Python string joins (``''.join([...])``) will not work on lists -containing lazy translation objects. Instead, you can use -``django.utils.translation.string_concat()``, which creates a lazy object that -concatenates its contents *and* converts them to strings only when the result -is included in a string. For example:: - - from django.utils.translation import string_concat - ... - name = ugettext_lazy(u'John Lennon') - instrument = ugettext_lazy(u'guitar') - result = string_concat([name, ': ', instrument]) - -In this case, the lazy translations in ``result`` will only be converted to -strings when ``result`` itself is used in a string (usually at template -rendering time). - -The allow_lazy() decorator -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Django offers many utility functions (particularly in ``django.utils``) that -take a string as their first argument and do something to that string. These -functions are used by template filters as well as directly in other code. - -If you write your own similar functions and deal with translations, you'll -face the problem of what to do when the first argument is a lazy translation -object. You don't want to convert it to a string immediately, because you might -be using this function outside of a view (and hence the current thread's locale -setting will not be correct). - -For cases like this, use the ``django.utils.functional.allow_lazy()`` -decorator. It modifies the function so that *if* it's called with a lazy -translation as the first argument, the function evaluation is delayed until it -needs to be converted to a string. - -For example:: - - from django.utils.functional import allow_lazy - - def fancy_utility_function(s, ...): - # Do some conversion on string 's' - ... - fancy_utility_function = allow_lazy(fancy_utility_function, unicode) - -The ``allow_lazy()`` decorator takes, in addition to the function to decorate, -a number of extra arguments (``*args``) specifying the type(s) that the -original function can return. Usually, it's enough to include ``unicode`` here -and ensure that your function returns only Unicode strings. - -Using this decorator means you can write your function and assume that the -input is a proper string, then add support for lazy translation objects at the -end. - -.. _how-to-create-language-files: - -2. How to create language files -=============================== - -Once you've tagged your strings for later translation, you need to write (or -obtain) the language translations themselves. Here's how that works. - -.. admonition:: Locale restrictions - - Django does not support localizing your application into a locale for - which Django itself has not been translated. In this case, it will ignore - your translation files. If you were to try this and Django supported it, - you would inevitably see a mixture of translated strings (from your - application) and English strings (from Django itself). If you want to - support a locale for your application that is not already part of - Django, you'll need to make at least a minimal translation of the Django - core. See the relevant :ref:`LocaleMiddleware note` - for more details. - -Message files -------------- - -The first step is to create a **message file** for a new language. A message -file is a plain-text file, representing a single language, that contains all -available translation strings and how they should be represented in the given -language. Message files have a ``.po`` file extension. - -Django comes with a tool, ``django-admin.py makemessages``, that automates the -creation and upkeep of these files. - -.. admonition:: A note to Django veterans - - The old tool ``bin/make-messages.py`` has been moved to the command - ``django-admin.py makemessages`` to provide consistency throughout Django. - -.. admonition:: Gettext utilities - - The ``makemessages`` command (and ``compilemessages`` discussed later) use - commands from the GNU gettext toolset: ``xgetetxt``, ``msgfmt``, - ``msgmerge`` and ``msguniq``. - - .. versionchanged:: 1.2 - - The minimum version of the ``gettext`` utilities supported is 0.15. - -To create or update a message file, run this command:: - - django-admin.py makemessages -l de - -...where ``de`` is the language code for the message file you want to create. -The language code, in this case, is in 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 three places: - - * The root directory of your Django project. - * The root directory of your Django app. - * The root ``django`` directory (not a Subversion checkout, but the one - that is linked-to via ``$PYTHONPATH`` or is located somewhere on that - path). This is only relevant when you are creating a translation for - Django itself, see :ref:`contributing-translations`. - -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`` 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 `creating JavaScript translation catalogs`_ 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 `gettext on Windows`_ for more - information. - -The format of ``.po`` files is straightforward. Each ``.po`` file contains a -small bit of metadata, such as the translation maintainer's contact -information, but the bulk of the file is a list of **messages** -- simple -mappings between translation strings and the actual translated text for the -particular language. - -For example, if your Django app contained a translation string for the text -``"Welcome to my site."``, like so:: - - _("Welcome to my site.") - -...then ``django-admin.py makemessages`` will have created a ``.po`` file -containing the following snippet -- a message:: - - #: path/to/python/module.py:23 - msgid "Welcome to my site." - msgstr "" - -A quick explanation: - - * ``msgid`` is the translation string, which appears in the source. Don't - change it. - * ``msgstr`` is where you put the language-specific translation. It starts - out empty, so it's your responsibility to change it. Make sure you keep - the quotes around your translation. - * As a convenience, each message includes, in the form of a comment line - prefixed with ``#`` and located above the ``msgid`` line, the filename - and line number from which the translation string was gleaned. - -Long messages are a special case. There, the first string directly after the -``msgstr`` (or ``msgid``) is an empty string. Then the content itself will be -written over the next few lines as one string per line. Those strings are -directly concatenated. Don't forget trailing spaces within the strings; -otherwise, they'll be tacked together without whitespace! - -.. admonition:: Mind your charset - - When creating a PO file with your favorite text editor, first edit - the charset line (search for ``"CHARSET"``) and set it to the charset - you'll be using to edit the content. Due to the way the ``gettext`` tools - work internally and because we want to allow non-ASCII source strings in - Django's core and your applications, you **must** use UTF-8 as the encoding - for your PO file. This means that everybody will be using the same - encoding, which is important when Django processes the PO files. - -To reexamine all source code and templates for new translation strings and -update all message files for **all** languages, run this:: - - django-admin.py makemessages -a - -Compiling message files ------------------------ - -After you create your message file -- and each time you make changes to it -- -you'll need to compile it into a more efficient form, for use by ``gettext``. -Do this with the ``django-admin.py compilemessages`` utility. - -This tool runs over all available ``.po`` files and creates ``.mo`` files, -which are binary files optimized for use by ``gettext``. In the same directory -from which you ran ``django-admin.py makemessages``, run ``django-admin.py -compilemessages`` like this:: - - django-admin.py compilemessages - -That's it. Your translations are ready for use. - -.. admonition:: A note to Django veterans - - The old tool ``bin/compile-messages.py`` has been moved to the command - ``django-admin.py compilemessages`` to provide consistency throughout - Django. - -.. admonition:: Working on Windows? - - If you're using Windows and need to install the GNU gettext utilities so - ``django-admin compilemessages`` works see `gettext on Windows`_ for more - information. - -.. _how-django-discovers-language-preference: - -3. 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 your language, all you need to do is set -``LANGUAGE_CODE``. - -If you want to let each individual user specify which language he or she -prefers, use ``LocaleMiddleware``. ``LocaleMiddleware`` enables language -selection based on data from the request. It customizes content for each user. - -To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'`` -to your ``MIDDLEWARE_CLASSES`` setting. Because middleware order matters, you -should follow these guidelines: - - * Make sure it's one of the first middlewares installed. - * It should come after ``SessionMiddleware``, because ``LocaleMiddleware`` - makes use of session data. - * If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it. - -For example, your ``MIDDLEWARE_CLASSES`` might look like this:: - - MIDDLEWARE_CLASSES = ( - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.common.CommonMiddleware', - ) - -(For more on middleware, see the :ref:`middleware documentation -`.) - -``LocaleMiddleware`` tries to determine the user's language preference by -following this algorithm: - - * First, it looks for a ``django_language`` key in the current user's - session. - - * Failing that, it looks for a cookie. - - .. versionchanged:: 1.0 - - In Django version 0.96 and before, the cookie's name is hard-coded to - ``django_language``. In Django 1,0, The cookie name is set by the - ``LANGUAGE_COOKIE_NAME`` setting. (The default name is - ``django_language``.) - - * Failing that, it looks at the ``Accept-Language`` HTTP header. This - header is sent by your browser and tells the server which language(s) you - prefer, in order by priority. Django tries each language in the header - until it finds one with available translations. - - * Failing that, it uses the global ``LANGUAGE_CODE`` setting. - -.. _locale-middleware-notes: - -Notes: - - * In each of these places, the language preference is expected to be in the - standard 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 ``LANGUAGES`` to a list of languages. For example:: - - LANGUAGES = ( - ('de', _('German')), - ('en', _('English')), - ) - - This example restricts languages that are available for automatic - selection to German and English (and any sublanguage, like de-ch or - en-us). - - .. _LANGUAGES setting: ../settings/#languages - - * If you define a custom ``LANGUAGES`` setting, as explained in the - previous bullet, it's OK to mark the languages as translation strings - -- but use a "dummy" ``ugettext()`` function, not the one in - ``django.utils.translation``. You should *never* import - ``django.utils.translation`` from within your settings file, because that - module in itself depends on the settings, and that would cause a circular - import. - - The solution is to use a "dummy" ``ugettext()`` function. Here's a sample - settings file:: - - ugettext = lambda s: s - - LANGUAGES = ( - ('de', ugettext('German')), - ('en', ugettext('English')), - ) - - With this arrangement, ``django-admin.py makemessages`` will still find - and mark these strings for translation, but the translation won't happen - at runtime -- so you'll have to remember to wrap the languages in the - *real* ``ugettext()`` in any code that uses ``LANGUAGES`` at runtime. - - * The ``LocaleMiddleware`` can only select languages for which there is a - Django-provided base translation. If you want to provide translations - for your application that aren't already in the set of translations - in Django's source tree, you'll want to provide at least basic - translations for that language. For example, Django uses technical - message IDs to translate date formats and time formats -- so you will - need at least those translations for the system to work correctly. - - A good starting point is to copy the English ``.po`` file and to - translate at least the technical messages -- maybe the validation - messages, too. - - Technical message IDs are easily recognized; they're all upper case. You - don't translate the message ID as with other messages, you provide the - correct local variant on the provided English value. For example, with - ``DATETIME_FORMAT`` (or ``DATE_FORMAT`` or ``TIME_FORMAT``), this would - be the format string that you want to use in your language. The format - is identical to the format strings used by the ``now`` template tag. - -Once ``LocaleMiddleware`` determines the user's preference, it makes this -preference available as ``request.LANGUAGE_CODE`` for each -: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 - -.. _translations-in-your-own-projects: - -Using translations in your own projects -======================================= - -Django looks for translations by following this algorithm: - - * First, it looks for a ``locale`` directory in the application directory - of the view that's being called. If it finds a translation for the - selected language, the translation will be installed. - * Next, it looks for a ``locale`` directory in the project directory. If it - finds a translation, the translation will be installed. - * Finally, it checks the Django-provided base translation in - ``django/conf/locale``. - -This way, you can write applications that include their own translations, and -you can override base translations in your project path. Or, you can just build -a big project out of several apps and put all translations into one big project -message file. The choice is yours. - -.. note:: - - If you're using manually configured settings, as described - :ref:`settings-without-django-settings-module`, the ``locale`` directory in - the project directory will not be examined, since Django loses the ability - to work out the location of the project directory. (Django normally uses - the location of the settings file to determine this, and a settings file - doesn't exist if you're manually configuring your settings.) - -All message file repositories are structured the same way. They are: - - * ``$APPPATH/locale//LC_MESSAGES/django.(po|mo)`` - * ``$PROJECTPATH/locale//LC_MESSAGES/django.(po|mo)`` - * All paths listed in ``LOCALE_PATHS`` in your settings file are - searched in that order for ``/LC_MESSAGES/django.(po|mo)`` - * ``$PYTHONPATH/django/conf/locale//LC_MESSAGES/django.(po|mo)`` - -To create message files, you use the same ``django-admin.py makemessages`` -tool as with the Django message files. You only need to be in the right place --- in the directory where either the ``conf/locale`` (in case of the source -tree) or the ``locale/`` (in case of app messages or project messages) -directory are located. And you use the same ``django-admin.py -compilemessages`` to produce the binary ``django.mo`` files that are used by -``gettext``. - -You can also run ``django-admin.py compilemessages ---settings=path.to.settings`` to make the compiler process all the directories -in your ``LOCALE_PATHS`` setting. - -Application message files are a bit complicated to discover -- they need the -``LocaleMiddleware``. If you don't use the middleware, only the Django message -files and project message files will be processed. - -Finally, you should give some thought to the structure of your translation -files. If your applications need to be delivered to other users and will -be used in other projects, you might want to use app-specific translations. -But using app-specific translations and project translations could produce -weird problems with ``makemessages``: ``makemessages`` will traverse all -directories below the current path and so might put message IDs into the -project message file that are already in application message files. - -The easiest way out is to store applications that are not part of the project -(and so carry their own translations) outside the project tree. That way, -``django-admin.py makemessages`` on the project level will only translate -strings that are connected to your explicit project and not strings that are -distributed independently. - -The ``set_language`` redirect view -================================== - -As a convenience, Django comes with a view, ``django.views.i18n.set_language``, -that sets a user's language preference and redirects back to the previous page. - -Activate this view by adding the following line to your URLconf:: - - (r'^i18n/', include('django.conf.urls.i18n')), - -(Note that this example makes the view available at ``/i18n/setlang/``.) - -The view expects to be called via the ``POST`` method, with a ``language`` -parameter set in request. If session support is enabled, the view -saves the language choice in the user's session. Otherwise, it saves the -language choice in a cookie that is by default named ``django_language``. -(The name can be changed through the ``LANGUAGE_COOKIE_NAME`` setting.) - -After setting the language choice, Django redirects the user, following this -algorithm: - - * Django looks for a ``next`` parameter in the ``POST`` data. - * If that doesn't exist, or is empty, Django tries the URL in the - ``Referrer`` header. - * If that's empty -- say, if a user's browser suppresses that header -- - then the user will be redirected to ``/`` (the site root) as a fallback. - -Here's example HTML template code: - -.. code-block:: html+django - -
- - - -
- -Translations and JavaScript -=========================== - -Adding translations to JavaScript poses some problems: - - * JavaScript code doesn't have access to a ``gettext`` implementation. - - * JavaScript code doesn't have access to .po or .mo files; they need to be - delivered by the server. - - * The translation catalogs for JavaScript should be kept as small as - possible. - -Django provides an integrated solution for these problems: It passes the -translations into JavaScript, so you can call ``gettext``, etc., from within -JavaScript. - -The ``javascript_catalog`` view -------------------------------- - -The main solution to these problems is the ``javascript_catalog`` view, which -sends out a JavaScript code library with functions that mimic the ``gettext`` -interface, plus an array of translation strings. Those translation strings are -taken from the application, project or Django core, according to what you -specify in either the info_dict or the URL. - -You hook it up like this:: - - js_info_dict = { - 'packages': ('your.app.package',), - } - - urlpatterns = patterns('', - (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), - ) - -Each string in ``packages`` should be in Python dotted-package syntax (the -same format as the strings in ``INSTALLED_APPS``) and should refer to a package -that contains a ``locale`` directory. If you specify multiple packages, all -those catalogs are merged into one catalog. This is useful if you have -JavaScript that uses strings from different applications. - -You can make the view dynamic by putting the packages into the URL pattern:: - - urlpatterns = patterns('', - (r'^jsi18n/(?P\S+?)/$', 'django.views.i18n.javascript_catalog'), - ) - -With this, you specify the packages as a list of package names delimited by '+' -signs in the URL. This is especially useful if your pages use code from -different apps and this changes often and you don't want to pull in one big -catalog file. As a security measure, these values can only be either -``django.conf`` or any package from the ``INSTALLED_APPS`` setting. - -Using the JavaScript translation catalog ----------------------------------------- - -To use the catalog, just pull in the dynamically generated script like this:: - - - -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). - -Creating JavaScript translation catalogs ----------------------------------------- - -You create and update the translation catalogs the same way as the other - -Django translation catalogs -- with the ``django-admin.py makemessages`` tool. -The only difference is you need to provide a ``-d djangojs`` parameter, like -this:: - - django-admin.py makemessages -d djangojs -l de - -This would create or update the translation catalog for JavaScript for German. -After updating translation catalogs, just run ``django-admin.py -compilemessages`` the same way as you do with normal Django translation -catalogs. - -Specialties of Django translation -================================== - -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. - -``gettext`` on Windows -====================== - -This is only needed for people who either want to extract message IDs or -compile message files (``.po``). Translation work itself just involves editing -existing files of this type, but if you want to create your own message files, -or want to test or compile a changed message file, you will need the -``gettext`` utilities: - - * Download the following zip files from the GNOME servers - http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/ or from one - of its mirrors_ - - * ``gettext-runtime-X.zip`` - * ``gettext-tools-X.zip`` - - ``X`` is the version number, we are requiring ``0.15`` or higher. - - * Extract the contents of the ``bin\`` directories in both files to the - same folder on your system (i.e. ``C:\Program Files\gettext-utils``) - - * Update the system PATH: - - * ``Control Panel > System > Advanced > Environment Variables`` - * In the ``System variables`` list, click ``Path``, click ``Edit`` - * Add ``;C:\Program Files\gettext-utils\bin`` at the end of the - ``Variable value`` field - -.. _mirrors: http://ftp.gnome.org/pub/GNOME/MIRRORS - -You may also use ``gettext`` binaries you have obtained elsewhere, so long as -the ``xgettext --version`` command works properly. Some version 0.14.4 binaries -have been found to not support this command. Do not attempt to use Django -translation utilities with a ``gettext`` package if the command ``xgettext ---version`` entered at a Windows command prompt causes a popup window saying -"xgettext.exe has generated errors and will be closed by Windows". - -.. _format-localization: - -Format localization -=================== - -Django's formatting system is disabled by default. To enable it, it's necessay -to set :setting:`USE_L10N = True ` in your settings file. - -When using Django's formatting system, dates and numbers on templates will be -displayed using the format specified for the current locale. Two users -accessing the same content, but in different language, will see date and -number fields formatted in different ways, depending on the format for their -current locale. - -Django will also use localized formats when parsing data in forms. That means -Django uses different formats for different locales when guessing the format -used by the user when inputting data on forms. Note that Django uses different -formats for displaying data, and for parsing it. - -Creating custom format files ----------------------------- - -Django provides format definitions for many locales, but sometimes you might -want to create your own, because a format files doesn't exist for your locale, -or because you want to overwrite some of the values. - -To use custom formats, first thing to do, is to specify the path where you'll -place format files. To do that, just set your :setting:`FORMAT_MODULE_PATH` -setting to the the path (in the format ``'foo.bar.baz``) where format files -will exists. - -Files are not placed directly in this directory, but in a directory named as -the locale, and must be named ``formats.py``. - -To customize the English formats, a structure like this would be needed:: - - mysite/ - formats/ - __init__.py - en/ - __init__.py - formats.py - -where :file:`formats.py` contains custom format definitions. For example:: - - THOUSAND_SEPARATOR = ' ' - -to use a space as a thousand separator, instead of the default for English, -a comma. diff --git a/docs/topics/i18n/deployment.txt b/docs/topics/i18n/deployment.txt new file mode 100644 index 0000000000..6cb772d3ab --- /dev/null +++ b/docs/topics/i18n/deployment.txt @@ -0,0 +1,195 @@ +.. _topics-i18n-deployment: + +========================== +Deployment of translations +========================== + +If you don't need internationalization +====================================== + +Django's internationalization hooks are on by default, and that means there's a +bit of i18n-related overhead in certain places of the framework. If you don't +use internationalization, you should take the two seconds to set +:setting:`USE_I18N = False ` in your settings file. If +:setting:`USE_I18N` is set to ``False``, then Django will make some +optimizations so as not to load the internationalization machinery. + +You'll probably also want to remove ``'django.core.context_processors.i18n'`` +from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting. + +.. note:: + + There is also an independent but related :setting:`USE_L10N` setting that + controls if Django should implement format localization. + + If :setting:`USE_L10N` is set to ``True``, Django will handle numbers times, + and dates in the format of the current locale. That includes representation + of these field types on templates and allowed input formats for dates, + times on model forms. + + See :ref:`format-localization` for more details. + +If you do need internationalization +=================================== + +.. _how-django-discovers-language-preference: + +How Django discovers language preference +---------------------------------------- + +Once you've prepared your translations -- or, if you just want to use the +translations that come with Django -- you'll just need to activate translation +for your app. + +Behind the scenes, Django has a very flexible model of deciding which language +should be used -- installation-wide, for a particular user, or both. + +To set an installation-wide language preference, set :setting:`LANGUAGE_CODE`. +Django uses this language as the default translation -- the final attempt if no +other translator finds a translation. + +If all you want to do is run Django with your native language, and a language +file is available for it, all you need to do is set ``LANGUAGE_CODE``. + +If you want to let each individual user specify which language he or she +prefers, use ``LocaleMiddleware``. ``LocaleMiddleware`` enables language +selection based on data from the request. It customizes content for each user. + +To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'`` +to your ``MIDDLEWARE_CLASSES`` setting. Because middleware order matters, you +should follow these guidelines: + + * Make sure it's one of the first middlewares installed. + * It should come after ``SessionMiddleware``, because ``LocaleMiddleware`` + makes use of session data. + * If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it. + +For example, your ``MIDDLEWARE_CLASSES`` might look like this:: + + MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.common.CommonMiddleware', + ) + +(For more on middleware, see the :ref:`middleware documentation +`.) + +``LocaleMiddleware`` tries to determine the user's language preference by +following this algorithm: + + * First, it looks for a ``django_language`` key in the current user's + session. + + * Failing that, it looks for a cookie. + + .. versionchanged:: 1.0 + + In Django version 0.96 and before, the cookie's name is hard-coded to + ``django_language``. In Django 1,0, The cookie name is set by the + ``LANGUAGE_COOKIE_NAME`` setting. (The default name is + ``django_language``.) + + * Failing that, it looks at the ``Accept-Language`` HTTP header. This + header is sent by your browser and tells the server which language(s) you + prefer, in order by priority. Django tries each language in the header + until it finds one with available translations. + + * Failing that, it uses the global ``LANGUAGE_CODE`` setting. + +.. _locale-middleware-notes: + +Notes: + + * In each of these places, the language preference is expected to be in the + standard :term:`language format`, as a string. For example, + Brazilian Portuguese is ``pt-br``. + + * If a base language is available but the sublanguage specified is not, + Django uses the base language. For example, if a user specifies ``de-at`` + (Austrian German) but Django only has ``de`` available, Django uses + ``de``. + + * Only languages listed in the :setting:`LANGUAGES` setting can be selected. + If you want to restrict the language selection to a subset of provided + languages (because your application doesn't provide all those languages), + set ``LANGUAGES`` to a list of languages. For example:: + + LANGUAGES = ( + ('de', _('German')), + ('en', _('English')), + ) + + This example restricts languages that are available for automatic + selection to German and English (and any sublanguage, like de-ch or + en-us). + + .. _LANGUAGES setting: ../settings/#languages + + * If you define a custom ``LANGUAGES`` setting, as explained in the + previous bullet, it's OK to mark the languages as translation strings + -- but use a "dummy" ``ugettext()`` function, not the one in + ``django.utils.translation``. You should *never* import + ``django.utils.translation`` from within your settings file, because that + module in itself depends on the settings, and that would cause a circular + import. + + The solution is to use a "dummy" ``ugettext()`` function. Here's a sample + settings file:: + + ugettext = lambda s: s + + LANGUAGES = ( + ('de', ugettext('German')), + ('en', ugettext('English')), + ) + + With this arrangement, ``django-admin.py makemessages`` will still find + and mark these strings for translation, but the translation won't happen + at runtime -- so you'll have to remember to wrap the languages in the + *real* ``ugettext()`` in any code that uses ``LANGUAGES`` at runtime. + + * The ``LocaleMiddleware`` can only select languages for which there is a + Django-provided base translation. If you want to provide translations + for your application that aren't already in the set of translations + in Django's source tree, you'll want to provide at least a basic + one as described in the :ref:`Locale restrictions` + note. + +Once ``LocaleMiddleware`` determines the user's preference, it makes this +preference available as ``request.LANGUAGE_CODE`` for each +:class:`~django.http.HttpRequest`. Feel free to read this value in your view +code. Here's a simple example:: + + def hello_world(request, count): + if request.LANGUAGE_CODE == 'de-at': + return HttpResponse("You prefer to read Austrian German.") + else: + return HttpResponse("You prefer to read another language.") + +Note that, with static (middleware-less) translation, the language is in +``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's +in ``request.LANGUAGE_CODE``. + +.. _settings file: ../settings/ +.. _middleware documentation: ../middleware/ +.. _session: ../sessions/ +.. _request object: ../request_response/#httprequest-objects + +How Django discovers translations +--------------------------------- + +As described in :ref:`using-translations-in-your-own-projects`, +at runtime, Django looks for translations by following this algorithm: + + * First, it looks for a ``locale`` directory in the application directory + of the view that's being called. If it finds a translation for the + selected language, the translation will be installed. + * Next, it looks for a ``locale`` directory in the project directory. If it + finds a translation, the translation will be installed. + * Finally, it checks the Django-provided base translation in + ``django/conf/locale``. + +In all cases the name of the directory containing the translation is expected to +be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``, +etc. diff --git a/docs/topics/i18n/index.txt b/docs/topics/i18n/index.txt new file mode 100644 index 0000000000..e3db1a2d52 --- /dev/null +++ b/docs/topics/i18n/index.txt @@ -0,0 +1,115 @@ +.. _topics-i18n: + +===================================== +Internationalization and localization +===================================== + +Overview +======== + +Django has full support for internationalization of text in code and +templates, and format localization of dates and numbers. Here's how it works. + +Essentially, Django does two things: + + * It allows developers and template authors to specify which parts of + their apps should be translatable. + * It uses these hooks to translate Web apps for particular users according + to their language preferences. + +The complete process can be seen as divided in three stages. It is also possible +to identify an identical number of roles with very well defined responsabilities +associated with each of these tasks (although it's perfectly normal if you +find yourself performing more than one of these roles): + + * For applicacion authors wishing to make sure their Django apps can be + used in different locales: :ref:`topics-i18n-internationalization`. + * For translators wanting to translate Django apps: :ref:`topics-i18n-localization`. + * For system administrators/final users setting up internationalized apps or + developers integrating third party apps: :ref:`topics-i18n-deployment`. + +.. toctree:: + :hidden: + :maxdepth: 1 + + internationalization + localization + deployment + +.. _ seealso:: + +For more general information about the topic, see the `GNU gettext documentation`_ +and the `Wikipedia article`_. + +.. _GNU gettext documentation: http://www.gnu.org/software/gettext/manual/gettext.html#Concepts +.. _Wikipedia article: http://en.wikipedia.org/wiki/Internationalization_and_localization + +Glossary +======== + +First lets define some terms that will help us to handle a common language: + +.. glossary:: + + locale name + A locale name, either a language specification of the form ``ll`` or a + combined language and country specification of the form ``ll_CC``. + Examples: ``it``, ``de_AT``, ``es``, ``pt_BR``. Note the underscore in + some of them and the case of the part located to its right. + + language code + Represents the name of a language. Browsers send the names of the + languages they accept in the ``Accept-Language`` HTTP header using this + format. Examples: ``it``, ``de-at``, ``es``, ``pt-br``. Note the ``-`` + separator. + + message file + A message file is a plain-text file, representing a single language, + that contains all available :term:`translation strings + ` and how they should be represented in the given + language. Message files have a ``.po`` file extension. + + translation string + A literal that can be translated. + +.. _specialties-of-django-i18n: + +Specialties of Django translation +================================= + +Django's translation machinery uses the standard ``gettext`` module that comes +with Python. If you know ``gettext``, you might note these specialties in the +way Django does translation: + + * The string domain is ``django`` or ``djangojs``. This string domain is + used to differentiate between different programs that store their data + in a common message-file library (usually ``/usr/share/locale/``). The + ``django`` domain is used for python and template translation strings + and is loaded into the global translation catalogs. The ``djangojs`` + domain is only used for JavaScript translation catalogs to make sure + that those are as small as possible. + * Django doesn't use ``xgettext`` alone. It uses Python wrappers around + ``xgettext`` and ``msgfmt``. This is mostly for convenience. + +.. _technical-messages: + +Django technical message IDs +---------------------------- + +.. versionchanged:: 1.2 + Starting with Django 1.2, technical message IDs are being replaced by :ref:`format-localization` + +Django uses technical message IDs to translate date formats and time formats. +Technical message IDs are :term:`translation strings ` and +can be easily recognized; they're all upper case. You don't translate the +message ID as with other translation strings, you provide the correct local +variant on the provided English value. The format is identical to the format +strings used by the ``now`` template tag. + +For example, with ``DATETIME_FORMAT`` (or ``DATE_FORMAT`` or ``TIME_FORMAT``), +this would be the format string that you want to use in your language. A Django +contributor localizing it to Spanish probably would provide a ``"j N Y P"`` +"translation" for it in the relevant ``django.po`` file:: + + msgid "DATETIME_FORMAT" + msgstr "j N Y P" diff --git a/docs/topics/i18n/internationalization.txt b/docs/topics/i18n/internationalization.txt new file mode 100644 index 0000000000..316e538581 --- /dev/null +++ b/docs/topics/i18n/internationalization.txt @@ -0,0 +1,578 @@ +.. _topics-i18n-internationalization: + +==================== +Internationalization +==================== + +Overview +======== + +The goal of internationalization is to allow a single Web application to offer +its content and functionality in multiple languages and locales. + +For text translations, you, the Django developer, can accomplish this goal by +adding a minimal amount of hooks to your Python and templates. These hooks +are called **translation strings**. They tell Django: "This text should be +translated into the end user's language, if a translation for this text is +available in that language." It's your responsibility to mark translatable +strings; the system can only translate strings it knows about. + +Django takes care of using these hooks to translate Web apps, on the fly, +according to users' language preferences. + +Specifying translation strings: In Python code +============================================== + +Standard translation +-------------------- + +Specify a translation string by using the function ``ugettext()``. It's +convention to import this as a shorter alias, ``_``, to save typing. + +.. note:: + Python's standard library ``gettext`` module installs ``_()`` into the + global namespace, as an alias for ``gettext()``. In Django, we have chosen + not to follow this practice, for a couple of reasons: + + 1. For international character set (Unicode) support, ``ugettext()`` is + more useful than ``gettext()``. Sometimes, you should be using + ``ugettext_lazy()`` as the default translation method for a particular + file. Without ``_()`` in the global namespace, the developer has to + think about which is the most appropriate translation function. + + 2. The underscore character (``_``) is used to represent "the previous + result" in Python's interactive shell and doctest tests. Installing a + global ``_()`` function causes interference. Explicitly importing + ``ugettext()`` as ``_()`` avoids this problem. + +.. highlightlang:: python + +In this example, the text ``"Welcome to my site."`` is marked as a translation +string:: + + from django.utils.translation import ugettext as _ + + def my_view(request): + output = _("Welcome to my site.") + return HttpResponse(output) + +Obviously, you could code this without using the alias. This example is +identical to the previous one:: + + from django.utils.translation import ugettext + + def my_view(request): + output = ugettext("Welcome to my site.") + return HttpResponse(output) + +Translation works on computed values. This example is identical to the previous +two:: + + def my_view(request): + words = ['Welcome', 'to', 'my', 'site.'] + output = _(' '.join(words)) + return HttpResponse(output) + +Translation works on variables. Again, here's an identical example:: + + def my_view(request): + sentence = 'Welcome to my site.' + output = _(sentence) + return HttpResponse(output) + +(The caveat with using variables or computed values, as in the previous two +examples, is that Django's translation-string-detecting utility, +``django-admin.py makemessages``, won't be able to find these strings. More on +``makemessages`` later.) + +The strings you pass to ``_()`` or ``ugettext()`` can take placeholders, +specified with Python's standard named-string interpolation syntax. Example:: + + def my_view(request, m, d): + output = _('Today is %(month)s, %(day)s.') % {'month': m, 'day': d} + return HttpResponse(output) + +This technique lets language-specific translations reorder the placeholder +text. For example, an English translation may be ``"Today is November, 26."``, +while a Spanish translation may be ``"Hoy es 26 de Noviembre."`` -- with the +placeholders (the month and the day) with their positions swapped. + +For this reason, you should use named-string interpolation (e.g., ``%(day)s``) +instead of positional interpolation (e.g., ``%s`` or ``%d``) whenever you +have more than a single parameter. If you used positional interpolation, +translations wouldn't be able to reorder placeholder text. + +Marking strings as no-op +------------------------ + +Use the function ``django.utils.translation.ugettext_noop()`` to mark a string +as a translation string without translating it. The string is later translated +from a variable. + +Use this if you have constant strings that should be stored in the source +language because they are exchanged over systems or users -- such as strings in +a database -- but should be translated at the last possible point in time, such +as when the string is presented to the user. + +Pluralization +------------- + +Use the function ``django.utils.translation.ungettext()`` to specify pluralized +messages. + +``ungettext`` takes three arguments: the singular translation string, the plural +translation string and the number of objects. + +This function is useful when you need your Django application to be localizable +to languages where the number and complexity of `plural forms +`_ is +greater than the two forms used in English ('object' for the singular and +'objects' for all the cases where ``count`` is different from zero, irrespective +of its value.) + +For example:: + + from django.utils.translation import ungettext + def hello_world(request, count): + page = ungettext('there is %(count)d object', 'there are %(count)d objects', count) % { + 'count': count, + } + return HttpResponse(page) + +In this example the number of objects is passed to the translation languages as +the ``count`` variable. + +Lets see a slightly more complex usage example:: + + from django.utils.translation import ungettext + + count = Report.objects.count() + if count == 1: + name = Report._meta.verbose_name + else: + name = Report._meta.verbose_name_plural + + text = ungettext( + 'There is %(count)d %(name)s available.', + 'There are %(count)d %(name)s available.', + count + ) % { + 'count': count, + 'name': name + } + +Here we reuse localizable, hopefully already translated literals (contained in +the ``verbose_name`` and ``verbose_name_plural`` model ``Meta`` options) for +other parts of the sentence so all of it is consistently based on the +cardinality of the elements at play. + +.. _pluralization-var-notes: + +.. note:: + + When using this technique, make sure you use a single name for every + extrapolated variable included in the literal. In the example above note how + we used the ``name`` Python variable in both translation strings. This + example would fail:: + + from django.utils.translation import ungettext + from myapp.models import Report + + count = Report.objects.count() + d = { + 'count': count, + 'name': Report._meta.verbose_name + 'plural_name': Report._meta.verbose_name_plural + } + text = ungettext( + 'There is %(count)d %(name)s available.', + 'There are %(count)d %(plural_name)s available.', + count + ) % d + + You would get a ``a format specification for argument 'name', as in + 'msgstr[0]', doesn't exist in 'msgid'`` error when running + ``django-admin.py compilemessages`` or a ``KeyError`` Python exception at + runtime. + +.. _lazy-translations: + +Lazy translation +---------------- + +Use the function ``django.utils.translation.ugettext_lazy()`` to translate +strings lazily -- when the value is accessed rather than when the +``ugettext_lazy()`` function is called. + +For example, to translate a model's ``help_text``, do the following:: + + from django.utils.translation import ugettext_lazy + + class MyThing(models.Model): + name = models.CharField(help_text=ugettext_lazy('This is the help text')) + +In this example, ``ugettext_lazy()`` stores a lazy reference to the string -- +not the actual translation. The translation itself will be done when the string +is used in a string context, such as template rendering on the Django admin +site. + +The result of a ``ugettext_lazy()`` call can be used wherever you would use a +unicode string (an object with type ``unicode``) in Python. If you try to use +it where a bytestring (a ``str`` object) is expected, things will not work as +expected, since a ``ugettext_lazy()`` object doesn't know how to convert +itself to a bytestring. You can't use a unicode string inside a bytestring, +either, so this is consistent with normal Python behavior. For example:: + + # This is fine: putting a unicode proxy into a unicode string. + u"Hello %s" % ugettext_lazy("people") + + # This will not work, since you cannot insert a unicode object + # into a bytestring (nor can you insert our unicode proxy there) + "Hello %s" % ugettext_lazy("people") + +If you ever see output that looks like ``"hello +"``, you have tried to insert the result of +``ugettext_lazy()`` into a bytestring. That's a bug in your code. + +If you don't like the verbose name ``ugettext_lazy``, you can just alias it as +``_`` (underscore), like so:: + + from django.utils.translation import ugettext_lazy as _ + + class MyThing(models.Model): + name = models.CharField(help_text=_('This is the help text')) + +Always use lazy translations in :ref:`Django models `. +Field names and table names should be marked for translation (otherwise, they +won't be translated in the admin interface). This means writing explicit +``verbose_name`` and ``verbose_name_plural`` options in the ``Meta`` class, +though, rather than relying on Django's default determination of +``verbose_name`` and ``verbose_name_plural`` by looking at the model's class +name:: + + from django.utils.translation import ugettext_lazy as _ + + class MyThing(models.Model): + name = models.CharField(_('name'), help_text=_('This is the help text')) + class Meta: + verbose_name = _('my thing') + verbose_name_plural = _('mythings') + +Working with lazy translation objects +------------------------------------- + +.. highlightlang:: python + +Using ``ugettext_lazy()`` and ``ungettext_lazy()`` to mark strings in models +and utility functions is a common operation. When you're working with these +objects elsewhere in your code, you should ensure that you don't accidentally +convert them to strings, because they should be converted as late as possible +(so that the correct locale is in effect). This necessitates the use of a +couple of helper functions. + +Joining strings: string_concat() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Standard Python string joins (``''.join([...])``) will not work on lists +containing lazy translation objects. Instead, you can use +``django.utils.translation.string_concat()``, which creates a lazy object that +concatenates its contents *and* converts them to strings only when the result +is included in a string. For example:: + + from django.utils.translation import string_concat + ... + name = ugettext_lazy(u'John Lennon') + instrument = ugettext_lazy(u'guitar') + result = string_concat([name, ': ', instrument]) + +In this case, the lazy translations in ``result`` will only be converted to +strings when ``result`` itself is used in a string (usually at template +rendering time). + +The allow_lazy() decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Django offers many utility functions (particularly in ``django.utils``) that +take a string as their first argument and do something to that string. These +functions are used by template filters as well as directly in other code. + +If you write your own similar functions and deal with translations, you'll +face the problem of what to do when the first argument is a lazy translation +object. You don't want to convert it to a string immediately, because you might +be using this function outside of a view (and hence the current thread's locale +setting will not be correct). + +For cases like this, use the ``django.utils.functional.allow_lazy()`` +decorator. It modifies the function so that *if* it's called with a lazy +translation as the first argument, the function evaluation is delayed until it +needs to be converted to a string. + +For example:: + + from django.utils.functional import allow_lazy + + def fancy_utility_function(s, ...): + # Do some conversion on string 's' + ... + fancy_utility_function = allow_lazy(fancy_utility_function, unicode) + +The ``allow_lazy()`` decorator takes, in addition to the function to decorate, +a number of extra arguments (``*args``) specifying the type(s) that the +original function can return. Usually, it's enough to include ``unicode`` here +and ensure that your function returns only Unicode strings. + +Using this decorator means you can write your function and assume that the +input is a proper string, then add support for lazy translation objects at the +end. + +Specifying translation strings: In template code +================================================ + +.. highlightlang:: html+django + +Translations in :ref:`Django templates ` uses two template +tags and a slightly different syntax than in Python code. To give your template +access to these tags, put ``{% load i18n %}`` toward the top of your template. + +The ``{% trans %}`` template tag translates either a constant string +(enclosed in single or double quotes) or variable content:: + + {% 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 ``ugettext`` call. + +It's not possible to mix a template variable inside a string within ``{% trans +%}``. If your translations require strings with variables (placeholders), use +``{% blocktrans %}``:: + + {% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %} + +To translate a template expression -- say, using template filters -- you need +to bind the expression to a local variable for use within the translation +block:: + + {% blocktrans with value|filter as myvar %} + This will have {{ myvar }} inside. + {% endblocktrans %} + +If you need to bind more than one expression inside a ``blocktrans`` tag, +separate the pieces with ``and``:: + + {% blocktrans with book|title as book_t and author|title as author_t %} + This is {{ book_t }} by {{ author_t }} + {% endblocktrans %} + +To pluralize, specify both the singular and plural forms with the +``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and +``{% endblocktrans %}``. Example:: + + {% blocktrans count list|length as counter %} + There is only one {{ name }} object. + {% plural %} + There are {{ counter }} {{ name }} objects. + {% endblocktrans %} + +When you use the pluralization feature and bind additional values to local +variables apart from the counter value that selects the translated literal to be +used, have in mind that the ``blocktrans`` construct is internally converted +to an ``ungettext`` call. This means the same :ref:`notes regarding ungettext +variables ` apply. + +Each ``RequestContext`` has access to three translation-specific variables: + + * ``LANGUAGES`` is a list of tuples in which the first element is the + :term:`language code` and the second is the language name (translated into + the currently active locale). + + * ``LANGUAGE_CODE`` is the current user's preferred language, as a string. + Example: ``en-us``. (See :ref:`how-django-discovers-language-preference`.) + + * ``LANGUAGE_BIDI`` is the current locale's direction. If True, it's a + right-to-left language, e.g.: Hebrew, Arabic. If False it's a + left-to-right language, e.g.: English, French, German etc. + + +If you don't use the ``RequestContext`` extension, you can get those values with +three tags:: + + {% get_current_language as LANGUAGE_CODE %} + {% get_available_languages as LANGUAGES %} + {% get_current_language_bidi as LANGUAGE_BIDI %} + +These tags also require a ``{% load i18n %}``. + +Translation hooks are also available within any template block tag that accepts +constant strings. In those cases, just use ``_()`` syntax to specify a +translation string:: + + {% some_special_tag _("Page not found") value|yesno:_("yes,no") %} + +In this case, both the tag and the filter will see the already-translated +string, so they don't need to be aware of translations. + +.. note:: + In this example, the translation infrastructure will be passed the string + ``"yes,no"``, not the individual strings ``"yes"`` and ``"no"``. The + translated string will need to contain the comma so that the filter + parsing code knows how to split up the arguments. For example, a German + translator might translate the string ``"yes,no"`` as ``"ja,nein"`` + (keeping the comma intact). + +.. _Django templates: ../templates_python/ + +Specifying translation strings: In JavaScript code +================================================== + +Adding translations to JavaScript poses some problems: + + * JavaScript code doesn't have access to a ``gettext`` implementation. + + * JavaScript code doesn't have access to .po or .mo files; they need to be + delivered by the server. + + * The translation catalogs for JavaScript should be kept as small as + possible. + +Django provides an integrated solution for these problems: It passes the +translations into JavaScript, so you can call ``gettext``, etc., from within +JavaScript. + +The ``javascript_catalog`` view +------------------------------- + +The main solution to these problems is the ``javascript_catalog`` view, which +sends out a JavaScript code library with functions that mimic the ``gettext`` +interface, plus an array of translation strings. Those translation strings are +taken from the application, project or Django core, according to what you +specify in either the info_dict or the URL. + +You hook it up like this:: + + js_info_dict = { + 'packages': ('your.app.package',), + } + + urlpatterns = patterns('', + (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), + ) + +Each string in ``packages`` should be in Python dotted-package syntax (the +same format as the strings in ``INSTALLED_APPS``) and should refer to a package +that contains a ``locale`` directory. If you specify multiple packages, all +those catalogs are merged into one catalog. This is useful if you have +JavaScript that uses strings from different applications. + +You can make the view dynamic by putting the packages into the URL pattern:: + + urlpatterns = patterns('', + (r'^jsi18n/(?P\S+?)/$', 'django.views.i18n.javascript_catalog'), + ) + +With this, you specify the packages as a list of package names delimited by '+' +signs in the URL. This is especially useful if your pages use code from +different apps and this changes often and you don't want to pull in one big +catalog file. As a security measure, these values can only be either +``django.conf`` or any package from the ``INSTALLED_APPS`` setting. + +Using the JavaScript translation catalog +---------------------------------------- + +To use the catalog, just pull in the dynamically generated script like this:: + + + +This uses reverse URL lookup to find the URL of the JavaScript catalog view. +When the catalog is loaded, your JavaScript code can use the standard +``gettext`` interface to access it:: + + document.write(gettext('this is to be translated')); + +There is also an ``ngettext`` interface:: + + var object_cnt = 1 // or 0, or 2, or 3, ... + s = ngettext('literal for the singular case', + 'literal for the plural case', object_cnt); + +and even a string interpolation function:: + + function interpolate(fmt, obj, named); + +The interpolation syntax is borrowed from Python, so the ``interpolate`` +function supports both positional and named interpolation: + + * Positional interpolation: ``obj`` contains a JavaScript Array object + whose elements values are then sequentially interpolated in their + corresponding ``fmt`` placeholders in the same order they appear. + For example:: + + fmts = ngettext('There is %s object. Remaining: %s', + 'There are %s objects. Remaining: %s', 11); + s = interpolate(fmts, [11, 20]); + // s is 'There are 11 objects. Remaining: 20' + + * Named interpolation: This mode is selected by passing the optional + boolean ``named`` parameter as true. ``obj`` contains a JavaScript + object or associative array. For example:: + + d = { + count: 10 + total: 50 + }; + + fmts = ngettext('Total: %(total)s, there is %(count)s object', + 'there are %(count)s of a total of %(total)s objects', d.count); + s = interpolate(fmts, d, true); + +You shouldn't go over the top with string interpolation, though: this is still +JavaScript, so the code has to make repeated regular-expression substitutions. +This isn't as fast as string interpolation in Python, so keep it to those +cases where you really need it (for example, in conjunction with ``ngettext`` +to produce proper pluralizations). + +The ``set_language`` redirect view +================================== + +As a convenience, Django comes with a view, ``django.views.i18n.set_language``, +that sets a user's language preference and redirects back to the previous page. + +Activate this view by adding the following line to your URLconf:: + + (r'^i18n/', include('django.conf.urls.i18n')), + +(Note that this example makes the view available at ``/i18n/setlang/``.) + +The view expects to be called via the ``POST`` method, with a ``language`` +parameter set in request. If session support is enabled, the view +saves the language choice in the user's session. Otherwise, it saves the +language choice in a cookie that is by default named ``django_language``. +(The name can be changed through the ``LANGUAGE_COOKIE_NAME`` setting.) + +After setting the language choice, Django redirects the user, following this +algorithm: + + * Django looks for a ``next`` parameter in the ``POST`` data. + * If that doesn't exist, or is empty, Django tries the URL in the + ``Referrer`` header. + * If that's empty -- say, if a user's browser suppresses that header -- + then the user will be redirected to ``/`` (the site root) as a fallback. + +Here's example HTML template code: + +.. code-block:: html+django + +
+ + + +
diff --git a/docs/topics/i18n/localization.txt b/docs/topics/i18n/localization.txt new file mode 100644 index 0000000000..5e909d5fa6 --- /dev/null +++ b/docs/topics/i18n/localization.txt @@ -0,0 +1,294 @@ +.. _topics-i18n-localization: + +============ +Localization +============ + +This document covers two localization-related topics: `Creating language +files`_ and `locale aware date, time and numbers input/output in forms`_ + +.. _`Creating language files`: how-to-create-language-files_ +.. _`locale aware date, time and numbers input/output in forms`: format-localization_ + +.. seealso:: + + The :ref:`howto-i18n` document included with the Django HOW-TO documents collection. + +.. _how-to-create-language-files: + +How to create language files +============================ + +Once the string literals of an application have been tagged for later +translation, the translation themselves need to be written (or obtained). Here's +how that works. + +.. _locale-restrictions: + +.. admonition:: Locale restrictions + + Django does not support localizing your application into a locale for which + Django itself has not been translated. In this case, it will ignore your + translation files. If you were to try this and Django supported it, you + would inevitably see a mixture of translated strings (from your application) + and English strings (from Django itself). If you want to support a locale + for your application that is not already part of Django, you'll need to make + at least a minimal translation of the Django core. + + A good starting point is to copy the Django English ``.po`` file and to + translate at least some :term:`translation strings `. + +Message files +------------- + +The first step is to create a :term:`message file` for a new language. A message +file is a plain-text file, representing a single language, that contains all +available translation strings and how they should be represented in the given +language. Message files have a ``.po`` file extension. + +Django comes with a tool, ``django-admin.py makemessages``, that automates the +creation and upkeep of these files. + +.. admonition:: A note to Django veterans + + The old tool ``bin/make-messages.py`` has been moved to the command + ``django-admin.py makemessages`` to provide consistency throughout Django. + +.. admonition:: Gettext utilities + + The ``makemessages`` command (and ``compilemessages`` discussed later) use + commands from the GNU gettext toolset: ``xgetetxt``, ``msgfmt``, + ``msgmerge`` and ``msguniq``. + + .. versionchanged:: 1.2 + + The minimum version of the ``gettext`` utilities supported is 0.15. + +To create or update a message file, run this command:: + + django-admin.py makemessages -l de + +...where ``de`` is the language code for the message file you want to create. +The language code, in this case, is in :term:`locale format`. For +example, it's ``pt_BR`` for Brazilian Portuguese and ``de_AT`` for Austrian +German. + +The script should be run from one of two places: + + * The root directory of your Django project. + * The root directory of your Django app. + +Th script runs over your project source tree or your application source tree and +pulls out all strings marked for translation. It creates (or updates) a message +file in the directory ``locale/LANG/LC_MESSAGES``. In the ``de`` example, the +file will be ``locale/de/LC_MESSAGES/django.po``. + +By default ``django-admin.py makemessages`` examines every file that has the +``.html`` file extension. In case you want to override that default, use the +``--extension`` or ``-e`` option to specify the file extensions to examine:: + + django-admin.py makemessages -l de -e txt + +Separate multiple extensions with commas and/or use ``-e`` or ``--extension`` +multiple times:: + + django-admin.py makemessages -l de -e html,txt -e xml + +When :ref:`creating message files from JavaScript source code +` you need to use the special 'djangojs' +domain, **not** ``-e js``. + +.. admonition:: No gettext? + + If you don't have the ``gettext`` utilities installed, ``django-admin.py + makemessages`` will create empty files. If that's the case, either install + the ``gettext`` utilities or just copy the English message file + (``locale/en/LC_MESSAGES/django.po``) if available and use it as a starting + point; it's just an empty translation file. + +.. admonition:: Working on Windows? + + If you're using Windows and need to install the GNU gettext utilities so + ``django-admin makemessages`` works see :ref:`gettext_on_windows` for more + information. + +The format of ``.po`` files is straightforward. Each ``.po`` file contains a +small bit of metadata, such as the translation maintainer's contact +information, but the bulk of the file is a list of **messages** -- simple +mappings between translation strings and the actual translated text for the +particular language. + +For example, if your Django app contained a translation string for the text +``"Welcome to my site."``, like so:: + + _("Welcome to my site.") + +...then ``django-admin.py makemessages`` will have created a ``.po`` file +containing the following snippet -- a message:: + + #: path/to/python/module.py:23 + msgid "Welcome to my site." + msgstr "" + +A quick explanation: + + * ``msgid`` is the translation string, which appears in the source. Don't + change it. + * ``msgstr`` is where you put the language-specific translation. It starts + out empty, so it's your responsibility to change it. Make sure you keep + the quotes around your translation. + * As a convenience, each message includes, in the form of a comment line + prefixed with ``#`` and located above the ``msgid`` line, the filename and + line number from which the translation string was gleaned. + +Long messages are a special case. There, the first string directly after the +``msgstr`` (or ``msgid``) is an empty string. Then the content itself will be +written over the next few lines as one string per line. Those strings are +directly concatenated. Don't forget trailing spaces within the strings; +otherwise, they'll be tacked together without whitespace! + +.. admonition:: Mind your charset + + When creating a PO file with your favorite text editor, first edit + the charset line (search for ``"CHARSET"``) and set it to the charset + you'll be using to edit the content. Due to the way the ``gettext`` tools + work internally and because we want to allow non-ASCII source strings in + Django's core and your applications, you **must** use UTF-8 as the encoding + for your PO file. This means that everybody will be using the same + encoding, which is important when Django processes the PO files. + +To reexamine all source code and templates for new translation strings and +update all message files for **all** languages, run this:: + + django-admin.py makemessages -a + +Compiling message files +----------------------- + +After you create your message file -- and each time you make changes to it -- +you'll need to compile it into a more efficient form, for use by ``gettext``. +Do this with the ``django-admin.py compilemessages`` utility. + +This tool runs over all available ``.po`` files and creates ``.mo`` files, which +are binary files optimized for use by ``gettext``. In the same directory from +which you ran ``django-admin.py makemessages``, run ``django-admin.py +compilemessages`` like this:: + + django-admin.py compilemessages + +That's it. Your translations are ready for use. + +.. admonition:: A note to Django veterans + + The old tool ``bin/compile-messages.py`` has been moved to the command + ``django-admin.py compilemessages`` to provide consistency throughout + Django. + +.. admonition:: Working on Windows? + + If you're using Windows and need to install the GNU gettext utilities so + ``django-admin compilemessages`` works see :ref:`gettext_on_windows` for more + information. + +.. _creating-message-files-from-js-code: + +Creating message files from JavaScript source code +================================================== + +You create and update the message files the same way as the other Django message +files -- with the ``django-admin.py makemessages`` tool. The only difference is +you need to provide a ``-d djangojs`` parameter, like this:: + + django-admin.py makemessages -d djangojs -l de + +This would create or update the message file for JavaScript for German. +After updating message files, just run ``django-admin.py compilemessages`` +the same way as you do with normal Django message files. + +.. _gettext_on_windows: + +``gettext`` on Windows +====================== + +This is only needed for people who either want to extract message IDs or compile +message files (``.po``). Translation work itself just involves editing existing +files of this type, but if you want to create your own message files, or want to +test or compile a changed message file, you will need the ``gettext`` utilities: + + * Download the following zip files from the GNOME servers + http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/ or from one + of its mirrors_ + + * ``gettext-runtime-X.zip`` + * ``gettext-tools-X.zip`` + + ``X`` is the version number, we are requiring ``0.15`` or higher. + + * Extract the contents of the ``bin\`` directories in both files to the + same folder on your system (i.e. ``C:\Program Files\gettext-utils``) + + * Update the system PATH: + + * ``Control Panel > System > Advanced > Environment Variables``. + * In the ``System variables`` list, click ``Path``, click ``Edit``. + * Add ``;C:\Program Files\gettext-utils\bin`` at the end of the + ``Variable value`` field. + +.. _mirrors: http://ftp.gnome.org/pub/GNOME/MIRRORS + +You may also use ``gettext`` binaries you have obtained elsewhere, so long as +the ``xgettext --version`` command works properly. Some version 0.14.4 binaries +have been found to not support this command. Do not attempt to use Django +translation utilities with a ``gettext`` package if the command ``xgettext +--version`` entered at a Windows command prompt causes a popup window saying +"xgettext.exe has generated errors and will be closed by Windows". + +.. _format-localization: + +Format localization +=================== + +Django's formatting system is disabled by default. To enable it, it's necessay +to set :setting:`USE_L10N = True ` in your settings file. + +When using Django's formatting system, dates and numbers on templates will be +displayed using the format specified for the current locale. Two users +accessing the same content, but in different language, will see date and +number fields formatted in different ways, depending on the format for their +current locale. + +Django will also use localized formats when parsing data in forms. That means +Django uses different formats for different locales when guessing the format +used by the user when inputting data on forms. Note that Django uses different +formats for displaying data, and for parsing it. + +Creating custom format files +---------------------------- + +Django provides format definitions for many locales, but sometimes you might +want to create your own, because a format files doesn't exist for your locale, +or because you want to overwrite some of the values. + +To use custom formats, first thing to do, is to specify the path where you'll +place format files. To do that, just set your :setting:`FORMAT_MODULE_PATH` +setting to the the path (in the format ``'foo.bar.baz``) where format files +will exists. + +Files are not placed directly in this directory, but in a directory named as +the locale, and must be named ``formats.py``. + +To customize the English formats, a structure like this would be needed:: + + mysite/ + formats/ + __init__.py + en/ + __init__.py + formats.py + +where :file:`formats.py` contains custom format definitions. For example:: + + THOUSAND_SEPARATOR = ' ' + +to use a space as a thousand separator, instead of the default for English, +a comma. diff --git a/docs/topics/index.txt b/docs/topics/index.txt index 7fa283aa1a..33f425a03e 100644 --- a/docs/topics/index.txt +++ b/docs/topics/index.txt @@ -21,7 +21,7 @@ Introductions to all the key parts of Django you'll need to know: cache conditional-view-processing email - i18n + i18n/index pagination serialization settings