Handling Internationalization And Localization
==============================================

:Status: Work in progress

.. contents:: Table of Contents
    :depth: 2

Turbogears2 relies on Babel for i18n and l10n support. So
if this document is not enough you will want to check their respective
documentation:

 * Babel's UserGuide_

A quickstarted project comes fully i18n enabled so you should get
started quickly.

If you're lucky enough you'll even see "Your application is now
running" message in your language.

Language Auto-Select
--------------------

Turbogears2 contains the logic to setup request's language 
based on browser's preferences(*).

[*] - Every modern browser sends a special header along with every web
request which tells the server which language it would prefer to see
in a response.

The language in use during the request is available through the 
:func:`tg.i18n.get_lang` function. This will report the currently
selected languages both in case of an auto-detected language preference
or in case of languages forced by the developer.

Languages returned by :func:`tg.i18n.get_lang` are ordered by
user preference. By default all the requested languages are returned,
if you want to get only the ones your application supports call
``get_lang(all=False)``.

The current language in use is usually ``get_lang(all=False)[0]``.

Forcing a Language
------------------------------

Developer can force the currently used language for each request
using the :func:`tg.i18n.set_temporary_lang` function. This
will change the language only for the current request.

If you need to permanently change the language for the user
session duration, :func:`tg.i18n.set_lang` can be used.
If TurboGears session support is enabled it will store the
choosen language inside the session and recover it whenever
the user comes back on next request.

Making your code international
-------------------------------

Whenever you write a message that has to displayed you must let
TurboGears know that it has to be translated.

Even though TurboGears is able to automatically detect content
inside tags and mark them for translation all the strings inside
controllers must be explicitly marked as translated.

This can be achieved with the :func:`tg.i18n.ugettext` and 
:func:`tg.i18n.lazy_ugettext` calls which are usually imported 
with ``_`` and ``l_`` names:

.. code-block:: python

    from tg.i18n import ugettext as _

    class RootController(BaseController):
        @expose('myproj.templates.index')
        def index(self):
            return dict(msg=_('Hello World'))

In the previous example the 'Hello World' string will be detect by
TurboGears when collecting translatable text and will display in the
browser language if a translation for that language is available.

While :func:`ugettext() <tg.i18n.ugettext>` works perfectly to translate 
strings inside a request it does not allow translating strings outside a request. 
This is due to the fact that TurboGears won't know the browser language when
there isn't a running request. To translate global variables, parameters
default values or any other string which is created outside a controller
method the :func:`lazy_ugettext <tg.i18n.lazy_ugettext>` method must be used:

.. code-block:: python

    from tg.i18n import lazy_ugettext as l_

    class RootController(BaseController):
        @expose('myproj.templates.index')
        def index(self, msg=l_('Hello World')):
            return dict(msg=msg)

In this case the `msg` parameter is translated using :func:`lazy_ugettext() <tg.i18n.lazy_ugettext>`
as it is constructed at controller import time when no request is available.
This will create an object that will translate the given string only when
the string itself is displayed or evaluated.

Keep in mind that as the lazy string object built by :func:`lazy_ugettext() <tg.i18n.lazy_ugettext>` is
translated whenever the string is evaluated joining strings or editing it
will force the translation. So the resulting object must still be evaluated
only inside a request or it will always be translated to the default project
language only.

An i18n Quick Start
-------------------

After quickstarting your project, you will notice that the setup.py
file contains the following section::

    message_extractors = {'yourapp': [
             ('**.py', 'python', None),
             ('templates/**.mako', 'mako', None),
             ('templates/**.html', 'genshi', None),
             ('public/**', 'ignore', None)]},

This is an extraction method mapping that indicates to distutils which
files should be searched for strings to be translated.  TurboGears2
uses Babel to extract messages to a .pot file in your project's i18n
directory.  Don't forget to add it to your revision control system if
you use one.

1. Extract all the translatable strings from your project's files by
using the following command::

    python setup.py extract_messages

This command will generate a "pot" file in the i18n folder of your
application.  This pot file is the reference file that serves for all
the different translations.


2. Create a translation catalog for your language, let's take 'zh_tw'
for example::

    python setup.py init_catalog -l zh_tw

3. Edit your language in i18n/[country
code]/LC_MESSAGES/[project-name].po

If you're not an expert in i18n or if you would like to give the files
to someone else so that he helps you we recommend that you use the
really nice poedit program. This program works nicely on GNU/Linux and
Windows and provides a nice user-interface to edit po files.

.. image:: ../_static/poedit.png

4. Compile your lang::

    python setup.py compile_catalog  

5. Config development.ini::

    [app:main]
    use = egg: my-project
    full_stack = true
    lang = zh_tw

6. Start server::

    gearbox serve --reload

And see the local message show on the screen.


Commands
--------

To fresh start a translation, you could use the following command to
handle your locales:

init_catalog
~~~~~~~~~~~~

You can extract all messages from the project with the following
command::

  python setup.py init_catalog -l [country code]

The country code could be es(Spanish), fr(France), zh_tw(Taiwan),
jp(JAPAN), ru(Russian), or any other country code.

Compile Catalog
~~~~~~~~~~~~~~~

You can extract all messages from the project with the following command::

  python setup.py compile_catalog

Update Catalog
~~~~~~~~~~~~~~

You can update the catalog with the following command::

  python setup.py update_catalog


.. _UserGuide: http://babel.edgewall.org/wiki/Documentation/index.html
