[2024-feb-29] Sad news: Eric Layton aka Nocturnal Slacker aka vtel57 passed away on Feb 26th, shortly after hospitalization. He was one of our Wiki's most prominent admins. He will be missed.

Welcome to the Slackware Documentation Project

Dit is een oude revisie van het document!


Internationalisatie en localisatie van shell scripts

Presentatie

Doel, reikwijdte en doelgroep

Dit document is bedoeld om ontwikkelaars, maintainers en vertalers te helpen bij het schrijven, onderhouden en vertalen van ge-internationaliseerde en dan gelocaliseerde shell scripts, met gebruikmaking van de gereedschappen die door GNU gettext worden geleverd.

Het referentiedocument is de handleiding getiteld GNU `gettext' utilities.

De handleiding behandelt alle programmeertalen die bruikbaar zijn met gettext, met een speciale focus op de taal 'C'.

De POSIX specificatie is aanbevolen leesvoer, met name de volumes Basis Definities en Shell en Gereedschappen.

In contrast met de handleiding blijft de reikwijdte van het onderhavige document beperkt tot shell scripts.

Theorie van de operatie

Het doel is om berichten (gewoonlijk stukken tekst) die de uitvoer zijn van shell scripts, weer te geven op het systeem van de gebruiker in diens voorkeurstaal.

De gebruiker geeft zijn/haar voorkeur aan door het instellen van de LANG of de LANGUAGE omgevingsvariabele (de laatste bevat een prioriteitenlijst van talen die voor het weergeven van berichten gebruikt mogen worden).

Het Internationalisatie proces (verkort tot I18N) bestaat uit:

  • de teksten markeren in de shell scripts die de te vertalen uitvoer-berichten vormen,
  • vervolgende de gettext gereedschappen inzetten om uit deze verzameling gemarkeerde scripts een berichten sjabloon-catalogus te vormen.

Een berichten sjabloon-catalogus wordt gewoonlijk een “Portable Object Template” of POT bestand genoemd.

Een POT bestand, leesbare platte tekst, bestaat voornamelijk uit de geëxtraheerde tekst-reeksen, vooraf gegaan door “msgid” wat zoveel betekent als “message identifier”, waatbij elk weer wordt gevolgd door een vertaling van dat bericht, dat wordt vooraf gegaan door de tekst “msgstr”.

Het Localisatie proces (verkort tot L10N) bestaat uit:

  • het genereren van aparte “Portable Object” of PO bestanden voor iedere doel-taal uit het enkele POT bestand,
  • alle “msgstr” teksten voorzien van vertalingen in ieder PO bestand,
  • deze PO bestanden controleren/verifiëren na de vertaalslag,
  • ieder PO bestand individueel compileren tot een “Machine Objet” of MO bestand.

De MO bestanden die bedoeld zijn voor de machine, niet de mens, vandaar de naam, worden traditioneel opgeslagen als:

/usr/share/locale/<locale>/LC_MESSAGES/<software naam>.mo

In het bovenstaande pad is <locale> een locale code in de vorm <tt[_LL]>, waar tt de twee-letterige code is van de doel-taal zoals gedefinieerd in de ISO 639-1 standaard, en LL (indien aanwezig) is de twee-letterige land-code van deze locale zoals gedefinieerd in ISO 3166.

Ieder gemarkeerd script moet het volgende commando bevatten:

export TEXTDOMAIN=<software naam>

Tijdens het uitvoeren van het script wordt zo 'gettext' in staat gesteld om het juiste MO bestand te vinden en iedere gemarkeerde bericht-tekst weer te geven in de voorkeurstaal zoals die door de LANG of LANGUAGE omgevings-variabele is bepaald.

Procesdiagrammen

Laten we aannemen dat een bepaalde software bestaat uit een set shell scripts die we willen internationaliseren enlocaliseren.

De onderstaande diagrammen tonen een overzicht van ieder onderdeel van de betrokken processen: internationalisatie, localisatie, gebruik en onderhoud.

Deze diagrammen zijn hybride, dwz ze tonen data zowel als acties.

Onder deze acties zijn uitvoering van een aantal programma's uit de gettext verzameling:

  • gettetxt: markeert de te internationaliseren tekstreeksen, en toont later de gelocaliseerde berichten tijdens de uitvoering van het script
  • xgettext: extraheert de gemarkeerde tekstreeksen uit een verzameling shell scripts om er een POT of een PO bestand van op te bouwen
  • msgcmp: controleert een PO bestand op consistentie aan de hand van een ander PO of POT bestand
  • msginit: schrijft een PO bestand met een POT bestand als de input
  • msgfmt: genereert een MO bestand met een POT bestand als de input
  • msgmerge: samenvoegen of wijzigen van PO of POT bestanden

In onderstaande diagrammen worden de gettext programma's omgeven door vierkante haken.

(1) Internationalisatie

 Verzameling shell scripts ───> Preparatie ───> Gemarkeerde shell scripts ───╮
                                                                             │
          ╭──────────────<── software.pot <─── [xgettext] <──────────────────╯
          │
          ├────> (2) Localisatie
          │
          ╰────> (4) Onderhoud

(2) Localisatie (voorbeeld met Frans en Nederlands als talen).

     ╭──────────────<── software.pot <── (1) Internationalisatie
     │
     │
     ├──> [msginit] ──> fr_FR.po ──> PO editor ──> fr_FR.po ──> [msgcmp] ──╮
     │                                                                     │
     │   ╭─ installatie <── fr_FR.mo <── [msgfmt] <─┬─ fr_FR.po controle <─╯
     │   │                                          │
     │   │                                          ╰────> (4) Onderhoud
     │   │
     │   ╰──> /usr/share/locale/fr_FR/LC_MESSAGES/software.mo ─> (3) Gebruik
     │
     ╰──> [msginit] ──> nl_NL.po ──> PO editor ──> nl_NL.po ──> [msgcmp] ──╮
                                                                           │
         ╭─ installatie <── nl_NL.mo <── [msgfmt] <─┬─ nl_NL.po controle <─╯
         │                                          │
         │                                          ╰────> (4) Onderhoud
         │
         ╰───> /usr/share/locale/nl_NL/LC_MESSAGES/software.mo ─> (3) Gebruik

(3) Gebruik

Veronderstel dat een van de scripts, “myscript.sh” het volgende commando bevat:

 gettext "Good morning"

en dat “Good morning” als volgt vertaald is in de berichten catalogi:

 /usr/share/locale/fr_FR/LC_MESSAGES/PACKAGE.mo ─> "Bonjour"
 /usr/share/locale/nl/LC_MESSAGES/PACKAGE.mo ─> "Goedemorgen"

Hier is wat de gebruiker zal zien afhankelijk van de instelling van de LANG omgevingsvariabele:

            ╭──────────────<── (2) Localisatie
            │
 LANG=fr_FR ├───> sh myscript.sh of ./myscript.sh ───> "Bonjour"
            │
 LANG=nl_NL ╰───> sh myscript.sh of ./myscript.sh ───> "Goedemorgen"
         

(4) Onderhoud

Het onderhoudsproces kan worden gestart door het creëren, wijzigen of verwijderen van een script.

In onderstaand diagram moet het onderdeel van het proces dat begint met het msmerge commando herhaald worden voor ieder beschikbaar PO bestand.

Het is daarom aan te raden om een actuele lijst bij te houden van alle beschikbare vertalingen in de vorm van PO bestanden.

      Shell scripts gewijzigd en gemarkeerd ───> [xgettext] ───> software.pot ──╮
                                                                                │
                                   (1) Localisatie ──> <locale>.po ────>┬<──────╯
                                                                        │
╭─ [msgcmp] <── <locale>.po <─ PO editor <── <locale>.po <─ [msmerge] <─╯
│
╰──> <locale>.po controle ─> [msgfmt] ─> <locale>.mo ─> installatie  ────╮
                                                                         │
                  /usr/share/locale/<locale>/LC_MESSAGES/software.mo <───╯
                   

Het onderhoudsproces kan ook gestart worden door een wijziging van een berichten catalogus voor een specifieke taal (om bijvoorbeeld een fout te corrigeren).

Deze variant van het proces is korter:

 
╭─ [msgcmp] <── <locale>.po <── PO editor <── <locale>.po <── Wijziging gewenst
│
╰─> <locale>.po controle ──> [msgfmt] ──> <locale>.mo ──> installatie  ──╮
                                                                         │
                   /usr/share/locale/<locale>/LC_MESSAGES/software.mo <──╯

Internationalisatie proces

Deze paragraaf is bedoeld voor ontwikkelaars en maintainers.

Het internationalisatie proces omvat de volgende taken:

  1. Bereid scripts voor op internationalisatie
  2. Markeer de tekstberichten die gelocalizeerd moeten worden
  3. Gebruik 'xgettext' om een sjabloon catalogus van de tekstberichten te produceren

Scripts ter internationalisatie voorbereiden

Deze taak is nodig voor shell scripts die nog niet aan de voorwaarden voldoen voor internationalisatie.

Technische notitie: Gettext vereisten voor shell scripts.

Onderstaande opsomming van vereisten is niet compleet.

Het noemt enkel de voornaamste die de ontwikkelaar of maintainer aangeraden worden, gebaseerd op de auteur's eigen ervaringen.

Gettext vervangt tijdens de uitvoering van het script (dwz 'runtime') tekstberichten die bijvoorbeeld worden uitgestuurd door:

  • Een “echo” commando of
  • een programma (zoals 'dialog' bijvoorbeeld)

met vertaalde tekst (die gevonden wordt in de berichten catalogus voor de taal die middels $LANG of $LANGUAGE ingesteld is)

Maar deze vervanging vindt alleen plaats wanneer aan de volgende voorwaarden voldaan is:

  • Een MO bestand is aanwezig in het pad dat samengesteld wordt uit de TEXTDOMAIN omgevingsvariabele tot <directorynaam>/<locale>/LC_MESSAGES/text_domain.mo.
    Om een voorbeeld te geven, als TEXTDOMAIN=software en $LANG=de_DE.utf8, dan zal gettext zoeken naar: <directorynaam>/de_DE/LC_MESSAGES/software.mo
    <directorynaam> kan worden ingesteld door de waarde van de TEXTDOMAINDIR omgevingsvariabele, en anders wordt een standaardwaarde gebruikt.
    In Slackware Linux bijvoornbeeld is de standaard waarde /usr/share/locale.
    Er zijn ook alternatieve lokaties: bijvoorbeeld als <locale> gelijk is aan “de_DE” dan kan het MO bestand ook worden geplaatst in <directorynaam>/de/LC_MESSAGES/ in plaats van <directorynaam>/de_DE/LC_MESSAGES/
  • TEXTDOMAIN variabele wordt geëxporteerd, voordat enig *gettext commando wordt uitgevoerd.
  • gettext.sh, dat de functies eval_gettext en eval_ngettext aanbiedt, wordt ge'source'd voor de eerste aanroep van een van deze functies.
  • Een “msgid” tekstreeks in het MO bestand past exact bij het argument voor gettext (of eval_gettext als de tekstreeks een parameter expansie bevat).
  • De corresponderende “msgstr” tekstreeks bevat geen backslash die gevolgd wordt door een spatie.
  • De “msgstr” tekstreeks string begint en eindigt met een newline - of niet - precies zoals de “msgid” het doet.
  • Indien de tekstreeks een parameter expansie bevat, moet eval_gettext worden gebruikt in plaats van gettext.
  • “De variabele namen moeten enkel bestaan uit alfanumerieke of underscore ASCII karakters, niet starten met een cijfer en niet leeg zijn; anders wordt zo een variabele referentie genegeerd.” (gettext handleiding)
  • Parameter expansies worden escaped met een enkele backslash zoals dit:
    \$parameter of \${parameter}
    tenzij de eval_gettext aanroep zich bevindt binnen een commando substitutie zoals dit:
    “`eval_gettext ”…“`” of “$(eval_gettext ”…“)“
    In het laatste geval zijn drie backslashes noodzakelijk zoals dit:
    \\\$parameter of \\\${parameter}.
  • Enkel de voorkomens $parameter en ${parameter} van een parameter expansie worden gebruikt binnen een eval_gettext argument (alle andere voorkomens zijn verboden).
  • Positionele parameters, speciale parameters en commando substituties worden *niet* gebruikt binnen een argument van gettext of eval_gettext.

As a practical consequence of the two last rules, it is advisable that all positional parameters, special parameters, command substitutions and not allowed forms of parameter substitutions be assigned upstream to named variables, then expanded in the text string argument of eval_gettext or eval_negettext.

Tip: if a text string has been included as a msgid in a catalog of messages and is assigned to a named variable in a script, then the commands: “gettext $parameter” and “gettext ${parameter}” will output the translated string at run time, even though 'xgettext' would discard that command when parsing the script, because 'gettext' is used instead of 'eval_gettext'. This can be handy. In this case the parameter expansion should not be escaped.

Markeer berichten om te lokaliseren

I recommend to mark messages:

  • arguments of a not redirected 'echo' command
  • arguments of redirected 'echo' commands whenever a further processing displays it on user's screen
  • arguments of other commands which displays the message, for instance the 'dialog' program

On the contrary I recommend not to mark:

  • comments intended for readers of the script,
  • text string whose value will be processed later, for instance as arguments of a 'case' compound command, or <tag> arguments of a dialog –menu' command.

Sometimes the shell script writes other shell scripts.

Then the developer or maintainer have to decide on a case by case basis what to mark depending on the intended scope of internationalization.

Gebruik 'xgettext' om een sjabloon-catalogus van berichten te produceren

The choice to produce only one POT file for the software as a whole or to make one POT files per set of scripts have to be made, considering for instance which choice will minimize maintenance work, how localizations work can be organized, relative frequency of updates for the different sets of scripts which comprise the software, and the relevance of distinguishing groups of features like setup vs configuration vs package management.

I'm inclined to produce only one POT file, but the choice is yours.

If the software comprises of numerous scripts located in different places or included in several packages, it can be handy to collect a copy of all scripts in a single directory, and/or to register in a text file a list of all of them with their paths.

The POT file will be generated using the 'xgettext' command (see the manual or 'xgettext –help' for details).

Include following options in the command:

-L Shell (of course!)
--strict (to facilitate checks and management of the messages catalogs)
-c       (to include comments useful for the translators in the POT file)
-n       (to identify the source file and the line number of each message.
         This is the default.)

Once the POT file is generated you could check that it includes entries for all *gettext invocation in shell script(s).

Localisatie proces

Once the POT file is available, the 'msginit' command writes a PO file for each target language.

In PO files the “msgid” strings should never be modified, otherwise the translation won't occur at run time.

The 'msgcmp' command allow to checks each PO file against the POT after translation, to make sure all messages are translated.

The translator can use the 'msgfmt' command to check the layout of the translated text.

The PO file should be carefully saved somewhere, as it will be needed for subsequent maintenance (it is still possible to 'msgunfmt' a MO file to re-create a PO file but then you would loose the context, which would make it almost useless).

The checked PO file is handed over to the maintainer, who runs 'msgfmt' to produce the MO file, then installs it.

Gebruik

The only thing the user will have to take care of is set up his preferred language(s).

The primary way to do that is setting the LANG environment variable.

This can be done at run time, preceding the command used to run the script with LANG=<locale>, but usually the user will set it up permanently.

For instance in Slackware Linux this will be done in editing the file(s) /etc/profile.d/lang.sh and/or /etc/profile.d/lang.csh (see these files).

The changes will be effective at next reboot.

I suggest to use an UTF-8 locale, as for reading this document.

If the user is polyglot, another option is to set gettext's specific LANGUAGE environment variable to specify a prioritized list of languages.

For instance, if LANGUAGE is set to 'de:fr' then a Deutsch translation will be used if available, else a French translation will be used if available, else messages will be displayed in the original language, usually English. See gettext's manual for details.

Onderhoud

In most cases the maintenance process will be triggered by a script's creation, modification or deletion.

In such a case the maintainer will generate a new POT file with 'xgettext' then hand it hover to the translators.

The translators will use the new POT file to update their respective (saved) PO files with the 'msgmerge –update' command.

Then they will edit/complete the translations, focusing on the not yet translated messages and on those marked as “fuzzy” in the PO files, using a PO editor.

After that the PO file will be checked against the POT file with 'msgcmp', carefully saved, handed over to the maintainer who will generate the new MO file with 'msgfmt' and install it as in the initial localization process.

The maintenance process triggered by a needed modification of a PO file for a specific language is similar, only shorter: it will begin with the update of the relevant PO file by the translator. To minimize the workload caused by this type of maintenance, I suggest that the maintainer demand that he or she be provided only with complete and well reviewed translations.

Practische aanbevelingen voor ontwikkelaars en maintainers

Many English words are polysemous: their meaning can only be determined from the context of their usage. As a practical consequence, the more context you provide, the more accurate the translation can be.

Example: recently, while downloading a software I saw something like this:
31min gauche
Go figure? After a while I realized that “left” had been translated “gauche” (as in “left hand”).

Also, order of words in a sentence vary upon language, furthermore not all languages are written left to right. Thus, mark entire paragraphs, or at least entire sentences, not lines, let alone isolated words but in special cases.

For instance, if text paragraphs were split in lines displayed by 'echo' commands, replace all consecutive 'echo' commands by a single 'gettext' or 'eval_gettext' command.

Do not fear to include the variable substitutions in the sentences, PO editor will check that they be present as is in the translations.

Aanbevelingen voor 'dialog' programma.

The 'dialog' program provides an UI taking the form of dialog boxes.

There are other programs with similar feature, to which I guess (only a guess), these recommendations are also applicable.

Bear in mind following considerations, when making or reviewing the design choices for dialog's boxes.

  • Messages translated in other languages will often be significantly longer than the original (usually in English) ones.
  • In situations where only VGA drivers are available (e.g. in text installers) screen display is generally restricted to 25 rows of 80 columns with most widely used fonts, but in practice word wrapping can occur if line's length is more than 74 characters.
    As a consequence, for static layouts text lines' length should be at most 74 characters.
  • Vertical scrolling of text is widely accepted as frequently used to display web pages, and sometimes unavoidable.
    On the contrary, horizontal scrolling should be avoided as much as possible.

Therefore I suggest to:

  • renounce to tightly adjust the dimensions of the boxes to the size of English text as the translation will probably break your carefully crafted layout, unless you impose unreasonable (IMO) constraints to the translators,
  • in particular, not narrow boxes' width to what is strictly needed for displaying English texts, especially in tabular layouts where the text can't flow on next lines,
  • favor a fluid layout of the displayed text over a fixed one to avoid too long lines in translations, whose complete display would then necessitate horizontal scrolling (which, moreover, is not always possible).

In particular, I recommend to favor options which take as first argument a text string instead of a file, to allow line wrapping. It is still possible to preserve the intended layout using white spaces for indentation.

For instance,
dialog <common-options> –textbox <file> <height> <width>
can be replaced with
dialog –no-collapse <common-options> –msgbox “`cat <file>`” <height> <width>

Practische aanbevelingen voor vertalers

Depending on amount of work needed and available resources, there can be one translator or a team of translators per target language. In all cases, I recommend that at least one person be responsible for organizing the team's work, checking the translations and transmitting the checked PO file to the maintainer(s). Let's call this person the team coordinator.

Don't feel obliged to translate verbatim. Not only is this rarely the best way to convey the meaning, but in addition this often leads to sentences too long to fit in allowed space.

Use a specialized PO editor, 'not' a general text editor. This will not only prevent inadvertently editing 'msgid' strings but also facilitate their work and automatize additional checks, as the presence of a variable in the translation with the same spelling as in the original.

While translating, choose a serif fixed width (or “monospaced”) font, like Courier. That allow to visually distinguish characters that otherwise would look the same, and check line's length when that matters.

If possible, check the layout of the messages. You could do that looking at the context in the relevant source file. Even better, simply run the translated script.

This is especially important if you are translating dialog boxes. In particular, take care not to write too long sentences on one single line if it appears that the text can't flow on next one.

Bear in mind that in VGA mode (used in text installers, in particular), line's width is limited theoretically to 80 characters, but practically often to 74.

Do not add question marks that are not present in the original message.

If the message refers to tags (text on the buttons) of dialog boxes, like “OK”, “Yes”, “NO”, “Continue”, “Cancel”, check how theses tags are translated in your language in dialog's interface and use the same words.

Avoid colloquialisms and technical slang.

To “cut” (or end) a line inside a “dialog” box you should type \n: pressing [Enter] will 'not' insert a “new line” character in the text viewed by user.

In addition, you will have to comply to gettext's requirements for it to work:

  • If a word beginning with a dollar sign is included in the original text it should be present in the translation with exactly the same spelling (case matters).
  • The translation text should include a “new line” character (or line feed, represented by “\n”) at the beginning or at the end, exactly as the original text does. Conversely, if the original text doesn’t have the character, then the translation shouldn’t have it.
  • A single backslash character “\” is not allowed in the translation.

To “cut” (or end) a line inside a “dialog” box you should type \n: pressing [Enter] will 'not' insert a “new line” character in the text viewed by user.

To check your translation against gettext's requirements you could run following command:

msgfmt -c <name of the PO file>

Waarschuwing over vertaling van man paginas

Behoud zorgvuldig de syntax van man pagina's zoals die in de Engelse markup gevonden wordt. Wat je bijvoorbeeld niet moet vervangen:

  • 'B<' door 'B <' (voeg geen spatie toe)
  • 'B<' door 'b<' (houd B als een hoofdletter - en vervang het ook niet door de Griekse hoofdletter BETA die er op het scherm identiek uitziet)
  • “I” door '|' (vervang de hoofdletter I niet door een pipe symbool)

Bewaar bij een vertaling van shell commando's de Engelse pad-namen waar nodig. Maar je mag (en moet) argumenten vertalen die bij uitvoering van het commando vervangen worden door een waarde of string - zoals 'packagename'.

Bronnen

Afdrukken/exporteren
 nl:howtos:misc:internationalization_and_localization_of_shell_scripts ()
Deze vertaling is ouder dan de originele pagina en kan verouderd zijn. Kijk wat er is veranderd.