Edgewall Software

Version 5 (modified by hasienda <hoff.st@…>, 14 years ago) ( diff )

added more content going towards desired amount of information

Adding i18n/l10n to Trac plugins (Trac ≥ 0.12)

Motivation

[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)]

i18n, l10n, … help!

For professionals: please skip this, since there can't be any news for you.

In short i18n stands for internationalization (count 18 more chars between i and n) and is defined as software design for programs with translation support. localisation 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

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

Background and basics of i18n/l10n support for plugin

The evolution of native i18n/l10n support for Trac plugins is documented in ticket 7497. The final implementation as documented there in comment 12 was introduced to Trac trunk in changeset r7705 and finally done with changeset r7714. Adding the needed i18n/l10n helper functions is as easy as adding

from trac.util.translation import domain_functions

_, tag_, N_, add_domain = domain_functions('tracmercurial', 
    '_', 'tag_', 'N_', 'add_domain')

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:

    def __init__(self):
        self._version = None
        self.ui = None
        # bind the 'foo' catalog to the specified locale directory
        locale_dir = pkg_resources.resource_filename(__name__, 'locale')
        add_domain(self.env.path, locale_dir)

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'.

[FIXME: explain what _version and ui are used for or leave them out, if unnecessary here]

Required minimal workflow

a walk-through

Preparing the plugin code

Provide translation configuration

Add some lines to setup.cfg or, if it doesn't exist by now, create it with the following content:

[extract_messages]
add_comments = TRANSLATOR:
msgid_bugs_address =
output_file = foo/locale/messages.pot
keywords = _ ngettext:1,2 N_ tag_
width = 72

[init_catalog]
input_file = foo/locale/messages.pot
output_dir = foo/locale
domain = foo

[compile_catalog]
directory = foo/locale
domain = foo

[update_catalog]
input_file = foo/locale/messages.pot
output_dir = foo/locale
domain = foo

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.

In 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.

Mark text for extraction

In python scripts you'll have to wrap text with the translation function _() to get it handled by translation helper programs. Some code, that was

    msg = 'This is a msg text.'

before, will read like

    msg = _('This is a msg text.')

afterwards.

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.

Message extraction for Genshi templates should be done auto-magically. However there is a markup available, to ensure extraction even from less common tags.

[FIXME: add details about msg extraction from templates and other files]

Register new files for packaging

To include the translated messages into the packaged plugin you need to add the path to the catalog files to package_data in setup.py.

New plugin version

The 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.

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.

Translation work

General advice from TracL10N on making good translation for Trac in general applies here too.

Compilation and testing

Compile the messages.po catalog file with your translations into a machine readable messages.mo file.

python ./setup.py compile_catalog -f -l de_DE

The 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.

Now finish your work by packaging the plugin. Make the python egg as usual:

python ./setup.py bdist_egg

Advanced stuff

Finally 'true' l10n

Related resources

1 http://en.wikipedia.org/wiki/Internationalization_and_localization - Internationalization and localization
2 http://en.wikipedia.org/w/index.php?title=Multilingualism&section=18 - Multilingualism in computing

Attachments (2)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.