Changes between Version 29 and Version 30 of CookBook/PluginL10N
- Timestamp:
- Jan 10, 2015, 12:26:27 AM (9 years ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
CookBook/PluginL10N
v29 v30 3 3 4 4 == Intro and Motivation == 5 Are you a user of Trac and do your work with it, nothing more? Well, you may ignore this page and go on reading another subpage of [wiki:CookBook CookBook].6 7 Professional coders/translators, please skip to the actual cookbook content in '[#Requiredworkflow Required workflow]', since there can't be any news for you before that section.8 9 5 If you want to learn about translation for a plugin, that as you know already provides one/several message catalog/s, the section '[#Dotranslatorswork Do translators work]' and following parts are for you. 10 6 11 7 Ultimately, all plugin maintainers and developers in general, who are facing requests and are willing to take care for growing demand of their plugin to speak same (foreign) language(s) as Trac >= 0.12 should, just read on. 12 8 13 === i18n, l10n , ... help!14 In short '''i18n''' stands for '''`i`'''`nternationalizatio`'''`n`''' (count 18 chars between i and n) and is defined as software design for programs with translation support. '''`l`'''`ocalisatio`'''`n`''' that is abbreviated as '''l10n''' could be seen as a follow-up process providing data for one or more locales. It is taking care of feature differences between the original/default (that is English is most cases including Trac) and a given locale as well. Such features are i.e. sentence structure including punctuation and formatting of numbers, date/time strings, and currencies. Once you did some ground work at the source (i18n), what's remaining is proper translation work (l10n), putting more or less effort in preserving the senseof the original while looking as native locale as possible.^[#a1 1]^15 16 '''NLS''' (National Language Support or Native Language Support) is meant to be the sum of both. And there are more related terms that we could safely skip for now.^[#a1 1], [#a2 2]^9 === i18n, l10n introduction 10 '''i18n''' stands for '''`i`'''`nternationalizatio`'''`n`''' (count 18 chars between i and n) and is defined as software design for programs with translation support. '''`l`'''`ocalisatio`'''`n`''' that is abbreviated as '''l10n''' could be seen as a follow-up process providing data for one or more locales. It is taking care of feature differences between the original/default (that is English in most cases including Trac) and a given locale as well. Such features are eg sentence structure, including punctuation and formatting of numbers, date/time strings and currencies. Once you did some ground work at the source (i18n), what's remaining is proper translation work (l10n), preserving the meaning of the original while looking as native locale as possible.^[#a1 1]^ 11 12 '''NLS''' (National Language Support or Native Language Support) is meant to be the sum of both.^[#a1 1], [#a2 2]^ 17 13 18 14 === Background and concept of i18n/l10n support for Trac plugins 19 15 20 It begun with adding [Babel:] to Trac. Babel is a very powerful translation framework. For one part, it is a message extraction tool: it can extract messages from source code files (in our case, Python and Javascript) as well as from Genshi templates, and create catalog templates (`.pot` files). It can also create and update the message catalogs (`.po` files), and compile those catalogs (`.mo` files). For the other part, as a Python library used within Trac, it provides the implementation of the message retrieval functions (`gettext` and related). And there's even more to it than that, if you're interested please visit the Babel project (also here on edgewall.org). 21 22 Now back to the plugins... 23 24 Some plugin maintainers created their own translation module inside each plugin separately. Growing amount of code redundancy and possibility of error within imperfect copies and variants of a translation module all that was certainly not a desirable situation. And Trac core maintainers took responsibility with adding functions dedicated to i18n/l10n support for Trac plugins. 16 It begun with adding [Babel:] to Trac, a powerful translation framework. For one part, it is a message extraction tool: it can extract messages from source code files (in our case, Python and Javascript) as well as from Genshi templates, and create catalog templates (`.pot` files). It can also create and update the message catalogs (`.po` files), and compile those catalogs (`.mo` files). For the other part, as a Python library used within Trac, it provides the implementation of the message retrieval functions (`gettext` and related). For more information, see [http://babel.edgewall.org/ Babel]. 17 18 Some plugin maintainers created their own translation module inside each plugin separately. Growing amount of code redundancy and possibility of error within imperfect copies and variants of a translation module was not a desirable situation. And Trac core maintainers took responsibility with adding functions dedicated to i18n/l10n support for Trac plugins. 25 19 26 20 The evolution of this functions has been documented in [comment:11:ticket:7497 ticket 7497]. The final implementation as mentioned there in [comment:12:ticket:7497 comment 12] was introduced to Trac trunk in changeset r7705 and finally done with changeset r7714. … … 30 24 The message catalog selection is done according to the locale setting. Valid settings are a combination of language and country code, optionally extended further by the character encoding used, i.e. to read like βde_DE.UTF-8β. Trac uses UTF-8 encoding internally, so there is not much to tell about that. 'C' is a special locale code since it disables all translations and programs use English texts as required by POSIX standard.^[#a3 3]^ 31 25 32 33 26 == Required workflow == 34 27 35 28 You need to: 36 - specify in you plugin's `setup.py` file on which files the Babel commands will have to operate29 - specify in your plugin's `setup.py` file on which files the Babel commands will have to operate 37 30 - create a `setup.cfg` files for adding options to the Babel commands 38 31 - in your Python source code: 39 - define specializations of the translation functions for your specific domain (there's a helper function for doing that easily)32 - define specializations of the translation functions for your specific domain; there's a helper function for doing that easily 40 33 - in the "root" `Component` in your plugin (one you're sure is always enabled) and initialize the translation domain in its `__init__` method 41 34 - use your translation functions appropriately … … 47 40 - use the translation functions as appropriate 48 41 49 Now, a detailed walk-through...50 51 42 === Enable Babel support for your plugin === 52 43 ==== Add Babel commands to the setup (`setup.py`) 53 44 54 Babel only does extract from Python scripts by default. To extract messages from Genshi templates as well, you'll have to declare the needed extractors in [=#setup `setup.py`]:45 Babel by default only extracts from Python scripts. To extract messages from Genshi templates also, you'll have to declare the needed extractors in [=#setup `setup.py`]: 55 46 {{{#!diff 56 47 diff --git a/setup.py b/setup.py … … 99 90 }}} 100 91 101 102 92 ==== Preset configuration for Babel commands (`setup.cfg`) ==== 103 93 Add some lines to `setup.cfg` or, if it doesn't exist by now, create it with the following content: … … 133 123 This will tell Babel where to look for and store message catalog files. 134 124 135 136 In the `extract_messages` section there is just one more lines you may like to change: `msgid_bugs_address`. To allow for direct feedback regarding your i18n work add a valid e-mail address or a mailing list dedicated to translation issues there. 137 138 The `add_comments` line simply lists the tags in the comments surrounding the calls to the translation functions in the source code that have to be propagated to the catalogs (see [Babel:wiki:Documentation/0.9/setup.html#extract-messages extract_messages] in Babel's documentation). So you will want to leave that one untouched. 125 In the `extract_messages` section there is just one more lines you may like to change: `msgid_bugs_address`. To allow for direct feedback regarding your i18n work, add a valid e-mail address or a mailing list dedicated to translation issues there. 126 127 The `add_comments` line simply lists the tags in the comments surrounding the calls to the translation functions in the source code that have to be propagated to the catalogs, see [Babel:wiki:Documentation/0.9/setup.html#extract-messages extract_messages] in Babel's documentation. So you will want to leave that one untouched. 139 128 140 129 ==== Register message catalog files for packaging ==== … … 154 143 }}} 155 144 156 157 145 === Make the Python code translation-aware 158 146 ==== Prepare domain-specific translation helper functions ==== 159 Pick a reasonably unique name for the domain, e.g. ** 'foo' ** (if your plugin is named 'foo', that is). 160 161 This will be the basename for the various translation catalog files 162 (e.g. `foo/locale/fr/LC_MESSAGES/foo.po` for the French catalog). 147 Pick a unique name for the domain, as this will be the basename for the various translation catalog files, eg `foo/locale/fr/LC_MESSAGES/foo.po` for the French catalog. 163 148 164 149 At run-time, the translation functions (typically `_(...)`) have to know in which catalog the translation will be found. Specifying the 'foo' domain in every such call would be tedious, that's why there's a facility for creating partially instantiated domain-aware translation functions: `domain_functions`. 165 150 166 This helper function should be called at module load time, like this:151 This sample helper function should be called at module load time: 167 152 {{{#!python 168 153 from trac.util.translation import domain_functions … … 212 197 }}} 213 198 214 Note, that quoting of (i18n) message texts should reallybe done in double quotes. Single quotes are reserved for string constants (see commit note for r9751).215 216 This is a somewhat time consuming task depending on the size of the plugin's code. If you initially fail to find all desired texts you may notice this by missing them from the message catalog later and come back to this step again. If the plugin maintainer is unaware of your i18n work or unwilling to support it and he adds more message without the translation function call, remember thatyou have to do the wrapping of these new texts too.199 Note, that quoting of (i18n) message texts should be done in double quotes. Single quotes are reserved for string constants (see commit note for r9751). 200 201 If you fail to find all desired texts, you will notice this by seeing those messages missing from the message catalog. If the plugin maintainer is unaware of your i18n work or unwilling to support it and he adds more messages without the translation function call, you have to do the wrapping of these new texts too. 217 202 218 203 === Make the Genshi templates translation-aware 219 204 220 First, keep an eye onthe Genshi documentation on this topic, [http://genshi.edgewall.org/wiki/Documentation/0.6.x/i18n.html Internationalization and Localization].205 See the Genshi documentation on this topic, [http://genshi.edgewall.org/wiki/Documentation/0.6.x/i18n.html Internationalization and Localization]. 221 206 222 207 ==== Text extraction from Python code and Genshi templates ==== 223 Message extraction for Genshi templates should be done auto -magically. However there is the markup `i18n:msg` available to ensure extraction even from less common tags. For a real-world example have a look at [changeset:9542/trunk/trac/ticket/templates/ Trac SVN changeset r9542] for marking previously undetected text in templates.208 Message extraction for Genshi templates should be done automatically. However there is the markup `i18n:msg` available to ensure extraction even from less common tags. For a real-world example have a look at [changeset:9542/trunk/trac/ticket/templates/ Trac SVN changeset r9542] for marking previously undetected text in templates. 224 209 225 210 ==== Runtime support 226 Extraction is auto -magical, however message retrieval at runtime is not. You have to make sure you've specified the appropriate //domain// in your template, by adding a `i18n:domain` directive. Usually you would put it in the top-level element, next to the mandatory `xmlns:i18n` namespace declaration.211 Extraction is automatic, however message retrieval at runtime is not. You have to make sure you've specified the appropriate //domain// in your template, by adding a `i18n:domain` directive. Usually you would put it in the top-level element, next to the mandatory `xmlns:i18n` namespace declaration. 227 212 228 213 For example: … … 242 227 ==== Text extraction from Javascript code ==== #Javascript 243 228 244 Adding support for translating the marked strings in the Javascript code is a bit more involved , but if you made it to this point, that shouldn't scare you away...229 Adding support for translating the marked strings in the Javascript code is a bit more involved. 245 230 246 231 We currently support only statically deployed Javascript files, which means they can't be translated like template files on the server, but that the translation has to happen dynamically on the client side. To this end, we want to send an additional `.js` file containing a dictionary of the messages that have to be translated, and only those. In order to clearly identify which strings have to be present in this dictionary, we'll extract the messages marked for translation (the usual `_(...)` ways) from the Javascript code into a dedicated catalog template, and from there, we'll create dedicated catalogs for each locale. In the end, the translations present in each compiled catalog will be extracted and placed into a `.js` file containing the messages dictionary and some setup code. … … 265 250 }}} 266 251 267 That was the easiest part.268 269 252 Now, as you need to actually send that `.js` file containing the messages dictionary, call `add_script()` as appropriate in the `process_request()` method from your module: 270 253 … … 283 266 }}} 284 267 285 Now ,you need to expand the `setup.cfg` file with the configuration that the new `cmdclass` dedicated to Javascript translation need. Those classes all end with an `_js` suffix.268 Now you need to expand the `setup.cfg` file with the configuration that the new `cmdclass` dedicated to Javascript translation need. Those classes all end with an `_js` suffix. 286 269 287 270 {{{#!ini … … 316 299 As before, replace `<path>` with what's appropriate for your plugin. Note that the domain name is now `foo-js`, not just `foo` as before. This is necessary as we want to have only the strings actually needed Javascript to be stored in the `.js` file containing the messages dictionary. 317 300 318 We're nearly done yet... noticed the `mapping_file = messages-js.cfg` line?319 301 We need to configure separately how to do the extraction for this `messages-js.pot` catalog template. 320 302 The `messages-js.cfg` file has the following content: … … 330 312 }}} 331 313 332 Bonus points for anyone who will manage to //simplify// a bit this procedure ;-) 333 314 This procedure needs to be //simplified//. 334 315 335 316 === Announce new plugin version === … … 341 322 }}} 342 323 343 All the work you did by now will go unnoticed, at least with regard to package naming. To help with identification of the new revision you should bump the plugin's version. This is done by changing the version/revision, typically in `setup.cfg` or `setup.py`. And you may wish to leave a note regarding your i18n work along the copyright notices as well.324 To help with identification of the new revision and make your work visible to other users, you should bump the plugin's version number. This is done by changing the version/revision, typically in `setup.cfg` or `setup.py`. And you may wish to leave a note regarding your i18n work along the copyright notices as well. 344 325 345 326 === Summing it up === … … 355 336 You'll find another example attached to this page. That is [th:wiki:SubticketsPlugin Sub-Tickets plugin] v0.1.0 and a [attachment:trac-subtickets-plugin_i18n-l10n.patch diff] containing all i18n/l10n related work to produce a German translation based on that source. 356 337 357 358 359 338 == Do translators work 360 339 General advice from [[TracL10N]] on making good translation for Trac applies here too. … … 365 344 Trac has some language teams at Transifex as well, so this is a good chance for tight translator cooperation. 366 345 367 For those who read previous parts, you do notice that we switch from talking about i18n to 'l10n' now, don't you? 368 No source code mangling. All code below is no meant to become part of the plugin source but meant to be put to the command line. 369 370 Switch to root directory of plugin's source, i.e.: 346 Switch to root directory of plugin's source at the command line: 371 347 {{{ 372 348 cd /usr/src/trac_plugins/foo … … 379 355 The attentive reader will notice that the argument to `setup.py` has the same wording as a section in `setup.cfg`, that is not incidental. And this does apply to the following command lines as well. 380 356 381 If you attempt to do improvements on existing message catalogsyou'll update the one for your desired language:357 If you attempt to improve on existing message catalogs, you'll update the one for your desired language: 382 358 {{{ 383 359 python ./setup.py update_catalog -l de_DE 384 360 }}} 385 If you omit the language selection argument `-l` and identifier string, existing catalogs of all languages will be updated, what is acceptably fast (just seconds) on currenthardware.386 387 But if you happen to do all the i18n work before, the you know you there's nothing to update right now. Well, so now it's time tocreate the message catalog for your desired language:361 If you omit the language selection argument `-l` and identifier string, existing catalogs of all languages will be updated, and within seconds on stock hardware. 362 363 But if you happen to do all the i18n work before, then you know you there's nothing to update right now. In that case create the message catalog for your desired language: 388 364 {{{ 389 365 python ./setup.py init_catalog -l de_DE 390 366 }}} 391 As you may guess, there is not much to be done, if the helper programs don't know what language you'd like to work on, so the language selection argument `-l` and identifier string are mandatory here.367 The language selection argument `-l` and identifier string are mandatory here. 392 368 393 369 Now fire up the editor of your choice. There are dedicated message catalog (.po) file editors that ensure for quick results as a beginner as well as make working on large message catalogs with few untranslated texts or translations marked 'fuzzy' much more convenient. See dedicated resources for details on choosing an editor program as well as for help on editing .po files.^[#a4 4], [#a5 5]^ … … 396 372 397 373 === Compile and use it === 398 Compile the `messages.po` catalog file with your translations into a machine readable `messages.mo` file .374 Compile the `messages.po` catalog file with your translations into a machine readable `messages.mo` file: 399 375 {{{ 400 376 python ./setup.py compile_catalog -f -l de_DE … … 436 412 }}} 437 413 438 The default `cleanup_keywords` (the `Option` subclasses) are not automatically `keywords` however. The corresponding option for the `[extract_messages]` section of the `setup.cfg` file should therefore contain the `cleandoc_` token, or the various`Config` subclasses together with the position of the doc argument in that subclass.414 The default `cleanup_keywords` (the `Option` subclasses) are not automatically `keywords` however. The corresponding option for the `[extract_messages]` section of the `setup.cfg` file should therefore contain the `cleandoc_` token, or the `Config` subclasses together with the position of the doc argument in that subclass. 439 415 As an example, see the following excerpt from the SpamFilter plugin [source:plugins/1.0/spam-filter/setup.cfg setup.cfg] file: 440 416 {{{#!python … … 460 436 461 437 === About 'true' l10n 462 A human translator will/should do better than an automatic translation, 463 since good l10n has more of a transcription than pure translation word by word. 438 A human translator will/should do better than an automatic translation, since good l10n has more of a transcription than pure translation word by word. 464 439 It's encouraging to see the raise of native words for such terms like changeset, ticket and repository in different languages. 465 This will help Trac to not only fulfill its promise to help project teams focusing on their work but even extend its use to project management in general, where use of native language is much more common or even required in contrast to the traditional software development. 466 467 Details on that matter tend to become religious, so let's stop here. 440 This will help Trac to not only fulfil its promise to help project teams focusing on their work, but even extend its use to project management in general, where use of native language is much more common or even required in contrast to the traditional software development. 468 441 469 442 == Related resources ==