Home | History | Annotate | Download | only in tutorials
      1 .. _tutorials.i18n:
      2 
      3 Internationalization and localization with webapp2
      4 ==================================================
      5 In this tutorial we will learn how to get started with
      6 :mod:`webapp2_extras.i18n`. This module provides a complete collection of
      7 tools to localize and internationalize apps. Using it you can create
      8 applications adapted for different locales and timezones and with
      9 internationalized date, time, numbers, currencies and more.
     10 
     11 
     12 Prerequisites
     13 -------------
     14 If you don't have a package installer in your system yet (like ``pip`` or
     15 ``easy_install``), install one. See :ref:`tutorials.installing.packages`.
     16 
     17 
     18 Get Babel and Pytz
     19 ------------------
     20 The i18n module depends on two libraries: ``babel`` and ``pytz`` (or
     21 ``gaepytz``). So before we start you must add the ``babel`` and ``pytz``
     22 packages to your application directory (for App Engine) or install it in your
     23 virtual environment (for other servers).
     24 
     25 For App Engine, download ``babel`` and ``pytz`` and add those libraries to
     26 your app directory:
     27 
     28 - Babel: http://babel.edgewall.org/
     29 - Pytz: http://pypi.python.org/pypi/gaepytz
     30 
     31 For other servers, install those libraries in your system using ``pip``.
     32 App Engine users also need babel installed, as we use the command line
     33 utility provided py it to extract and update message catalogs.
     34 This assumes a `*nix` environment:
     35 
     36 .. code-block:: text
     37 
     38    $ sudo pip install babel
     39    $ sudo pip install gaepytz
     40 
     41 Or, if you don't have pip but have ``easy_install``:
     42 
     43 .. code-block:: text
     44 
     45    $ sudo easy_install babel
     46    $ sudo easy_install gaepytz
     47 
     48 
     49 Create a directory for translations
     50 -----------------------------------
     51 We need a directory inside our app to store a messages catalog extracted
     52 from templates and Python files. Create a directory named ``locale`` for
     53 this.
     54 
     55 If you want, later you can rename this directory the way you prefer and adapt
     56 the commands we describe below accordingly. If you do so, you must change the
     57 default i18n configuration to point to the right directory. The configuration
     58 is passed when you create an application, like this::
     59 
     60     config = {}
     61     config['webapp2_extras.i18n'] = {
     62         'translations_path': 'path/to/my/locale/directory',
     63     }
     64 
     65     app = webapp2.WSGIApplication(config=config)
     66 
     67 If you use the default ``locale`` directory name, no configuration is needed.
     68 
     69 
     70 Create a simple app to be translated
     71 ------------------------------------
     72 For the purposes of this tutorial we will create a very simple app with a
     73 single message to be translated. So create a new app and save this as
     74 ``main.py``::
     75 
     76     import webapp2
     77 
     78     from webapp2_extras import i18n
     79 
     80     class HelloWorldHandler(webapp2.RequestHandler):
     81         def get(self):
     82             # Set the requested locale.
     83             locale = self.request.GET.get('locale', 'en_US')
     84             i18n.get_i18n().set_locale(locale)
     85 
     86             message = i18n.gettext('Hello, world!')
     87             self.response.write(message)
     88 
     89     app = webapp2.WSGIApplication([
     90         ('/', HelloWorldHandler),
     91     ], debug=True)
     92 
     93     def main():
     94         app.run()
     95 
     96     if __name__ == '__main__':
     97         main()
     98 
     99 Any string that should be localized in your code and templates must be wrapped
    100 by the function :func:`webapp2_extras.i18n.gettext` (or the shortcut ``_()``).
    101 
    102 Translated strings defined in module globals or class definitions should use
    103 :func:`webapp2_extras.i18n.lazy_gettext` instead, because we want translations
    104 to be dynamic -- if we call ``gettext()`` when the module is imported we'll
    105 set the value to a static translation for a given locale, and this is not
    106 what we want. ``lazy_gettext()`` solves this making the translation to be
    107 evaluated lazily, only when the string is used.
    108 
    109 
    110 Extract and compile translations
    111 --------------------------------
    112 We use the `babel command line interface <http://babel.edgewall.org/wiki/Documentation/cmdline.html>`_
    113 to extract, initialize, compile and update translations. Refer to Babel's
    114 manual for a complete description of the command options.
    115 
    116 The extract command can extract not only messages from several template engines
    117 but also ``gettext()`` (from :py:mod:`gettext`) and its variants from Python
    118 files. Access your project directory using the command line and follow this
    119 quick how-to:
    120 
    121 **1.** Extract all translations. We pass the current app directory to be
    122 scanned. This will create a ``messages.pot`` file in the ``locale``
    123 directory with all translatable strings that were found:
    124 
    125 .. code-block:: text
    126 
    127    $ pybabel extract -o ./locale/messages.pot ./
    128 
    129 You can also provide a `extraction mapping file <http://babel.edgewall.org/wiki/Documentation/messages.html#extraction-method-mapping-and-configuration>`_
    130 that configures how messages are extracted. If the configuration file is
    131 saved as ``babel.cfg``, we point to it when extracting the messages:
    132 
    133 .. code-block:: text
    134 
    135    $ pybabel extract -F ./babel.cfg -o ./locale/messages.pot ./
    136 
    137 **2.** Initialize the directory for each locale that your app will support.
    138 This is done only once per locale. It will use the ``messages.pot`` file
    139 created on step 1. Here we initialize three translations, ``en_US``, ``es_ES``
    140 and ``pt_BR``:
    141 
    142 .. code-block:: text
    143 
    144    $ pybabel init -l en_US -d ./locale -i ./locale/messages.pot
    145    $ pybabel init -l es_ES -d ./locale -i ./locale/messages.pot
    146    $ pybabel init -l pt_BR -d ./locale -i ./locale/messages.pot
    147 
    148 **3.** Now the translation catalogs are created in the ``locale`` directory.
    149 Open each ``.po`` file and translate it. For the example above, we have only
    150 one message to translate: our ``Hello, world!``.
    151 
    152 Open ``/locale/es_ES/LC_MESSAGES/messages.po`` and translate it to
    153 ``Hola, mundo!``.
    154 
    155 Open ``/locale/pt_BR/LC_MESSAGES/messages.po`` and translate it to
    156 ``Ol, mundo!``.
    157 
    158 **4.** After all locales are translated, compile them with this command:
    159 
    160 .. code-block:: text
    161 
    162    $ pybabel compile -f -d ./locale
    163 
    164 That's it.
    165 
    166 
    167 Update translations
    168 -------------------
    169 When translations change, first repeat step 1 above. It will create a new
    170 ``.pot`` file with updated messages. Then update each locales:
    171 
    172 .. code-block:: text
    173 
    174    $ pybabel update -l en_US -d ./locale/ -i ./locale/messages.pot
    175    $ pybabel update -l es_ES -d ./locale/ -i ./locale/messages.pot
    176    $ pybabel update -l pt_BR -d ./locale/ -i ./locale/messages.pot
    177 
    178 After you translate the new strings to each language, repeat step 4, compiling
    179 the translations again.
    180 
    181 
    182 Test your app
    183 -------------
    184 Start the development server pointing to the application you created for this
    185 tutorial and access the default language:
    186 
    187     http://localhost:8080/
    188 
    189 Then try the Spanish version:
    190 
    191     http://localhost:8080/?locale=es_ES
    192 
    193 And finally, try the Portuguese version:
    194 
    195     http://localhost:8080/?locale=pt_BR
    196 
    197 Voil! Our tiny app is now available in three languages.
    198 
    199 
    200 What else
    201 ---------
    202 The :mod:`webapp2_extras.i18n` module provides several other functionalities
    203 besides localization. You can use it to internationalize dates, currencies
    204 and numbers, and there are helpers to set the locale or timezone automatically
    205 for each request. Explore the API documentation to learn more.
    206