App Developer Week -- Making Your App Speak Languages with Launchpad Translations -- dpm -- Tue, Sep 6th, 2011
1 [15:57] <dpm> is everyone ready for another day of cool app developer sessions? 2 [15:57] <dpm> day 2 of ubuntu app developer week is about to start! 3 [15:58] <Andy80> yeeeeeaaahh 4 [15:58] <dpm> :) 5 [16:00] <dpm> ok, let's wait for Classbot to kick off and then get started 6 [16:00] <dpm> oh, there it is... :) 7 [16:01] <dpm> allright, let's roll 8 [16:01] <dpm> Welcome to this session on setting up your project for translations in Launchpad 9 [16:02] <dpm> My name is David Planella I work as the Ubuntu Translations Coordinator in the Community team at Canonical, 10 [16:02] <dpm> where I work with our translations community to bring you a localized Operating System. 11 [16:03] <dpm> I also tend to help with any topics related to translations and Launchpad, and that's what we're going to talk about today :) 12 [16:03] <dpm> That's a really exciting topic to me, as Launchpad makes it really easy to make your applications translatable and available to everyone in almost any language, and I hope you enjoy it as much as I do. 13 [16:03] <dpm> Translators are really awesome people! 14 [16:04] <dpm> I'll leave some time for questions at the end, but feel free to ask them throughout the session 15 [16:05] <dpm> Anyway, let's get started, shall we? 16 [16:05] <dpm> Assumptions 17 [16:05] <dpm> ----------- 18 [16:05] <dpm> 19 [16:05] <dpm> I will start with an application ready set up for translations, so I'm not going to go into much detail there. 20 [16:06] <dpm> (I'll take questions, though, if there are any) 21 [16:06] <dpm> My intention is to focus in getting you started with exposing your project's translations to everyone for translation. 22 [16:06] <dpm> We're going to be using these tools: 23 [16:07] <dpm> bzr 24 [16:07] <dpm> quickly 25 [16:07] <dpm> python-distutils-extra 26 [16:07] <dpm> In particular quickly, which we'll use to start with a nearly ready-made project with translations set up 27 [16:08] <dpm> (You can install it by running the 'sudo apt-get install quickly' command or you can get it from the Ubuntu Software Centre) 28 [16:08] <dpm> 29 [16:08] <dpm> Creating the project and setting it up for translations 30 [16:08] <dpm> ------------------------------------------------------- 31 [16:08] <dpm> 32 [16:08] <dpm> We'll use quickly to create a project called 'fooby', and then we'll set up the last touches needed to configure translations. 33 [16:09] <dpm> As I said, we won't go into the detail of setting up the application for translation, as quickly will do the heavy lifting for you, 34 [16:09] <dpm> but here's a high level overview of what needs to be done to add internationalization support to an app, in case you need to do it outside of quickly: 35 [16:10] <dpm> Generic Steps to Internationalize an Application 36 [16:10] <dpm> ================================================ 37 [16:10] <dpm> 38 [16:11] <dpm> * Integrate gettext (http://www.gnu.org/s/gettext/) into the application. Initialize gettext in your main function 39 [16:11] <dpm> * Integrate gettext into the build system. There are generally gettext rules in the most common build systems. Use them. 40 [16:12] <dpm> * Mark translatable messages. Use the _() gettext call to mark all translatable messages in your application 41 [16:12] <dpm> * Care for your translation community. Not necessarily a step related to adding i18n support, but you'll want an active and healthy translation community around your project. Keep the templates with translatable messages up to date. Announce these updates and give translators time to do their work before a release. Be responsive to feedback. 42 [16:12] <dpm> anyway, going back to the subject 43 [16:13] <dpm> So if you've got all those tools installed, you can simply fire up a terminal window (Ctrl + Alt + T) and run the following command: 44 [16:13] <dpm> quickly create ubuntu-application fooby 45 [16:13] <dpm> This will create an application named 'fooby' 46 [16:13] <dpm> and give you some information about it on the first run 47 [16:14] <dpm> then change to the fooby folder: 48 [16:14] <dpm> cd fooby 49 [16:14] <dpm> And finally run: 50 [16:14] <dpm> python setup.py build_i18n 51 [16:15] <dpm> That should have finished the last bits to set up translations, so that you can already link up your application with Launchpad Translations 52 [16:16] <dpm> in particular, what that last command did was to created the po folder in your project, to contain what it's called the translations template and the translations files themselves 53 [16:16] <dpm> you can see the template there by running: 54 [16:16] <dpm> ls po 55 [16:16] <dpm> have a look at it: 56 [16:16] <dpm> gedit po/fooby.pot 57 [16:17] <dpm> It's important to get a bit familiar with it, but you don't have to remember the whole format 58 [16:17] <dpm> you should simply know what it is for now :) 59 [16:17] <dpm> and that Launchpad needs it to be in your project 60 [16:18] <dpm> A few bits and pieces on translation templates: 61 [16:18] <dpm> * Gettext: They follow the gettext format: http://is.gd/fC8p6 62 [16:19] <dpm> * Name: They are generally named after your project, with a .pot extension. E.g. fooby.pot 63 [16:19] <dpm> * One template per app: Generally applications need only one template 64 [16:19] <dpm> * Layout: They generally live in the po/ folder, along with the translations 65 [16:19] <dpm> * Content: They are text files which contain: 66 [16:19] <dpm> * A header with metadata 67 [16:20] <dpm> * A set of message pairs: msgid are the original strings extracted from the code and exposed to translators, and msgstr are the placeholders for the translations, which are always empty in the templates. 68 [16:20] <dpm> * Launchpad import: They are imported into Launchpad and exposed for translations for all languages in https://translations.launchpad.net/$YOUR_PROJECT 69 [16:20] <dpm> * Updates: You update the template whenever you have new strings in your app and you think they are stable for translation (generally shortly before release) 70 [16:21] <dpm> * Tools: you update templates with gettext based tools: 71 [16:21] <dpm> * generally intltool -> 'cd po && intltool-update -p' 72 [16:21] <dpm> * or increasingly python-distutils-extra for python projects -> 'python setup.py build_i18n -p' 73 [16:22] <dpm> You don't have to remember all of this 74 [16:22] <dpm> But hopefully these points will give you some insight on translation templates 75 [16:22] <dpm> At least you should know how that you must update the template from time to time 76 [16:23] <dpm> and the command to do it 77 [16:23] <dpm> Repeat it with me - "I _will_ update the translation from time to time, at least once before a release" 78 [16:23] <dpm> :-) 79 [16:24] <dpm> You'll see that your project still does not contain any translations, but again, let me give you a quick overview on translations, so you know what we're talking about: 80 [16:24] <dpm> So here we go, a few words on translations: 81 [16:26] <dpm> * Template-based: They are created from the template and share the same gettext format 82 [16:26] <dpm> * Name: They are named after the $CODE.po scheme, where $CODE is an ISO 639-2 code. E.g. ca.po for Catalan, de.po for German. Some have an optional country specifier. E.g. pt_BR.po (Portuguese from Brazil) 83 [16:27] <dpm> * Layout: they are all in the same directory as the POT template. So: 84 [16:27] <dpm> * po/fooby.pot 85 [16:27] <dpm> * po/ca.po 86 [16:27] <dpm> * po/pt_BR.po 87 [16:27] <dpm> * ... 88 [16:27] <dpm> * Creation: Launchpad creates them for you the minute someone translates the first message online 89 [16:27] <dpm> * Code integration: you can let Launchpad commit them to a branch of your choice or you can export a tarball containing them all (more on that later) 90 [16:28] <dpm> Anyway, let's continue. Now that you've added the template to your code, you can commit it by running the following commands: 91 [16:28] <dpm> bzr add po 92 [16:28] <dpm> bzr commit -m 'Created my first ever awesome .pot template. Go translators, go!' 93 [16:29] <dpm> And let's publish it in Launchpad (note that you'll have to change the Launchpad URL to your user name instead of 'dpm'): 94 [16:29] <dpm> bzr push lp:~dpm/fooby/translations 95 [16:29] <dpm> Nothing particularly hard to understand on the naming scheme above: dpm is my user name, fooby is the project and translations is the bzr branch name in Launchpad 96 [16:29] <dpm> Ok, so that completed the first step! 97 [16:29] <dpm> Next: 98 [16:30] <dpm> ah, forgot to ask, any questions so far? 99 [16:30] <ClassBot> Andy80 asked: what about Qt application? They don't use gettext. They have their own method to be translatable (basically it's just a matter of tr("This is a message") ) and I noticed that in Unity-2D we're using a sort of macro. Why don't we improve the launchpad integration even for Qt applications? 100 [16:30] <dpm> good question 101 [16:31] <dpm> as Unity 2D is an Ubuntu project, it has to integrate well with Launchpad and the way Ubuntu translators translate, which is again Launchpad Translations 102 [16:32] <dpm> in short, in the Unity 2D project there is some code that intercepts the Qt tr() calls and converts them to gettext() calls 103 [16:33] <dpm> so effectively Unity 2D, despite being a Qt project, uses gettext for translations 104 [16:33] <dpm> I agree that it'd be nice for Launchpad to support the Qt format 105 [16:33] <dpm> but as you see, you can easily use gettext from Qt-based apps too 106 [16:34] <dpm> Ok, let's continue 107 [16:34] <dpm> Setting up code hosting 108 [16:34] <dpm> ----------------------- 109 [16:34] <dpm> 110 [16:34] <dpm> We want our project to be available to everyone to translate, so we'll need to publish it in Launchpad first. 111 [16:34] <dpm> That's beyond the scope of this session, so we'll continue from the already registered fooby project in Launchpad: 112 [16:34] <dpm> https://code.launchpad.net/fooby 113 [16:35] <dpm> In case you are interested, though, registering a new project in Launchpad is as easy as going to https://launchpad.net/projects/+new 114 [16:35] <dpm> Some of the URLs I'll post to set up your project will not allow you to open some of the pages due to permissions, so if you have your own project in Launchpad, just substitute the 'fooby' part in the URL with your project's Launchpad id 115 [16:36] <dpm> The first thing we'll have to do in our project is registering a bzr branch, 116 [16:36] <dpm> so we'll simply go to the Code tab in Launchpad, choose the "Configure code hosting" link 117 [16:36] <dpm> and then on the "Link to a Bazaar branch already on Launchpad" you can enter the branch we published earlier on (~dpm/fooby/translations) 118 [16:36] <dpm> A shortcut is to simply go to: 119 [16:36] <dpm> https://code.launchpad.net/fooby/trunk/+setbranch 120 [16:36] <dpm> to do this 121 [16:37] <dpm> So now we have all we need to start setting up translations. 122 [16:37] <dpm> You see that all components in Launchpad are integrated, so you set up a branch to be linked to translations 123 [16:37] <dpm> Just as a recap, you can see and explore the resulting code from here: 124 [16:37] <dpm> https://code.launchpad.net/fooby 125 [16:37] <dpm> Feel free to browse it (http://bazaar.launchpad.net/~dpm/fooby/translations/files) 126 [16:37] <dpm> or download it (bzr branch lp:fooby) and play with it 127 [16:38] <dpm> Ok, so code hosting setup: (./) Finished! 128 [16:38] <dpm> 129 [16:38] <dpm> Setting up translations in Launchpad 130 [16:38] <dpm> ------------------------------------ 131 [16:38] <dpm> 132 [16:38] <dpm> Now we come to the most interesting part 133 [16:38] <dpm> Let's divide this in 4 steps 134 [16:38] <dpm> 1. Telling Launchpad where translations are hosted 135 [16:38] <dpm> The first step it to tell Launchpad that we want to host translations there. 136 [16:39] <dpm> On your Launchpad's project, just click on the Translations tab, or go to this URL: 137 [16:39] <dpm> (remember to change 'fooby' to your project's name!) 138 [16:39] <dpm> https://translations.launchpad.net/fooby/+configure-translations 139 [16:39] <dpm> Then choose the "Launchpad" option to tell Launchpad translations will be done there, and click on "Change" 140 [16:39] <dpm> That was an easy one, wasn't it? 141 [16:39] <dpm> 2. Configuring permissions 142 [16:39] <dpm> Now we are going to tell Launchpad how we want our translations permissions to be (i.e. who and how can translate it), 143 [16:40] <dpm> and which branch translators should focus on. 144 [16:40] <dpm> Simply go to the Translations tab again and click on the "Change permissions link" 145 [16:40] <dpm> Or here's the direct link: https://translations.launchpad.net/fooby/+settings :) 146 [16:40] <dpm> I recommend the following setup: 147 [16:40] <dpm> Translations group: Launchpad Translators 148 [16:40] <dpm> Translations permissions policy: Structured 149 [16:40] <dpm> Translation focus: trunk (or choose your branch here) 150 [16:40] <dpm> Assigning the translations to a translation group will make sure a team for each language will review translations before they are submitted, ensuring the quality of translations 151 [16:41] <dpm> A translations group is simply a set of teams, one per language, that takes care of translations in that language 152 [16:41] <dpm> They can be specific to a project or generic. 153 [16:41] <dpm> I recommend the Launchpad Translators group because it contains a set of already established and experienced teams: 154 [16:41] <dpm> https://translations.launchpad.net/+groups/launchpad-translators 155 [16:41] <dpm> as per the Structured policy 156 [16:42] <dpm> it gives you a good balance between openness and quality control: 157 [16:42] <dpm> only the team members of an established team will be able to translate your project 158 [16:42] <dpm> And for languages without a team it will allow everyone to translate, facilitating the barrier of entry to translators at the expense of QA 159 [16:42] <dpm> The other ends of the permissions spectrum are Open or Restricted 160 [16:42] <dpm> You can learn more about these here: 161 [16:42] <dpm> https://help.launchpad.net/Translations/YourProject/PermissionPolicies 162 [16:43] <dpm> It's the project maintainer's call, but I personally discourage them to use Open 163 [16:43] <dpm> Ok, we're nearly there, next step: 164 [16:43] <dpm> 3. Setting up what needs to be translated 165 [16:43] <dpm> You need to also tell Launchpad what needs to be translated. That's again quite easy. On the Translations tab again, choose the trunk series and specify your branch there 166 [16:43] <dpm> Direct link: https://launchpad.net/fooby/trunk/+linkbranch 167 [16:43] <dpm> Another easy one done :) 168 [16:44] <dpm> 4. Configuring imports and exports 169 [16:44] <dpm> That's for me the most interesting bit 170 [16:44] <dpm> The settings on this section basically enable Launchpad to do the work of managing translations for you 171 [16:44] <dpm> You can tell Launchpad to import your translation templates automatically whenever you do a commit in your branch 172 [16:44] <dpm> So you don't have to upload them manually 173 [16:44] <dpm> If you are migrating a project with existing translations, you can tell it to import them too 174 [16:44] <dpm> And finally, you can let Launchpad commit translations automatically to a branch of your choice 175 [16:45] <dpm> I find that just awesome 176 [16:45] <dpm> So for the imports, on the Launchpad page, on the "Import translations from branch" section: 177 [16:45] <dpm> I recommend choosing "Import template files" and then "Save settings" 178 [16:45] <dpm> For exports: look at the "Export translations to branch" section and then click on the "Choose exports branch" link 179 [16:45] <dpm> So that was it! 180 [16:46] <dpm> 4 easy steps that should not take you more than a few minutes to set up, and your app is ready for the world to translate! 181 [16:46] <dpm> Just a few final words: 182 [16:46] <dpm> Play with translations 183 [16:46] <dpm> ---------------------- 184 [16:46] <dpm> As a developer, it might be interesting to see how translators do their work. 185 [16:46] <dpm> Exceptionally for this project (remember how I advised not to use Open permissions, tough :) I've set the translations permissions on the fooby project to Open 186 [16:47] <dpm> So you can submit translations online 187 [16:47] <dpm> and get a feel for the work that translators do 188 [16:47] <dpm> As a developer, it will give you an understanding on how they work. It is always interesting to get to know other workflows 189 [16:47] <dpm> and it's always good to have an insight on all areas of contribution related to your project 190 [16:47] <dpm> You can start translating fooby here: 191 [16:47] <dpm> https://translations.launchpad.net/fooby 192 [16:48] <dpm> So that was it really, it wasn't that hard, was it? 193 [16:48] <dpm> Let me give you a quick summary of what we've talked about and then take questions 194 [16:48] <dpm> Summary 195 [16:48] <dpm> ------- 196 [16:49] <dpm> Most of the steps described here today you'll only need to do once, unless you need to change the settings. They were: 197 [16:49] <dpm> 1. Setting up code hosting (in case you hadn't already) 198 [16:49] <dpm> 2. Setting up translations in Launchpad 199 [16:49] <dpm> 2.1. Telling Launchpad that it's hosting your translations (https://translations.launchpad.net/fooby/+configure-translations) 200 [16:49] <dpm> 2.2. Configuring permissions: recommended -> Structured, Launchpad Translators (https://translations.launchpad.net/fooby/+settings) 201 [16:49] <dpm> 2.3. Setting up the translations branch (https://launchpad.net/fooby/trunk/+linkbranch) 202 [16:49] <dpm> 2.4. Configuring imports and exports (https://translations.launchpad.net/fooby/trunk/+translations-settings) 203 [16:49] <dpm> So really, once your project is set up for translation, the only things you'll have to remember are: 204 [16:49] <dpm> to update the template before a release, 205 [16:50] <dpm> announce to translators that they can start their work, 206 [16:50] <dpm> and merge the translations to your main branch. 207 [16:50] <dpm> If you are using the same branch for translation imports and exports, you won't even have to do that! 208 [16:50] <ClassBot> There are 10 minutes remaining in the current session. 209 [16:50] <dpm> ok, so we've got 10 minutes left - if you've got any questions, bring them on! :) 210 [16:50] <ClassBot> jsjgruber_natty_ asked: â€‹â€†How can an application bring up a translation for a particular local "on-demand"? Now get the spanish version, now french version? 211 [16:51] <dpm> I'm guessing you're asking this for test purposes 212 [16:52] <dpm> in normal usage, the application will automatically load the translation for the right locale defined in the system 213 [16:52] <dpm> so if my desktop's language is set to Catalan, the app, assuming gettext has been correctly initialised in the code, will automatically load the Catalan translation 214 [16:53] <dpm> but if you want an app to load another language, you can call the app on the command line specifying the language code, e.g. 215 [16:53] <dpm> $ LANGUAGE=de myapp 216 [16:54] <dpm> Running that command would load the German translation of myapp ('de' is the ISO code for German) 217 [16:54] <dpm> other questions? 218 [16:55] <ClassBot> matteonardi asked: how does translations get into users machines? Is there 1 (and only 1) package for each language? What if my application comes from a ppa? 219 [16:55] <ClassBot> There are 5 minutes remaining in the current session. 220 [16:56] <dpm> translations get distributed and installed along with the application. On the technical level, translations are .mo files, one for each available translation in your app 221 [16:56] <dpm> It does not matter if your application comes from a PPA 222 [16:57] <dpm> the PPA will contain all the necessary .mo files, which means the translations will be installed on the system of everyone who installs the PPA 223 [16:57] <dpm> ok, 3 minutes left, I can probably take another one if there is any 224 [16:58] <dpm> ok, if there aren't any, the only thing remaining is to thank everyone for listening in and asking interesting questions, and I'll see you next time!