2021-05-31
md
Traduction de programmes Free Pascal/Lazarus
<-Traduction d'applications de type console dans Free Pascal

Il n'y a pas très longtemps, j'ai téléchargé un ensemble d'utilitaires de ligne de commande dans un référentiel nommé poutils sur GitHub. Ces utilitaires, qui manipulent les fichiers de traduction PO (extention .po) créés automatiquement par l'EDI Lazarus lorsque l'internationalisation est activée, sont écrits en Free Pascal. Cependant, ils ont été initialement écrits en Delphi de manière désordonnée pendant un certain nombre d'années lorsque j'ai utilisé dxgettext pour traduire certaines applications. Rétrospectivement, il aurait été préférable d'être plus patient avant de télécharger ces programmes, en attendant d'avoir examiné le système de tra duction GNU gettext de plus près. Ce texte présente certaines des informations obtenues à partir du projet GNU et d'expérimentations pour voir comment Free Pascal et Lazarus implémentent gettext.

Il existe une unité appelée gettext dans la bibliothèque d'exécution de Free Pascal. Elle fournit des procédures pour traduire des chaînes de ressources à partir de fichiers MO qui sont des fichiers PO compilés. Ce n'est qu'une petite partie de ce qui est appelé l' implémentation Lazarus de [GNU] gettext ci-dessous. Espérons que cela ne créera pas de confusion.

Table des matières

  1. Fichiers PO
  2. Exemple Lazarus simple
    1. Dans les coulisses
    2. Première traduction
    3. Seconde traduction
  3. Avec un peu d'aide
  4. Traduction étendues
  5. Cas pathalogiques
  6. Contexte des messages et fichiers MO
  7. Caveat
  8. Références et conclusion

Fichiers PO toc

Un fichier PO, on dit aussi un catalogue gettext, est un fichier texte qui contient une liste de chaînes utilisées dans un programme avec leur traduction dans un autre langage naturel. Chaque langue dans laquelle un programme a été traduit a son propre fichier PO. Le système original de gettext (et dxgettext) utilisait une hiérarchie complexe de répertoires pour identifier le langage naturel trouvé dans chaque fichier. Comme elle le fait souvent, l'équipe Lazarus a simplifié l'approche et la convention est désormais de stocker tous les fichiers de traduction dans un sous-répertoire nommé languages or locale. La langue naturelle de chaque traduction est spécifiée avec un suffixe de code de langue (généralement un code à deux lettres, mais il y a des exceptions) avant l'extension de fichier qui est invariablement .po. Ainsi app.fr.po et app.sp.po sont, respectivement, les traductions française et espagnole de l'application app.

Un fichier de traduction est composé d'entrées pour chaque chaîne à traduire. Chaque entrée doit contenir une paire de chaînes: la chaîne non traduite, marquée du mot clé msgid, suivie de sa traduction marquée du mot clé msgstr. Lorsqu'un programme est lancé, chacune de ses chaînes non traduites est remplacée par sa traduction sauf si la chaîne traduite est vide. Il s'agit d'une opération ponctuelle qui ne devrait pas avoir d'incidence sur le temps d'exécution par la suite. La syntaxe de l'entrée de base est la suivante.

msgid "chaîne-non-traduite" msgstr "chaîne-traduite"

Les guillemets d'ouverture et de fermeture ne font pas partie de la chaîne, mais sont utiles, car ils autorisent les chaînes avec des espaces de début ou de fin. Si aucune traduction n'est disponible, l'entrée serait

msgid "chaîne-non-traduite" msgstr ""

En toute logique, le champ msgid ne peut pas être vide, contrairement au champ msgstr. Il y a une seule exception dite l'entrée d'en-tête dont il sera question plus tard.

Pour plus de commodité, les longues chaînes peuvent être divisées en morceaux de taille gérable.

msgid "" "premier-morceau de la chaîne-non-traduite" "second-morceau de la chaîne-non-traduite" msgstr "" "premier-morceau de la chaîne-traduite" "second-morceau de la chaîne-traduite"

Je pense que la forme suivante est tout aussi valable, mais elle n'est généralement pas utilisée.

msgid "premier-morceau de la chaîne-non-traduite" "second-morceau de la chaîne-non-traduite" msgstr "premier-morceau de la chaîne-traduite" "second-morceau de la chaîne-traduite"

Les divisions entre les morceaux n'ont pas de signification. La chaîne non traduite sera une concaténation de tous les morceaux, donc la chaîne non traduite dans le dernier exemple est

premier-morceau of the chaîne-non-traduitesecond-morceau of the chaîne-non-traduite

De la même manière, il n'est pas nécessaire que la chaîne traduite soit divisée en le même nombre de morceaux, ou que les morceaux correspondent et ainsi de suite. Bien sûr, il est possible d'avoir des chaînes multilignes dans un programme, par exemple lorsqu'une étiquette a une légende de trois lignes. Cela peut être fait en identifiant les fins des lignes avec la séquence d'échappement habituelle \n, comme illustré dans l'exemple suivant.

msgid "first line\nsecond line\nof the untranslated-string" msgstr "première ligne\nsecondeligne\nde la chaîne traduite"

Encore une fois, cela pourrait être écrit en morceaux, chaque morceau se terminant par la nouvelle séquence d'échappement de fin de ligne pour rendre la disposition de la chaîne plus évidente

msgid "" "first line\n" "second line\n" "of the untranslated-string" msgstr "" "première ligne\n" "secondeligne\n" "de la chaîne traduite"

En fait, « chaîne non traduite et chaîne traduite [respectent] la syntaxe C pour une chaîne de caractères, y compris les guillemets et les séquences intégrées d'échappement de type \. » selon la documentation GNU. Les séquences d'échappement de base, soit \n pour une nouvelle ligne, \t pour une tabulation, \" pour un guillemet double et \\ pour le caractère d'échappement \ fonctionneront en Free Pascal. Je n'ai pas examiné l'utilisation des séquences d'échappement plus ésotériques.

La spécification GNU inclut d'autres éléments dans une entrée. Voici une définition d'une entrée standard.

    white-space
    #  translator-comments
    #. extracted-comments
    #: reference…
    #, flag…
    #| msgctxt previous-context
    #| msgid previous-untranslated-string
    msgctxt context
    msgid untranslated-string
    msgstr translated-string
white-space (espace blanc)
Alors que les outils gettext, y compris l' implémentation Lazarus , génèrent une seule ligne vide entre les entrées, je pense que c'est facultatif.
# translator-comments (commentaires-traducteurs)
Ces commentaires faits par les traducteurs selon leurs propres besoins ne sont jamais créés par le système gettext.
#. extracted-comments (commentaires-extraits)
Il s'agit de commentaires provenant du code source de l'application. Ces commentaires ne sont jamais créés par l'implémentation Lazarus.
#: référence... (référence...)
Les références sont des « références au code des programmes » facultatives selon la documentation GNU. Dans l'implémentation Lazarus il s'agit du nom qualifié de la chaîne ressource non traduite. Cette référence est un élément essentiel du système de traduction de Free Pascal.

Si une application a une fiche de type TForm2 avec un composant TLabel nommé Label5 dont la légende est «Valeur», alors le fichier PO généré contiendrait ces deux entrées au minimum.

#: tform2.caption msgid "Form2" msgstr "" #: tform2.label5.caption msgid "Value" msgstr ""

Bien entendu, ces références sont garanties uniques si deux conditions sont remplies: le programme se compile sans erreur et aucun humain n'a modifié le fichier PO généré. Malheureusement, cette dernière condition ne s’applique pas au domicile de l’auteur, d'où la création des utilitaires poutils.

#, flag (options)
D'après la documentation GNU gettext, je suppose qu'une ligne d'options ressemblerait à ceci:
#, fuzzy, chaîne-format [, chaîne-format]

où les chaîne de formatage (qui pourrait être c-format, no-c-format, python-format, no-python-format, etc.) précisent le type de symbole de formatage (des choses comme %s, %d, %2.8f, etc.) utilisé dans la chaîne non traduite. Autant que je sache le système d'internationalisation de Lazarus ne prend pas en charge ces chaînes.

En revanche, l'option fuzzy est pris en charge. Sa présence indique que la traduction ne sera pas utilisée. Le système gettext ajoute l'option fuzzy dès qu'un conflit possible entre traductions est décelé. Le système gettext n'enlève jamais ce mot clé, il faut qu'un traducteur l'élimine pour ainsi valider la traduction.

#|, msgctxt previous-context (ancien contexte)
Ancien contexte utilisé pour éliminé l'ambiguité d'une traduction. Je n'ai pas vu de tels éléments dans un fichier PO généré par l'EDI Lazarus, mais cela ne prouve pas qu'ils ne sont jamais présents.
#|, msgid previous-untranslated-string (ancienne chaîne non traduite)
Ancienne chaîne non traduite. Le système gettext, incluant celui de Lazarus, conserve dans ce champ toute version précédente d'une chaîne non traduite.
msgctxt context (contexte)
Un contexte est utilisé pour résoudre l'ambiguïté chaque fois qu'une chaîne non traduite apparaît plus d'une fois dans un fichier PO. Un contexte unique doit être ajouté à chaque entrée pour les différencier. L'EDI Lazarus génère automatiquement tout contexte requis, mais ce n'est que le champ référence obligatoire (soit le nom qualifié de la chaîne ressource) entre guillemets.

Dans GNU gettext, toutes les champs commençant par le caractère de commentaire "#" sont facultatifs. Dans l'implémentation Lazarus, le commentaire de référence commençant par "#:" est obligatoire sinon l'entrée sera traitée comme un en-tête ou ignorée si un en-tête a déjà été défini. Les autres types de commentaires sont facultatifs dans l' implémentation de Lazarus.

L'entrée d'en-tête, qui doit être le premier enregistrement du fichier PO, est la seule entrée qui a un champ msgid vide Le champs msgstr contient des métadonnées. Voici l'entrée de base créée par l'EDI.

msgid "" msgstr "Content-Type: text/plain; charset=UTF-8"

Il n'est pas rare de rencontrer des en-têtes avec beaucoup plus d'informations.

msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Project-Id-Version: \n" "MIME-Version: 1.0\n" "CContent-Transfer-Encoding: 8bit\n" "Language: fr_FR\n"

De plus, on ajoute l'identité du traducteur et la date de la traduction, etc. Mais ce qui est inclus dépasse le cadre de cet article.

Aussi en dehors de ce qui sera examiné dans cet article, il y a le troisième type d'entrée qui traite des formes plurielles. Ce n’est que de la paresse de ma part, mais peut-être que le sujet sera réexaminé à l’avenir.

Exemple Lazarus simple toc

Expérimentons les capacités d'internationalisation de Lazarus avec un programme simple qui ne fait rien d'autre que d'afficher trois légendes et de se fermer lorsque le bouton est enfoncé. Voici sa fiche principale dans le concepteur de l'EDI.

Il contient une étiquette et un bouton avec du texte qui sera traduit. La légende du bouton est définie sur «Close» dans le concepteur, tandis que la légende de l'étiquette est définie au moment de l'exécution dans l'événement FormShow. Sa valeur est égale au contenu de la chaîne de ressources SHello définie dans le code source. Comme c'est souvent le cas, la langue naturelle est l'anglais à la conception du programme. Ceci n'est nullement nécessaire. D'ailleurs, l'utilitaire poswap a été conçu pour faciliter le changement du langage initial des programmes.

unit main; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Label1: TLabel; procedure Button1Click(Sender: TObject); procedure FormShow(Sender: TObject); private public end; var Form1: TForm1; implementation {$R *.lfm} Resourcestring SHello = 'Hello'; procedure TForm1.FormShow(Sender: TObject); begin label1.caption := SHello; end; procedure TForm1.Button1Click(Sender: TObject); begin close end;

Voici la fiche lorsque le programme est compilé puis activé.

Activez l'internationalisation dans l'EDI. Pour ce faire, ouvrez la boîte de dialogue Options pour le projet: en appuyant sur la combinaison de touches CtrlShiftF11 ou en naviguant dans le système de menus: Projet/Options du projet.... Sélectionnez l'option i18n dans le panneau de gauche, puis cochez la case Activer i18n. Le Répertoire de sortie PO: doit également être spécifié.

Tel qu'affiché, j'ai spécifié le répertoire languages, mais ce n'est que pour faciliter l'examen de l'implémentation Lazarus de gettext. Normalement, j'utilise le répertoire langs et j'évite d'utiliser languages ou locale parce que ce sont les répertoires traditionnels qui contiennent les fichiers de traduction distribué ou créé manuellement. Il ne serait pas judicieux de laisser l'EDI Lazarus écraser les fichiers PO corrigés.

Générez le projet et un fichier nommé test.po sera créé dans le répertoire languages lors de la compilation du programme. Voici son contenu.

msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" #: main.shello msgid "Hello" msgstr "" #: tform1.button1.caption msgid "Close" msgstr "" #: tform1.caption msgid "Test" msgstr "" #: tform1.label1.caption msgid "Label1" msgstr ""

Il s'agit d'un fichier de traduction correctement formé avec un en-tête comme première entrée et des entrées valides supplémentaires pour les composants visuels et les chaînes de ressources. Chacun de ceux-ci commence par une référence identifiée par le marqueur #:. La référence est le nom complet de la propriété text en minuscules. Le champ msgid contient la chaîne non traduite entre guillemets. Le champ msgstr qui peut éventuellement contenir la chaîne traduite est vide. C'est au traducteur de fournir les traductions manquantes.

Étant donné que ce fichier n'a pas de traduction, on dit qu'il s'agit du modèle de traduction. En tant que tel, il serait probablement nommé test.pot dans les systèmes gettext standard, mais les concepteurs de Lazarus a choisi de ne pas utiliser l'extension .pot. Peut-être que l'utilisation de l'extension serait un problème dans les systèmes avec Microsoft Office qui utilise l'extension à d'autres fins.

Dans les coulisses toc

L'EDI a également créé un fichier nommé test.ljr et l'a enregistré au côté du cose source main.pas.

{"version":1,"strings":[ {"hash":371876,"name":"tform1.caption","sourcebytes":[84,101,115,116],"value":"Test"}, {"hash":86477809,"name":"tform1.label1.caption","sourcebytes":[76,97,98,101,108,49],"value":"Label1"}, {"hash":4863637,"name":"tform1.button1.caption","sourcebytes":[67,108,111,115,101],"value":"Close"} ]}

Comme on peut le voir, il s'agit d'un fichier au format JSON. Il est clair que la clé "name" correspond au champ #: (référence) et que la clé "value" correspond au mot clé msgid des enregistrements dans le catalogue .po. En changeant le texte du composant Label1 en "été", il était possible de déterminer que le tableau d'octets stockés associé à la clé "sourcebytes" est le codage UTF-8 de la chaîne non traduite tandis que la "valeur" est stockée sous forme de chaîne avec des valeurs d'échappement Unicode pour tous les caractères non ASCII.

{"hash":13504729,"name":"tform1.label1.caption","sourcebytes":[195,169,116,195,169],"value":"\u00E9t\u00E9"},

Après avoir changer le nom du composant Label1 à Label2 dans l'EDI et après avoir engendré un nouveau fichier test.ljr en compilant l'application, on ne note aucun changement dans la valeur du champ "hash". On peut donc conclure que ce champ est l'empreinte numériquede la chaîne non traduite uniquement.

{"hash":13504729,"name":"tform1.label2.caption","sourcebytes":[195,169,116,195,169],"value":"\u00E9t\u00E9"},

Voilà donc le contenu du fichier décrit, mais quel est sa raison d'être? Probablement qu'il est utilisé par l'EDI pour suivre les modifications lors de la reconstruction des fichiers PO. Il se passe beaucoup plus de choses en arrière-scène lorsque des traductions sont ajoutées, mais ces fichiers .lrj méritent une mention spéciale, car il «... est très important que vous incluiez [les fichiers .ljr] avec votre code source dans le système de version que vous utilisez, ne les ajoutez pas dans les listes de fichiers à ignorer (tel .gitignore), sinon vos traductions seront interrompues.» (Source: Translations / i18n / localizations for programs)

Première traduction toc

Comme première expérience, copiez le fichier modèle vers test.fr.po dans le même répertoire et ajoutez-y les traductions pour la chaîne de ressources et le bouton.

msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" #: main.shello msgid "Hello" msgstr "Allô" #: tform1.button1.caption msgid "Close" msgstr "Fermer" #: tform1.caption msgid "Test" msgstr "" #: tform1.label1.caption msgid "Label1" msgstr ""

Pour tout contrôler, j'ai utilisé un simple éditeur de texte pour ajouter les deux mots «Allô» et «Fermer», mais on peut se servir de poedit ou un utilitaire de traduction. Dans ce cas, attendez-vous à avoir un en-tête beaucoup plus gros, mais cela n'a aucune conséquence réelle. Après ces modifications, voici comment essayer d'afficher le programme en français qu'importe les paramètres linguistiques du système.

michel@hp:~/Documents/Lazarus_projects/i18n$ ./test --lang fr
ou dans Windows
michel@hp:~\Documents\Lazarus_projects\i18n> test --lang fr

La démarche s'est révélée décevante, car les traductions ne sont pas affichées. L'ingrédient manquant est l'unité DefaultTranslator qui doit être ajoutée à la clause uses de l'unité principale.

unit main; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, DefaultTranslator; ...

Maintenant, le code nécessaire pour rechercher le fichier test.fr.po (ou test.fr.mo) et l'utiliser pour traduire l'application sera inclus. Compilez la source modifiée et relancez le programme test.

Cette fois, l'étiquette et la légende du bouton sont traduites. Cela montre à quel point il est simple d'internationaliser les programmes Lazarus. Le travail ardu consiste à traduire le fichier PO de l'application.

Seconde traduction toc

Copiez le fichier modèle test.po vers un second fichier PO, soit test.es.po, dans le même répertoire et ajoutez-y les traductions pour la chaîne de ressources et le bouton.

msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" #: main.shello msgid "Hello" msgstr "Hola" #: tform1.button1.caption msgid "Close" msgstr "Cerca" #: tform1.caption msgid "Test" msgstr "" #: tform1.label1.caption msgid "Label1" msgstr ""

En raison de lacunes béantes quant à la plupart des langues, cette traduction très compliquée a été réalisée à l'aide du Web avec tout son potentiel d'erreurs inhérent. Mes excuses aux hispanophones si les traductions n'ont pas de sens. Pour profiter de cette nouvelle traduction, il n'y a pas besoin de recompiler l'application, il suffit de lancer avec l'option appropriée: test --lang es.

Cela révèle la puissance de ce système. N'importe qui peut traduire les chaînes de ressources d'un programme internationalisé s'il est fourni avec un fichier modèle correct.

Le système est en fait meilleur que ce qui a été montré. Un utilisateur n'aura normalement pas besoin de spécifier la langue à utiliser. L'unité DefaultTranslation utilisera les paramètres régionaux du système pour charger le fichier de langue approprié s'il existe. La langue de mon système est fr_CA soit le français comme langue et le Canada comme région. À défaut de trouver une version française régionalisée nommée test.fr_CA.po, le programme chargera la traduction test.fr.po lorsque le programme test sera lancé sans option --lang sur la ligne de commande (voir la mise en garde ci-dessous).

michel@hp:~$ locale LANG=fr_CA.UTF-8 LANGUAGE=fr_CA:fr ... michel@hp~$ Documents/Lazarus_Projects/i18n/test

Si un utilisateur français ou espagnol voulait voir la langue d'origine, le --lang paramètre peut être utilisé pour contourner la sélection automatique du fichier de langue.

michel@hp~$ Documents/Lazarus_Projects/i18n/test --lang en or michel@hp~$ Documents/Lazarus_Projects/i18n/test --lang bizarro

Utilisez en, bizarro ou toute autre «langue» pour laquelle un fichier de traduction n'est pas fourni. Dans ce cas DefaultTranslation ne chargera aucun fichier de traduction et les chaînes seront affichées dans la langue par défaut. Une autre façon d'atteindre le même objectif est de renommer le répertoire languages en quelque chose qui n'est pas recherché.

Avec un peu d'aide toc

L'implémentation gettext de Lazarus fournit une aide bienvenue en suggérant des traductions lorsque cela est possible. Modifiez le titre de la fiche Form1 en "Hello" dans l'EDI avec le concepteur de fiches. Construisez le programme et exécutez-le en spécifiant que la traduction française doit être utilisée. Le résultat est probablement sans surprise.

Le titre de la fiche «Hello», car c'est ce qui a été spécifié dans le concepteur de fiches. Cependant, regardez le fichier de traduction en français qui a été mis à jour par l'EDI.

msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" #: main.shello msgctxt "main.shello" msgid "Hello" msgstr "Allô" #: tform1.button1.caption msgid "Close" msgstr "Fermer" #: tform1.caption #, fuzzy msgctxt "tform1.caption" msgid "Hello" msgstr "Allô" #: tform1.label1.caption msgid "Label1" msgstr ""

Il y a maintenant une traduction suggérée pour le titre de la fiche. Mais ce n'est qu'une proposition, car l'option fuzzy a été ajoutée à l'enregistrement tform1.caption. Cet indicateur est utilisé par le mécanisme de traduction de l'application pour ignorer la traduction suggérée et utiliser la chaîne non traduite qui dans ce cas était "Hello". L'option fuzzy est également un signal pour le traducteur que l'entrée doit être révisée. Supprimez la ligne #, fuzzy du fichier test.fr.po et relancez l'application. Pas besoin de le recompiler. La légende du formulaire sera traduite.

Notez l'ajout de la ligne de contexte msgctxt dans les deux enregistrements du fichier PO qui ont le même msgid égal à "Hello". Il en sera question plus loin.

L'IDE a fait la même chose, mutatis mutandis, avec le fichier de traduction espagnole. Il aurait fait la même chose avec tous les autres fichiers PO dans le Répertoire de sortie PO.

Encore une fois, tout cela est très utile. Vraisemblablement, le programmeur pourrait transmettre les fichiers PO aux traducteurs et leur demander de regarder les «fuzzy». Ces derniers pourraient décider que la traduction suggérée est correcte et simplement supprimer la ligne #, fuzzy dans l'enregistrement. Au lieu de cela, ils pourraient décider que la traduction suggérée n'est pas correcte dans les circonstances données et saisir la traduction correcte dans le champ msgstr puis supprimer l'indicateur #, fuzzy.

Traductions étendues toc

Au lieu de supprimer l'indicateur fuzzy, supprimons du catalogue l'enregistrement pour la traduction du titre de la fiche puis exécutons l'application. Le titre de la fiche du programme sera toujours traduit.

msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" #: main.shello msgid "Hello" msgstr "Bonjour" #: tform1.button1.caption msgid "Close" msgstr "Fermer" #: tform1.label1.caption msgid "Label1" msgstr ""

De toute évidence, face à une chaîne qui n'a pas de traduction spécifique, le système de traduction recherchera un enregistrement dans le fichier PO avec la même chaîne non traduite et s'il y en a un, sa chaîne traduite sera utilisée. Cela soulève un certain nombre de questions.

En tant que programmeur, j'ai souvent pensé qu'il était astucieux de simplifier le fichier de traduction en supprimant des enregistrements pour bénéficier de la fonction de traduction étendue décrite ci-dessus. Il faut éviter d'en faire autant, parce que parfois "Bonjour" est une traduction appropriée de "Hello", mais à d'autres moments "Allô" ou "Salut" pourrait être mieux. Laisser les enregistrements que le système gettext place dans le fichier PO modèle et laisser les traducteurs décider de la meilleure façon de traduire les chaînes non traduites dupliquées selon le contexte est une bien meilleure stratégie.

Cas pathologiques toc

Il n'est pas nécessaire de me suivre dans le terrier suivant, car il s'agit d'une enquête sur ce qui se passe si le fichier PO n'est pas correct. Le tableau suivant présente le titre d'une fiche en fonction du contenu du fichier PO. Le programme a été compilé une seule fois avec la chaîne de ressources pour le titre de la fiche, comme spécifié dans le fichier .lfm, étant fixé sur "Hello". Les tests, divisés en 6 groupes de quatre, sont uniquement des modifications du fichier PO. Dans les deux premiers groupes de tests, le fichier PO ne contenait que 2 entrées. Il y avait le premier enregistrement (entrée 0) d'en-tête qui n'apparaît pas dans le tableau. Pour les 4 premiers tests, le second enregistrement (entrée 1) est la traduction du titre de la fiche. Dans les deux premiers tests, le contenu du champ msgid est le même que la chaîne de ressources. Sans surprise, la légende affichée est telle qu'anticipée. Dans les troisième et quatrième tests du premier groupe, le contenu de msgid est incorrect. Néanmoins, la titre affichée n'est pas la valeur d'origine de la chaîne de ressources, ce qui montre l'importance du champ de référence dans les fichiers PO au sein de l'implémentation Lazarus.

Dans le deuxième groupe de tests (5 à 8), il n'y a pas d'enregistrement pour le titre de la fiche dans le fichier PO. En revanche, il y a un enregistrement référence à une entité non existante. Néanmoins, comme le montre le test 6, cette traduction sera utilisée pour traduire le titre. Comme le montrent les tests 7 et 8, l'enregistrement 1 n'a aucun impact lorsqu'il contient un msgid qui ne correspond pas au contenu du champ caption de la fiche.

Les troisième et quatrième groupes de tests (9 à 16) montrent qu'un autre enregistrement avec une chaîne non traduite égale au titre de la fiche n'a aucun impact sur une traduction potentielle de ce titre s'il existe un enregistrement avec la référence exacte au titre de la fiche. Et cela reste vrai, peu importe si ce second enregistrement pour le titre de fiche a une traduction ou non. L'ordre dans lequel les enregistrements apparaissent est sans conséquence.

Entrée 1Entrée 2Titre affiché
referencemsgidmsgstrreferencemsgidmsgstr
1tform1.caption"Hello"""Hello
2tform1.caption"Hello""Bonjour"Bonjour
3tform1.caption"bye"""bye
4tform1.caption"bye""Bonjour"Bonjour
5ref.nowhere"Hello"""Hello
6ref.nowhere"Hello""Allô"Allô
7ref.nowhere"bye"""Hello
8ref.nowhere"bye""Allô"Hello
9ref.nowhere"Hello""Allô"tform1.caption"Hello"""Hello
10ref.nowhere"Hello""Allô"tform1.caption"Hello""Bonjour"Bonjour
11ref.nowhere"Hello""Allô"tform1.caption"bye"""bye
12ref.nowhere"Hello""Allô"tform1.caption"bye""Bonjour"Bonjour
13tform1.caption"Hello"""ref.nowhere"Hello""Allô"Hello
14tform1.caption"Hello""Bonjour"ref.nowhere"Hello""Allô"Bonjour
15tform1.caption"bye"""ref.nowhere"Hello""Allô"bye
16tform1.caption"bye""Bonjour"ref.nowhere"Hello""Allô"Bonjour
17ref.nowhere"bye""Allô"tform1.caption"Hello"""Hello
18ref.nowhere"bye""Allô"tform1.caption"Hello""Bonjour"Bonjour
19ref.nowhere"bye""Allô"tform1.caption"bye"""Hello
20ref.nowhere"bye""Allô"tform1.caption"bye""Bonjour"Bonjour
21tform1.caption"Hello"""ref.nowhere"bye""Allô"Hello
22tform1.caption"Hello""Bonjour"ref.nowhere"bye""Allô"Bonjour
23tform1.caption"bye"""ref.nowhere"bye""Allô"bye
24tform1.caption"bye""Bonjour"ref.nowhere"bye""Allô"Bonjour

Les deux derniers groupes de tests me laissent perplexe. Comparez les tests 15, 19 et 23. Dans les trois cas, il existe une entrée valide pour le titre de la fiche avec la bonne référence tform1.caption. Même si le contenu msgid est incorrect, il ne devrait pas y avoir de traduction puisque msgstr est vide. Or dans deux cas, la chaîne non traduite du fichier PO est affichée et dans le troisième la chaîne non traduite originale est affichée. Il semble que la présence et l'ordre d'apparition de l'enregistrement avec une référence incorrect peuvent avoir un impact sur la traduction affichée. Ce n'est pas la seule anomalie rencontrée. Prenons cet exemple.

msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" #: main.shello msgid "Hello" msgstr "" #: reference.to.nowhere msgid "Hello" msgstr "Bonjour" #: tform1.button1.caption msgid "Close" msgstr "Fermer" #: tform1.label1.caption msgid "Label1" msgstr ""

Voilà qui est surprenant, car il semblait plausible que, étant donné la référence correcte à la chaîne de ressources SHello et son champ msgstr vide, celle-ci ne serait pas traduite. Or l'étiquette Label1 qui est fixé sur SHello affiche "Hello" et non "Bonjour".

Contexte des messages et fichiers MO toc

Comme mentionné ci-dessus, deux entrées avec la même chaîne non traduite dans un fichier PO sont considérées comme ambiguës et le système GNU gettext ajoute un champ de contexte de message, appelé msgsctxt, pour différencier les entrées. L'implémentation Lazarus en fait autant, mais le contenut du champ mgsctxt rajouté est invariablement le même que celui du champ de réference (#:) bien qu'il soit toujours entre guillemets. Cette duplication semble peu utile, mais en réalité cette approche satisfait une exigence de GNU gettext tout en préservant la dépendance de Lazarus sur le champ de référence.

Un fichier MO (extension .mo) est un fichier binaire compilé à partir d'un fichier PO. En principe, le fichier compilé est plus petit et plus rapide que le fichier .po. L'ensemble des outils fournis avec Lazarus n'inclut pas de compilateur, mais le projet GNU gettext en fournit un nommé msgfmt. Il est inclus par défaut dans Mint 20.1 et probablement dans de nombreuses autres distributions Linux, mais il n'est probablement pas inclus dans Windows. C'est peut-être pourquoi le wiki de Free Pascal recommande d'utiliser l'éditeur de traduction poedit pour créer des fichiers MO.

Ces deux compilateurs peuvent être utilisés pour générer des fichiers MO à partir de fichiers PO créés par l'implémentation Lazarus de gettext. Cependant, ils ignorent le champ de référence essentiel dans Lazarus. Or on sait que le champ msgctxt est utilisé pour fournir des traductions uniques lorsque les traductions sont ambiguës. En gardant les deux champs identiques, l'implémentation Lazarus assure la compatibilité avec le système gettext d'origine. Du moins, c'est mon interprétation de ce qui se passe et j'y tiens jusqu'à ce que des preuves du contraire soient fournies.

Il est révélateur que Lazarus ne fournit pas de compilateur. Ce l'est encore plus qu'il n'est livré avec aucun fichier .mo compilé. Les nombreuses traductions de l'EDI lui-même sont stockées dans des fichiers PO uniquement. Pour mon cas d'utilisation, distribuer des fichiers MO serait une erreur. Cela n'a de sens de le faire que lorsque l'on veut concentrer entre ses mains le contrôle des traductions, ce qui est précisément ce je ne peux pas faire.

Caveat toc

Une déclaration trompeuse a été faite au sujet de la recherche automatique de la version régionalisée d'une langue nationale en fonction des paramètres régionaux du système. Ce n'est pas tout à fait vrai. Comme je l'ai dit, la variable d'environnement LANG de mon système est fr_CA.UTF-8. En conséquence, le mécanisme de traduction automatique (activé avec l'inclusion de l'unité DefaultTranslator dans l'énoncé uses du programme) recherche le fichier test.fr_CA.UTF-8.po dans tous les dossiers habituels. Si un tel fichier n'est pas trouvé, la recherche est répétée pour un fichier nommé test.fr.po, mais une recherche pour un fichier nommé test.fr_CA.po n'est jamais effectuée. Je ne sais pas si cela doit être considéré comme un comportement normal ou si cela doit être considéré comme un bogue.

Ce problème est survenu en 2017 lorsque j'ai voulu a traduire des applications console. Dans mon unité unittranslator.pas qui remplace LCLTranslator pour éviter la dépendance de ce dernier vis-à-vis du paquet LCL, la fonction GetLang supprimera le suffixe d'encodage UTF-8 des paramètres régionaux du système. Cela fonctionne pour mes besoins, mais je ne sais pas si c'est une solution généralement acceptable.

Références and conclusion toc

La principale référence pour la traduction des programmes Lazarusest la page du wiki de Free Pascal intitulée Translations / i18n / localizations for programs. Il y a une traduction de cette page en français, mais elle est désuète. Des informations supplémentaires se trouvent dans la page Everything else about translations. Il existe de nombreuses autres pages wiki dédiées au sujet. Attention, certaines d'entre elles sont anciennes et contiennent des conseils qui peuvent être techniquement corrects, mais qui sont néanmoins obsolètes. Malheureusement, cet avertissement est valable pour la traduction en français de Translations / i18n / localizations for programs. Traduire les programmes Lazarus est vraiment aussi simple que décrit dans le wiki de référence principal ou comme expliqué ci-dessus.

Une documentation complète pour le système GNU gettext est disponible sous de nombreuses formes. J'ai trouvé que le manuel de l'utilisateur de Pology était également une ressource utile.

<-Traduction d'applications de type console dans Free Pascal