1.. _tutorials.i18n: 2 3Internationalization and localization with webapp2 4================================================== 5In this tutorial we will learn how to get started with 6:mod:`webapp2_extras.i18n`. This module provides a complete collection of 7tools to localize and internationalize apps. Using it you can create 8applications adapted for different locales and timezones and with 9internationalized date, time, numbers, currencies and more. 10 11 12Prerequisites 13------------- 14If 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 18Get Babel and Pytz 19------------------ 20The i18n module depends on two libraries: ``babel`` and ``pytz`` (or 21``gaepytz``). So before we start you must add the ``babel`` and ``pytz`` 22packages to your application directory (for App Engine) or install it in your 23virtual environment (for other servers). 24 25For App Engine, download ``babel`` and ``pytz`` and add those libraries to 26your app directory: 27 28- Babel: http://babel.edgewall.org/ 29- Pytz: http://pypi.python.org/pypi/gaepytz 30 31For other servers, install those libraries in your system using ``pip``. 32App Engine users also need babel installed, as we use the command line 33utility provided py it to extract and update message catalogs. 34This assumes a `*nix` environment: 35 36.. code-block:: text 37 38 $ sudo pip install babel 39 $ sudo pip install gaepytz 40 41Or, 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 49Create a directory for translations 50----------------------------------- 51We need a directory inside our app to store a messages catalog extracted 52from templates and Python files. Create a directory named ``locale`` for 53this. 54 55If you want, later you can rename this directory the way you prefer and adapt 56the commands we describe below accordingly. If you do so, you must change the 57default i18n configuration to point to the right directory. The configuration 58is 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 67If you use the default ``locale`` directory name, no configuration is needed. 68 69 70Create a simple app to be translated 71------------------------------------ 72For the purposes of this tutorial we will create a very simple app with a 73single 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 99Any string that should be localized in your code and templates must be wrapped 100by the function :func:`webapp2_extras.i18n.gettext` (or the shortcut ``_()``). 101 102Translated strings defined in module globals or class definitions should use 103:func:`webapp2_extras.i18n.lazy_gettext` instead, because we want translations 104to be dynamic -- if we call ``gettext()`` when the module is imported we'll 105set the value to a static translation for a given locale, and this is not 106what we want. ``lazy_gettext()`` solves this making the translation to be 107evaluated lazily, only when the string is used. 108 109 110Extract and compile translations 111-------------------------------- 112We use the `babel command line interface <http://babel.edgewall.org/wiki/Documentation/cmdline.html>`_ 113to extract, initialize, compile and update translations. Refer to Babel's 114manual for a complete description of the command options. 115 116The extract command can extract not only messages from several template engines 117but also ``gettext()`` (from :py:mod:`gettext`) and its variants from Python 118files. Access your project directory using the command line and follow this 119quick how-to: 120 121**1.** Extract all translations. We pass the current app directory to be 122scanned. This will create a ``messages.pot`` file in the ``locale`` 123directory with all translatable strings that were found: 124 125.. code-block:: text 126 127 $ pybabel extract -o ./locale/messages.pot ./ 128 129You can also provide a `extraction mapping file <http://babel.edgewall.org/wiki/Documentation/messages.html#extraction-method-mapping-and-configuration>`_ 130that configures how messages are extracted. If the configuration file is 131saved 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. 138This is done only once per locale. It will use the ``messages.pot`` file 139created on step 1. Here we initialize three translations, ``en_US``, ``es_ES`` 140and ``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. 149Open each ``.po`` file and translate it. For the example above, we have only 150one message to translate: our ``Hello, world!``. 151 152Open ``/locale/es_ES/LC_MESSAGES/messages.po`` and translate it to 153``¡Hola, mundo!``. 154 155Open ``/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 164That's it. 165 166 167Update translations 168------------------- 169When 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 178After you translate the new strings to each language, repeat step 4, compiling 179the translations again. 180 181 182Test your app 183------------- 184Start the development server pointing to the application you created for this 185tutorial and access the default language: 186 187 http://localhost:8080/ 188 189Then try the Spanish version: 190 191 http://localhost:8080/?locale=es_ES 192 193And finally, try the Portuguese version: 194 195 http://localhost:8080/?locale=pt_BR 196 197Voilà! Our tiny app is now available in three languages. 198 199 200What else 201--------- 202The :mod:`webapp2_extras.i18n` module provides several other functionalities 203besides localization. You can use it to internationalize dates, currencies 204and numbers, and there are helpers to set the locale or timezone automatically 205for each request. Explore the API documentation to learn more. 206