[[PageOutline(2-5,Contents,pullout)]] = Transifex and Trac Trac is available in a number of languages and the translations for the captions are performed in [https://www.transifex.com/ Transifex], which is a modern, open-source localization platform. It is a web system which automates the translation workflow for complex international projects. It is the alternative to the more laborious [[TracL10N]] process, involving svn, Babel, make, etc. {{{#!box info This page is targeted at translation team coordinators who need to synchronize the translation updates with the translation catalogs in the repository. If you just want to contribute translations by using Transifex, you can read the [#Translationteammember section for team members below] and then head directly on the [transifex: Transifex project]. }}} The key is to keep all sources in sync, if there is more than one: - a Transifex translator might work on an out-of-date catalog, redoing the work the translator coordinator committed directly in the catalog file in the repository - worse, pulling updates from Transifex and blindly committing them could lead to regressions - conversely, a translator coordinator could easily miss the changes done by another translator on Transifex - here as well there's a risk for him to overwrite changes made by Transifex contributors if he pushes changes carelessly In the following, a workflow which avoids those pitfalls and enables changes to flow in both directions is described. See also the related discussion in Trac-dev, [gdiscussion:trac-dev:8e4PFgkabmM Trac translations and Transifex]. == Roles Once you're familiar with Transifex (and they have some very good [http://help.transifex.com/ documentation] on their site), you can apply for membership to one translation team for a language, or create a new team if your language of choice isn't yet represented there. Be aware that a new translation for Trac usually involves a //lot// of work. === Translation team coordinator It's much better if the translation team coordinator on Transifex is also the committer responsible for that language in the Trac project itself. A committer is ultimately in charge of the synchronization work, pulling changes made by members of the translation team on Transifex and pushing back changes made directly to the repository. Here's an overview of the different teams and status: ||= Language name ||= Repository ||= Committer ||= Transifex ||= Coordinator || |----------------------------------------------------------------------------- || **.pot** files |||||||| |----------------------------------------------------------------------------- |||||||||||||||| \\//[=#group1 1)] The languages for which we have a committer and a team coordinator// || |----------------------------------------------------------------------------- ||=Chinese (China) || \ || [source:branches/1.4-stable/trac/locale/zh_CN zh_CN]|| zengjie || \ || [TransifexLang:zh_CN zh_CN]|| gnozil || ||=Chinese (Taiwan) || \ || [source:branches/1.4-stable/trac/locale/zh_TW zh_TW]|| zclin || \ || [TransifexLang:zh_TW zh_TW]|| zclin || ||=English (United Kingdom) || \ || [source:branches/1.4-stable/trac/locale/en_GB en_GB]|| mrelbe || \ || [TransifexLang:en_GB en_GB]|| mrelbe || ||=French || \ || [source:branches/1.4-stable/trac/locale/fr fr]|| cboos || \ || [TransifexLang:fr fr]|| cboos || ||=German || \ || [source:branches/1.4-stable/trac/locale/de de]|| fschwarz || \ || [TransifexLang:de de]|| fschwarz || ||=Dutch || \ || [source:branches/1.4-stable/trac/locale/nl nl]|| lholst || \ || [TransifexLang:nl nl]|| trbs || ||=Hebrew || \ || [source:branches/1.4-stable/trac/locale/he he]|| itamaro || \ || [TransifexLang:he he]|| Yaron || ||=Italian || \ || [source:branches/1.4-stable/trac/locale/it it]|| lgaifax || \ || [TransifexLang:it it]|| seccanj || ||=Japanese || \ || [source:branches/1.4-stable/trac/locale/ja ja]|| jomae || \ || [TransifexLang:ja ja]|| jun66j5 (same ;-) ) || ||=Portuguese (Brazil) || \ || [source:branches/1.4-stable/trac/locale/pt_BR pt_BR]|| lwatter || \ || [TransifexLang:pt_BR pt_BR]|| mfcn || ||=Spanish || \ || [source:branches/1.4-stable/trac/locale/es es]|| raragon || \ || [TransifexLang:es es]|| codeadict || ||=Spanish (Argentina) || \ || [source:branches/1.4-stable/trac/locale/es_AR es_AR]|| rmorales || \ || [TransifexLang:es_AR es_AR]|| cramm (same) || ||=Russian || \ || [source:branches/1.4-stable/trac/locale/ru ru]|| aprudnikov || \ || [TransifexLang:ru ru]|| bishop (same) || ||=Swedish || \ || [source:branches/1.4-stable/trac/locale/sv sv]|| mrelbe || \ || [TransifexLang:sv sv]|| mrelbe || |----------------------------------------------------------------------------- |||||||||||||||| \\//[=#group2 2)] The languages for which we have a committer but no team coordinator// || |----------------------------------------------------------------------------- ||=Armenian || \ || [source:branches/1.4-stable/trac/locale/hy hy]|| narine_martirosyan || \ || [TransifexLang:hy hy]|| || ||=Catalan || \ || [source:branches/1.4-stable/trac/locale/ca ca]|| jordi || \ || [TransifexLang:ca ca]|| || ||=Esperanto || \ || [source:branches/1.4-stable/trac/locale/eo eo]|| wdiestel || \ || [TransifexLang:eo eo]|| || ||=Hungarian || \ || [source:branches/1.4-stable/trac/locale/hu hu]|| nzoltan || \ || [TransifexLang:hu hu]|| || ||=Norwegian Bokmål || \ || [source:branches/1.4-stable/trac/locale/nb nb]|| osimons || \ || [TransifexLang:nb nb]|| || ||=Slovenian || \ || [source:branches/1.4-stable/trac/locale/sl sl]|| vezjakv || \ || [TransifexLang:sl sl]|| || ||=Turkish || \ || [source:branches/1.4-stable/trac/locale/tr tr]|| asurion || \ || [TransifexLang:tr tr]|| || |----------------------------------------------------------------------------- |||||||||||||||| \\//[=#group3 3)] The languages present in the repository and **no** committers but have a team coordinator// || |----------------------------------------------------------------------------- ||=Czech || \ || [source:branches/1.4-stable/trac/locale/cs cs]|| || \ || [TransifexLang:cs cs]|| Tsbook || ||=Danish || \ || [source:branches/1.4-stable/trac/locale/da da]|| || \ || [TransifexLang:da da]|| jenshyllegaard || ||=Estonian || \ || [source:branches/1.4-stable/trac/locale/et et]|| || \ || [TransifexLang:et et]|| lkraav || ||=Finnish || \ || [source:branches/1.4-stable/trac/locale/fi fi]|| || \ || [TransifexLang:fi fi]|| mikkorantalainen || ||=Greek || \ || [source:branches/1.4-stable/trac/locale/el el]|| || \ || [TransifexLang:el el]|| Evropi || ||=Polish || \ || [source:branches/1.4-stable/trac/locale/pl pl]|| || \ || [TransifexLang:pl pl]|| wiget || ||=Spanish (Mexico) || \ || [source:branches/1.4-stable/trac/locale/es_MX es_MX]|| || \ || [TransifexLang:es_MX es_MX]|| tzkmx || ||=Ukrainian || \ || [source:branches/1.4-stable/trac/locale/uk uk]|| || \ || [TransifexLang:uk uk]|| sgalashyn || ||=Korean || \ || [source:branches/1.4-stable/trac/locale/ko ko]|| || \ || [TransifexLang:ko ko]|| theYT || |----------------------------------------------------------------------------- |||||||||||||||| \\//[=#group4 4)] The languages **not yet** present in the repository but have content and team coordinator// || |----------------------------------------------------------------------------- ||=Bosnian || \ || || || \ || [TransifexLang:bs bs]|| kenan3008 || |----------------------------------------------------------------------------- |||||||||||||||| \\//[=#group5 5)] The languages present in the repository, but no committers and no team coordinator// || |----------------------------------------------------------------------------- ||=Galician || \ || [source:branches/1.4-stable/trac/locale/gl gl]|| || \ || [TransifexLang:gl gl]|| || ||=Persian || \ || [source:branches/1.4-stable/trac/locale/fa fa]|| || \ || [TransifexLang:fa fa]|| || ||=Portuguese || \ || [source:branches/1.4-stable/trac/locale/pt pt]|| || \ || [TransifexLang:pt pt]|| || ||=Vietnamese || \ || [source:branches/1.4-stable/trac/locale/vi vi]|| || \ || [TransifexLang:vi vi]|| || ||=Romanian || \ || [source:branches/1.4-stable/trac/locale/ro ro]|| || \ || [TransifexLang:ro ro]|| || ||=Albanian || \ || [source:branches/1.4-stable/trac/locale/sq sq]|| || \ || [TransifexLang:sq sq]|| || |----------------------------------------------------------------------------- |||||||||||||||| \\//[=#group6 6)] The languages for which there's no content, neither in the repository, nor in Transifex// || |----------------------------------------------------------------------------- ||=Afrikaans || \ || || || \ || [TransifexLang:af af]|| pjdelport || ||=Hindi || \ || || || \ || [TransifexLang:hi hi]|| Sangeeta || ||=Indonesian (Indonesia) || \ || || || \ || [TransifexLang:id_ID id_ID]|| sumodirjo || ||=Panjabi (Punjabi) || \ || || || \ || [TransifexLang:pa pa]|| aalam || ||=Turkish (Turkey) || \ || || || \ || [TransifexLang:tr_TR tr_TR]|| zeugma || |----------------------------------------------------------------------------- - for the languages in [#group1] the translation maintainers are primarily in charge for doing the synchronization - for [#group2] there is only the usual maintenance to do - for [#group3] the translation team coordinators are invited to ask for commit privileges and integrate [#group1] - for [#group4] the translations will be "promoted" to [#group3], ie integrated in the repository, as soon as a decent coverage is reached (say > 50%) {{{#!comment - for the languages in [#group5] }}} - for the languages in [#group6] there's nothing to do as long as there's no content === Translation team member Updating translations on Transifex is only one part of the story, since you need to get your changes integrated into the source code. For that, you need to inform the translator coordinator for your language. See [..#Translationcoordination tickets for translation coordination]. The translator coordinator will pull the changes from Transifex using the procedure described below, verify them, and commit them. If the translator coordinator doesn't react in due time or is not active anymore, a member of the Trac team will likely notice the activity on the ticket and he could do the same, but without the language verification step, obviously. It's even a better idea to contact and discuss with the translator coordinator **before** updating the catalogs on Transifex, as some might not be up to date, and you will risk duplicating work and making integration harder. Transifex provides all the facilities for communication between team members. There are also the tickets, or plain e-mail on trac-dev@googlegroups.com or directly to the translators. == Synchronization with Transifex As discussed above, this is normally the responsibility of the translation team coordinator who has the commit permissions on the catalog files in the Trac subversion repository. Typically you'd do this for approving and committing changes if you're a translation coordinator. Even if you're not a committer, you could do this if you want to prepare a patch for submitting those changes for review on a ticket. === Prerequisites You first need to install the [https://developers.transifex.com/docs/cli#installing-with-a-script-linuxmac Transifex client]: {{{#!sh $ curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash }}} or a [https://developers.transifex.com/docs/cli#installation variation of the above]. If you're on Windows, you'll also need to [https://developers.transifex.com/docs/cli#download-from-github-releases-linuxmacwindows download from GiHub releases]. Of course, you also need to have a [TracRepositories checkout of the source] of the branch you want to work on: * [source:branches/1.2-stable] will be maintained for the foreseeable future, so you may want to continue contributing there * [source:branches/1.4-stable] is the latest stable release branch * [source:trunk] is where the new interesting stuff happen (TracDev/Proposals/ConfigEnumTranslation?). Now let's go through a complete example, starting from one update to the resource corresponding to the messages catalog on trunk for the [TransifexLang:fr French translation], more specifically, on that page: TransifexView:fr:trunk-messages-pot. === Checking the status The key is to not **lose** translations, in one direction or the other. Unlike Trac, Transifex does not support version control. In the command line, verify that everything is correctly configured: {{{#!console cboos@linux:~/trac/0.12-stable$ tx status No authentication data found. trac -> 0_12-stable-messages-js-pot (1 of 2) Translation Files: - en: trac/locale/messages-js.pot (source) - ca: trac/locale/ca/LC_MESSAGES/messages-js.po - de: trac/locale/de/LC_MESSAGES/messages-js.po - en_GB: trac/locale/en_GB/LC_MESSAGES/messages-js.po - en_US: trac/locale/en_US/LC_MESSAGES/messages-js.po - eo: trac/locale/eo/LC_MESSAGES/messages-js.po ... }}} If you get the `No authentication data found.` line, you first need to authenticate yourself: {{{#!console cboos@linux:~/trac/0.12-stable$ mkdir tmp cboos@linux:~/trac/0.12-stable$ cd tmp/ cboos@linux:~/trac/0.12-stable/tmp$ tx init Creating .tx folder... Transifex instance [https://www.transifex.com]: Creating skeleton... Creating config file... No entry found for host https://www.transifex.com. Creating... Please enter your transifex username: cboos Password: Updating /home/cboos/.transifexrc file... Done. cboos@linux:~/trac/0.12-stable/tmp$ cd .. cboos@linux:~/trac/0.12-stable$ rm -fr tmp }}} Alternatively, you can directly edit your `~/.transifexrc` file, and add: {{{#!ini [https://www.transifex.com] rest_hostname = https://rest.api.transifex.com token = username = }}} Please generate the token at https://www.transifex.com/user/settings/api/ first. Then try again: {{{#!console cboos@linux:~/trac/0.12-stable$ tx status | grep fr - fr: trac/locale/fr/LC_MESSAGES/messages-js.po - fr: trac/locale/fr/LC_MESSAGES/messages.po }}} An important preparation task that must be done before pulling is to make sure the `.po` file was updated. Even if you think it must be because there are no local changes, it's a good idea to `make update-..` (`fr` in this example). Perhaps the Babel version you're using at the moment is different from the one which was used before the last version was committed, or a manual change was done at the last minute. Doing a catalog update ensures that we have a "normalized" content to compare with the version of the catalog pulled from Transifex, after the latter has been "normalized" as well by another Babel update, as the automatic formatting done by Transifex is completely different. Make sure to "freeze" that updated Trac version, by making a commit in a local branch (if you use git or hg) or even a "maintenance" commit in Subversion. === Pulling the catalogs from Transifex #pull-example Now, before pulling changes from Transifex, **make sure you have no local modifications to the catalogs you're about to update**. Otherwise, those local modifications will be lost, on Windows at least; seems that on Linux it manages to make better checks with the timestamps between the local file and the file on the server. Once you made sure you have no pending local modifications, you may now pull: {{{#!console cboos@linux:~/trac/0.12-stable$ tx pull -l fr Pulling new translations for resource trac.0_12-stable-messages-js-pot (source: trac/locale/messages-js.pot) -> fr: trac/locale/fr/LC_MESSAGES/messages-js.po Pulling new translations for resource trac.0_12-stable-messages-pot (source: trac/locale/messages.pot) -> fr: trac/locale/fr/LC_MESSAGES/messages.po Done. }}} If you see 'Skipping...' messages, the Transifex client thought it should better avoid updating your local copy. If you're sure you know better, you can pass the `-f` flag so that it updates it nevertheless. ==== Examining the changes If at this point you want to check the differences. Transifex introduces a bunch of unrelated formatting changes in the translations, folding multiple lines into one, etc. You can get rid of these spurious changes by doing an update with Babel, which can be seen as a kind of "normalization" step: {{{#!console cboos@linux:~/trac/0.12-stable$ make update-fr python setup.py update_catalog -l fr update_catalog_js -l fr running update_catalog updating catalog 'trac/locale/fr/LC_MESSAGES/messages.po' based on 'trac/locale/messages.pot' running update_catalog_js updating catalog 'trac/locale/fr/LC_MESSAGES/messages-js.po' based on 'trac/locale/messages-js.pot' }}} Now the changes should be much more significant: {{{#!diff diff --git a/trac/locale/fr/LC_MESSAGES/messages.po b/trac/locale/fr/LC_MESSAGES/messages.po index 00f3c3c..a33bf50 100644 --- a/trac/locale/fr/LC_MESSAGES/messages.po +++ b/trac/locale/fr/LC_MESSAGES/messages.po @@ -1,19 +1,21 @@ # French translations for Trac. -# Copyright (C) 2007-2008 Edgewall Software +# Copyright (C) 2012 Edgewall Software # This file is distributed under the same license as the Trac project. # +# Translators: +# Christian Boos , 2008,2012. +# Christian Boos , 2008. # Emmanuel Blot , 2007. -# Christian Boos , 2008. # Michel Briand , 2008. -# Stéphane Raimbault , 2009 +# Stéphane Raimbault , 2009. msgid "" msgstr "" -"Project-Id-Version: Trac 0.12\n" -"Report-Msgid-Bugs-To: trac-dev@googlegroups.com\n" +"Project-Id-Version: Trac\n" +"Report-Msgid-Bugs-To: http://trac.edgewall.org/\n" "POT-Creation-Date: 2012-01-29 14:04+0100\n" -"PO-Revision-Date: 2011-01-25 20:28+0100\n" +"PO-Revision-Date: 2012-10-27 13:13+0000\n" "Last-Translator: Christian Boos \n" -"Language-Team: fr_FR \n" +"Language-Team: French \n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" @@ -2654,7 +2655,7 @@ msgstr "à %(owner)s" #: trac/ticket/default_workflow.py:251 trac/ticket/default_workflow.py:271 #, python-format msgid "The owner will be changed from %(current_owner)s" -msgstr "Le propriétaire changera de %(current_owner)s" +msgstr "Le propriétaire ne sera plus %(current_owner)s" #: trac/ticket/default_workflow.py:259 #, python-format }}} It's a good idea to get rid of the changes in the first instance. With git, it's easy: {{{#!console cboos@linux:~/trac/0.12-stable> git checkout -p diff --git a/trac/locale/fr/LC_MESSAGES/messages.po b/trac/locale/fr/LC_MESSAGES/messages.po index 00f3c3c..a33bf50 100644 --- a/trac/locale/fr/LC_MESSAGES/messages.po +++ b/trac/locale/fr/LC_MESSAGES/messages.po @@ -1,19 +1,21 @@ # French translations for Trac. -# Copyright (C) 2007-2008 Edgewall Software +# Copyright (C) 2012 Edgewall Software # This file is distributed under the same license as the Trac project. # +# Translators: +# Christian Boos , 2008,2012. +# Christian Boos , 2008. # Emmanuel Blot , 2007. -# Christian Boos , 2008. # Michel Briand , 2008. -# Stéphane Raimbault , 2009 +# Stéphane Raimbault , 2009. msgid "" msgstr "" -"Project-Id-Version: Trac 0.12\n" -"Report-Msgid-Bugs-To: trac-dev@googlegroups.com\n" +"Project-Id-Version: Trac\n" +"Report-Msgid-Bugs-To: http://trac.edgewall.org/\n" "POT-Creation-Date: 2012-01-29 14:04+0100\n" -"PO-Revision-Date: 2011-01-25 20:28+0100\n" +"PO-Revision-Date: 2012-10-27 13:13+0000\n" "Last-Translator: Christian Boos \n" -"Language-Team: fr_FR \n" +"Language-Team: French \n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" Discard this hunk from worktree [y,n,q,a,d,/,j,J,g,s,e,?]? y @@ -2654,7 +2655,7 @@ msgstr "à %(owner)s" #: trac/ticket/default_workflow.py:251 trac/ticket/default_workflow.py:271 #, python-format msgid "The owner will be changed from %(current_owner)s" -msgstr "Le propriétaire changera de %(current_owner)s" +msgstr "Le propriétaire ne sera plus %(current_owner)s" #: trac/ticket/default_workflow.py:259 #, python-format Discard this hunk from worktree [y,n,q,a,d,/,K,j,J,g,e,?]? q }}} Note that you can also do `make diff` for a more up-to-the-point output: `make diff vc=git` if you're working in a git checkout. ==== Publish your changes You can commit the changes to the source with an appropriate log message, **but**, please!, before that: - remember to check your catalog with `make check`, here: `make check-fr` - remember to check your catalog by compiling it, here: `make compile-fr` Once you are done, make sure you **push back** the changes to Transifex. See the other example below. === Pushing local modifications of the catalogs to Transifex #push-example After committing changes to the message catalog, it is a good idea to push those changes to Transifex so that other contributors can see these changes. Even if you just pulled changes from Transifex (coming from the other example above), you most certainly did local adaptations like the removal of the spurious metadata changes, or small fixes, then you should also push those changes back to Transifex the same way, as Transifex **won't** pick up the changes made to the translated catalogs from the repository. It will do that for the catalog templates though, so the latter are always up-to-date. But if you didn't do a pull just before, doing a push //might// overwrite changes contributed to Transifex, so you should be careful. The safest thing to do is to simply do a pull first, normalize the changes by running `make update-..`, compare, and //only then//, if there are no changes but spurious ones in the metadata in the comments in the header, revert those spurious changes and do the push of your committed state. If the changes were significant, you're simply back to the [#pull-example] situation described before. {{{#!console cboos@linux:~/trac/0.12-stable$ tx push -t -l fr Pushing translations for resource trac.0_12-stable-messages-js-pot: Pushing 'fr' translations (file: trac/locale/fr/LC_MESSAGES/messages-js.po) Pushing translations for resource trac.0_12-stable-messages-pot: Pushing 'fr' translations (file: trac/locale/fr/LC_MESSAGES/messages.po) Done. }}} For some languages, push can fail like this: {{{#!sh $ tx push -t -l gl Pushing translations for resource trac.0_12-stable-messages-js-pot: Warning: No mapping found for language code 'gl'. KeyError: 'gl' }}} This is because there's no `message-js.po` catalog for the 'gl' translation, only a `message.po` one. The workaround is to push explicitly this one, by using the `-r` option: {{{#!sh $ tx push -t -r trac.0_12-stable-messages-pot -l gl Pushing translations for resource trac.0_12-stable-messages-pot: Pushing 'gl' translations (file: trac\locale\gl\LC_MESSAGES\messages.po) Done. }}} That can be done for several languages at once: {{{#!sh $ tx push -t -r trac.0_12-stable-messages-pot -l fa,gl,ko,pt,ro,vi Pushing translations for resource trac.0_12-stable-messages-pot: Pushing 'fa' translations (file: trac\locale\fa\LC_MESSAGES\messages.po) Pushing 'gl' translations (file: trac\locale\gl\LC_MESSAGES\messages.po) Pushing 'ko' translations (file: trac\locale\ko\LC_MESSAGES\messages.po) Pushing 'pt' translations (file: trac\locale\pt\LC_MESSAGES\messages.po) Pushing 'ro' translations (file: trac\locale\ro\LC_MESSAGES\messages.po) Pushing 'vi' translations (file: trac\locale\vi\LC_MESSAGES\messages.po) Done. }}} == Notes about the Transifex "Releases" #Releases In Transifex is not exactly clear if a release should be a line of development (branch) or a given released version. It supports both and lets you choose to organize the way you wish. I opted to make the "releases" match our maintenance branches, and not individual releases; we still have a [Transifex:r/012-stable-3/ few of these], as apparently you can't delete a release. The downside is that string freeze periods and release dates will need to be adjusted constantly on the same release. The alternative, creating a new "release" for each actual version we release bears as much overhead (moreover, are there's no //copy// operation) and doesn't bring any benefit besides keeping a record of the versions we made, record that we obviously maintain here anyway in the [/roadmap]. On the other hand, the big benefit is that all the links from t.e.o to Tx won't need to be updated after each minor release. The only updates needed will be when introducing a new major release, for example for Trac 1.2. See also: [wiki:TracL10N]