Wednesday, June 24, 2015

GSoC 2015 Report #2 - Internationalization support for GnomeKeysign

Because the replacement of the GPG wrapper is taking longer than I have expected, I have done something that will surely make GnomeKeysign one step closer to become GNOME ready - I have added support for Internationalization and Localization.

I had no knowledge about this so I started reading this tutorial which is more customized for C projects that use autotools.
GnomeKeysign is a Python pure project and it uses distutils/setuptools for building and installing.

I will explain step by step how to add support for translations in a Python application but first I will talk a little about building & packaging in Python.

"Real artists ship. Or so says Steve Jobs." (taken from

It seemed reasonable that Python should have a packaging framework , hence distutils.
Distutils is many things: a build tool (for you), an installation tool (for your users), a package metadata format (for search engines), and more. It integrates with the Python Package Index  (“PyPI”), a central repository for open source Python libraries.

A good project layout could be the following

| -- package
|    | --
|    | --
|    `-- subpackage
|         | --
|         | --
| -- runner
| --
| -- [setup.cfg]
| --

If you want to know more about what every file does then check the Python Packaging User Guide .
Already having a good project structure , I could easily add support for internationalization (i18n).

To  add i18n support we need to accomplish these tasks:
  1. Configure the function that translates the strings
  2. Mark the strings in the code with that function
  3. Generate a template for the translators (the .pot file)
  4. Add translations
  5. Include the translations in the installation

Configure the '_()' macro

The Python gettext module allows you to mark strings in source code, extract those strings for translation, and use the translated strings in your application. It is a common practice to define macros which are shorter wrappers, like the '_()'  macro that replaces a gettext() call

import gettext
t = gettext.translation("gnome-keysign", PATH_TO_MO_FILES)
_ = t.ugettext

Mark strings for i18n

Search the source code files for strings that are visible in the GUI (window title, labels, button labels, messages, and so on) and wrap them inside '_()' function

print _("Getting ready for i18n.")

Generate template for translators

Using a command line tool, you generate a ".pot" file from the source code. This file contains all strings that need translation in the project. We only need to generate it  once or after the strings change in the code. The file is generated inside /po folder and the command used is
xgettext --language=Python --keyword=_ --output=po/gnome-keysign.pot `find . -name "*.py"`

Add translations

Then, from the ".pot" file , using another command line tool we will generate the ".po" files for each language. These are the files that translators need to complete. Inside the po/ folder we use this command
msginit --input=PROJECTNAME.pot --locale=LOCALE

But once the program is running, it doesn't use .po files directly, but a binary (compiled) version of them: the .mo files. These .mo files must be re-created on build time (when creating the package) and installed in the right location in the system.

Include translations into the project installation

The file is where various aspects of the project are configured. Alongside with the general setup arguments there is a `cmdclass` argument where we put the code to perform custom actions such as code generation, running tests, building translation files, etc.

I implemented a custom command derived from distutils.cmd.Command which will generate the '.mo' files in build time and in install time will include the '.mo' files into the data_files list. In this way we include the translations by overloading the default 'build' and 'install' instructions.

Now I can translate GnomeKeysign and run it in my language :-).

That's all for now, I will be posting again after I'm done with the GPG replacement part.


No comments:

Post a Comment