summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorJacob Kaplan-Moss <jacob@jacobian.org>2005-11-04 04:59:46 +0000
committerJacob Kaplan-Moss <jacob@jacobian.org>2005-11-04 04:59:46 +0000
commit5cf8f684237ab5addaf3549b2347c3adf107c0a7 (patch)
tree73ba55f337e0d5c6e4ed39474ab6132879cc3947 /docs
parentcb45fd0ae20597306cd1f877efc99d9bd7cbee98 (diff)
Merged i18n branch into the trunk! Fixes #65, and perhaps some others. NB: this means that the i18n branch is now obsolete and will be made read-only.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@1068 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'docs')
-rw-r--r--docs/settings.txt7
-rw-r--r--docs/translation.txt438
2 files changed, 443 insertions, 2 deletions
diff --git a/docs/settings.txt b/docs/settings.txt
index acbbca96d4..1d65d44c88 100644
--- a/docs/settings.txt
+++ b/docs/settings.txt
@@ -355,9 +355,12 @@ See http://www.thaiopensource.com/relaxng/jing.html .
LANGUAGE_CODE
-------------
-Default: ``'en-us'``
+Default: ``'en'``
-A string representing the language code for this installation.
+A string representing the language code for this installation. This should
+be in locale format, that's 'en_US' for us-english. If you want to send
+out the language in your HTML code, use the LANGUAGE_CODE attribute of the
+request, instead, as the chosen language will depend on the browsers settings.
MANAGERS
--------
diff --git a/docs/translation.txt b/docs/translation.txt
new file mode 100644
index 0000000000..637719b879
--- /dev/null
+++ b/docs/translation.txt
@@ -0,0 +1,438 @@
+======================
+How to do translations
+======================
+
+Django has support for internationalization of program strings and template
+content. Translations use the ``gettext`` library to produce strings in several
+languages. Here's an overview of how translation works with Django.
+
+The goal of this document is to explain how to use translations in projects,
+how to add translations to Django patches and how to update and create
+translation files.
+
+Using translations in Python
+============================
+
+The translation machinery in Django uses the standard ``gettext`` module that
+comes with Python. Django uses in its own functions and classes, but it uses
+standard ``gettext`` machinery under the hood.
+
+To translate strings in your code, use one of the ``gettext`` helper functions.
+There are essentially two ways to use them:
+
+ * Use the ``_()`` function, which is available globally. This function
+ translates any string value.
+ * Use ``django.utils.translation`` and import ``gettext`` or
+ ``gettext_noop`` from there. ``gettext`` is identical to ``_()``.
+
+Note one important thing about translations: The system can only translate
+strings it knows about. That means you have to mark strings for translation.
+This is done either by calling ``_()``, ``gettext()`` or ``gettext_noop()`` on
+string constants. You can translate variable values or computed values, but the
+system needs to know those strings beforehand.
+
+The usual method is to build your strings using string interpolation and using
+the ``gettext`` functions to do the actual translation. Example::
+
+ def hello_world(request, name, site):
+ page = _('Hello %(name)s, welcome to %(site)s!') % {
+ 'name': name,
+ 'site': site,
+ }
+ return HttpResponse(page)
+
+This short snippet shows one important thing: You shouldn't use positional
+string interpolation (e.g., ``%s`` or ``%d``). Use the named string
+interpolation (e.g., ``%(name)s``), instead. Do this because other languages
+might require reordering of text.
+
+The other two helper functions are similar::
+
+ from django.utils.translation import gettext
+ def hello_world(request, name, site):
+ page = gettext('Hello %(name)s, welcome to %(site)s!') % {
+ 'name': name,
+ 'site': site,
+ }
+ return HttpResponse(page)
+
+The difference here is that ``gettext`` is explicitly imported.
+
+Two important helper functions are available: ``gettext`` and ``gettext_noop``.
+
+ * ``gettext`` is just like ``_()`` -- it translates its argument.
+ * ``gettext_noop`` is different. It marks a string for inclusion into the
+ message file but doesn't do translation. Instead, 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.
+
+One function, ``django.utils.translation.gettext_lazy()``, isn't available in
+the standard ``gettext`` module. Use it for lazily translated strings, such as
+messages in Django models that are stored internally and translated on access
+-- but not translated on storage, as that would only take the default language
+into account.
+
+For example, to translate a model's ``help_text``, do the following::
+
+ from django.utils.translation import gettext_lazy
+
+ class MyThing(meta.Model):
+ name = meta.CharField(help_text=gettext_lazy('This is the help text'))
+
+In this example, ``gettext_lazy()`` stores a lazy reference to the string --
+not the actual translation. The translation itself will be done when the string
+is used in a string context, such as template rendering on the Django admin site.
+
+If you don't like the verbose name ``gettext_lazy``, you can just alias it as
+``_``, like so::
+
+ from django.utils.translation import gettext_lazy as _
+
+ class MyThing(meta.Model):
+ name = meta.CharField(help_text=_('This is the help text'))
+
+Always use lazy translations in Django models. And it's a good idea to add
+translations for the field names and table names, too. This means writing
+explicit ``verbose_name`` and ``verbose_name_plural`` options in the ``META``
+class, though::
+
+ from django.utils.translation import gettext_lazy as _
+
+ class MyThing(meta.Model):
+ name = meta.CharField(_('name'), help_text=_('This is the help text'))
+ class META:
+ verbose_name = _('my thing')
+ verbose_name_plural = _('mythings')
+
+A standard problem with translations is pluralization of strings. Use
+``ngettext`` to solve this problem. Example::
+
+ def hello_world(request, count):
+ from django.utils.translation import ngettext
+ page = ngettext('there is %(count)d object', 'there are %(count)d objects', count) % {
+ 'count': count,
+ }
+ return HttpResponse(page)
+
+Using translations in templates
+===============================
+
+Using translations in Django templates uses two template tags and a slightly
+different syntax than standard gettext. The ``{% trans %}`` template tag
+translates a constant string or a variable content::
+
+ <title>{% trans 'This is the title.' %}</title>
+
+If you only want to mark some value for translation, but translate it
+later from a variable, use the ``noop`` option::
+
+ <input name="field" value="{% trans "value" noop %}"/>
+
+It is not possible to use variables in this constant string. If you
+have variables you need to put in your translations, you have to use the
+``{% blocktrans %}`` tag::
+
+ {% blocktrans %}This will have {{ value }} inside{% endblocktrans %}
+
+If your expressions are more complex (like you need to have filters applied),
+you need to bind them to local variables for the translation block::
+
+ {% blocktrans with value|filter as variable %}
+ This will have {{ value }} inside
+ {% endblocktrans %}
+
+The last variant is the pluralization form: you need to specify both the singular
+and plural sentence with intersparsed variables like this::
+
+ {% blocktrans count list|counted as counter %}
+ There is only one {{ name }} object.
+ {% plural %}
+ There are {{ counter }} {{ name }} objects.
+ {% endblocktrans %}
+
+Internally all block translations and inline translations are translated into
+the actual gettext/ngettext call.
+
+Each ``DjangoContext`` has access to two translation-specific variables:
+
+ * ``LANGUAGES`` is a list of tuples in which the first element is the
+ language code and the second is the language name (in that language).
+ * ``LANGUAGE_CODE`` is the current user's preferred language, as a string.
+ Example: ``en-us``. (See "How language preference is discovered", below.)
+
+If you don't use the ``DjangoContext`` extension, you can get those values with
+two tags::
+
+ {% get_current_language as LANGUAGE_CODE %}
+ {% get_available_languages as LANGUAGES %}
+
+All tags live in the ``i18n`` tag library, so you need to specify
+``{% load i18n %}`` in the head of your template to make use of them.
+
+There are some places where you will encounter constant strings in your template code.
+One is filter arguments, the other are normal string constants for tags. If you need to
+translate those, you can use the ``_("....")`` syntax::
+
+ {% some_special_tag _("Page not found") value|yesno:_("yes,no") %}
+
+In this case both the filter and the tag will see the already translated string, so they
+don't need to be aware of translations. And both strings will be pulled out of the templates
+for translation and stored in the .po files.
+
+The ``setlang`` redirect view
+-----------------------------
+
+Django comes with a view, ``django.views.i18n.set_language`` that sets a user's
+language preference and redirects back to the previous page. For example, put
+this HTML code in your template::
+
+ <form action="/i18n/setlang/" method="POST">
+ <input name="next" type="hidden" value="/next/page/" />
+ <select name="language">
+ {% for lang in LANGUAGES %}
+ <option value="{{ lang.0 }}">{{ lang.1 }}</option>
+ {% endfor %}
+ </select>
+ <input type="submit" value="Go" />
+ </form>
+
+When a user submits the form, his chosen language will be saved in a cookie,
+and he'll be redirected either to the URL specified in the ``next`` field, or,
+if ``next`` is empty, to the URL in the ``Referer`` header. If the ``Referer``
+is blank -- say, if a user's browser suppresses that header -- then the user
+will be redirected to ``/`` (the site root) as a fallback.
+
+Activate the ``setlang`` redirect 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/``.
+
+How language preference is discovered
+=====================================
+
+Django has a very flexible model of deciding which language should be used --
+installation-wide, for a particular user, or both.
+
+To set an installation-wide language preference, set ``LANGUAGE_CODE`` in your
+settings file. Django uses this language as the default translation -- the
+final attempt if no other translator finds a translation.
+
+If all you want to do is run Django with your native language, and a language
+file is available for your language, all you need to do is set
+``LANGUAGE_CODE``.
+
+If you want to let each individual user specify which language he or she
+prefers, use ``LocaleMiddleware``. ``LocaleMiddleware`` enables language
+selection based on data from the request. It lets each user have his or her own
+setting.
+
+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.
+
+For example, your ``MIDDLEWARE_CLASSES`` might look like this::
+
+ MIDDLEWARE_CLASSES = (
+ 'django.middleware.sessions.SessionMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ )
+
+``LocaleMiddleware`` tries to determine the user's language preference by
+following this algorithm:
+
+ * First, it looks for a ``django_language`` key in the the current user's
+ session.
+ * Failing that, it looks for a cookie called ``django_language``.
+ * Failing that, it looks at the ``Accept-Language`` HTTP header. This
+ header is sent by your browser and tells the server which language(s) you
+ prefer, in order by priority. Django tries each language in the header
+ until it finds one with available translations.
+ * Failing that, it uses the global ``LANGUAGE_CODE`` setting.
+
+Notes:
+
+ * In each of these places, the language preference is expected to be in the
+ standard language format, as a string. For example, Brazilian is
+ ``pt-br``.
+ * If a base language is available but the sublanguage specified is not,
+ Django uses the base language. For example, if a user specifies ``de-at``
+ (Austrian German) but Django only has ``de`` available, Django uses
+ ``de``.
+
+Once ``LocaleMiddleware`` determines the user's preference, it makes this
+preference available as ``request.LANGUAGE_CODE`` for each `request object`_.
+Feel free to read this value in your view code. Here's a simple example::
+
+ def hello_world(request, count):
+ if request.LANGUAGE_CODE == 'de-at':
+ return HttpResponse("You prefer to read Austrian German.")
+ else:
+ return HttpResponse("You prefer to read another language.")
+
+Note that, with static (middleware-less) translation, the language is in
+``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
+in ``request.LANGUAGE_CODE``.
+
+.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
+
+Creating language files
+=======================
+
+So, you've tagged all of your strings for later translation. But you need to
+write the translations themselves.
+
+They need to be in a format grokable by ``gettext``. You need to update them.
+You may need to create new ones for new languages. This section shows you how
+to do it.
+
+Creating message files
+----------------------
+
+The first step is to create a message file for a new language. Django comes
+with a tool, ``make-messages.py``, that automates this.
+
+To run it on the Django source tree, navigate to the ``django`` directory
+itself -- not a Subversion check out, but the one linked to via ``$PYTHONPATH``
+or located somewhere on that path.
+
+Then run this command::
+
+ bin/make-messages.py -l de
+
+...where ``de`` is the language code for the message file you want to create.
+
+This script runs over the entire Django source tree and pulls out all strings
+marked for translation, creating or updating the language's message file.
+
+When it's done, it will have created (or updated) a message file under the
+directory ``conf/locale``. In this example, the file will be
+``conf/locale/de/LC_MESSAGES/django.po``.
+
+If you don't have the ``gettext`` utilities installed, ``make-messages.py``
+will create empty files. If that's the case, either install the ``gettext``
+utilities or just copy the English message file
+(``conf/locale/en/LC_MESSAGES/django.po``) and use it as a starting point; it's
+just an empty translation file.
+
+Once you've created the ``.po`` file, edit the 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. Then, proceed to write your
+translations.
+
+The language code for storage is in locale format -- so it's ``pt_BR`` for
+Brazilian and ``de_AT`` for Austrian German.
+
+Every message in the message file is in the same format:
+
+ * One line is the msgid. This is the actual string in the source. Don't
+ change it.
+ * The other line is msgstr. This is the translation. It starts out empty.
+ You change it.
+
+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!
+
+Compiling message files
+-----------------------
+
+After you create your message file, you'll need to transform it into a more
+efficient form to be read by ``gettext``. Do this with the
+``compile-messages.py`` utility. This tool runs over all available ``.po``
+files and creates ``.mo`` files. Run it like this::
+
+ bin/compile-messages.py
+
+That's it. You made your first translation. Now, if you configure your browser
+to request your language, Django apps will use your language preference.
+
+Another thing: Please submit the name of your newly-created language in that
+native language, so we can add it to the global list of available languages
+that is mirrored in ``settings.LANGUAGES`` (and the ``LANGUAGES`` template
+variable).
+
+Using translations in your own projects
+=======================================
+
+Of course, your own projects should make use of translations. Django makes this
+simple, because it looks for message files in several locations.
+
+Django looks for translations by following this algorithm:
+
+ * First, it looks for a ``locale`` directory in the application directory
+ of the view that's being called. If it finds a translation for the
+ selected language, the translation will be installed.
+ * Next, it looks for a ``locale`` directory in the project directory. If it
+ finds a translation, the translation will be installed.
+ * Finally, it checks the base translation in ``django/conf/locale``.
+
+This way, you can write applications that include their own translations, and
+you can override base translations in your project path if you want to do that.
+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.
+
+All message file repositories are structured the same way. They are:
+
+ * ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)``
+ * ``$PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)``
+ * all paths listed in ``LOCALE_PATHS`` in your settings file are
+ searched in that order for ``<language>/LC_MESSAGES/django.(po|mo)``
+ * ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)``
+
+To create message files, you use the same ``make-messages.py`` tool as with the
+Django message files. You only need to be in the right place -- in the directory
+where either the ``conf/locale`` (in case of the source tree) or the ``locale/``
+(in case of app messages or project messages) directory are located. And you
+use the same ``compile-messages.py`` to produce the binary ``django.mo`` files that
+are used by ``gettext``.
+
+Application message files are a bit complicated to discover -- they need the
+``LocaleMiddleware``. If you don't use the middleware, only the Django message
+files and project message files will be processed.
+
+Finally, you should give some thought to the structure of your translation
+files. If your applications need to be delivered to other users and will
+be used in other projects, you might want to use app-specific translations.
+But using app-specific translations and project translations could produce
+weird problems with ``make-messages``: ``make-messages`` will traverse all directories
+below the current path and so might put message IDs into the project
+message file that are already in application message files.
+
+The easiest way out is to store applications that are not part of the project
+(and so carry their own translations) outside the project tree. That way,
+``make-messages`` on the project level will only translate strings that are
+connected to your explicit project and not strings that are distributed
+independently.
+
+Specialities of Django translation
+==================================
+
+If you know ``gettext``, you might note these specialities in the way Django
+does translation:
+
+ * The string domain is always ``django``. The string domain is used to
+ differentiate between different programs that store their data in a
+ common messagefile library (usually ``/usr/share/locale/``). In Django's
+ case, there are Django-specific locale libraries, so the domain itself
+ isn't used. We could store app message files with different names and put
+ them, say, in the project library, but we decided against this. With
+ message files in the application tree, apps can be distributed more
+ easily.
+ * Django only uses ``gettext`` and ``gettext_noop``. That's because Django
+ always uses ``DEFAULT_CHARSET`` strings internally. There isn't much use
+ in using ``ugettext``, because you'll always need to produce utf-8
+ anyway.
+ * Django doesn't use ``xgettext`` alone. It uses Python wrappers around
+ ``xgettext`` and ``msgfmt``. That's mostly for convenience.