Il s'avère que DefaultTranslator
ne
peut pas être utilisé dans les applications de type console. La fonction
SetDefaultLang
, de l'unité LCLTranslator
,
qui est utilisée pour basculer entre les fichiers de traduction tire
le paquet LCL
entier. Or SetDefaultLang
veut traduire toutes les fiches du projet. Bien sûr il n'y a pas de fiches
dans une application console, mais la fonction ne le sait pas.
J'ai donc décidé de retirer la traduction des fiches du code dans
LCLTranslator
et d'enregistrer cette version modifiée dans une
unité appelée UnitTranslator
. Cette nouvelle unité nécessite le
paquet LCLBase
au lieu du paquet LCL
complet.
Comportement douteux de SetDefaultLang dans LCLTranslator
Si l'on veut sélectionner manuellement une traduction française adaptée
à la Belgique d'une application (appelée app
) alors, il devrait
suffire d'appeler SetDefaultLang('fr_BE','')
. La fonction
recherchera un fichier .po ou .mo correspondant dans tous les "lieux
habituels". Cela signifie que les fichiers suivants sont recherchés :
- {appdir}/languages/fr_BE/app.po
- {appdir}/locale/fr_BE/app.po
- {appdir}/locale/fr_BE/LC_MESSAGES/app.po
- /usr/share/locale/fr_BE/LC_MESSAGES/app.po (sur système Unix)
- {appdir}/app.po
- {appdir}/languages/app.fr_BE.po
- {appdir}/locale/app.fr_BE.po
.mo
compilé est recherché
dans les mêmes endroits. Si aucun fichier .po
ou .mo
n'a été trouvé, la localisation sera réduite à la langue ('fr' dans ce cas) et
la recherche sera répétée:
- {appdir}/languages/fr/app.po
- ... [snip]
- {appdir}/locale/app.fr.po
C'est logique: si une traduction vers une variation territoriale d'une langue n'est pas disponible, alors aussi bien utiliser une traduction générique dans cette langue. Jusqu'ici tout va bien. Mais les choses peuvent aller mal.
Habituellement, l'unité DefaultTranslator
est ajoutée aux
applications qui sont fournies avec des fichiers de traduction. Elle se résume
à l'appel SetDefaultLang('','')
dans son initialisation.
Lorsqu'aucune langue n'est spécifiée, SetDefaultLang
essaie de
trouver la langue du système, puis effectue la recherche décrite ci-dessus.
Dans Linux, cette fonction examine les variables d'environnement 'LANG',
'LC_ALL' et 'LC_MESSAGES'
dans cet ordre. Sur mon système, 'LANG' contient «fr_CA.UTF-8», qui est
une langue (fr pour le français), une région (CA pour le Canada) et un
codage Unicode (UTF-8).
Donc ce qui se passe, c'est qu'une recherche pour les fichiers suivants est menée:
- {appdir}/languages/fr_CA.UTF-8/app.?o
- ... [snip]
- {appdir}/locale/app.fr_CA.UTF-8.?o
- {appdir}/languages/fr/app.?o
- ... [snip]
- {appdir}/locale/app.fr.?o
Dans UnitTranslator
, qui est ma version du code, j'ai
décidé de créer une fonction
GetLang(const Lang: string): string
qui interroge
les variables d'environnement comme dans le code d'origine, mais qui, à la
fin, ajoute un test pour gérer les paramètres régionaux tels que trouvés sur
mon système.
if (length(Result) > 5) and (Result[3] = '_') then setlength(Result, 5);
La raison du test pour le '_' est que le répertoire
/usr/share/locale
de mon system Ubuntu contient des sous
répertoires tels que 'be@latin' dont les noms ne doivent pas être amputés à
5 caractères.
Puisque je réécrivais le code pour la fonction
FindLocaleFileName
, j'ai également décidé de tester l'existence
des sous-répertoires 'locale' et 'languages' avant d'exécuter une recherche
de fichiers .po et .mo. Il est inutile de rechercher des fichiers dans des
répertoires non existants.
Enfin, j'ai incorporé DefaultTranslator
dans
UnitTranslator
en incluant un appel à
SetDefaultLang('','')
dans le code d'initialisation de cette
dernière unité.
L'unité peut être trouvée ici. Je suis incertain quant aux droits d'auteur impliqués ici. Si j'avais écrit cette unité sans regarder le code de V.I. Volchencko et al, j'aurais utilisé une licence de type BDS à deux ou trois clauses. En ce qui me concerne, n'importe qui peut utiliser le code de façon éthique.
La bonne variable d'environnement
En examinant l'environnement de mon système Ubuntu, il me semble que la variable 'LANGUAGE' aurait peut-être été un meilleur choix.
Ceci est plus ou moins confirmé par la réponse de Rémi (modifiée par Édouard Lopez) à une question posée sur SuperUser. Il semble que l'utilisateur pourrait avoir quelque chose comme «LANGUAGE=fr:de:en» ce qui signifie que les messages doivent être en «français s'ils existent, sinon en allemand , et qu'ils passeront à l'anglais si un message français ou allemand ne se trouve pas».
Le hic est que je ne sais pas si c'est universel. La question portait sur les systèmes Debian. Qu'en est-il des autres distributions Linux ? La page man de Locale (7) ne mentionne pas 'LANGUAGE'. La page man sur gettext (3) indique qu'il s'agit d'une 'extension Gnu':
If the LANGUAGE environment variable is set to a nonempty value, and the locale is not the "C" locale, the value of LANGUAGE is assumed to contain a colon separated list of locale names. The functions will attempt to look up a translation of msgid in each of the locales in turn. This is a GNU extension.
Peut-être l'approche correcte est de supposer qu'il devrait y avoir une liste de langues à rechercher. La variable LANGUAGE doit être la première à être examinée pour établir cette liste. Si elle n'est pas présente, les autres variables d'environnement doivent être examinées.
Si seulement les choses étaient aussi simples. La variable 'LC_ALL', si elle est définie, a primauté sur les autres alors que 'LANG' est la valeur par défaut quand une autre n'est pas définie. Si je comprends bien toutes ces informations, les valeurs suivantes
LANG="en_GB"
LC_MESSAGES="fr_CA.UTF-8"
LC_MEASUREMENT=
LC_ALL=
veulent dire que 'en_GB' est utilisée pour LC_MEASUREMENT, alors que les messages
utilisent 'fr_CA.UTF-8'. Par contre si 'LC_ALL' avait été
LC_ALL="de"
alors 'de' doit être utilisé pour 'LC_MEASUREMENT' et 'LC_MESSAGES'. Mais est-ce que
cette primauté s'applique à 'LANGUAGES' ?
Puisque le sujet de la préséance de 'LC_ALL' est abordé, j'enchaîne en
notant que la première variable d'environnement examinée dans
GetLanguageIDs
de gettext
et puis 'LC_MESSAGES'
est considéré et enfin 'LANG'. Tout cela est correct à ma connaissance. Mais
dans LCLTranslator
, la variable d'environnement 'LANG' est
vérifiée avant d'appeler LazGetLanguageIDs
qui à son tour
appelle GetLanguageIDs
. Cela signifierait que 'LC_ALL' serait
ignoré! Il y a un commentaire dans le code que cela a quelque chose à faire
avec Windows.
J'ai mal à la tête.