Edgewall Software

Changes between Version 5 and Version 6 of CookBook/PluginL10N


Ignore:
Timestamp:
May 8, 2010, 11:08:59 PM (14 years ago)
Author:
hasienda <hoff.st@…>
Comment:

added more content for a step revision looking quite complete

Legend:

Unmodified
Added
Removed
Modified
  • CookBook/PluginL10N

    v5 v6  
    11= Adding i18n/l10n to Trac plugins (Trac >= 0.12) =
    2 == Motivation ==
    3 [FIXME: content needed, especially address the intended audience (plugin maintainers and developers in general) here - who should read on (and who shouldn't care at all)]
     2== Intro and Motivation ==
     3Are 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].
     4
     5Professional 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.
     6
     7If you want to learn about translation for a plugin, that as you know already provides one/several message catalog/s, section '[#Dotranslatorswork Do translators work]' and following parts are for you.
     8
     9Ultimately, all plugin maintainers and developers in general, that are facing requests and willing to take care for growing demand of their plugin speaking same (foreign) language(s) as Trac >= 0.12 should just read on.
    410
    511== i18n, l10n, ... help! ==
    6 For professionals: please skip this, since there can't be any news for you.
    7 
    812In 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.^1^
    913
    1014'''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.^1, 2^
    1115
    12 == Background and basics of i18n/l10n support for plugin ==
    13 The evolution of native i18n/l10n support for Trac plugins is documented in [comment:11:ticket:7497 ticket 7497]. The final implementation as documented there in [comment:12:ticket:7497 comment 12] was introduced to Trac trunk in [changeset: changeset r7705] and finally done with [changeset: changeset r7714]. Adding the needed i18n/l10n helper functions is as easy as adding
     16== Background and concept of i18n/l10n support for Trac plugins ==
     17It begun with adding Babel to Trac. Some plugin maintainers created their on translation module inside each plugin separately. This growing amount of code redundancy and possibility of error within imperfect copies of the translation module was certainly not a desirable situation. Trac core maintainers took responsibility with adding functions dedicated to i18n/l10n support for Trac plugins.
     18
     19The evolution of this functions is documented in [comment:11:ticket:7497 ticket 7497]. The final implementation as documented there in [comment:12:ticket:7497 comment 12] was introduced to Trac trunk in [changeset: changeset r7705] and finally done with [changeset: changeset r7714].
     20
     21Now 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 message texts 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 to Trac's main message catalog, if needed.
     22
     23Both searches take the locale setting as a second request argument. Valid settings are a combination of language and country code often extended by the character encoding used, i.e. β€˜de_DE.UTF-8’. The encoding might of special relevance for languages that had an older encoding per default that was not sufficient for all common chars needed there. '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.^3^
     24
     25First 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.
     26
     27== Required workflow ==
     28a walk-through
     29
     30=== Prepare plugin code ===
     31==== Import i18n/l10n helper programs ====
     32For a '''fictional plugin 'foo' ''' just add
    1433{{{
    1534from trac.util.translation import domain_functions
    1635
    17 _, tag_, N_, add_domain = domain_functions('tracmercurial',
     36_, tag_, N_, add_domain = domain_functions('foo',
    1837    '_', 'tag_', 'N_', 'add_domain')
    1938}}}
    20 at the beginning of the main python script file of an existing or new plugin. To bring in the compiled message catalog for actually using the translated texts one will have to extend the `__init__` function of plugins main class too. For a fictional plugin 'foo' this would be done like this:
     39at the beginning of the main python script file.
     40
     41To bring in the compiled message catalog for actually using plugin's message catalogs you'll have to extend the `__init__` function of plugins main class as well. This could be done like this:
    2142{{{
    2243    def __init__(self):
     
    2748        add_domain(self.env.path, locale_dir)
    2849}}}
    29 assuming that folder `locale` will reside in `./foo/locale/` within the directory structure (of the Python egg). If you didn't have it in the source by now, add a `import pkg_resources` to the beginning of the plugin script as well or the line `locale_dir = ...` will throw an '!ImportError'.
     50assuming that folder `locale` will reside in `./foo/locale/` within the directory structure (as observable inside the Python egg after packaging).
     51
     52If you didn't have it in the source by now, add a
     53{{{
     54import pkg_resources
     55}}}
     56at the beginning of the plugin script as well or the line `locale_dir = ...` (see previous code snippet) will throw an '!ImportError'.
    3057
    3158[FIXME: explain what _version and ui are used for or leave them out, if unnecessary here]
    3259
    33 == Required minimal workflow ==
    34 a walk-through
    35 
    36 === Preparing the plugin code ===
    37 ==== Provide translation configuration ====
     60==== Preset configuration for i18n/l10n helper programs ====
    3861Add some lines to `setup.cfg` or, if it doesn't exist by now, create it with the following content:
    3962{{{
     
    5982domain = foo
    6083}}}
    61 This will tell the translation helper programs where to look for and store message catalog files. Since this is a per plugin translation you need to change wherever it reads `foo` in the example to the existing plugin directory that is a short form of plugin's name in most cases.
     84This will tell the i18n/l10n helper programs where to look for and store message catalog files. Since we prepare for a domain dedicted to the plugin you need to change the configuration to the existing plugin directory (that is a short all-lowercase form of plugin's name in most cases) wherever it reads `foo` in the example.
    6285
    6386In the `extract_messages` section there are other lines you may like to change. Starting with `add_comments` there is the place to announce yourself as the translator by adding your name after the default `TRANSLATOR:` label. To allow for direct feedback regarding your translation add a valid e-mail address or a mailing list dedicated to translation issues to `msgid_bugs_address`.
     
    7497afterwards.
    7598
    76 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 l10n 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.
     99This 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.
    77100
    78101Message extraction for Genshi templates should be done auto-magically. However there is a markup available, to ensure extraction even from less common tags.
     
    80103[FIXME: add details about msg extraction from templates and other files]
    81104
    82 ==== Register new files for packaging ====
     105==== Register message catalog files for packaging ====
    83106To include the translated messages into the packaged plugin you need to add the path to the catalog files to `package_data` in `setup.py`.
    84107
    85 ==== New plugin version ====
     108==== Announce new plugin version ====
    86109The plugin will not work with any Trac version before 0.12dev, since import of the translation helper functions introduced for 0.12 will fail. It is possible to wrap the import with a `try:` and define dummy functions in a corresponding `except ImportError:` to allow the plugin to work with older versions of Trac, but there might already be a different version for 0.11 and 0.12, so this is not required in most cases.
    87110
    88111All 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`.
    89112
     113=== Do translators work ===
     114General advice from [wiki:TracL10N TracL10N] on making good translation for Trac in general applies here too. For those who read previous parts, you do notice that we switch from talking about i18n to 'l10n' now, don't you? 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.
    90115
    91 === Translation work ===
    92 General advice from [wiki:TracL10N TracL10N] on making good translation for Trac in general applies here too.
     116Switch to root directory of plugin's source, i.e.:
     117{{{
     118cd /usr/src/trac_plugins/foo
     119}}}
    93120
    94 === Compilation and testing ===
     121Extract the messages that where marked for translation before, or on case of Genshi templates are exposed by other means:
     122{{{
     123python ./setup.py extract_messages
     124}}}
     125The 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.
     126
     127If you attempt to do improvements on existing message catalogs you'll update the one for your desired language:
     128{{{
     129python ./setup.py update_catalog -l de_DE
     130}}}
     131If 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.
     132
     133But 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:
     134{{{
     135python ./setup.py init_catalog -l de_DE
     136}}}
     137As 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.
     138
     139Now 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.^4^
     140
     141=== Compile and use it ===
    95142Compile the `messages.po` catalog file with your translations into a machine readable `messages.mo` file.
    96143{{{
     
    99146The argument `-f` is needed to include even the msgid's marked 'fuzzy'. If you have prepared only one translated catalog the final language selection argument `-l` and identifier string are superfluous. But as soon as there are several other translations that you don't care, it will help to select just your work for compilation.
    100147
    101 Now finish your work by packaging the plugin. Make the python egg as usual:
     148Now you've used all four configuration sections in `setup.cfg`, that are dedicated to i18n/l10n helper programs. You could finish your work by packaging the plugin.
     149
     150Make the python egg as usual:
    102151{{{
    103152python ./setup.py bdist_egg
    104153}}}
     154Install the new egg and restart your web-server after you made sure to purge any former version of that plugin (without your latest work).
    105155
    106156=== Advanced stuff ===
     
    110160
    111161^1^ http://en.wikipedia.org/wiki/Internationalization_and_localization - Internationalization and localization[[BR]]
    112 ^2^ http://en.wikipedia.org/w/index.php?title=Multilingualism&section=18 - Multilingualism in computing
     162^2^ http://en.wikipedia.org/w/index.php?title=Multilingualism&section=18 - Multilingualism in computing[[BR]]
     163^3^ http://www.gnu.org/software/gettext/manual/gettext.html#Locale-Names - GNU 'gettext' utilities: Locale Names[[BR]]
     164^4^ http://www.gnu.org/software/gettext/manual/gettext.html#Editing - GNU 'gettext' utilities: Editing PO Files