Edgewall Software

Changes between Version 29 and Version 30 of CookBook/PluginL10N


Ignore:
Timestamp:
Jan 10, 2015, 12:26:27 AM (9 years ago)
Author:
figaro
Comment:

Cosmetic changes

Legend:

Unmodified
Added
Removed
Modified
  • CookBook/PluginL10N

    v29 v30  
    33
    44== 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 
    95If 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.
    106
    117Ultimately, 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.
    128
    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 sense of 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]^
    1713
    1814=== Background and concept of i18n/l10n support for Trac plugins
    1915
    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.
     16It 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
     18Some 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.
    2519
    2620The 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.
     
    3024The 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]^
    3125
    32 
    3326== Required workflow ==
    3427
    3528You need to:
    36  - specify in you plugin's `setup.py` file on which files the Babel commands will have to operate
     29 - specify in your plugin's `setup.py` file on which files the Babel commands will have to operate
    3730 - create a `setup.cfg` files for adding options to the Babel commands
    3831 - 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
    4033   - in the "root" `Component` in your plugin (one you're sure is always enabled) and initialize the translation domain in its `__init__` method
    4134   - use your translation functions appropriately
     
    4740   - use the translation functions as appropriate
    4841
    49 Now, a detailed walk-through...
    50 
    5142=== Enable Babel support for your plugin ===
    5243==== Add Babel commands to the setup (`setup.py`)
    5344
    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`]:
     45Babel 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`]:
    5546{{{#!diff
    5647diff --git a/setup.py b/setup.py
     
    9990}}}
    10091
    101 
    10292==== Preset configuration for Babel commands (`setup.cfg`) ====
    10393Add some lines to `setup.cfg` or, if it doesn't exist by now, create it with the following content:
     
    133123This will tell Babel where to look for and store message catalog files.
    134124
    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.
     125In 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
     127The `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.
    139128
    140129==== Register message catalog files for packaging ====
     
    154143}}}
    155144
    156 
    157145=== Make the Python code translation-aware
    158146==== 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).
     147Pick 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.
    163148
    164149At 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`.
    165150
    166 This helper function should be called at module load time, like this:
     151This sample helper function should be called at module load time:
    167152{{{#!python
    168153from trac.util.translation import domain_functions
     
    212197}}}
    213198
    214 Note, that quoting of (i18n) message texts should really be 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 that you have to do the wrapping of these new texts too.
     199Note, 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
     201If 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.
    217202
    218203=== Make the Genshi templates translation-aware
    219204
    220 First, keep an eye on the Genshi documentation on this topic, [http://genshi.edgewall.org/wiki/Documentation/0.6.x/i18n.html Internationalization and Localization].
     205See the Genshi documentation on this topic, [http://genshi.edgewall.org/wiki/Documentation/0.6.x/i18n.html Internationalization and Localization].
    221206
    222207==== 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.
     208Message 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.
    224209
    225210==== 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.
     211Extraction 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.
    227212
    228213For example:
     
    242227==== Text extraction from Javascript code ==== #Javascript
    243228
    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...
     229Adding support for translating the marked strings in the Javascript code is a bit more involved.
    245230
    246231We 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.
     
    265250}}}
    266251
    267 That was the easiest part.
    268 
    269252Now, 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:
    270253
     
    283266}}}
    284267
    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.
     268Now 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.
    286269
    287270{{{#!ini
     
    316299As 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.
    317300
    318 We're nearly done yet... noticed the `mapping_file = messages-js.cfg` line?
    319301We need to configure separately how to do the extraction for this `messages-js.pot` catalog template.
    320302The `messages-js.cfg` file has the following content:
     
    330312}}}
    331313
    332 Bonus points for anyone who will manage to //simplify// a bit this procedure ;-)
    333 
     314This procedure needs to be //simplified//.
    334315
    335316=== Announce new plugin version ===
     
    341322}}}
    342323
    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.
     324To 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.
    344325
    345326=== Summing it up ===
     
    355336You'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.
    356337
    357 
    358 
    359338== Do translators work
    360339General advice from [[TracL10N]] on making good translation for Trac applies here too.
     
    365344Trac has some language teams at Transifex as well, so this is a good chance for tight translator cooperation.
    366345
    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.:
     346Switch to root directory of plugin's source at the command line:
    371347{{{
    372348cd /usr/src/trac_plugins/foo
     
    379355The 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.
    380356
    381 If you attempt to do improvements on existing message catalogs you'll update the one for your desired language:
     357If you attempt to improve on existing message catalogs, you'll update the one for your desired language:
    382358{{{
    383359python ./setup.py update_catalog -l de_DE
    384360}}}
    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 current hardware.
    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 to create the message catalog for your desired language:
     361If 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
     363But 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:
    388364{{{
    389365python ./setup.py init_catalog -l de_DE
    390366}}}
    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.
     367The language selection argument `-l` and identifier string are mandatory here.
    392368
    393369Now 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]^
     
    396372
    397373=== Compile and use it ===
    398 Compile the `messages.po` catalog file with your translations into a machine readable `messages.mo` file.
     374Compile the `messages.po` catalog file with your translations into a machine readable `messages.mo` file:
    399375{{{
    400376python ./setup.py compile_catalog -f -l de_DE
     
    436412}}}
    437413
    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.
     414The 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.
    439415As an example, see the following excerpt from the SpamFilter plugin [source:plugins/1.0/spam-filter/setup.cfg setup.cfg] file:
    440416{{{#!python
     
    460436
    461437=== 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.
     438A human translator will/should do better than an automatic translation, since good l10n has more of a transcription than pure translation word by word.
    464439It'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.
     440This 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.
    468441
    469442== Related resources ==