gettext indexes or/and translates some indexed english messages

## Syntax

                gettext("The literal reference message")
                gettext(["item #1" ; "item#2"])
                translated = gettext("The literal reference message")
                translated = gettext(["item #1" ; "item#2"])
                translated = gettext(msgid)
                .. gettext(domain, ..)

## Arguments

"The literal reference message"

  Single case-sensitive literal text of an english message to be indexed or/and translated. A column of literal texts explicitly separated with semi-colons may also be specified. Messages can include some C-like placeholders starting with "%".

  Only standard ASCII characters can be used. Any other extended ASCII or UTF-8 characters would make gettext() failing.

msgid

  single or array of case-sensitive messages identifiers (in english) to be translated, in a variable.

  Only standard ASCII characters can be used.

translated

  Input messages translated in the current language of the Scilab session. If no translated version is available for an input message, the input version in english is returned. The input and output arrays have the same sizes.

  These messages are labelled `msgstr` and defined in the ./locales/*.po files. They can include extended ASCII or UTF-8 characters.

domain

  word of text: the name of a domain. When localizing an external module, `domain` may be usually set to the technical name of the module.

  `domain` can indifferently be a literal or a variable. It is case-sensitive.

  `domain` is required by `tbx_generate_pofile()` to make the literal msgid string indexed (harvesting stage. See below).

  When `gettext(domain, msgid)` is used to retrieve the translated version, `domain` is used to get the path to the directory where translations are stored, as beforehand registered with `addlocalizationdomain(domain, path)`.

## Description

### Harvesting messages and overall processing

gettext is a free and open external application shipped within Scilab to support multilinguism for messages. This support consists in 4 main stages:

1. Harvesting messages among the code and indexing them to be translated. For an external module, this is explicitly performed with the `xgettext` external binary, that is part of the `gettext` external application, and that is called by the tbx_generate_pofile Scilab function. In Scilab, it is also possible to use `tbx_build_localization(..)` or `tbx_make . localization`, that both call `tbx_generate_pofile(..)`.

   Each collected message is considered as an identifier (a message id, = msgid) for all its forthcoming translations. In Scilab, the reference language for identifiers is english. Therefore, the msgids to be indexed must be written in english.

   Only single literal and in one piece messages being as `gettext` input argument are collected. Hence, some of the following syntaxes and cases won't index the considered message, and no further translated versions will be available for them:

   # | Syntax | Status | Harvesting results
   --- | --- | --- | ---
   1 | `gettext("To be translated")` | OK | standard syntax
   2 |
       msg = "To be translated";
       gettext(msg)
   | NOK | The text is in a variable. It is not a literal. It won't be indexed. Nevertheless, this syntax will work to retrieve the translated version, provided that the message has been collected elsewhere in a right way.
   3 | `gettext("To be" + " translated")` | NOK | "To be" and " translated" are indexed as 2 separate msgid. Then, since the concatenated msgid "To be translated" does not exist, retrieving its translation will fail.
   4 |
       gettext("To be" + ..
               " translated")
   | NOK | Same as #3
   5 | `gettext(["item#1", "item#2"])` | NOK | Only "item#1" is collected. "item#2" is ignored.
   6 | `gettext(["item#1" "item#2"])` | NOK | "item#1item#2" is indexed. Then, since "item#1" and "item#2" are unknown separate msgid, retrieving their respective translation will fail.
   7 |
       gettext(["item#1"
                "item#2"])
   | NOK | Same as #6
   8 | `gettext(["item#1" ; "item#2"])` | OK | "item#1" and "item#2" are indexed as separate msgids.
   9 |
       gettext(["item#1" ;
                "item#2"])
   | OK | Same as #8. Please take care of the semi-colon. Without it, the case #7 fails.
   10 |
       // gettext("item")
       /* gettext("item #2") */
       /*
          gettext("item #3")
       */
   | OK | gettext() occurrences in comments are indexed as well.

   For an external module, tbx_generate_pofile() harvests only `gettext` occurrences that specify a `domain`. It ignores any call to `gettext` with only one input argument.

   Harvesting does not need to beforehand declare the `domain` with `addlocalizationdomain()`

2. All indexed texts are then translated by the author of the code or by some contributors. Anyway, by some humans. Good translation tools are also available online.

3. All translated versions are bundled in a way that `gettext(..)` becomes efficient to retrieve the version in the current session's language. This is done by running `tbx_generate_pofile()` another time.

4. Finally, some calls like `gettext(message)` or `gettext("The message content")` are used to retrieve and return the translated version.

   When the message is not a native Scilab one (for instance it is specific to an external module), the `domain` where the message and its translations can be found must be specified, as in `gettext(domain, message)` or `gettext(domain, "The message content")`. In this case, `addlocalizationdomain(domain, path)` must be run before, in order to indicate to Scilab the directory where the translated messages of the given `domain` are stored.

Most often, a message is declared to be harvested and is used to retrieve its translated version through the same `gettext("The literal message")` call. However, it is not mandatory. Hence, a piece of code like `if %F, gettext("The literal message"), end` will never execute the `gettext` call, but is nevertherless meaningfull: It is here only to make the message visible to the `xgettext` scanner/harvester. Then, somewhere else in the code, it will be possible to use `msg = "The literal message"; gettext(msg)` to return the translated version.

As well, using several times the same literal `gettext("The literal message")` call won't be rare, for example to retrieve the translation in several files. In this case, the `xgettext` harvester will collect the message only once, and the same translation will be returned for all calls.

Limitations: There is no way to translate a message

- in a language other than the current session's one. If this is really required, the session's language will have to be temporarily switched to the desired language, then `gettext()` called, and at last the initial session's language restored.

- from a translated version rather than from the message id in english.

### msgid messages

To be correctly processed, english messages must comply with a few rules:

- Literal harvestable messages may be indifferently delimited with single or double quotes.

  When `gettext()` or `_()` is used in a XML or XSL file such as in the preferences files of an external module, the literal domain string and the literal message id must no longer be delimited.

  For instance, `_(my_domain, My message)` will be used instead of `_("my_domain", "My message")`.

  In addition, spaces before and after the messageID are then never taken into account and never belong to the message. For example, the actual message ID corresponding to `_(my_domain, My message: )` is `"My message:"`, not `" My message: "`.

- Double quotes must be avoided in the body of messages. Please use single quotes.

- Inner escaped sequences like "\t" "\n" etc are not interpreted, neither by the `xgettext` scanner nor by the `gettext` function. They are collected and stored as is in the messages.

- msgid messages often include some "%" C-like placeholders aiming to be replaced with some custom input data through `msprintf()` at run time. For instance, `msprintf("Hi %s, could you come at %s today?", "John", "12:30")` will return `"Hi John, could you come at 12:30 today?"`.

### msgstr translated messages

This section will be mainly useful to persons contributing to messages translation.

Special characters and sequences:

1. When a trailing \n is used in the `msgid`, it must also end the `msgstr` translated message.

2. ' single quotes are regular characters in `msgstr` strings. They do NOT need any special care or protection.

3. " double quotes must be protected with \".

4. When some C-like placeholders like %s, %d, %f, or some C-like escaped sequences \n, \r, or \t are used in the `msgid` string, this means that later in the code the corresponding `translated` `msgstr` string is processed with `msprintf()` to replace placeholders with actual values, and/or to interpret \n as a new_line character, \r as a carriage-return, \t as a tabulation, etc. Hence, all the C-like printf rules apply on the considered `msgstr`, and translaters must take a major care to them.
   - All and Only the placeholders used in the `msgid` must also appear in the `msgstr`.

   - If placeholders are not numbered: they must appear in the same order in the `translated` `msgstr` message than in the `msgid` message. Example (english to french):

         msgid  "%s is %d year old.\n"
         msgstr "%s a %d ans.\n"
         // first %s (a string), second %d (a decimal number), in both strings.

         Then, from a Scilab session set to french language, we will get
         gettext("%s is %d year old.\n") // => "%s a %d ans.\n"
         // and                                     first v      v second
         t = msprintf(gettext("%s is %d year old.\n"), "Peter", 18)
         // => "Peter a 18 ans."

   - Otherwise, All placeholders must be numbered in the `translated` message. Each numbered placeholder can be used only once in the format (Scilab limitation). Example (english to japanese):

         msgid "%s: Wrong number of input argument: %d to %d expected.\n"
         msgstr "%1$s: 入力引数で不正な数: %3$d への %2$d を想定します。\n"

         Then,
         msprintf(gettext("%s: Wrong number of input argument: %d to %d expected.\n"), "myFun", 2, 5)

         will return in a Scilab session set to english
         "myFun: Wrong number of input argument: 2 to 5 expected."

         while, in a Scilab session set to japanese
         "myFun: 入力引数で不正な数: 5 への 2 を想定します。"

5. \n, \r, \t sequences fortuitously appearing in a `msgid` ending with "\n" should be considered as prone to issues, due to a lack of care from the programmer. This can occur for instance when some literal paths are part of the `msgid`, like in

   `msgid "Please use the file C:\users\you\net\record\time.txt instead.\n"`

   The trailing `\n` suggests that the corresponding `msgstr` will be processed by `msprintf()` or so. Being done, `\n` in `\net`, `\r` in `\record` and `\t` in `\time.txt` will be also -- unexpectedly -- interpreted. These `\` will have to be protected in the `msgstr`, even in the reference version (often english):

   `msgstr "Please use the file C:\users\you\\net\\record\\time.txt instead.\n"`:

## Examples

    setlanguage("fr");

    // Messages ids are case-sensitive:
    // "monday" is not an indexed native msgid, while "Monday" is:
    gettext(["monday" ; "Monday"])

    //_() is an alias of gettext():
    _("Sunday")

    // In Scilab, messages ids are in english:
    setlanguage("ru");
    _("Dimanche")       // The french version can't be used as msgid
    _("Sunday")

    --> gettext(["monday" ; "Monday"])
     ans  =
    !monday  !
    !Lundi   !

    --> _("Sunday")
     ans  =
     Dimanche

    --> setlanguage("ru");
    --> _("Dimanche")
     ans  =
     Dimanche

    --> _("Sunday")
     ans  =
     Воскресенье

Using a domain: Here, "tbx" is a predefined domain used to test translation features:

    setlanguage("fr");
    msg = "%s: something not in Scilab.\n";  // unknown in the main native Scilab domain:
    gettext(msg)          // So, it is returned as is.

    //  If we use the domain without having declared it before,
    //  Scilab does not know where to find the translation:
    gettext("tbx", msg)   // The input message is still returned as is.

    // So, let's declare the domain:
    addlocalizationdomain("tbx", "SCI/modules/localization/tests/unit_tests/locale");
    gettext("tbx", msg)   // Now it works

    // The answer is a joke: Here it is still in english (while french is expected).
    // The point is that it has been set as the french translation of the input msgid.

    // The same msgid can be used as a native Scilab one with its native translations,
    // and in one or several domains, with other translations:
    msg = "%s: No more memory.\n";
    [_(msg) ; _("tbx", msg)]

    --> msg = "%s: something not in Scilab.\n"; // unknown in the main native Scilab domain:
    --> gettext(msg)
     ans  =
     %s: something not in Scilab.\n

    --> //  Scilab does not know yet where to find the translation:
    --> gettext("tbx", msg)
     ans  =
     %s: something not in Scilab.\n

    --> // We declare the domain:
    --> addlocalizationdomain("tbx", "SCI/modules/localization/tests/unit_tests/locale");
    --> gettext("tbx", msg)   // Now it works
     ans  =
     %s : it is true, that is not in Scilab.\n

    --> msg = "%s: No more memory.\n";
    --> [_(msg) ; _("tbx", msg)]
     ans  =
    !%s : Plus de mémoire disponible.\n    !
    !%s : Overwrite Scilab translation.\n  !

Messages with numbered C-like placeholders:

    in = getlanguage();
    msg = "%s: Unknown value %s for %s option"; // has already some translations
    setlanguage("en");
    t = gettext(msg)
    msprintf(t, "setdiff", "''f''", "''direction''")

    setlanguage("ja");
    t = gettext(msg)
    msprintf(t, "setdiff", "''f''", "''direction''")

    setlanguage(in);

    --> setlanguage("en");
    --> t = gettext(msg)
     t  =
      "%s: Unknown value %s for %s option"

    --> msprintf(t, "setdiff", "''f''", "''direction''")
     ans  =
      "setdiff: Unknown value 'f' for 'direction' option"

    --> setlanguage("ja");
    --> t = gettext(msg)
     t  =
      "%1$s: %3$s オプション の未知の値 %2$s"

    --> msprintf(t, "setdiff", "''f''", "''direction''")
     ans  =
      "setdiff: 'direction' オプション の未知の値 'f'"

## See also

- printf_conversion
- msprintf
- setlanguage
- addlocalizationdomain
- tbx_generate_pofile
- tbx_make . localization
- tbx_build_localization
- GNU gettext website

## History

5.5.0 Domain management added.
6.1.0 C-like placeholders in messages can now be numbered.