Localization (L10N) of Trac
Trac uses Babel for localization. Starting with version 0.12, Trac contains the i18n framework and L10N message catalog files.
Trac users can select their localisation from the Preferences / Language panel. Otherwise the web browsers' language preferences will be honored, if possible.
If you are only interested in using a localized version of Trac, then only a standard TracInstall is needed, provided you installed Babel before.
The installation instructions below are aimed at developers and translators, who want to contribute translations for Trac 0.12.x and beyond.
This page describes the technical details of translating the Trac web application. There is also a parallel effort to translate the TracGuide, see TracGuideTranslations. It has much more "text" to translate, but there are also fewer technical constraints, so if you're intimidated by what follows but nevertheless would like to contribute back to Trac by providing your language expertise, here is a nice alternative!
Installation
Getting the Tool Chain
Python Tools
- Install Babel version [Babel:wiki:Download#LatestRelease:0.9.6 0.9.6] or newer.
Read the detailed installation instructions for Babel if you want to build it from source. Alternatively you can install it witheasy_install
or by using one of the packages in the Download page linked above. - Likewise, install Genshi version 0.7,
using
pip install
or by using one of the packages available for download. - Get a source code checkout for the Trac release line you want to translate:
- trunk for 1.3.x,
e.g.svn checkout https://svn.edgewall.org/repos/trac/trunk trac-1.3dev
- branches/1.4-stable for 1.4.x,
e.g.svn checkout https://svn.edgewall.org/repos/trac/branches/1.4-stable trac-1.4.x
- branches/1.2-stable for 1.2.x,
e.g.svn checkout https://svn.edgewall.org/repos/trac/branches/1.2-stable trac-1.2.x
- branches/1.0-stable for 1.0.x,
e.g.svn checkout https://svn.edgewall.org/repos/trac/branches/1.0-stable trac-1.0.x
- branches/0.12-stable for 0.12.x,
e.g.svn checkout https://svn.edgewall.org/repos/trac/branches/0.12-stable trac-0.12.x
- trunk for 1.3.x,
All the discussion and examples below refer to trunk
, but everything works the same way for the 0.12-stable
branch or any other more recent branch.
Translation Workflow
With new Trac versions being released, there is a real chance the catalog becomes out of date or is incomplete. In the repository, only the catalog templates (trac/locale/messages.pot and 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 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 or before integrating updates coming from contributors when they're acting as translation team coordinators.
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
- Run
python setup.py extract_messages
.
This will generate the catalog template file: source:trunk/trac/locale/messages.pot
You should never have to edit that file manually.
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. - Run
python setup.py update_catalog
.
This will regenerate the 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 (be careful, they will be discarded by the next update, so if they contain valuable content, save it somewhere).
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:
python setup.py update_catalog -l nl
.
- Open your favorite editor and add or modify translations for the extracted strings in the messages.po file for your language.
- Once you're done, validate your translations:
- Run
python setup.py update_catalog -l
again to normalize the manual edits. For example:
python setup.py update_catalog -l nl
.
This step is also useful for spotting mistakes, like accidental changes ofmsgid
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 secondupdate_catalog
run. - Run
python setup.py check_catalog -f
.
This will search the catalog for common mistakes, like placeholder mismatches, invalid embedding in Genshi's structure messages, etc. - Run
python setup.py compile_catalog -f
.
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.
You will likely need to use the force option (-f
) to compile catalogs marked "fuzzy". It is fine to have fuzzy translations but try not to 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 if fuzzy was the only keyword on that line.
You can also use the-l
option here to specify the locale for which the messages.po has to be compiled. - Run
python setup.py install
for installing Trac and locale data. Alternatively you can usepython setup.py develop
once for all, if you want to run Trac from your working copy. Beware that there's no on the fly compilation of the catalogs, so any change will have to go through step 4.3 (compile) before being visible in your Trac instance.
- Run
If you skip the validation step, you risk submitting incomplete or buggy messages.po files, which reduces the user experience. Also, seeing your translations "live" in Trac is not only a great way to establish whether the translation still makes sense once put in context, but also quite gratifying when you see that you picked the correct wording.
Note that the above was written at a time there was only one catalog file, messages.pot. So to be complete, you would have to repeat this for all the catalogs:
messages-js
extract_messages_js
update_catalog_js
check_catalog_js
compile_catalog_js
generate_messages_js
(the last step specific to Javascript i18n)
tracini
extract_messages_tracini
update_catalog_tracini
check_catalog_tracini
compile_catalog_tracini
If this looks tedious and you're thinking about scripting all of this, don't worry, that has already been done, see #using-make below.
Using msgfmt
tools
Although Babel is Python specific and is the tool which is advised to use, the traditional msgfmt toolset (part of GNU gettext
) can also be used. However, msgfmt
is best used for auxiliary tasks like checking the catalogs and getting statistics, rather than updating or the compiling the catalogs:
$ msgfmt --statistics --check trac/locale/pl/LC_MESSAGES/messages.po 884 translated messages, 166 fuzzy translations, 80 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 neither python setup.py compile_catalog -l zh_CN
nor python setup.py check_catalog -l zh_CN
would have detected that issue. FIXME we detect mismatches, but not missing placeholders in msgstr
Using Transifex
The web-based collaborative site for translations Transifex has a project page for Trac and its convenient user interface makes it attractive for translators who don't want to edit the source code directly.
There, a number of "resources" (.pot
messages catalog files in our case) are grouped in several "releases". Currently the "releases" corresponds to our maintenance branches.
So here's an overview of the Transifex "Releases" and their corresponding "Resources":
- 0.12-stable contains:
- 1.0-stable contains:
- 1.2-stable contains:
- 1.4-stable contains:
- trunk contains:
Once you're member of a translation team, you can add translations directly online. But in the end, the changes will have to be committed back to the source tree, so you have to check beforehand with the translator coordinator of your language to see if (s)he is comfortable with getting updates from Transifex.
If you are a translation team coordinator, please refer to the Transifex sub page for more details.
The easy way using make
and l10n targets
You'll find a Makefile in the toplevel directory of the Trac source. If you 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 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 locale-specific files get created.
Adding translations for a new 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 is a real need.
We also 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-<locale>
, this will create the missing catalog(s) if needed.
Then proceed through the steps listed above starting at step 2.
How far would you like to go?
Though the amount of translatable text can be overwhelming, you don't have to translate everything to provide a good l10n experience to your users.
We can distinguish 3 levels:
- User-oriented translations: those are all contained in the
messages.po
andmessages-js.po
catalogs; if you reach 100% there you're already a hero (Hello Itamar!) - Administrator-oriented translations: since 1.0 we also introduced the possibility to translate the documentation information for the TracIni settings, in the
tracini.po
catalog; those are targeted at Trac administrators, so translating those could really be seen as an "bonus" level - TracGuideTranslations: there have already been attempts to translate the TracGuide, but this would be a huge effort; maybe in the future we could isolate a smaller subset of user oriented pages of the TracGuide
- Translating the API documentation (and why not also the source code?).
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.
messages.pot | messages-js.pot | tracini.pot | |
---|---|---|---|
0.12-stable | |||
1.0-stable | |||
1.2-stable | |||
1.4-stable | |||
trunk |
Charts kindly provided by , based on the content of the catalogs uploaded there, which should correspond to the content of the catalogs in the repository.
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 yourself, do make summary
. Those statistics are only accurate if the catalogs are actually up to date with respect to the catalog template, so do a make update
beforehand. As this is computationally expensive, you may want to make update summary locale="..."
for the locales you're interested in.
Editing Guidelines
There are many good guidelines on the web about how to write good translations. See for example: PO_Odyssey and step-by-step, and other resources from http://l10n.kde.org. The references also apply to translators using Windows, though the set of translation editors seems to be narrower. However, see Poedit for a multi-platform 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 is 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, ie don't replace a %(id)s
with a %(id)d
, although you can reorder them as needed.
Another example, here is 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). The check_catalog_*
commands (make check
) now look for such errors.
Of course, you can have messages that combine markup groups and parameter 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 is 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 will have
#, fuzzy, python-format
as the first comment. Be careful to remove only thefuzzy,
part, but not thepython-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)
Consistent and careful translation of terms like timeline, ticket, 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 this is to set up wiki pages for different languages.
Look also at the term definition pages: Am, Cy, Cz, De, El, Es, Fa, Fi, Fr, Gl, He, Hu, It, Ja, Ko, Lv, Nl, Pl, Pt, PtBr, Ru, Sv, Tr, ZhCn, ZhTw.
Translation Coordination
For the various languages there are already tickets logged in which the work on them is tracked:
- #1513
- Multi-language support in the wiki
- #11114
- Translation of Trac to Arabic/عربى [ar_EG]
- #7186
- Translation of Trac to Argentinian Spanish/español de Argentina [es_AR]
- #8991
- Translation of Trac to Armenian [hy_AM]
- #7353
- Translation of Trac to Catalan/Català [ca]
- #5472
- Translation of Trac to Chinese/中文 [zh_CN]
- #5592
- Translation of Trac to Czech/Česky [cs_CZ]
- #5473
- Translation of Trac to Dutch/Nederlands [nl_NL]
- #7417
- Translation of Trac to English [en_GB]
- #7578
- Translation of Trac to Estonian/eesti [et_EE]
- #6382
- Translation of Trac to Farsi/پارسی [fa_IR]
- #7178
- Translation of Trac to Finnish/suomi [fi_FI]
- #5474
- Translation of Trac to French/Français [fr_FR]
- #7918
- Translation of Trac to Galician/Galego [gl_ES]
- #5475
- Translation of Trac to German/Deutsch [de_DE]
- #5481
- Translation of Trac to Greek/Ελληνικά [el_GR]
- #9040
- Translation of Trac to Hebrew [he_HE]
- #5486
- Translation of Trac to Hungarian/Magyar [hu_HU]
- #5489
- Translation of Trac to Italian/Italiano [it_IT]
- #5471
- Translation of Trac to Japanese/日本語 [ja_JP]
- #5477
- Translation of Trac to Korean/한국어 [ko_KR]
- #7177
- Translation of Trac to Norwegian/bokmål [nb_NO]
- #5480
- Translation of Trac to Polish/Polski [pl_PL]
- #5476
- Translation of Trac to Portuguese/Português [pt_BR]
- #5470
- Translation of Trac to Portuguese/Português [pt_PT]
- #8008
- Translation of Trac to Romanian/Română [ro_RO]
- #5478
- Translation of Trac to Russian/Русский [ru_RU]
- #7487
- Translation of Trac to Slovene/Slovensko [sl_SI]
- #5469
- Translation of Trac to Spanish/Español [es_ES]
- #5487
- Translation of Trac to Swedish/Svenska [sv]
- #5482
- Translation of Trac to Traditional Chinese/正體中文 [zh_TW]
- #5488
- Translation of Trac to Turkish/Türkçe [tr_TR]
- #6629
- Translation of Trac to Vietnamese/Tiếng Việt [vi_VN]
- #9259
- Translations to Esperanto [eo]
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, 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=<yourlocale>
should really be fixed before committing. Please also take care of fixing any [n:...]
nesting errors reported by make check
, as those mistakes are quite deadly (#9171).
The commit message should have the following format:
<branch-name>/l10n/<yourlocale>: <some message> (<xy>%) <additional text if needed>
<branch-name>
can be omitted for trunk.
The percentage being the ratio of:
# translated messages from make stats- <yourlocale>
|
# 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
).
Now that all the maintenance branches have l10n support, special care should be taken when merging l10n changes from one branch to the next.
One problem to be expected with the merge is a high rate of conflicts due to line number differences. Even if the source text (the msgid
) are often unchanged, it's very likely that their position in the file will be different.
The trunk/contrib/l10n_revert_lineno_conflicts.py can be used to remove those spurious conflicts.
Example workflow:
1.0-stable$ make update 1.0-stable$ svn merge ^/branches/0.12-stable --accept postpone 1.0-stable$ $ svn st -q | sed -e 's,\\,/,g' | grep ^C | cut -c2- > conflicted.files 1.0-stable$ python contrib/l10n_revert_lineno_conflicts.py `cat conflicted.files` reverted 6 ignorable changes in trac/locale/fa/LC_MESSAGES/messages.po reverted 1 ignorable changes in trac/locale/nb/LC_MESSAGES/messages.po reverted 147 ignorable changes in trac/locale/nl/LC_MESSAGES/messages.po no ignorable changes in trac/locale/nl/LC_MESSAGES/messages-js.po [...]
At this point, one can inspect the remaining meaningful conflicts and resolve them manually.
Then:
1.0-stable$ make update
to remove the spurious line number difference, review and commit.
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, ie 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. use
_(";")
when ";" is used as a separator,_("%(field)s:")
instead of'%(field)s:'
) - messages consolidation
- when adding new messages, always look if there is not already a very close message, and then reuse it (e.g. we now have 7 occurrences of one single "Repository '%(repo)s' not found" message, previously we had 7 messages, each slightly different from the other!)
- non-translatable messages
- in Genshi templates, you can use
xml:lang="en"
to mark the text in the element as not to be extracted (r10265)
Tips for translating plugins can be found in CookBook/PluginL10N.
Open Issues
- help us spot the MissingTranslations in the source code
- some terms that should be translated are simply not present in the source code, but rather live in the TracIni or the database. Even for these nasty ones we have a plan: TracDev/Proposals/ConfigEnumTranslation
- translation of wiki pages (#1513) and help pages (TracDev/Proposals/NewHelp)
- #1513
- Multi-language support in the wiki
- #4636
- Component field sort order doesn't use locale
- #5441
- Timeline shows incomplete information about status changes for customized workflow
- #9472
- Workflow actions not translated
- #10027
- TicketQuery groups without translation
- #10339
- Created, Modified and Ticket columns are not translated in SQL reports
- #11536
- Wrong label on wiki diff view between initial version and version 1
- #12367
- Error message with untranslated text when removing no selected items
See also: