Edgewall Software

Changes between Version 22 and Version 23 of CookBook/PluginL10N


Ignore:
Timestamp:
Sep 25, 2012, 11:02:34 PM (12 years ago)
Author:
Christian Boos
Comment:

review the page, add some overviews: what is Babel? what are the steps involved when translating a plugin?

Legend:

Unmodified
Added
Removed
Modified
  • CookBook/PluginL10N

    v22 v23  
    77If 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.
    88
    9 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.
     9Ultimately, 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.
    1010
    1111== i18n, l10n, ... help! ==
    12 In short '''i18n''' stands for '''`i`'''`nternationalizatio`'''`n`''' (count 18 more 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), most 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]^
     12In 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]^
    1313
    1414'''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]^
    1515
    1616== Background and concept of i18n/l10n support for Trac plugins ==
    17 It begun with adding Babel to Trac. 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.
     17
     18It 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).
     19
     20Now back to the plugins...
     21
     22Some 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.
    1823
    1924The 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.
    2025
    21 Now adding the needed i18n/l10n helper functions is done by importing a set of functions from `trac/util/translation.py` and providing proper configuration for an additional translation layer ('domain') inside the plugin code. On plugin initialization the dedicated translation domain is created as well and corresponding catalog files holding translated messages are loaded into it. Whenever a translatable text is encountered during runtime inside plugin's code, i18n/l10n helper functions will try to get the corresponding translation from the message catalog of plugin's domain and fall back silently to Trac's main message catalog, if needed.
     26Now adding the needed i18n/l10n helper functions is done by importing a set of functions from `trac/util/translation.py` and providing the necessary extra information (''domain'')  for storing and fetching the messages from the plugin code into plugin specific message catalogs. During plugin initialization, the dedicated translation domain is created as well and corresponding catalog files holding translated messages are loaded in memory. If everything is setup correctly, when a translatable text is encountered at runtime inside the plugin's code, the i18n/l10n helper functions will try to get the corresponding translation from a message catalog of the plugin's domain.
    2227
    2328The 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]^
    2429
    25 {{{#!comment
    26 Both searches take the locale setting as a second request argument. Valid settings are a combination of language and country code, often extended further by the character encoding used, i.e. to read like ‘de_DE.UTF-8’. The encoding is of special relevance for languages that had an older encoding per default that was not sufficient for all common chars used by native speakers of that language. 'C' is a special locale code since it disables all translations and programs use English texts as required by POSIX standard. Character encoding is highly dependent on the underlying operation system then.^[#a3 3]^
    27 //
    28  I'm not sure to what the above refers. Which search? What locale argument?
    29  I don't think the character encoding plays any role here (we deal with unicode internally,
    30  catalogs themselves are always encoded in UTF-8) - cboos
    31   Thanks for the hint on non-relevance for Trac since this has that uniform encoding. So I hope the current text is better.
    32   This might be deleted than.
    33 }}}
    34 
    35 First matching translation will replace the default text what by gettext convention is the same as the msgid, that is used, if all attempts fail to find an exact matching translation.
    3630
    3731== Required workflow ==
    38 A walk-through...
     32
     33You need to:
     34 - specify in you plugin's `setup.py` file on which files the Babel commands will have to operate
     35 - create a `setup.cfg` files for adding options to the Babel commands
     36 - in your Python source code:
     37   - define specializations of the translation functions for your specific domain (there's a helper function for doing that easily)
     38   - in the "root" `Component` in your plugin (one you're sure is always enabled) and initialize the translation domain in its `__init__` method
     39   - use your translation functions appropriately
     40 - in your Genshi templates:
     41   - be sure to have the necessary namespace declaration and domain directive in place
     42   - use the i18n: directive as appropriate
     43 - in your Javascript code:
     44   - be sure to load your catalog and define your domain specific translation functions
     45   - use the translation functions as appropriate
     46
     47Now, a detailed walk-through...
    3948
    4049=== Prepare plugin code ===
    4150==== Import i18n/l10n helper programs ====
    42 Pick a reasonably unique name for the domain, e.g. ** 'foo' **
     51Pick a reasonably unique name for the domain, e.g. ** 'foo' ** (if your plugin is named 'foo', that is).
    4352
    4453This will be the basename for the various translation catalog files
    45 (e.g. `<path>/locale/fr/LC_MESSAGES/foo.po` for the French catalog).
    46 
    47 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`.
     54(e.g. `foo/locale/fr/LC_MESSAGES/foo.po` for the French catalog).
     55
     56At 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`.
    4857
    4958This helper function should be called at module load time, like this:
     
    6069 - `'tgettext'`, `'tag_'`: same as `'_'` but for Markup
    6170 - `'tngettext'`, `'tagn_'`: same as `'ngettext'` but for Markup
    62  - `'gettext'`: translate only, don't extract
    63  - `'N_'`: extract only, don't translate
     71 - `'gettext'`: translate //only//, don't extract
     72 - `'N_'`: extract //only//, don't translate
    6473 - `'add_domain'`: register the catalog file for the bound domain
     74
     75Note: `N_` and `gettext()` are usually used in tandem. For example, when you have a global dict containing strings that need to extracted, you want to mark those strings for extraction but you don't want to put their //translation// in the dict: use `N_("the string")`; when you later use that dict and want to retrieve the translation for the string corresponding to some key, you don't want to mark anything here: use `gettext(mydict.get(key))`.
    6576
    6677To inform Trac about where the plugin's message catalogs can be found, you'll have to call the `add_domain` function obtained via `domain_functions` as shown above. One place to do this is in the `__init__` function of your plugin's main component, like this:
     
    7283        add_domain(self.env.path, locale_dir)
    7384}}}
    74 assuming that folder `locale` will reside in the same folder as the file containing the code above, referred to as `<path>` (as observable inside the Python egg after packaging).
    75 
    76 The i18n/l10n helper programs are available inside the plugin now, but if the plugin code contains several python script files and you encounter text for translation in one of them too, you need to import the functions from the main script, say its name is `api.py`, there:
     85assuming that folder `locale` will reside in the same folder as the file containing the code above, referred to as `<path>` below (as can be observed inside the Python egg after packaging).
     86
     87The i18n/l10n helper functions are available inside the plugin now, but if the plugin code contains several python script files and you encounter text for translation in one of them too, you need to import the functions from the main script, say its name is `api.py`, there:
    7788{{{#!python
    7889from api import _, tag_, N_
    7990}}}
    8091
    81 ==== Preset configuration for i18n/l10n helper programs ====
     92==== Preset configuration for Babel commands ====
    8293Add some lines to `setup.cfg` or, if it doesn't exist by now, create it with the following content:
    8394{{{#!ini
     
    110121Replace `<path>` as appropriate (i.e. the relative path to the folder containing the `locale` directory, for example `mytracplugin`).
    111122
    112 This will tell the i18n/l10n helper programs where to look for and store message catalog files.
     123This will tell Babel where to look for and store message catalog files.
    113124
    114125
     
    185196}}}
    186197
     198
     199
    187200==== Text extraction from Javascript code ==== #Javascript
    188201