[[PageOutline(2-4)]] = Localization (L10N) of Trac = Trac uses [http://babel.edgewall.org/ Babel] for localization. Starting with version [milestone:0.12], Trac contains the i18n framework and L10N files. The ''Preferences / Language'' panel can be used to select the language from the list of available translations, otherwise the web browsers' language preferences will be honored, if possible. If you're only interested in using a localized version of Trac, then nothing more than a standard TracInstall is needed, provided you installed Babel before. The more complete installation instructions below are aimed at developers and translators, who want to contribute translations for Trac 0.12.x and 0.13dev. == Installation == === Getting the Tool Chain === ==== Python Tools ==== #Pythontools 1. Install [http://babel.edgewall.org/ Babel] version [Babel:wiki:Download#LatestRelease:0.9.5 0.9.5] [[br]] Read the [http://babel.edgewall.org/wiki/SubversionCheckout detailed installation instructions] for Babel if you want to build it from source, but you should also be able to install it with a simple `easy_install` or by using one of the packages in the Download page linked above. 1. Likewise, install [http://genshi.edgewall.org/ Genshi] version [Genshi:wiki:Download#LatestRelease:0.6 0.6], using `easy_install` or by using one of the packages available for download. 1. Get a svn checkout from the Trac release line you want to translate: - [[span(style=color: grey,[source:trunk] for 0.13dev, e.g. `svn checkout http://svn.edgewall.org/repos/trac/trunk trac-0.13dev`)]] (not yet opened for translations) - [source:branches/0.12-stable] for 0.12.x, e.g. `svn checkout http://svn.edgewall.org/repos/trac/trunk trac-0.12.x` (opened for translations) All the discussion and examples below refer to `trunk`, but everything will work out exactly the same way for the `0.12-stable` branch or any other more recent branch. === Translation Workflow === It is very important to not waste time translating an out-of-date catalog. In the repository, only the catalog **templates** ([source:trunk/trac/locale/messages.pot trac/locale/messages.pot] and [source:trunk/trac/locale/messages-js.pot trac/locale/messages-js.pot]) get updated regularly by Trac developers. If we would do so for the all the `.po` catalogs, this would generate lots of big and noisy changesets. So we leave that task up to the translators, which is something they should do before starting the actual translation work. There are several different procedures to get the catalogs in sync with the templates, choose the one you're more comfortable with. ==== Using Python tools ==== 1. Run `python setup.py extract_messages`. [[br]] This will generate the catalog template file: source:trunk/trac/locale/messages.pot [[br]] You should never have to edit that file manually. [[br]] '''Note:''' ''this step is for committers only.'' If you're not a Trac svn committer, you have to use the available messages.pot, or bug us to update that file (though we do it on a regular basis). 1. Run `python setup.py update_catalog`. [[br]] This will regenerate the various string catalogs (`trac/locale/*_*/LC_MESSAGES/messages.po`), preserving the previously translated strings and merging the new translations found in the messages.pot file. Translations that are no longer needed are commented out (`#~`) and moved at the bottom of the .po file. [[br]] Usually, you will be interested in working on one locale only, so you can pass the `-l` option to specify which catalog should be updated. For example: [[br]] `python setup.py update_catalog -l nl_NL`. [[br]] 1. Now, open your favorite editor and add or modify translations for the extracted strings in the messages.po file for your language. [[comment(See examples below.)]] 1. Once you're done, you should test your translations: 1. Run `python setup.py update_catalog -l` again, for normalizing the manual edits. For example: [[br]] `python setup.py update_catalog -l nl_NL`. [[br]] This step is also quite useful for spotting the possible mistakes, like accidental changes of `msgid` strings. Look for lines beginning with `#~`, they are indicative of such errors. Note that the comments corresponding to translations which are no longer present in the messages.pot file (as discussed in step 2. above) have been discarded by this second `update_catalog` run. 2. Run `python setup.py compile_catalog -f`. [[br]] This will generate one compiled catalog (`message.mo` file) for each (or each specified) messages.po file. You don't need to do anything with those files, they'll get installed automatically. [[br]] You will most probably need to use the force option (`-f`) in order to compile catalogs marked "fuzzy". It is fine to have ''fuzzy'' translations but try to never contribute a translation still containing those markers, as they mean that a given translation has not been reviewed by a human but were machine generated. Fuzzy translations should be either cleared when they are wrong, or validated when they are correct, by removing the ''fuzzy'' tag or even the whole line iff ''fuzzy'' was the only keyword on that line [[br]] You can also use the `-l` option here to specify the locale for which the messages.po has to be compiled. 3. Run `python setup.py install` for installing Trac and locale data. Alternatively you can use `python setup.py develop` once for all, if you want to be able to run Trac from your working copy. Note that you could stop at step 3, but then you risk to submit incomplete or, worse, buggy messages.po files. Going through the 4^th^ step will ensure a better quality standard. Finally, seeing your translations "live" in Trac is not only a great way to realize if the translation still makes sense once put in context, but also quite gratifying when you see that you picked the correct wording ;-) ==== Using `msgfmt` tools ==== Although Babel is Python specific and is the tool which is advised to use, the traditional msgfmt toolset can also be used. In particular, the following command is quite useful: {{{ $ msgfmt --statistics --check trac/locale/pl/LC_MESSAGES/messages.po 979 translated messages, 85 fuzzy translations, 32 untranslated messages. }}} All is well, except for some fuzzy and missing translations ;-) {{{ $ msgfmt --statistics --check trac/locale/zh_CN/LC_MESSAGES/messages.po trac/locale/zh_CN/LC_MESSAGES/messages.po:2155: a format specification for argument 'unit', as in 'msgstr[0]', doesn't exist in 'msgid' trac/locale/zh_CN/LC_MESSAGES/messages.po:2162: a format specification for argument 'unit', as in 'msgstr[0]', doesn't exist in 'msgid' trac/locale/zh_CN/LC_MESSAGES/messages.po:2169: a format specification for argument 'unit', as in 'msgstr[0]', doesn't exist in 'msgid' msgfmt: found 3 fatal errors 1058 translated messages. }}} Some errors were detected with format specifications. This is a useful ''additional'' check, as `python setup.py compile_catalog -l zh_CN` wouldn't have detected that issue. ==== Using Transifex ==== The web-based collaborative site for translations Transifex has a [http://www.transifex.net/projects/p/trac project page for Trac] and two components: - [http://www.transifex.net/projects/p/trac/c/default/ Default] (for trunk, i.e. 0.13dev) - [http://www.transifex.net/projects/p/trac/c/012-stable 0.12.x] From there, it is easy to download always up to date `messages.po` files and start translating right away. Your translation can then be uploaded and will for now be sent back to us by mail, but in the future they will be directly committed in a dedicated i18n repository clone of Trac. It is important however to keep the messages.po files free from fatal errors as reported by `msgfmt --check`, as this might confuse Transifex and prevent it to report correct statistics or merge the messages.pot. ==== The easy way using `make` and l10n targets ==== You'll find a Makefile in the toplevel directory of the Trac source. If you're lucky enough to have the GNU `make` tool, you can benefit from some automation for these translation tasks. {{{ $ make help ... ---------------- L10N tasks init-xy create catalogs for given xy locale extraction regenerate the catalog templates update update all the catalog files from the templates update-xy update the catalogs for the xy locale only compile compile all the catalog files compile-xy compile the catalogs for the xy locale only check verify all the catalog files check-xy verify the catalogs for the xy locale only stats detailed translation statistics for all catalogs stats-pot total messages in the catalog templates stats-xy translated, fuzzy, untranslated for the xy locale only summary display percent translated for all catalogs summary-xy display percent translated for the xy locale only (suitable for a commit message) diff show relevant changes after an update for all catalogs diff-xy show relevant changes after an update for the xy locale [locale=...] variable for selecting a set of locales }}} Example usage: {{{ $ make stats-pot update check compile stats locale='fr de' translation statistics for messages.pot: stats-pot: 0 translated messages, 1096 untranslated messages. python setup.py update_catalog -l fr running update_catalog updating catalog 'trac/locale\\fr\\LC_MESSAGES\\messages.po' based on 'trac/locale/messages.pot' python setup.py update_catalog -l de running update_catalog updating catalog 'trac/locale\\de\\LC_MESSAGES\\messages.po' based on 'trac/locale/messages.pot' checking catalogs for fr de... check-fr: OK check-de: OK All catalogs checked are OK python setup.py compile_catalog -l fr running compile_catalog compiling catalog 'trac/locale\\fr\\LC_MESSAGES\\messages.po' to 'trac/locale\\fr\\LC_MESSAGES\\messages.mo' python setup.py compile_catalog -l de running compile_catalog compiling catalog 'trac/locale\\de\\LC_MESSAGES\\messages.po' to 'trac/locale\\de\\LC_MESSAGES\\messages.mo' translation statistics for fr de... stats-fr: 1096 translated messages. stats-de: 932 translated messages, 164 untranslated messages. }}} == Contributing == The various catalog files (`messages.po`) can be found in the repository under source:trunk/trac/locale. The `messages.pot` is the template file from which the various locale-specific files get created. === Adding translations for a new language #add-a-language If you need to create a catalog for a new locale (e.g. `et`), do: {{{ $ ./setup.py init_catalog -l et }}} Use ISO:639-1 language codes, only use national variant when there's a real need. We recently added support for translating messages in Javascript code. Those messages are collected in a separate catalog, so you need a separate command: {{{ $ ./setup.py init_catalog_js -l et }}} Alternatively, you can do `make init-`, this will create the missing catalog(s) if needed. Then proceed through the steps listed [#Pythontools above] starting at step 2. === Statistics === Those statistics reflect the coverage status of the `.po` files (catalogs) compared to the `.pot` files (catalog templates). The template catalogs themselves **might** be out-of-date compared to the source itself, though we regularly perform an automatic extraction. **0.12.x** [[Image(http://www.transifex.net/projects/p/trac/c/012-stable/chart/image_png, align=middle, title=go to the Trac page for 0.12-stable on Transifex.net, link=http://www.transifex.net/projects/p/trac/c/012-stable)]] **0.13.x** [[Image(http://www.transifex.net/projects/p/trac/c/default/chart/image_png, align=middle, title=go to the Trac page for 0.13dev on Transifex.net, link=http://www.transifex.net/projects/p/trac/c/default)]] Kindly provided by [[Image(http://sw.transifex.net/2/static/charts/images/tx-logo-micro.png, link=http://www.transifex.net/, title=the open translation platform, valign=bottom)]] Note that if the statistics for a language suddenly drop to 0%, this is indicative of the presence of ''fatal errors'' (as reported by `msgfmt --check`) in the corresponding `messages.po` file. If you want to generate those statistics by yourself, do `make summary`. Be aware that those statistics can only be accurate if the catalogs are actually up to date with respect to the catalog template, so you should do a `make update` before. As this is quite expensive, you may want to do only `make update summary locale="..."` for the locales you're interested in. === Editing Guidelines === There are tons of good guidelines on the web about how to write good translations. See for example: [http://techbase.kde.org/Localization/Concepts/PO_Odyssey PO_Odyssey] and [http://l10n.kde.org/docs/translation-howto/gui-step-by-step.html step-by-step], and other resources from http://l10n.kde.org. ''(The references also applies to translators using Windows, though the set of translation editors seems to be narrower. However, see [http://www.poedit.net/ Poedit] for a multiplatform dedicated PO editor.)'' In short, a translation unit is typically something like: {{{ #: trac/ticket/templates/milestone_delete.html:41 #: trac/ticket/templates/milestone_view.html:105 msgid "Delete milestone" msgstr "Supprimer le jalon" }}} - the "#:" lines are comments indicating the locations of the message in the source. - `msgid "..."` is the message itself, this should never be changed - `msgstr` is your translation; you should keep there the same structure, as in the msgid. In particular the presence of "\n" at the beginning or the end of the msgid should be mirrored in msgstr - right after an automatic catalog update you'll see lots of: - `# fuzzy` comments: this identifies translations that are only approximated (the msgid has changed) - `msgstr ""` translations: those are newly added translations There are a few more complex situations. Here's a message containing parameters: {{{ #: trac/ticket/templates/milestone_view.html:26 #, python-format msgid "" "Completed %(duration)s ago\n" " (%(date)s)" msgstr "" "Atteint il y a %(duration)s\n" " (%(date)s)" }}} Note that **you need to keep the same set of parameters in `msgid` and `msgstr`, both for the name and the keyword** (i.e. don't replace a `%(id)s` with a `%(id)d`), although you can reorder them as needed. Another example, here's how a structured message looks like: {{{ #: trac/ticket/templates/milestone_delete.html:45 msgid "" "[1:Note:] See\n" " [2:TracRoadmap] for help on using\n" " the roadmap." msgstr "" "[1:Note :] consultez [2:TracRoadmap] pour obtenir de l'aide sur " "l'utilisation de la feuille de route." }}} The `[1: ...]` groups are used when the message contains markup (this is Genshi specific). It is important to keep the same semantic structure. As with message parameters, the groups can be reordered but **you should have the same groups in `msgid` and `msgstr`** (see for example r9553). Of course, you can have messages that combine markup groups and parameters names: {{{ #: trac/ticket/templates/milestone_view.html:32 #, python-format msgid "" "[1:%(duration)s late]\n" " (%(date)s)" msgstr "" "[1:%(duration)s en retard]\n" " (%(date)s)" }}} Finally, there's the translation of sentences which have a singular and a plural form: {{{ #: trac/ticket/templates/query.html:29 #: trac/ticket/templates/query_results.html:30 #: trac/ticket/templates/report_view.html:94 #, python-format msgid "%(num)s match" msgid_plural "%(num)s matches" msgstr[0] "%(num)s résultat" msgstr[1] "%(num)s résultats" }}} - when such a message is newly added, you'll have [[br]] `#, fuzzy, python-format` as the first comment. Be careful to remove only the `fuzzy, ` part, but not the `python-format` keyword. - some languages (zh, ko, ja) don't have plural forms (plural == 1), so you only have to put a `msgstr[0]` line. - some other languages have more than 2 plural forms, so you need as many `msgstr[]` as needed. === Terms (Definitions) === #term-definitions Consistent and careful translation of terms like [wiki:TracTimeline timeline], [wiki:TracTickets ticket], [wiki:TracReports report] is very important. These terms are used everywhere and must be easy to remember and comfortable to use. The way to make up good translations of important terms is to discuss them before using everywhere. The easiest way to accomplish it is to set up wiki pages for different languages. Look also at the various term definition pages: [[TitleIndex(TracTerms,hideprefix,format=compact)]]. === Translation Coordination === #Translationcoordination For the various languages there are already various tickets logged in which the work on them is tracked: [[TicketQuery(keywords~=l10n,status!=closed,order=summary)]] This is a way by which translators for the same language can coordinate their work. It is also important to subscribe to the Trac-dev MailingList, in order to be notified of the release schedule, of the "string freeze" periods shortly preceding a release. During a string freeze we don't add new messages or modify existing ones, so this is the ideal period for finalizing a translation. === For Committers If you have commit access to the translation catalogs in the SubversionRepository, then you should be careful to commit only error-less updates. Translations can be fuzzy or missing, that's not a problem, but the errors spotted by `make check compile locale=` should really be fixed before committing. Please also take care of [n:...] nestings, as those mistakes are **not** detected by the above automated checks and are quite deadly (#9171). The commit message should have the following format: {{{ l10n/: (%) }}} The percentage being the ratio of: {{{#!table class="" || # translated messages from `make stats-`''``'' || {{{#!td style="border-top: 1px solid; text-align: center; margin-top: 0px" }}} |---------------------------------------------------- || # untranslated messages from `make stats-pot` || }}} Note that you can now easily get a template for the above by issuing the following command: \\ `make summary-`''`yourlocale`'' When you're adding a new catalog (`make init-...`), be sure to set "native" line-endings before committing (`svn pset svn:eol-style native trac/locale/.../LC_MESSAGES/....po`). === For Developers Here's a condensed list of a few things useful to know about i18n support when coding: title attributes:: those attributes are collected automatically (i.e. no need for explicit `${_('...')}` expressions), but only when the message doesn't contain an expression. In the latter case, they need an explicit `${_('... %(param)s ...', param=...)}` wrapping typographical translation:: some languages, like French, have special typographical rules (non breaking space before ":" or ";"), so don't consider that those signs can be left outside of translation scope when part of the text (e.g. `_(";")` when ";" is used as a separator, `_("%(field)s:")`) messages consolidation:: when adding new messages, always look if there's not already a very close message, and then reuse it (e.g. we now have 7 occurrences of ''"Repository '%(repo)s' not found"'', previously we had 7 variants of that message) Tips for translating plugins can be found in [[CookBook/PluginL10N]]. === Open Issues === #Openissues - ''' help us spot the MissingTranslations! ''' - translation of wiki pages (#1513) and help pages (TracDev/Proposals/NewHelp) [[TicketQuery(keywords~=i18n,status!=closed)]] ---- See also: [[TitleIndex(TracL10N/)]]