.. _tutorials.i18n:

Internationalization and localization with webapp2
==================================================
In this tutorial we will learn how to get started with
:mod:`webapp2_extras.i18n`. This module provides a complete collection of
tools to localize and internationalize apps. Using it you can create
applications adapted for different locales and timezones and with
internationalized date, time, numbers, currencies and more.


Prerequisites
-------------
If you don't have a package installer in your system yet (like ``pip`` or
``easy_install``), install one. See :ref:`tutorials.installing.packages`.


Get Babel and Pytz
------------------
The i18n module depends on two libraries: ``babel`` and ``pytz`` (or
``gaepytz``). So before we start you must add the ``babel`` and ``pytz``
packages to your application directory (for App Engine) or install it in your
virtual environment (for other servers).

For App Engine, download ``babel`` and ``pytz`` and add those libraries to
your app directory:

- Babel: http://babel.edgewall.org/
- Pytz: http://pypi.python.org/pypi/gaepytz

For other servers, install those libraries in your system using ``pip``.
App Engine users also need babel installed, as we use the command line
utility provided py it to extract and update message catalogs.
This assumes a `*nix` environment:

.. code-block:: text

   $ sudo pip install babel
   $ sudo pip install gaepytz

Or, if you don't have pip but have ``easy_install``:

.. code-block:: text

   $ sudo easy_install babel
   $ sudo easy_install gaepytz


Create a directory for translations
-----------------------------------
We need a directory inside our app to store a messages catalog extracted
from templates and Python files. Create a directory named ``locale`` for
this.

If you want, later you can rename this directory the way you prefer and adapt
the commands we describe below accordingly. If you do so, you must change the
default i18n configuration to point to the right directory. The configuration
is passed when you create an application, like this::

    config = {}
    config['webapp2_extras.i18n'] = {
        'translations_path': 'path/to/my/locale/directory',
    }

    app = webapp2.WSGIApplication(config=config)

If you use the default ``locale`` directory name, no configuration is needed.


Create a simple app to be translated
------------------------------------
For the purposes of this tutorial we will create a very simple app with a
single message to be translated. So create a new app and save this as
``main.py``::

    import webapp2

    from webapp2_extras import i18n

    class HelloWorldHandler(webapp2.RequestHandler):
        def get(self):
            # Set the requested locale.
            locale = self.request.GET.get('locale', 'en_US')
            i18n.get_i18n().set_locale(locale)

            message = i18n.gettext('Hello, world!')
            self.response.write(message)

    app = webapp2.WSGIApplication([
        ('/', HelloWorldHandler),
    ], debug=True)

    def main():
        app.run()

    if __name__ == '__main__':
        main()

Any string that should be localized in your code and templates must be wrapped
by the function :func:`webapp2_extras.i18n.gettext` (or the shortcut ``_()``).

Translated strings defined in module globals or class definitions should use
:func:`webapp2_extras.i18n.lazy_gettext` instead, because we want translations
to be dynamic -- if we call ``gettext()`` when the module is imported we'll
set the value to a static translation for a given locale, and this is not
what we want. ``lazy_gettext()`` solves this making the translation to be
evaluated lazily, only when the string is used.


Extract and compile translations
--------------------------------
We use the `babel command line interface <http://babel.edgewall.org/wiki/Documentation/cmdline.html>`_
to extract, initialize, compile and update translations. Refer to Babel's
manual for a complete description of the command options.

The extract command can extract not only messages from several template engines
but also ``gettext()`` (from :py:mod:`gettext`) and its variants from Python
files. Access your project directory using the command line and follow this
quick how-to:

**1.** Extract all translations. We pass the current app directory to be
scanned. This will create a ``messages.pot`` file in the ``locale``
directory with all translatable strings that were found:

.. code-block:: text

   $ pybabel extract -o ./locale/messages.pot ./

You can also provide a `extraction mapping file <http://babel.edgewall.org/wiki/Documentation/messages.html#extraction-method-mapping-and-configuration>`_
that configures how messages are extracted. If the configuration file is
saved as ``babel.cfg``, we point to it when extracting the messages:

.. code-block:: text

   $ pybabel extract -F ./babel.cfg -o ./locale/messages.pot ./

**2.** Initialize the directory for each locale that your app will support.
This is done only once per locale. It will use the ``messages.pot`` file
created on step 1. Here we initialize three translations, ``en_US``, ``es_ES``
and ``pt_BR``:

.. code-block:: text

   $ pybabel init -l en_US -d ./locale -i ./locale/messages.pot
   $ pybabel init -l es_ES -d ./locale -i ./locale/messages.pot
   $ pybabel init -l pt_BR -d ./locale -i ./locale/messages.pot

**3.** Now the translation catalogs are created in the ``locale`` directory.
Open each ``.po`` file and translate it. For the example above, we have only
one message to translate: our ``Hello, world!``.

Open ``/locale/es_ES/LC_MESSAGES/messages.po`` and translate it to
``¡Hola, mundo!``.

Open ``/locale/pt_BR/LC_MESSAGES/messages.po`` and translate it to
``Olá, mundo!``.

**4.** After all locales are translated, compile them with this command:

.. code-block:: text

   $ pybabel compile -f -d ./locale

That's it.


Update translations
-------------------
When translations change, first repeat step 1 above. It will create a new
``.pot`` file with updated messages. Then update each locales:

.. code-block:: text

   $ pybabel update -l en_US -d ./locale/ -i ./locale/messages.pot
   $ pybabel update -l es_ES -d ./locale/ -i ./locale/messages.pot
   $ pybabel update -l pt_BR -d ./locale/ -i ./locale/messages.pot

After you translate the new strings to each language, repeat step 4, compiling
the translations again.


Test your app
-------------
Start the development server pointing to the application you created for this
tutorial and access the default language:

    http://localhost:8080/

Then try the Spanish version:

    http://localhost:8080/?locale=es_ES

And finally, try the Portuguese version:

    http://localhost:8080/?locale=pt_BR

Voilà! Our tiny app is now available in three languages.


What else
---------
The :mod:`webapp2_extras.i18n` module provides several other functionalities
besides localization. You can use it to internationalize dates, currencies
and numbers, and there are helpers to set the locale or timezone automatically
for each request. Explore the API documentation to learn more.
