InternationalizingApps

App Developer Week -- From English to any language: internationalizing your apps -- dpm -- Mon, Apr 11th, 2011

   1 [19:00] <dpm> hey, hello everyone!
   2 [19:01] <dpm> thanks for joining in this session on how to internationalize your applications
   3 === ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - https://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Questions in #ubuntu-classroom-chat || Event: Ubuntu App Developer Week - Current Session: From English to any language: internationalizing your apps - Instructors: dpm
   4 [19:02] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/04/11/%23ubuntu-classroom.html following the conclusion of the session.
   5 [19:02] <dpm> ok, now that classbot is done...
   6 [19:03] <dpm> first of all, thanks to tomeu for a great session
   7 [19:03] <dpm> And now let's get started with translations
   8 [19:03] <dpm> First the introductions
   9 [19:03] <dpm> I'm David Planella, and I work in the Community team at Canonical as the Ubuntu Translations Coordinator
  10 [19:04] <dpm> Usually I work more on the community side of things, with the always awesome Ubuntu translation teams
  11 [19:05] <dpm> But today I've put my developer hat to show you how easy it is to get your app ready to speak a multitude of languages
  12 [19:05] <dpm> and set up so that the community can translate it.
  13 [19:05] <dpm> Regardless of the programming language, the process of adding internationalization support to an application is not only fairly easy
  14 [19:06] <dpm> but also, on a high level view, the same for all programming languages.
  15 [19:06] <dpm> This means that after this session you should have a pretty good overview on what it takes to make your application translatable
  16 [19:06] <dpm> and you can apply this to any programming language, slightly adapting the syntax, of course.
  17 [19:07] <dpm> In order for you to see how it all fits together, I've based the talk on a common framework that can get you quickstarted in just a few minutes
  18 [19:07] <dpm> I've used the Python programming language and Quickly
  19 [19:08] <dpm> https://wiki.ubuntu.com/Quickly
  20 [19:08] <dpm> Let's start with some background concepts to make it easier to understand the steps we'll be doing later on
  21 [19:09] <dpm> So let's have a quick look at the main players involved in the internationalization game:
  22 [19:09] <dpm>  
  23 [19:09] <dpm> Background Concepts
  24 [19:09] <dpm> ===================
  25 [19:09] <dpm>  
  26 [19:09] <dpm> GNU Gettext
  27 [19:09] <dpm> -----------
  28 [19:09] <dpm> Gettext is the underlying and most widely used technology to enable translations of Open Source projects.
  29 [19:09] <dpm> It defines a standard format of translation files translators can do their work with (PO files, more on them in a minute)
  30 [19:10] <dpm> and lets applications load those translations compiled in a binary format (MO files) at runtime.
  31 [19:10] <dpm> It has implementations for many programming languages, and amongst them, of course, Python.
  32 [19:10] <dpm> You'll find that the comprehensive gettext manual at http://www.gnu.org/software/gettext/manual/gettext.html can be a very useful reference,
  33 [19:10] <dpm> The Python implementation of the gettext API is what we'll use to internationalize our project with Quickly today.
  34 [19:11] <dpm> Needless to say, it also comes with some nifty documentation at http://docs.python.org/library/gettext.html
  35 [19:11] <dpm> * {i} In short, gettext does all the heavy lifting involved in exposing your application for translation and loading the translations for the end user
  36 [19:12] <dpm>  
  37 [19:12] <dpm> intltool
  38 [19:12] <dpm> --------
  39 [19:12] <dpm> Intltool is a higher level tool that adds functionality to gettext by allowing the extraction of translatable strings from a variety of file formats
  40 [19:13] <dpm> It has also become a standard tool when implementing internationalization for OSS projects.
  41 [19:13] <dpm> Nearly all (if not all) GNOME projects, for example, use intltool.
  42 [19:14] <dpm> * {i} intltool handles the translations of things such as the desktop shortcut of your application
  43 [19:14] <dpm>  
  44 [19:14] <dpm> python-distutils-extra
  45 [19:14] <dpm> ----------------------
  46 [19:14] <dpm> Python-distutils-extra is a python package that makes it easy to integrate themable icons, documentation and gettext based translations in your python install and build tools, and it's basically an enhancement to python-distutils.
  47 [19:15] <dpm> The project's page is at http://www.glatzor.de/projects/python-distutils-extra/
  48 [19:15] <dpm> * /!\ Note that this tool is Python-specific. I'm mentioning it here because we're going to be talking of a practical example with Python. If your application were a C application you'd probably use autotool rules to achieve the same result
  49 [19:15] <dpm>  
  50 [19:15] <dpm> The three above technologies (gettext, intltool, python-distutils-extra) are transparently used by Quickly, so we won't get into much more detail for now.
  51 [19:16] <dpm> I just want you to get an idea of what we're talking about
  52 [19:16] <dpm> There are also more aspects involved in internationalizing applications, such as font rendering, input methods, etc., but this should get you started for now.
  53 [19:16] <dpm>  
  54 [19:16] <dpm> Quickly
  55 [19:16] <dpm> -------
  56 [19:16] <dpm> I'll be very brief here and let you figure out more on quickly as we go along
  57 [19:16] <dpm> For now, it will suffice give you a teaser and tell you that it is the tool which brings back the fun in writing applications! ;-)
  58 [19:17] <dpm>  
  59 [19:17] <dpm> Finally, a tool that is not strictly needed for internationalization (or the shorter form: i18n), but that can help you build an active translation community around your project
  60 [19:18] <dpm> which will be the next step after your project adds i18n support
  61 [19:18] <dpm>  
  62 [19:18] <dpm> Launchpad Translations
  63 [19:18] <dpm> ----------------------
  64 [19:18] <dpm> Launchpad Translations (https://translations.launchpad.net/) is the collaborative online tool which allows translation communities to be brought together and translate applications online through its web UI.
  65 [19:18] <dpm> Apart from the very polished UI to provide translations, it has other nice features such as message sharing across project series (translate one message in a series and it instantly propagates to all other shared series),
  66 [19:19] <dpm> global suggestions (suggestions of translations across _all_ projects in Launchpad), automatic imports of translations and automatic commits to bzr branches, several levels of permissions, and a huge translator base.
  67 [19:20] <dpm> On the right hand side of the URL I gave you you can see that there are quite a lot of projects using Launchpad to make translations easy both for developers and translators.
  68 [19:20] <dpm> Bear with me: we're nearly there - let's also quickly trow in and review a couple of concepts related to the gettext technology
  69 [19:20] <dpm>  
  70 [19:20] <dpm> Gettext: MO files
  71 [19:20] <dpm> -----------------
  72 [19:21] <dpm> The message catalog, or MO file (for Machine Object) is the binary file that is actually used to load translations in a running system.
  73 [19:21] <dpm> It is created from a textual PO (more on that in a bit), which is used as the source, generally by a tool called msgfmt.
  74 [19:21] <dpm> Message catalogs are used for performance reasons, as they are implemented as a binary hash table that is much more efficient to look up at runtime than textual PO files
  75 [19:22] <dpm> * {i} .mo files are the binary files installed in the system where the application loads the translations from, using gettext
  76 [19:22] <dpm>  
  77 [19:22] <dpm> Gettext: PO files
  78 [19:22] <dpm> -----------------
  79 [19:22] <dpm> PO file stands for Portable Object file, and are the textual files translators work with to provide translations. They are plain text files with a special format:
  80 [19:22] <dpm>   msgid "English message"
  81 [19:22] <dpm>   msgstr "Traducció al català " <- Translated string
  82 [19:22] <dpm> (message pairs containing the original messages from the application, and its corresponding translation)
  83 [19:23] <dpm> You can see an example of a PO file here:
  84 [19:23] <dpm> http://bazaar.launchpad.net/~synaptic-developers/synaptic/trunk/view/head:/po/zh_CN.po
  85 [19:23] <dpm> * {i} Translators provide translations in PO files. If they use an online translation system they won't directly work with them, but your project sources will still contain them
  86 [19:23] <dpm> * {i} In each application source tree there is generally a PO file per language, named after the language code. E.g. ca.po, de.po, zh_CN.po, etc.
  87 [19:23] <dpm>  
  88 [19:23] <dpm> Gettext: POT files
  89 [19:23] <dpm> ------------------
  90 [19:23] <dpm> Once your project has added i18n support, you'll need to give translators an updated list of translatable messages they can start translating.
  91 [19:23] <dpm> You'll also need to update this list whenever you add new messages to your application.
  92 [19:24] <dpm> You achieve that through POT files, or simply templates in l10n (localization) slang (Portable Object Template).
  93 [19:24] <dpm> They are textual files with the same format as PO files,
  94 [19:24] <dpm> but they are empty of translations and are used as a template or stencil to create PO files from.
  95 [19:24] <dpm> * {i} There are special tools to update templates. Generally intltool is used, often called from a build system rule or a higher level tool such as python-distutils-extra
  96 [19:24] <dpm>  
  97 [19:24] <dpm> Gettext: Translation domain
  98 [19:24] <dpm> ---------------------------
  99 [19:25] <dpm> The translation domain is a unique string identifier assigned by the programmer in the code (usually in the build system)
 100 [19:25] <dpm> and used by the gettext functions to locate the message catalog where translations will be loaded from.
 101 [19:25] <dpm> The general form to compute the catalog’s location is:
 102 [19:26] <dpm>     locale_dir/locale_code/LC_category/domain_name.mo
 103 [19:26] <dpm> which in Ubuntu expand generally to /usr/share/locale/locale_code/LC_MESSAGES/domain_name.mo
 104 [19:26] <dpm> The locale_code part refers to the language code for the particular language. As an example, when using Nautilus in a Catalan locale with Ubuntu,
 105 [19:26] <dpm> the gettext functions will look for the message catalogue at:
 106 [19:26] <dpm>     /usr/share/locale-langpack/ca/LC_MESSAGES/nautilus.mo
 107 [19:27] <dpm> That's where the translations for your application will be installed and searched for
 108 [19:27] <dpm> Note that for your app this location might be slightly different:
 109 [19:27] <dpm>      /usr/share/locale/ca/LC_MESSAGES/myapp.mo
 110 [19:28] <dpm> * {i}The corresponding translation template should have the same translation domain in its filename, e.g. nautilus.pot.
 111 [19:28] <dpm> * {i} The translation domain must be unique across all applications and packages. I.e. something generic like messages.pot won’t work.
 112 [19:28] <dpm>  
 113 [19:29] <dpm> Ok, done with the concepts, let's get down to work and to questions
 114 [19:29] <dpm> Generic Steps to Internationalize an Application
 115 [19:29] <dpm> ================================================
 116 [19:29] <dpm> * Integrate gettext into the application. Initialize gettext in your main function, most especially the translation domain
 117 [19:30] <dpm> * Integrate gettext into the build system. There are generally gettext rules in the most common build systems. Use them.
 118 [19:30] <dpm> * Mark translatable messages. Use the _() gettext call to mark all translatable messages in your application
 119 [19:31] <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.
 120 [19:31] <dpm>  
 121 [19:31] <dpm> Hands-on: creating an internationalized app
 122 [19:31] <dpm> ===========================================
 123 [19:31] <dpm> Ok, enough theory, let's have a go at using quickly to create your first internationalized application
 124 [19:32] <dpm> You can install Quickly on any recent Ubuntu release by simply firing up a terminal and executing:
 125 [19:32] <dpm>     sudo apt-get install quickly
 126 [19:33] <dpm> Once you've done that, you can run quickly to create your first project:
 127 [19:34] <dpm>     quickly create ubuntu-application awesometranslations
 128 [19:34] <dpm> (if you like, substitute 'awesometranslations' by your favourite project name)
 129 [19:34] <dpm> We've just told Quickly to use the ubuntu-application template, and to call what is created "awesometranslations"
 130 [19:35] <dpm> you should probably have an open dialog from your new app in front of you.
 131 [19:35] <dpm> Quickly has created all that for you!
 132 [19:35] <dpm> You can close the dialog to continue
 133 [19:35] <dpm> What Quickly did was to copy over basically a sample application, and do some text switcheroos to customize the app
 134 [19:36] <dpm> What you could see there was the ui containing some text that needs translation.
 135 [19:36] <dpm> To start making change to your app, go to the directory where it's stored. Generally by simply running
 136 [19:36] <dpm>     cd awesometranslation
 137 [19:37] <dpm> You can then edit your code with $ quickly edit, change the UI with $ quickly glade, and try your changes with $ quickly run
 138 [19:37] <dpm> You can save your change with $ quickly save
 139 [19:37] <dpm> Finally, to package, share, release your apps so that other will be, with the following commands (not all are necessary): $ quickly package / $ quickly share / $ quickly release
 140 [19:37] <dpm> As it stands now, the application has nearly all you need to make it translatable
 141 [19:38] <dpm> which is the great thing about quickly
 142 [19:38] <dpm> From now on, while I'll let you play and investigate the application you've created, we'll be looking at the one I created for the purpose of this session
 143 [19:39] <dpm> and I'll show you the i18n bits
 144 [19:39] <dpm> so that you can add them to your existing applications if you want
 145 [19:39] <dpm> For new applications, I'd simply recommend you to use quickly
 146 [19:39] <dpm> and start from there
 147 [19:40] <dpm> which will set up everything for you, so that you can forget about it and concentrate on all those new cool functions your new app is going to provide :-)
 148 [19:40] <dpm> So let's have a look at:
 149 [19:40] <dpm> http://bazaar.launchpad.net/~dpm/+junk/awesometranslations/files
 150 [19:41] <dpm> Notice that we could just call the session finished at this point, as quickly did all the job for us :)
 151 [19:41] <dpm> Also notice how easy it is. I've just created and pushed the application to Launchpad a few minutes ago
 152 [19:42] <dpm> Remember we were talking about PO and POT files?
 153 [19:42] <dpm> They are right there, under the po/ folder:
 154 [19:42] <dpm> http://bazaar.launchpad.net/~dpm/+junk/awesometranslations/files/head:/po/
 155 [19:43] <dpm> the po/ folder could be called something else, but it is customary to call it like that, as some tools rely on this convention
 156 [19:43] <dpm> Notice the .pot file called the same name as your app
 157 [19:43] <dpm> and an example translation file (ca.po) submitted by a translator
 158 [19:44] <dpm> Now to the interesting bits:
 159 [19:44] <dpm> Remember the generic steps for internationalization we were talking about earlier on?
 160 [19:44] <dpm> * Initializing gettext:
 161 [19:44] <dpm> http://bazaar.launchpad.net/~dpm/+junk/awesometranslations/view/head:/awesometranslations/__init__.py#L8
 162 [19:45] <dpm> so here we include the gettext module
 163 [19:45] <dpm> and we define a function called simply _()
 164 [19:45] <dpm> And finally we define the translation domain
 165 [19:46] <dpm> which will be the name of the .mo file installed in the system and the name of the .pot file
 166 [19:47] <dpm> * Integrating gettext in the build system:
 167 [19:47] <dpm> http://bazaar.launchpad.net/~dpm/+junk/awesometranslations/view/head:/setup.py#L13
 168 [19:47] <dpm> Here the integration happens automagically by using python-distutils-extra
 169 [19:47] <dpm> C programs using autotools might need a more complex integration
 170 [19:48] <dpm> * Mark translatable messages:
 171 [19:48] <dpm> http://bazaar.launchpad.net/~dpm/+junk/awesometranslations/view/head:/awesometranslations/__init__.py#L23
 172 [19:49] <dpm> For every message you want to expose for translation, you simply have to wrap it in the _() function, e.g. _("Translatable message here")
 173 [19:49] <dpm> And that's basically it, really
 174 [19:50] <dpm> easy, isn't it?
 175 [19:50] <dpm> ok, so we're running out of time, let's see if there are questions!
 176 [19:52] <ClassBot> There are 10 minutes remaining in the current session.
 177 [19:52] <ClassBot> bdfhjk asked: What is the best way to translate QT application? Gettext or QTLinquist?
 178 [19:52] <dpm> bdfhjk asked: What is the best way to translate QT application? Gettext or QTLinquist?
 179 [19:52] <dpm> Tough question :)
 180 [19:53] <dpm> Both Gettext and QT Linguist are excellent i18n frameworks
 181 [19:53] <dpm> With similar functionality
 182 [19:53] <dpm> But I would personally use gettext
 183 [19:54] <dpm> Because it is framework-agnostic and used by the vast majority of Open Source projects
 184 [19:54] <dpm> Not only that, but most online translation tools rely on gettext
 185 [19:54] <dpm> KDE itself uses gettext, for example
 186 [19:54] <ClassBot> bulldog98_konv asked: what’s the difference between GNOMEs and KDEs handling of translations in code?
 187 [19:55] <dpm> Not much really
 188 [19:55] <dpm> As I said, both KDE and GNOME use gettext
 189 [19:55] <dpm> The majority of GNOME is written in C, and KDE in C++
 190 [19:56] <dpm> I gather that in KDE they wrap Qt Linguist calls through kdelib to actually use gettext to load the actual translations
 191 [19:56] <ClassBot> bdfhjk asked: Is Gettext working in windows?
 192 [19:57] <ClassBot> There are 5 minutes remaining in the current session.
 193 [19:57] <dpm> Yes, gettext works in any platform where glibc can run, including Windows
 194 [19:58] <dpm> There is still time to answer one last question if you've got one
 195 [19:59] <dpm> Ok, so I think I'll use the last minutes to thank everyone for their participation, and remind you that if you've got any questions on translations, feel free to ping me any time!
 196 [19:59] <dpm> I usually hang out on #ubuntu-devel
 197 

MeetingLogs/appdevweek1104/InternationalizingApps (last edited 2011-04-12 07:31:09 by dpm)