Page 1 of 1

[i18n] How to use plural form?

PostPosted: April 28th, 2013, 9:42 am
by arhangelsoft
Hi, Kambiz!
Can you explain how to use plural form on same word?

Where add it? How to translate, how to understand plural expressions syntax?

Thanks!

Re: [i18n] How to use plural form?

PostPosted: April 28th, 2013, 5:22 pm
by Kambiz
Let's have an example:

Suppose we have a simple program written for English audiences that shows the number of monitors connected to the computer.
Code: Select all
if Screen.MonitorCount = 1 then
  TheLabel.Caption := 'You have one monitor'
else
  TheLabel.Caption := Format('You have %d monitors', [Screen.MonitorCount]);

We are going to change this piece of code, so that it can be localized for any arbitrary culture.

We drop a TLocalizer and a TTranslator component on the form and change the code as follow:
Code: Select all
if Screen.MonitorCount = 1 then
  TheText := Translator.GetText('You have one monitor')
else
  TheText := Translator.GetText('You have {0:N0} monitors');
TheLabel.Caption := Localizer.FormatCS(TheText, [Screen.MonitorCount]);

This code looks fine but follows the English language rule for plurals. Depends on the language, plural form of a noun may have one or many forms. In addition to the number of plural forms, the way how plural forms are built differs in each language.

Hopefully, the i18n package knows the number of plural forms for many languages and also knows how to select the right form. We just need to know the rule for the default language of our program (in our case, English).
Code: Select all
TheText := Translator.GetNText(['You have one monitor', 'You have {0:N0} monitors'], Screen.MonitorCount);
TheLabel.Caption := Localizer.FormatCS(TheText, [Screen.MonitorCount]);

As you can see, instead of GetText, we used GetNText method of the Translator object. This method expects an array of strings as the first parameter. The number of strings in this array are identical to the number of plural forms of the programs' default language. The second parameter of this method is the number of items (in our case, monitors) that determines which string should be selected.

Because GetNText uses a custom expression (known as plural forms expression) to select a plural form from the array, there is no constant rule that we can use to find out the arrangement of plural forms in the array. By convention, usually the string at zero index refers to singular form and the rest are plural form variants.

But in fact we need to know which number will select a string in a specific index. For this purpose knowing a bit about the plural forms expression is necessary.

This plural forms expression is a C-like expression, which means:
  • arithmetic, bit-wise, comparison and ternary operators of C are functional
  • a comparison expression evaluates to either 0 (false) or 1 (true)
  • zero evaluates to false and any non-zero value evaluates to true
You can consider the plural forms expression as an inline function with one parameter named n. The parameter n is the number of items in subject. we should
use this value in our expression to evaluate the index of plural forms in the array of strings.

For example, in English language, we use singular form if and only if the quantity is one, otherwise we use plural form (including zero quantity). We decided to put singular form at zero index and plural form at one index. Based on this information, our plural form expression for English language can be:
Code: Select all
(n == 1) ? 0 : 1

We can also take advantage of C-like expression and write a bit more optimized expression:
Code: Select all
n != 1

Both of the above expressions evaluate to zero when n is one and in other cases evaluate to one.

For a language like Persian that has no plural form for nouns, the plural form expression can be as simple as literal 0. But for some languages it would be more complex. As an example, here is the plural form expression for Arabic expression with six plural form variations:
Code: Select all
n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5

In the i18n editor, the plural forms of a text appear on a tabset. The number of tabs is identical to the number of plural forms that the target language has. Besides that, the caption of each tab indicates the expected result of plural forms expression for selecting that item.

You can see or modify the plural forms rule of a language by selecting Translation => Edit Plural Rule... from the menu of the i18n editor.

Re: [i18n] How to use plural form?

PostPosted: April 30th, 2013, 5:27 am
by arhangelsoft
Oh, thanks! BIG Thanks for this!

I have some problems...
I write code:
Code: Select all
{
 TDM - is TDataModule
I using it for translating string resources.
Here is the method for translating plural forms..
}
function TDM.GetTC(aConstId, aCount: Integer): string; overload;
resourcestring
  rsTimeCount_Week = 'One week';
  rsTimeCount_Weeks = '{0:N0} weeks'; //{0:N0} - What this?

  rsTimeCount_Day = 'One day';
  rsTimeCount_Days = '{0:N0} days';

  rsTimeCount_Hour = 'One hour';
  rsTimeCount_Hours = '{0:N0} hours';

  rsTimeCount_Min = 'One minute';
  rsTimeCount_Mins = '{0:N0} minutes';

  rsTimeCount_Sec = 'One second';
  rsTimeCount_Secs = '{0:N0} seconds';

begin
  Result := '';
  case aConstId of
    TC_TIMECOUNT_WEEK:
      Result := ResTransl.GetNText([rsTimeCount_Week,
        rsTimeCount_Weeks], aCount);

    TC_TIMECOUNT_DAY:
      Result := ResTransl.GetNText([rsTimeCount_Day, rsTimeCount_Days], aCount);

    TC_TIMECOUNT_HOUR:
      Result := ResTransl.GetNText([rsTimeCount_Hour,
        rsTimeCount_Hours], aCount);

    TC_TIMECOUNT_MINUTE:
      Result := ResTransl.GetNText([rsTimeCount_Min, rsTimeCount_Mins], aCount);

    TC_TIMECOUNT_SECOND:
      Result := ResTransl.GetNText([rsTimeCount_Sec, rsTimeCount_Secs], aCount);
  end;
end;

I do all as you says. What wrong?

If aCount = 1, translation works fine.
If aCount > 1, translation also in default English is bad:


Russian language have a 3 plural forms.


What about line breaks in the text? How to save it?

Re: [i18n] How to use plural form?

PostPosted: May 1st, 2013, 6:24 pm
by Kambiz
The number that to pass to GetNText is not for formatting. It's just for selecting the right plural form.

You must pass the result string to Localizer's FormatCS method. In the i18n help look for FormatCS and FormatValue functions to find out about the available formatting options.

The line break is done in usual Delphi way.
Code: Select all
multilineText := 'First line'#10#13'Second line';

Re: [i18n] How to use plural form?

PostPosted: May 14th, 2013, 7:48 pm
by arhangelsoft
You must pass the result string to Localizer's FormatCS method. In the i18n help look for FormatCS and FormatValue functions to find out about the available formatting options.

Hm, I don't understand how to relize formatting for Russian:
Editor gives formula for plural forms in Russian(3):
Code: Select all
n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2


For example, numbers:
If number = 1 or 21 or 31 or 41 or ..1 result must be "{0:NO} число"
If number = 2 or 3 or 4 or 22 or 23 or 24 or result must be "{0:NO} числа"
If number = 6 or 7 or 8 or 9..20 or 25..30 result must be "{0:NO} чисел"

Currently,
If number = 1 the result is "1 числа"(must be "{0:NO} число"), what is wrong?
I use following code:
Code: Select all
function TDM.GetTC(aConstId, aCount: Integer): string;
resourcestring
  rsTimeCount_Day = 'One day';
  rsTimeCount_Days = '{0:N0} days';
var
  s: string;

begin
  Result := '';
  case aConstId of
    TC_TIMECOUNT_DAY:
      begin
        s := Translator.GetNText([rsTimeCount_Day, rsTimeCount_Days], aCount);
        Result := Localizer.FormatCS(s, [aCount]);
      end;


Can you give more info about this strings and formatting(Any links, differents between your and C# formating)? I don't quite understand how works i18nUtils.FormatCS. I don't know the C# language.

Re: [i18n] How to use plural form?

PostPosted: May 15th, 2013, 4:17 pm
by Kambiz
Please open i18n.chm and search for FormatValue function for detailed information about the format strings.

Your code is correct. You have to provide the proper translation in the editor.

According to your plural formula, the plural index for Russian is:
  • plural=0 if number = 1 or 21 or 31 or 41 or ..1
  • plural=1 if number = 2 or 3 or 4 or 22 or 23 or 24
  • plural=2 if number = 6 or 7 or 8 or 9..20 or 25..30
By the way, the format string for your case is {0:N0} not {0:NO}. There is a zero after N.

Re: [i18n] How to use plural form?

PostPosted: June 7th, 2013, 1:48 pm
by arhangelsoft
Hi, Kambiz!

I've a problems with plurals, again =(

I wrote this code for user-friendly time format:
Code: Select all
function TDM.GetTC(aConstId, aCount: Integer): string;
resourcestring
  rsTimeCount_Week = 'One week';
  rsTimeCount_Weeks = '{0:N0} weeks';

  rsTimeCount_Day = 'One day';
  rsTimeCount_Days = '{0:N0} days';

  rsTimeCount_Hour = 'One hour';
  rsTimeCount_Hours = '{0:N0} hours';

  rsTimeCount_Min = 'One minute';
  rsTimeCount_Mins = '{0:N0} minutes';

  rsTimeCount_Sec = 'One second';
  rsTimeCount_Secs = '{0:N0} seconds';
var
  s: string;

begin
  Result := '';
  case aConstId of
    TC_TIMECOUNT_WEEK:
      begin
        s := Translator.GetNText([rsTimeCount_Week, rsTimeCount_Weeks], aCount);
        Result := Localizer.FormatCS(s, [aCount]);
      end;

    TC_TIMECOUNT_DAY:
      begin
        s := Translator.GetNText([rsTimeCount_Day, rsTimeCount_Days], aCount);
        Result := Localizer.FormatCS(s, [aCount]);
      end;

    TC_TIMECOUNT_HOUR:
      begin
        s := Translator.GetNText([rsTimeCount_Hour, rsTimeCount_Hours], aCount);
        Result := Localizer.FormatCS(s, [aCount]);
      end;

    TC_TIMECOUNT_MINUTE:
      begin
        s := Translator.GetNText([rsTimeCount_Min, rsTimeCount_Mins], aCount);
        Result := Localizer.FormatCS(s, [aCount]);
      end;

    TC_TIMECOUNT_SECOND:
      begin
        s := Translator.GetNText([rsTimeCount_Sec, rsTimeCount_Secs], aCount);
        Result := Localizer.FormatCS(s, [aCount]);
      end;
  end;
end;


I'm correctly use the Translator.GetNText and Localizer.FormatCS methods?

Why Translator.GetNText returns plural=0(e.g. "One hour") if aCount = 17?
For English language this problem doesn't exists, but for Russian language if aCount = 17, function must returns plural=2, but return plural=0. What I'm doing wrong?

Re: [i18n] How to use plural form?

PostPosted: June 9th, 2013, 1:40 pm
by Kambiz
Hi,

Your code is correct. There was a bug in evaluation of plural rule expression.
I fixed the bug and you can download the update.
Sorry for the inconvenience.

Re: [i18n] How to use plural form?

PostPosted: June 9th, 2013, 3:29 pm
by arhangelsoft
Hi, thanks for this hotfix. You are the best.