| [634] | 1 | .. -*- mode: rst; encoding: utf-8 -*- |
|---|
| 2 | |
|---|
| 3 | ===================================== |
|---|
| 4 | Internationalization and Localization |
|---|
| 5 | ===================================== |
|---|
| 6 | |
|---|
| [1109] | 7 | Genshi provides comprehensive supporting infrastructure for internationalizing |
|---|
| [1108] | 8 | and localizing templates. That includes functionality for extracting |
|---|
| [1113] | 9 | localizable strings from templates, as well as a template filter and special |
|---|
| 10 | directives that can apply translations to templates as they get rendered. |
|---|
| [634] | 11 | |
|---|
| 12 | This support is based on `gettext`_ message catalogs and the `gettext Python |
|---|
| [1108] | 13 | module`_. The extraction process can be used from the API level, or through |
|---|
| 14 | the front-ends implemented by the `Babel`_ project, for which Genshi provides |
|---|
| 15 | a plugin. |
|---|
| [634] | 16 | |
|---|
| 17 | .. _`gettext`: http://www.gnu.org/software/gettext/ |
|---|
| 18 | .. _`gettext python module`: http://docs.python.org/lib/module-gettext.html |
|---|
| 19 | .. _`babel`: http://babel.edgewall.org/ |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | .. contents:: Contents |
|---|
| 23 | :depth: 2 |
|---|
| 24 | .. sectnum:: |
|---|
| 25 | |
|---|
| 26 | |
|---|
| 27 | Basics |
|---|
| 28 | ====== |
|---|
| 29 | |
|---|
| 30 | The simplest way to internationalize and translate templates would be to wrap |
|---|
| [1108] | 31 | all localizable strings in a ``gettext()`` function call (which is often |
|---|
| 32 | aliased to ``_()`` for brevity). In that case, no extra template filter is |
|---|
| 33 | required. |
|---|
| [634] | 34 | |
|---|
| 35 | .. code-block:: genshi |
|---|
| 36 | |
|---|
| 37 | <p>${_("Hello, world!")}</p> |
|---|
| 38 | |
|---|
| [1108] | 39 | However, this approach results in significant “character noise” in templates, |
|---|
| [634] | 40 | making them harder to read and preview. |
|---|
| 41 | |
|---|
| 42 | The ``genshi.filters.Translator`` filter allows you to get rid of the |
|---|
| [1113] | 43 | explicit `gettext`_ function calls, so you can (often) just continue to write: |
|---|
| [634] | 44 | |
|---|
| 45 | .. code-block:: genshi |
|---|
| 46 | |
|---|
| 47 | <p>Hello, world!</p> |
|---|
| 48 | |
|---|
| 49 | This text will still be extracted and translated as if you had wrapped it in a |
|---|
| 50 | ``_()`` call. |
|---|
| 51 | |
|---|
| [1108] | 52 | .. note:: For parameterized or pluralizable messages, you need to use the |
|---|
| 53 | special `template directives`_ described below, or use the |
|---|
| 54 | corresponding ``gettext`` function in embedded Python expressions. |
|---|
| [634] | 55 | |
|---|
| [1108] | 56 | You can control which tags should be ignored by this process; for example, it |
|---|
| [634] | 57 | doesn't really make sense to translate the content of the HTML |
|---|
| 58 | ``<script></script>`` element. Both ``<script>`` and ``<style>`` are excluded |
|---|
| 59 | by default. |
|---|
| 60 | |
|---|
| 61 | Attribute values can also be automatically translated. The default is to |
|---|
| [1108] | 62 | consider the attributes ``abbr``, ``alt``, ``label``, ``prompt``, ``standby``, |
|---|
| 63 | ``summary``, and ``title``, which is a list that makes sense for HTML |
|---|
| 64 | documents. Of course, you can tell the translator to use a different set of |
|---|
| 65 | attribute names, or none at all. |
|---|
| [634] | 66 | |
|---|
| [1108] | 67 | ---------------- |
|---|
| 68 | Language Tagging |
|---|
| 69 | ---------------- |
|---|
| [634] | 70 | |
|---|
| [1108] | 71 | You can control automatic translation in your templates using the ``xml:lang`` |
|---|
| 72 | attribute. If the value of that attribute is a literal string, the contents and |
|---|
| 73 | attributes of the element will be ignored: |
|---|
| 74 | |
|---|
| [634] | 75 | .. code-block:: genshi |
|---|
| 76 | |
|---|
| 77 | <p xml:lang="en">Hello, world!</p> |
|---|
| 78 | |
|---|
| 79 | On the other hand, if the value of the ``xml:lang`` attribute contains a Python |
|---|
| 80 | expression, the element contents and attributes are still considered for |
|---|
| 81 | automatic translation: |
|---|
| 82 | |
|---|
| 83 | .. code-block:: genshi |
|---|
| 84 | |
|---|
| 85 | <html xml:lang="$locale"> |
|---|
| 86 | ... |
|---|
| 87 | </html> |
|---|
| 88 | |
|---|
| 89 | |
|---|
| [1108] | 90 | .. _`template directives`: |
|---|
| 91 | |
|---|
| 92 | Template Directives |
|---|
| 93 | =================== |
|---|
| 94 | |
|---|
| 95 | Sometimes localizable strings in templates may contain dynamic parameters, or |
|---|
| 96 | they may depend on the numeric value of some variable to choose a proper |
|---|
| 97 | plural form. Sometimes the strings contain embedded markup, such as tags for |
|---|
| 98 | emphasis or hyperlinks, and you don't want to rely on the people doing the |
|---|
| 99 | translations to know the syntax and escaping rules of HTML and XML. |
|---|
| 100 | |
|---|
| 101 | In those cases the simple text extraction and translation process described |
|---|
| 102 | above is not sufficient. You could just use ``gettext`` API functions in |
|---|
| [1113] | 103 | embedded Python expressions for parameters and pluralization, but that does |
|---|
| 104 | not help when messages contain embedded markup. Genshi provides special |
|---|
| 105 | template directives for internationalization that attempt to provide a |
|---|
| 106 | comprehensive solution for this problem space. |
|---|
| [1108] | 107 | |
|---|
| 108 | To enable these directives, you'll need to register them with the templates |
|---|
| 109 | they are used in. You can do this by adding them manually via the |
|---|
| 110 | ``Template.add_directives(namespace, factory)`` (where ``namespace`` would be |
|---|
| 111 | “http://genshi.edgewall.org/i18n” and ``factory`` would be an instance of the |
|---|
| 112 | ``Translator`` class). Or you can just call the ``Translator.setup(template)`` |
|---|
| 113 | class method, which both registers the directives and adds the translation |
|---|
| 114 | filter. |
|---|
| 115 | |
|---|
| [1113] | 116 | After the directives have been registered with the template engine on the |
|---|
| 117 | Python side of your application, you need to declare the corresponding |
|---|
| 118 | directive namespace in all markup templates that use them. For example: |
|---|
| 119 | |
|---|
| 120 | .. code-block:: genshi |
|---|
| 121 | |
|---|
| 122 | <html xmlns:py="http://genshi.edgewall.org/" |
|---|
| [1150] | 123 | xmlns:i18n="http://genshi.edgewall.org/i18n"> |
|---|
| [1113] | 124 | … |
|---|
| 125 | </html> |
|---|
| 126 | |
|---|
| 127 | These directives only make sense in the context of `markup templates`_. For |
|---|
| 128 | `text templates`_, you can just use the corresponding ``gettext`` API calls as needed. |
|---|
| 129 | |
|---|
| [1108] | 130 | .. note:: The internationalization directives are still somewhat experimental |
|---|
| 131 | and have some known issues. However, the attribute language they |
|---|
| 132 | implement should be stable and is not subject to change |
|---|
| 133 | substantially in future versions. |
|---|
| 134 | |
|---|
| [1113] | 135 | .. _`markup templates`: xml-templates.html |
|---|
| 136 | .. _`text templates`: text-templates.html |
|---|
| [1108] | 137 | |
|---|
| 138 | -------- |
|---|
| 139 | Messages |
|---|
| 140 | -------- |
|---|
| 141 | |
|---|
| 142 | ``i18n:msg`` |
|---|
| 143 | ------------ |
|---|
| 144 | |
|---|
| 145 | This is the basic directive for defining localizable text passages that |
|---|
| 146 | contain parameters and/or markup. |
|---|
| 147 | |
|---|
| 148 | For example, consider the following template snippet: |
|---|
| 149 | |
|---|
| 150 | .. code-block:: genshi |
|---|
| 151 | |
|---|
| 152 | <p> |
|---|
| 153 | Please visit <a href="${site.url}">${site.name}</a> for help. |
|---|
| 154 | </p> |
|---|
| 155 | |
|---|
| 156 | Without further annotation, the translation filter would treat this sentence |
|---|
| [1110] | 157 | as two separate messages (“Please visit” and “for help”), and the translator |
|---|
| 158 | would have no control over the position of the link in the sentence. |
|---|
| [1108] | 159 | |
|---|
| 160 | However, when you use the Genshi internationalization directives, you simply |
|---|
| 161 | add an ``i18n:msg`` attribute to the enclosing ``<p>`` element: |
|---|
| 162 | |
|---|
| 163 | .. code-block:: genshi |
|---|
| 164 | |
|---|
| 165 | <p i18n:msg="name"> |
|---|
| 166 | Please visit <a href="${site.url}">${site.name}</a> for help. |
|---|
| 167 | </p> |
|---|
| 168 | |
|---|
| 169 | Genshi is then able to identify the text in the ``<p>`` element as a single |
|---|
| 170 | message for translation purposes. You'll see the following string in your |
|---|
| 171 | message catalog:: |
|---|
| 172 | |
|---|
| 173 | Please visit [1:%(name)s] for help. |
|---|
| 174 | |
|---|
| 175 | The `<a>` element with its attribute has been replaced by a part in square |
|---|
| 176 | brackets, which does not include the tag name or the attributes of the element. |
|---|
| 177 | |
|---|
| 178 | The value of the ``i18n:msg`` attribute is a comma-separated list of parameter |
|---|
| 179 | names, which serve as simplified aliases for the actual Python expressions the |
|---|
| 180 | message contains. The order of the paramer names in the list must correspond |
|---|
| 181 | to the order of the expressions in the text. In this example, there is only |
|---|
| 182 | one parameter: its alias for translation is “name”, while the corresponding |
|---|
| 183 | expression is ``${site.name}``. |
|---|
| 184 | |
|---|
| 185 | The translator now has complete control over the structure of the sentence. He |
|---|
| 186 | or she certainly does need to make sure that any bracketed parts are not |
|---|
| 187 | removed, and that the ``name`` parameter is preserved correctly. But those are |
|---|
| 188 | things that can be easily checked by validating the message catalogs. The |
|---|
| 189 | important thing is that the translator can change the sentence structure, and |
|---|
| 190 | has no way to break the application by forgetting to close a tag, for example. |
|---|
| 191 | |
|---|
| 192 | So if the German translator of this snippet decided to translate it to:: |
|---|
| 193 | |
|---|
| 194 | Um Hilfe zu erhalten, besuchen Sie bitte [1:%(name)s] |
|---|
| 195 | |
|---|
| 196 | The resulting output might be: |
|---|
| 197 | |
|---|
| [1112] | 198 | .. code-block:: xml |
|---|
| [1108] | 199 | |
|---|
| 200 | <p> |
|---|
| 201 | Um Hilfe zu erhalten, besuchen Sie bitte |
|---|
| 202 | <a href="http://example.com/">Example</a> |
|---|
| 203 | </p> |
|---|
| 204 | |
|---|
| [1110] | 205 | Messages may contain multiple tags, and they may also be nested. For example: |
|---|
| [1108] | 206 | |
|---|
| 207 | .. code-block:: genshi |
|---|
| 208 | |
|---|
| 209 | <p i18n:msg="name"> |
|---|
| 210 | <i>Please</i> visit <b>the site <a href="${site.url}">${site.name}</a></b> |
|---|
| 211 | for help. |
|---|
| 212 | </p> |
|---|
| 213 | |
|---|
| 214 | This would result in the following message ID:: |
|---|
| 215 | |
|---|
| 216 | [1:Please] visit [2:the site [3:%(name)s]] for help. |
|---|
| 217 | |
|---|
| 218 | Again, the translator has full control over the structure of the sentence. So |
|---|
| 219 | the German translation could actually look like this:: |
|---|
| 220 | |
|---|
| 221 | Um Hilfe zu erhalten besuchen Sie [1:bitte] |
|---|
| 222 | [3:%(name)s], [2:das ist eine Web-Site] |
|---|
| 223 | |
|---|
| 224 | Which Genshi would recompose into the following outout: |
|---|
| 225 | |
|---|
| [1112] | 226 | .. code-block:: xml |
|---|
| [1108] | 227 | |
|---|
| 228 | <p> |
|---|
| 229 | Um Hilfe zu erhalten besuchen Sie <i>bitte</i> |
|---|
| 230 | <a href="http://example.com/">Example</a>, <b>das ist eine Web-Site</b> |
|---|
| 231 | </p> |
|---|
| 232 | |
|---|
| 233 | Note how the translation has changed the order and even the nesting of the |
|---|
| 234 | tags. |
|---|
| 235 | |
|---|
| 236 | .. warning:: Please note that ``i18n:msg`` directives do not support other |
|---|
| 237 | nested directives. Directives commonly change the structure of |
|---|
| 238 | the generated markup dynamically, which often would result in the |
|---|
| 239 | structure of the text changing, thus making translation as a |
|---|
| 240 | single message ineffective. |
|---|
| 241 | |
|---|
| [1111] | 242 | ``i18n:choose``, ``i18n:singular``, ``i18n:plural`` |
|---|
| 243 | --------------------------------------------------- |
|---|
| 244 | |
|---|
| 245 | Translatable strings that vary based on some number of objects, such as “You |
|---|
| 246 | have 1 new message” or “You have 3 new messages”, present their own challenge, |
|---|
| 247 | in particular when you consider that different languages have different rules |
|---|
| 248 | for pluralization. For example, while English and most western languages have |
|---|
| 249 | two plural forms (one for ``n=1`` and an other for ``n<>1``), Welsh has five |
|---|
| 250 | different plural forms, while Hungarian only has one. |
|---|
| 251 | |
|---|
| 252 | The ``gettext`` framework has long supported this via the ``ngettext()`` |
|---|
| 253 | family of functions. You specify two default messages, one singular and one |
|---|
| 254 | plural, and the number of items. The translations however may contain any |
|---|
| 255 | number of plural forms for the message, depending on how many are commonly |
|---|
| 256 | used in the language. ``ngettext`` will choose the correct plural form of the |
|---|
| 257 | translated message based on the specified number of items. |
|---|
| 258 | |
|---|
| 259 | Genshi provides a variant of the ``i18n:msg`` directive described above that |
|---|
| 260 | allows choosing the proper plural form based on the numeric value of a given |
|---|
| 261 | variable. The pluralization support is implemented in a set of three |
|---|
| 262 | directives that must be used together: ``i18n:choose``, ``i18n:singular``, and |
|---|
| 263 | ``i18n:plural``. |
|---|
| 264 | |
|---|
| 265 | The ``i18n:choose`` directive is used to set up the context of the message: it |
|---|
| 266 | simply wraps the singular and plural variants. |
|---|
| 267 | |
|---|
| 268 | The value of this directive is split into two parts: the first is the |
|---|
| 269 | *numeral*, a Python expression that evaluates to a number to determine which |
|---|
| 270 | plural form should be chosen. The second part, separated by a semicolon, lists |
|---|
| 271 | the parameter names. This part is equivalent to the value of the ``i18n:msg`` |
|---|
| 272 | directive. |
|---|
| 273 | |
|---|
| 274 | For example: |
|---|
| 275 | |
|---|
| 276 | .. code-block:: genshi |
|---|
| 277 | |
|---|
| 278 | <p i18n:choose="len(messages); num"> |
|---|
| 279 | <i18n:singular>You have <b>${len(messages)}</b> new message.</i18n:singular> |
|---|
| 280 | <i18n:plural>You have <b>${len(messages)}</b> new messages.</i18n:plural> |
|---|
| 281 | </p> |
|---|
| 282 | |
|---|
| 283 | All three directives can be used either as elements or attribute. So the above |
|---|
| 284 | example could also be written as follows: |
|---|
| 285 | |
|---|
| 286 | .. code-block:: genshi |
|---|
| 287 | |
|---|
| 288 | <i18n:choose numeral="len(messages)" params="num"> |
|---|
| 289 | <p i18n:singular="">You have <b>${len(messages)}</b> new message.</p> |
|---|
| 290 | <p i18n:plural="">You have <b>${len(messages)}</b> new messages.</p> |
|---|
| 291 | </i18n:choose> |
|---|
| 292 | |
|---|
| 293 | When used as an element, the two parts of the ``i18n:choose`` value are split |
|---|
| 294 | into two different attributes: ``numeral`` and ``params``. The |
|---|
| 295 | ``i18n:singular`` and ``i18n:plural`` directives do not require or support any |
|---|
| 296 | value (or any extra attributes). |
|---|
| 297 | |
|---|
| 298 | -------------------- |
|---|
| 299 | Comments and Domains |
|---|
| 300 | -------------------- |
|---|
| 301 | |
|---|
| [1108] | 302 | ``i18n:comment`` |
|---|
| 303 | ---------------- |
|---|
| 304 | |
|---|
| 305 | The ``i18n:comment`` directive can be used to supply a comment for the |
|---|
| 306 | translator. For example, if a template snippet is not easily understood |
|---|
| 307 | outside of its context, you can add a translator comment to help the |
|---|
| 308 | translator understand in what context the message will be used: |
|---|
| 309 | |
|---|
| 310 | .. code-block:: genshi |
|---|
| 311 | |
|---|
| 312 | <p i18n:msg="name" i18n:comment="Link to the relevant support site"> |
|---|
| [1110] | 313 | Please visit <a href="${site.url}">${site.name}</a> for help. |
|---|
| [1108] | 314 | </p> |
|---|
| 315 | |
|---|
| 316 | This comment will be extracted together with the message itself, and will |
|---|
| 317 | commonly be placed along the message in the message catalog, so that it is |
|---|
| 318 | easily visible to the person doing the translation. |
|---|
| 319 | |
|---|
| 320 | This directive has no impact on how the template is rendered, and is ignored |
|---|
| 321 | outside of the extraction process. |
|---|
| 322 | |
|---|
| [1111] | 323 | ``i18n:domain`` |
|---|
| 324 | --------------- |
|---|
| [1108] | 325 | |
|---|
| [1111] | 326 | In larger projects, message catalogs are commonly split up into different |
|---|
| 327 | *domains*. For example, you might have a core application domain, and then |
|---|
| 328 | separate domains for extensions or libraries. |
|---|
| [1108] | 329 | |
|---|
| [1111] | 330 | Genshi provides a directive called ``i18n:domain`` that lets you choose the |
|---|
| 331 | translation domain for a particular scope. For example: |
|---|
| [1110] | 332 | |
|---|
| [1111] | 333 | .. code-block:: genshi |
|---|
| [1110] | 334 | |
|---|
| [1111] | 335 | <div i18n:domain="examples"> |
|---|
| 336 | <p>Hello, world!</p> |
|---|
| 337 | </div> |
|---|
| [1110] | 338 | |
|---|
| [1108] | 339 | |
|---|
| [634] | 340 | Extraction |
|---|
| 341 | ========== |
|---|
| 342 | |
|---|
| 343 | The ``Translator`` class provides a class method called ``extract``, which is |
|---|
| 344 | a generator yielding all localizable strings found in a template or markup |
|---|
| 345 | stream. This includes both literal strings in text nodes and attribute values, |
|---|
| 346 | as well as strings in ``gettext()`` calls in embedded Python code. See the API |
|---|
| 347 | documentation for details on how to use this method directly. |
|---|
| 348 | |
|---|
| [1108] | 349 | ----------------- |
|---|
| 350 | Babel Integration |
|---|
| 351 | ----------------- |
|---|
| 352 | |
|---|
| 353 | This functionality is integrated with the message extraction framework provided |
|---|
| [634] | 354 | by the `Babel`_ project. Babel provides a command-line interface as well as |
|---|
| 355 | commands that can be used from ``setup.py`` scripts using `Setuptools`_ or |
|---|
| 356 | `Distutils`_. |
|---|
| 357 | |
|---|
| 358 | .. _`setuptools`: http://peak.telecommunity.com/DevCenter/setuptools |
|---|
| 359 | .. _`distutils`: http://docs.python.org/dist/dist.html |
|---|
| 360 | |
|---|
| 361 | The first thing you need to do to make Babel extract messages from Genshi |
|---|
| 362 | templates is to let Babel know which files are Genshi templates. This is done |
|---|
| 363 | using a “mapping configuration”, which can be stored in a configuration file, |
|---|
| 364 | or specified directly in your ``setup.py``. |
|---|
| 365 | |
|---|
| 366 | In a configuration file, the mapping may look like this: |
|---|
| 367 | |
|---|
| 368 | .. code-block:: ini |
|---|
| 369 | |
|---|
| 370 | # Python souce |
|---|
| 371 | [python:**.py] |
|---|
| 372 | |
|---|
| 373 | # Genshi templates |
|---|
| 374 | [genshi:**/templates/**.html] |
|---|
| 375 | include_attrs = title |
|---|
| 376 | |
|---|
| 377 | [genshi:**/templates/**.txt] |
|---|
| 378 | template_class = genshi.template.TextTemplate |
|---|
| 379 | encoding = latin-1 |
|---|
| 380 | |
|---|
| 381 | Please consult the Babel documentation for details on configuration. |
|---|
| 382 | |
|---|
| 383 | If all goes well, running the extraction with Babel should create a POT file |
|---|
| 384 | containing the strings from your Genshi templates and your Python source files. |
|---|
| 385 | |
|---|
| 386 | |
|---|
| 387 | --------------------- |
|---|
| 388 | Configuration Options |
|---|
| 389 | --------------------- |
|---|
| 390 | |
|---|
| 391 | The Genshi extraction plugin for Babel supports the following options: |
|---|
| 392 | |
|---|
| 393 | ``template_class`` |
|---|
| 394 | ------------------ |
|---|
| 395 | The concrete ``Template`` class that the file should be loaded with. Specify |
|---|
| 396 | the package/module name and the class name, separated by a colon. |
|---|
| 397 | |
|---|
| 398 | The default is to use ``genshi.template:MarkupTemplate``, and you'll want to |
|---|
| 399 | set it to ``genshi.template:TextTemplate`` for `text templates`_. |
|---|
| 400 | |
|---|
| 401 | .. _`text templates`: text-templates.html |
|---|
| 402 | |
|---|
| 403 | ``encoding`` |
|---|
| [708] | 404 | ------------ |
|---|
| [634] | 405 | The encoding of the template file. This is only used for text templates. The |
|---|
| 406 | default is to assume “utf-8”. |
|---|
| 407 | |
|---|
| 408 | ``include_attrs`` |
|---|
| [708] | 409 | ----------------- |
|---|
| [634] | 410 | Comma-separated list of attribute names that should be considered to have |
|---|
| 411 | localizable values. Only used for markup templates. |
|---|
| 412 | |
|---|
| [708] | 413 | ``ignore_tags`` |
|---|
| 414 | --------------- |
|---|
| [634] | 415 | Comma-separated list of tag names that should be ignored. Only used for markup |
|---|
| 416 | templates. |
|---|
| 417 | |
|---|
| [708] | 418 | ``extract_text`` |
|---|
| 419 | ---------------- |
|---|
| 420 | Whether text outside explicit ``gettext`` function calls should be extracted. |
|---|
| 421 | By default, any text nodes not inside ignored tags, and values of attribute in |
|---|
| 422 | the ``include_attrs`` list are extracted. If this option is disabled, only |
|---|
| 423 | strings in ``gettext`` function calls are extracted. |
|---|
| [634] | 424 | |
|---|
| [1113] | 425 | .. note:: If you disable this option, and do not make use of the |
|---|
| 426 | internationalization directives, it's not necessary to add the |
|---|
| 427 | translation filter as described above. You only need to make sure |
|---|
| 428 | that the template has access to the ``gettext`` functions it uses. |
|---|
| [708] | 429 | |
|---|
| 430 | |
|---|
| [634] | 431 | Translation |
|---|
| 432 | =========== |
|---|
| 433 | |
|---|
| 434 | If you have prepared MO files for use with Genshi using the appropriate tools, |
|---|
| 435 | you can access the message catalogs with the `gettext Python module`_. You'll |
|---|
| 436 | probably want to create a ``gettext.GNUTranslations`` instance, and make the |
|---|
| 437 | translation functions it provides available to your templates by putting them |
|---|
| 438 | in the template context. |
|---|
| 439 | |
|---|
| 440 | The ``Translator`` filter needs to be added to the filters of the template |
|---|
| 441 | (applying it as a stream filter will likely not have the desired effect). |
|---|
| 442 | Furthermore it needs to be the first filter in the list, including the internal |
|---|
| 443 | filters that Genshi adds itself: |
|---|
| 444 | |
|---|
| 445 | .. code-block:: python |
|---|
| 446 | |
|---|
| 447 | from genshi.filters import Translator |
|---|
| 448 | from genshi.template import MarkupTemplate |
|---|
| 449 | |
|---|
| 450 | template = MarkupTemplate("...") |
|---|
| 451 | template.filters.insert(0, Translator(translations.ugettext)) |
|---|
| 452 | |
|---|
| [1113] | 453 | The ``Translator`` class also provides the convenience method ``setup()``, |
|---|
| 454 | which will both add the filter and register the i18n directives: |
|---|
| [634] | 455 | |
|---|
| 456 | .. code-block:: python |
|---|
| 457 | |
|---|
| 458 | from genshi.filters import Translator |
|---|
| [1113] | 459 | from genshi.template import MarkupTemplate |
|---|
| [634] | 460 | |
|---|
| [1113] | 461 | template = MarkupTemplate("...") |
|---|
| 462 | translator = Translator(translations.ugettext) |
|---|
| 463 | translator.setup(template) |
|---|
| [634] | 464 | |
|---|
| [1113] | 465 | .. warning:: If you're using ``TemplateLoader``, you should specify a |
|---|
| 466 | `callback function`_ in which you add the filter. That ensures |
|---|
| 467 | that the filter is not added everytime the template is rendered, |
|---|
| 468 | thereby being applied multiple times. |
|---|
| [634] | 469 | |
|---|
| [1113] | 470 | .. _`callback function`: loader.html#callback-interface |
|---|
| [634] | 471 | |
|---|
| [1113] | 472 | |
|---|
| [634] | 473 | Related Considerations |
|---|
| 474 | ====================== |
|---|
| 475 | |
|---|
| 476 | If you intend to produce an application that is fully prepared for an |
|---|
| 477 | international audience, there are a couple of other things to keep in mind: |
|---|
| 478 | |
|---|
| 479 | ------- |
|---|
| 480 | Unicode |
|---|
| 481 | ------- |
|---|
| 482 | |
|---|
| 483 | Use ``unicode`` internally, not encoded bytestrings. Only encode/decode where |
|---|
| 484 | data enters or exits the system. This means that your code works with characters |
|---|
| 485 | and not just with bytes, which is an important distinction for example when |
|---|
| 486 | calculating the length of a piece of text. When you need to decode/encode, it's |
|---|
| 487 | probably a good idea to use UTF-8. |
|---|
| 488 | |
|---|
| 489 | ------------- |
|---|
| 490 | Date and Time |
|---|
| 491 | ------------- |
|---|
| 492 | |
|---|
| 493 | If your application uses datetime information that should be displayed to users |
|---|
| 494 | in different timezones, you should try to work with UTC (universal time) |
|---|
| 495 | internally. Do the conversion from and to "local time" when the data enters or |
|---|
| 496 | exits the system. Make use the Python `datetime`_ module and the third-party |
|---|
| 497 | `pytz`_ package. |
|---|
| 498 | |
|---|
| 499 | -------------------------- |
|---|
| 500 | Formatting and Locale Data |
|---|
| 501 | -------------------------- |
|---|
| 502 | |
|---|
| 503 | Make sure you check out the functionality provided by the `Babel`_ project for |
|---|
| 504 | things like number and date formatting, locale display strings, etc. |
|---|
| 505 | |
|---|
| 506 | .. _`datetime`: http://docs.python.org/lib/module-datetime.html |
|---|
| 507 | .. _`pytz`: http://pytz.sourceforge.net/ |
|---|