Plurals

A common problem in localization is plurals. The most broken example is something like this:

     g_printf (_("Found %d file%s."), nbr_of_files, ((nbr_of_files == 1) ? _("s") : ""));

There are a pletora of localization problems with a design like this, although it might seem like a nice hack to a programmer. It splits up the sentence into several messages, and it doesn't take into account nouns with irregular plural endings (like "foot"/"feet" or "sheep"/"sheep" in English). The nouns that have irregular plural endings are of course different in different languages. Also, in other languages other words in the sentence, like adjectives and verbs, may also depend on the number of items, which is impossible to accomplish with this design. Fortunately, this message design is uncommon.

A more common approach, and a much better one, is this:

   if (nbr_of_files == 1) {
     g_printf (_("Found %d file."), nbr_of_files);
   } else {
     g_printf (_("Found %d files."), nbr_of_files);
   }

Unfortunately, there are also problems with this design. Many western languages use plural forms the same way as in English, i.e. when the number of items is equal to one plural isn't used, but for all other numbers of items the plural form is used. This can formally be expressed as:

   Plural-Forms: nplurals=2; plural=(n != 1);

This C-like syntax should be interpreted as English using two forms when it comes to plural endings. These different plural forms are denoted, although it is not immediately visible here, 0 (singular) and 1 (plural). Plural is used when the number of items, n, is anything else than 1, because then the plural= expression above evalutates to 1. Vice versa, when the expression evaluates to 0, singular is used because singular is denoted by that number.

However, there are many languages that don't have the same rule for using plurals. As an example, French is an exception to the rule above, as it also uses singular, not plural, for the case where the number is 0. But things really get interesting with languages that not only use different rules but that also have more than one plural form. Many eastern European languages are examples of this. Slovak for example uses this rule:

   Plural-Forms: nplurals=3; plural= (n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;

There are three forms of plural endings here, denoted 0, 1, and 2, and the rules are somewhat more complex. Slovenian uses this even more complex rule:

   Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0);

This requires a much more capable form of handling messages with plurals. GNU gettext 0.10.38 and later versions has a feature for solving this called ngettext. Below is the same message example using ngettext.

     g_printf (ngettext ("Found %d file.", "Found %d files.", nbr_of_files), nbr_of_files);

The first parameter to the ngettext call is the message in singular form, the second the message in plural form, and the third parameter is used to determine the plural form, in our example nbr_of_files. With this design, it's possible for the translator to use any rule for handling of plurals and make it automatically work for any number of items.

Please also note that even for situations where you know the number in the message will always be greater than 1 or even much greater than 1, the use of ngettext is still advised. The reason for this is, as you can see above, that many languages use several different plural forms also for numbers greater than one, and that the plural rules are usually based on the last digit of the number. As an example, if the number is 65301 or 65302 still makes a difference in many languages, even though it doesn't happen to do so in English, as English uses the same plural form for everything greater than 1. So there is still a clear need to be able to distinguish these different plural forms also for everything greater than one.

So use ngettext even for situations where you know the number will always be greater than one, or even much greater than one, as in the example below.

     g_printf (ngettext ("You tried to move %d folder at once. You can't move more than 256 folders at once.",
                         "You tried to move %d folders at once. You can't move more than 256 folders at once.",
                         nbr_of_folders),
               nbr_of_folders);

This example, and the extra piece of code involved, probably looks both weird and unnecessary to most people that are accustomed to the English way of using plural, but please keep in mind that you, as pointed out above, cannot assume anything about what type of plural will be used in other languages even in these cases, so use of ngettext is still required here.

The ngettext functionality is specified in the OpenI18N 1.2 standard and compatible platforms are supposed to support it. Glib will also specifically require the functionality to be present in a future version.

TranslationProject/DevGuidelines/Plurals (last edited 2008-02-03 14:48:06 by anonymous)