Coding for translation

From Gramps
Revision as of 17:24, 4 February 2009 by Romjerome (talk | contribs) (Provide support for plural forms.)
Jump to: navigation, search

Coding guidelines to enabling easy and correct translation of strings on the User Interface.

Introduction

In order to consider including a report in the offical GRAMPS release, a report generator must support internationalization. What this means is that the report must support translations into different languages. GRAMPS provides support to make this as easy as possible for the developer.

How to allow translations

GRAMPS provides a simple interface (based on the gettext interface) to mark strings as being translatable. First, import the gettext function from the intl library.

from gettext import gettext as _

This statement imports the sgettext function under the name of _. This is the function that both marks the strings for translation and performs the actual translation at runtime. Strings that should be translated should be enclosed as an argument to the function.

Example 1:

print "Hello world!"

In this example, the string will always be printed as specified.

Example 1 internationalized:

print _("Hello world!")

In this example, GRAMPS will attempt to translate the string. If a translation exists, the call to the function will return the translation. If a translation does not exist, the original string is returned.

How it works

There are two stages to getting a translation to work. Translations are stored in a .po file that contains the mappings between the original strings and the translated strings, see Translating GRAMPS.

Translators use a generic file gramps.pot to generate their .po file. GRAMPS uses a utility that extracts the strings from the source code to build the .po file. This utility (a perl script called by the command make) examines the source files for strings that have been marked as translatable. In the python source, these are the strings enclosed in the _() function calls.

If you want this script to take your translatable strings into account, you must add your source file path in the file : po/POTFILES.in. For this report example, you should add:

...
# plugins directory
src/plugins/AncestorChart2.py
src/plugins/AncestorReport.py
...
src/plugins/Leak.py
src/plugins/FindDupes.py
src/plugins/MediaManager.py
src/plugins/Myreport.py                # <------
src/plugins/NarrativeWeb.py
src/plugins/PatchNames.py
...

In this file, the sources are sorted within each directory or category.

Note that because strings are extracted by a script from the source file, string constants and not variables must be enclosed in the _() call. In the following example, the extraction script will not extract the string.

mystring = "Hello World!"
print _(mystring)

The correct method would be to use one of the following:

mystring = _("Hello World!")
print mystring

At run time, the _() calls will translate the string by looking it up in the translation database (created from the .po files) and returning the translated string.

Tips for writing a translatable report

Use complete sentences

Don't build up a sentence from phrases. Because a sentence is ordered in a particular way in your language does not mean that it is ordered the same way in another. Providing the entire sentence as a single unit allows the translator to make a meaningful translation.

Use named %s values

Python provides a powerful mechanism that allows the reordering of %s values in a string. A translator may need to rearrange the structure of a sentence, and it may not match the order you chose. For example:

print "%s was born in %s" % ('Joe','Toronto')

In some languages it may make more sense to say:

print "%s is the city in which %s was born" % ('Toronto', 'Joe')

The problem is that this requires a change to the order of the arguments. Python provides a solution for this. By using named operators and dictionaries, we can say:

print "%(male_name)s was born in %(city)" % {
           'city' : 'Toronto', 'male_name' : 'Joe'}

In this case, the order of the %s formatters is not important, since the values will be looked up in the dictionary at run time to resolve the value. The translator can reorder the %s formatters, or even remove them without causing any problems.

Provide separate strings for masculine and feminine.

Many languages have the concept of gender, while others don't. A sentence may need to be phrased differently depending on if the subject is male or female. By using the named %s values along with a bit of code, this problem can be solved.

if person.getGender() == Person.male:
       print _("%{male_name} was born in %{city}\n") % {
               'male_name' : name, 'city' : city }
else:
       print _("%{female_name} was born in %{city}\n") % {
               'female_name' : name, 'city' : city }

This allows languages with gender differences to map nicely into your sentence.

Provide support for plural forms.

Gramps-3.1 provides a plural forms support, useful for locales with multiples plurals according to a number (often slavic based languages) or for Asian languages (singular = plural).

We need to call module :

from gettext import ngettext

and code like this :

 ngettext("singular %d", "plural %d" n) %n

Sample:

text = ngettext('%(person)s, %(age)d%(relation)s', 
                '%(person)s, %(age)d%(relation)s', nyears)
                % {'person'   : short_name,
                   'age'      : nyears,
                   'relation' : comment}

Provide a context support.

Translator needs context for a good translation. Keep in mind you can help him/her, by using context on translation string.

We need to call module :

from TransUtils import sgettext as _