Commits

Anonymous committed 39f8e32

Preliminary clean-up of docs/translation.txt in i18n branch

Comments (0)

Files changed (1)

docs/translation.txt

 ======================
 
 Django has support for internationalization of program strings and template
-content. Translations use the gettext library to produce strings in several
-languages. Here is an overview how translation works with django.
+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 howto is to give programmers the needed informations on how
-to use translations in their own projects, on how to add translations to
-django patches and on how to update and create translation files.
+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
+Using translations in Python
 ============================
 
-The translation machinery in django uses the standard gettext module that
-comes as part of your Python installation. It does wrap it in it's own
-functions and classes to accomplish all of it's goals, but essentially it's
-just standard gettext machinery.
+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.
 
-So to translate strings in your source you have to make use of one of the
-gettext helper functions. There are essentially two ways to make use of them:
+To translate strings in your code, use one of the ``gettext`` helper functions.
+There are essentially two ways to use them:
 
-- you can use the _() function that is available globally. This function will
-  translate any string value it get's as parameter.
-- you can use django.utils.translation and import gettext or gettext_noop
-  from there. gettext is identical to _()
+    * 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 ``_()``.
 
-There is one important thing to know about translations: the system can only
-translate strings it knows about. So to know about those strings you have to
-mark them for translation. That is done by either calling _(), gettext() or
-gettext_noop() on those string constants. You can translate variable values
-or computed values, but the system needs to know those strings beforehand.
+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 way is to build your strings by standard string interpolation and
-to use the gettext functions to do the actual translation of the string
-itself, like so::
+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 page
+    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 the positional
-string interpolation (the one that uses %s and %d) but use the named string
-interpolation (the one that uses %(name)s), instead. The reason is that other
-languages might require a reordering of text.
+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 in use::
+The other two helper functions are similar::
 
-   def hello_world(request, name, site):
-       from django.utils.translation import gettext
-       page = gettext('Hello %(name)s, welcome to %(site)s!') % {
-           'name': name,
-           'site': site,
-       }
-       return page
+    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 is, you explicitly import them. There are two important
-helpers: gettext and gettext_noop. gettext is just like _() - it will
-translate it's argument. gettext_noop is different in that it does only
-mark a string for inclusion into the message file but doesn't do translation.
-Instead the string is later translated from a variable. This comes up if you
-have constant strings that should be stored in the source language because
-they are exchanged over systems or users - like strings in a database - but
-should be translated at the last possible point in time, when the string
-is presented to the user.
+The difference here is that ``gettext`` is explicitly imported.
 
-One special case that isn't available in other gettext usages are lazily
-translated strings. This is needed for stuff that you set up in your django
-model files - those messages are stored internally and translated on access, but
-not translated on storage (as that would only take the default language into account).
-To translate a model helptext, do the following::
+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):
+    class MyThing(meta.Model):
+        name = meta.CharField(help_text=gettext_lazy('This is the help text'))
 
-        name = meta.CharField('Name', 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.
 
-This way only a lazy reference is stored for the string, not the actual translation.
-The translation itself will be done when the string is used in a string context, like
-template rendering in the admin.
-
-If you don't like the verbose name gettext_lazy, you can just alias it as _ - in the model
-file you will allways use lazy translations anyway. 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_names options in the META subclass, though::
+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(_('Name'), help_text=_('This is the help text'))
+    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_names 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')
 
-            verbose_name = _('Mything')
-            verbose_name_plural = _('Mythings')
-
-There is another standard problem with translations, that is pluralization of
-strings. This is done by the standard helper ngettext like so::
+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 page
+        return HttpResponse(page)
 
-Using Translations in Templates
+Using translations in templates
 ===============================
 
-Using translations in the templates is much like in python code. There is
-just a template tag that will allow you to use the same _() helper function
-as with your source::
+Using translations in Django templates works much like translations in Python
+code. The ``{% i18n %}`` template tag lets you use the same ``_()`` helper
+function as in your Python code::
 
-   <html>
-   <title>{% i18n _('This is the title.') %}</title>
-   <body>
-   <p>{% i18n _('Hello %(name)s, welcome at %(site)s!') %}</p>
-   <p>{% i18n ngettext('There is %(count)d file', 'There are %(count)d files', files|count) %}</p>
-   </body>
-   </html>
+    <html>
+    <title>{% i18n _('This is the title.') %}</title>
+    <body>
+    <p>{% i18n _('Hello %(name)s, welcome at %(site)s!') %}</p>
+    <p>{% i18n ngettext('There is %(count)d file', 'There are %(count)d files', files|count) %}</p>
+    </body>
+    </html>
 
-This short snippet shows you how to do translations. You can just translate
-strings, but there is one speciality: the strings can contain interpolation
-parts. Those parts are automatically resolved from the template context, just
-as they would be if you had used them in {{ ... }}. But this can only resolve
-variables, not more complex expressions.
+It's not only possible to translate hard-coded strings, but the strings can
+contain interpolated parts, e.g. ``%(name)s``. Those parts are automatically
+resolved from the template context, just as they would be if you had used them
+in ``{{ ... }}``. But this can only resolve variables, not more complex
+expressions.
 
-To translate a variable value, you can just do {% i18n _(variable) %}. This
-can even include filters like {% i18n _(variable|lower} %}.
+To translate a variable value, do this::
 
-There is additional support for i18n string constants for other situations
-as well. All template tags that do variable resolving (with or without filters)
-will accept string constants, too. Those string constants can now be i18n
-strings like this::
+    {% i18n _(variable) %}
 
-   <html>
-   <title>{{ _('This is the title') }}</title>
-   <body>
-   <p>{{ _('Hello World!') }}</p>
-   </body>
-   </html>
+Filters are allowed, too::
 
-This is much shorter, but won't allow you to use gettext_noop or ngettext.
+    {% i18n _(variable|lower) %}
 
-Sometimes you might want to give the user a selection of languages. This
-can be done by accessing the LANGUAGES variable of a DjangoContext. This
-is a list of tuples where the first element is the language code and the
-second element is the language name (in that language). The code might
-look like this::
+Any template tag that resolves variables accepts i18n-string constants, too.
+
+Also, note you can use ``_()`` around variable names, like so::
+
+    <html>
+    <title>{{ _('This is the title') }}</title>
+    <body>
+    <p>{{ _('Hello, world!') }}</p>
+    </body>
+    </html>
+
+This syntax is much shorter, but it doesn't allow you to use ``gettext_noop``
+or ``ngettext``.
+
+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.)
+
+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="http://server.doma.in/path/to/go/to/after/switch/"/>
+    <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>
 
-This would jump to a language switcher and then be redirected back to the
-page it came from. The switcher page will just store the language in either
-the users session or a special cookie for use in the language discovery.
+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.
 
-You can leave out the "next" field. In that case the /i18n/setlang/ view
-will redirect to the URL that is in the "Referer" header. Please keep in mind
-that people might suppress that header, so in those cases there won't be a
-URL to redirect to - in those cases the view function will redirect to "/"
-as a fallback.
+Activate the ``setlang`` redirect view by adding the following line to your
+URLconf::
 
-The /i18n/setlang/ url can be set up by including the following in your
-ROOT_URLCONF::
+    (r'^i18n/', include('django.conf.urls.i18n'),
 
-    urlpatterns = patterns('',
-        (r'^i18n/', include('django.conf.urls.i18n'),
-    )
+Note that this example makes the view available at ``/i18n/setlang/``.
 
-This adds the setlang handler to your urlspace and hooks up a standard view
-function to it.
+How language preference is discovered
+=====================================
 
-How the Language is Discovered
-==============================
+Django has a very flexible model of deciding which language should be used --
+installation-wide, for a particular user, or both.
 
-Django has a very flexible model of deciding what language is to be used.
-The first line in choice is the LANGUAGE_CODE setting in your config file.
-This is used as the default translation - the last try if none of the other
-translattors find a translation. Actually if youre requirement is just to
-run django with your native language, you only need to set LANGUAGE_CODE
-and that's it - if there is a language file for django for your language.
+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.
 
-But with web applications, users come from all over the world. So you don't
-want to have a single translation active, you want to decide what language to
-present to each and every user. This is where the LocaleMiddleware comes
-into the picture. You need to add it to your middleware setting. It should
-be one of the first middlewares installed, but it should come after the
-session middleware - that's because it makes uses of the session data. And
-it must be installed before the AdminUserRequired middleware, as that will
-do redirects based on not-logged-in state and so the LocaleMiddleware won't
-ever see the login page (and so not initialize the language correctly).
+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``.
 
-So your middleware settings might look like this::
+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.admin.AdminUserRequired',
        'django.middleware.common.CommonMiddleware',
     )
 
-This activates the LocalMiddlware in your server (in this case it was taken
-from the admin.py settings file).
+``LocaleMiddleware`` tries to determine the user's language preference by
+following this algorithm:
 
-The LocaleMiddleware allows a selection of the language based on data from
-the request - every user can have her own settings.
+    * 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.
 
-The first thing the LocaleMiddleware does, it looks at the session data for the
-user. If that carries a key django_language, it's contents will be used as the
-language code. If the session doesn't contain a language setting, the
-middleware will look at the cookies for a django_language cookie.  If that is
-found, it gives the language code.
+Notes:
 
-The format for the explicit django_language parameters is allways the
-language to use - for example it's pt-br for Brazilian. If a base language
-is available, but the sublanguage specified is not, the base language is used.
-For example if you specify de-at (Austrian German), but there is only a
-language de available, that language is used.
+    * 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``.
 
-If neither the session nor the cookie carry a language code, the middleware
-will look at the HTTP header Accept-Language. This header is sent by your
-browser and tells the server what languages you prefer. Languages are ordered
-by some choice value - the higher, the more you prefer the language.
+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::
 
-So the middleware will iterate over that header, ordered by the preference
-value. The language with the highest preference that is in the django base
-message file directory will be used as the language to present to the user.
+    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.")
 
-Since the middlware discovers the language based on the request, your app
-might need to know what language is selected (if only to show the flag of
-that language). The selected language is stored by the middleware in the
-request as the LANGUAGE_CODE attribute. So with static translation (when
-you don't use the middlware) the language is in settings.LANGUAGE_CODE, while
-with dynamic translations (when you do use the middleware) it's in
-request.LANGUAGE_CODE. And if your application builds on DjangoContext
-instances for template rendering, it will be automatically be available
-as LANGUAGE_CODE in your template (with automatic determination where to
-pull it from).
+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``.
 
-Creating Language Files
+.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
+
+Creating language files
 =======================
 
-So now you have tagged all 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 will show you how to do it.
+So, you've tagged all of your strings for later translation. But you need to
+write the translations themselves.
 
-The first step is to create a message file for a new language. This can
-be created with a tool delivered with django. To run it on the django
-source tree (best from a subversion checkout), just go to the django-Directory
-itself. Not the one you checked out, but the one you linked to your
-$PYTHONPATH or the one that's localted somewhere on that path.
+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.
 
-That directory includes a subdirectory conf, and that a directory locale. The
-tools to do translations are in the django/bin directory. The first tool
-to use is make-messages.py - this tool will run over the whole source tree
-and pull out all strings marked for translation.
+Creating message files
+----------------------
 
-To run it, just do the following::
+The first step is to create a message file for a new language. Django comes
+with a tool, ``make-messages.py``, that automates this.
 
-   bin/make-messages.py -l de
+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.
 
-This will create or update the german message file. This file is located
-at conf/locale/de/LC_MESSAGES/django.po - this file can be directly edited
-with your favorite editor. You need to first edit the charset line - search
-for CHARSET and set it to the charset you will use to edit the content. It
-might be that it is utf-8 - if you prefer another encoding, you can use some
-tools like recode or iconv to change the charset of the file and then change
-the charset definition in the file (it's in the Content-Type: line).
+Then run this command::
 
-The language code for storage is in locale format - so it is pt_BR for
-Brazilian or de_AT for Austrian German.
+    bin/make-messages.py -l de
 
-If you don't have the gettext utilities installed, make-messages.py will create
-empty files. If that is the case, either install the gettext utilities, or
-just copy the conf/locale/en/LC_MESSAGES/django.po and start with that - it's just
-an empty translation file.
+...where ``de`` is the language code for the message file you want to create.
 
-Every message in the message file is of the same format. One line is the msgid.
-This is the actual string in the source - you don't change it. The other line
-is msgstr - this is the translation. It starts out empty. You change it.
+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.
 
-There is one speciality for long messages: there the first string directly
-after the msgstr (or msgid) is an emtpy 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 will be tacked together without whitespace!
+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``.
 
-After you created your message file you need to transform it into some more
-efficient form to read by gettext. This is done with the second tool, that's
-compile-messages.py. This tool just runs over all available .po files and
-turns them into .mo files. Run it as follows::
+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. If you now configure your browser
-to request your language, it show up in the admin for example.
+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 give us 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 DjangoContext variable
-LANGUAGES in templates).
+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
+Using translations in your own projects
 =======================================
 
-Of course you want to make use of the translations in your own projects, too.
-This is very simple with django, as django looks in several locations for
-message files. The base path in your django distribution is only the last
-place to look for translations. Before that, django looks first into your
-application directory (actually in the application directory of the view
-function that is about to be called!) for message files. If there is one for
-the selected language, it will be installed. After that django looks into the
-project directory for message files. If there is one for the selected language,
-it will be installed after the app-specific one. And only then comes the
-base translation.
+Of course, your own projects should make use of translations. Django makes this
+simple, because it looks for message files in several locations.
 
-That way you can write applications that bring their own translations with
-them 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. They are:
+Django looks for translations by following this algorithm:
 
-- $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)
+    * 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``.
 
-Actually the appliaction doesn't need to be stored below the project path -
-django uses module introspection to find the right place where your application
-is stored. It only needs to be listed in your INSTALLED_APPS setting.
+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.
 
-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/
+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.
+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
-i18n middleware to be found. If you don't use the middleware, only the
-django message files and project message files will be processed.
+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.
 
-Additionally you should think about how to structure your translation
-files. If your applications should be delivered to other users and should
+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
+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. 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 stuff that is connected to your
-explicit project and not stuff that is distributed independently.
+message file that are already in application message files.
 
-Specialities of Django Translation
+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 see some specialities with the way django does
-translations. For one, the string domain is allways django. The string domain
-is used to differentiate between different programs that store their stuff
-in a common messagefile library (usually /usr/share/locale/). In our case there
-are django-specific locale libraries and so the domain itself isn't used. We
-could store app message files with different names and put them for example
-in the project library, but decided against this: with message files in the
-application tree, they can more easily be distributed.
+If you know ``gettext``, you might note these specialities in the way Django
+does translation:
 
-Another speciality is that we only use gettext and gettext_noop - that's
-because django uses allways DEFAULT_CHARSET strings internally. There isn't
-much use in using ugettext or something like that, as you allways will need to
-produce utf-8 anyway.
-
-And last we don't use xgettext alone and some makefiles but use python
-wrappers around xgettext and msgfmt. That's mostly for convenience.
-
+    * 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.