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