home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Freelog Special Edition 1: Linux
/
CD2.iso
/
docs
/
locale.fr
< prev
next >
Wrap
Text File
|
1999-03-14
|
46KB
|
1,160 lines
MANUEL "Locale"
Ecrit par Patrick D'Cruze
(pdcruze@orac.iinet.com.au, pdcruze@li.org)
avec l'aide de Mitchum Dsouza
(m-dsouza@mrc-applied-psychologie.cambridge.ac.uk)
---------------------------------------------------------------------------
Traduction Francaise : Eric DUMAS dumas@emi.u-bordeaux.fr
---------------------------------------------------------------------------
Note du traducteur :
Le texte original peut etre trouve sur ftp.sunsite.edu.
La version francaise sera deposee sur ftp.sunsite.edu et sur
ftp.ibp.fr (/pub/linux/french/docs).
La traduction de notes anglaises est un sport assez difficile,
surtout lorsqu'il s'agit d'un texte technique. Bien souvent, j'ai essaye de
traduire les termes techniques par leur equivalent en francais. J'ai conserve
les termes anglais lorsqu'il n'y avait pas d'equivalent.
Le terme central de ce texte est "Local". Ce terme peut etre traduit
par milieu. Toutefois, a de nombreuses occasions, je conserverai le terme
anglais pour des raisons de comprehension.
Ce texte ne possede par d'accents. Une version accentuee sera
disponible sous peu, sous la forme d'un fichier postscript ou d'un fichier
LaTeX.
En esperant qu'il n'y ait pas trop d'erreurs.
Fevrier 1995, Bordeaux - France
Eric DUMAS
dumas@emi.u-bordeaux.fr
-------------------------------------------------------------------------
Sommaire :
I Une Introduction a "Locale" et aux catalogues.
I.1 Qu'est-ce que "Locale" ?
I.2 Qu'est-ce que sont les catalogues ?
I.3 Quel est le format d'un message du catalogue ?
II Quelles sont les routines appelees ?
II.1 SetLocale().
II.2 CatOpen().
II.3 CatGets().
II.4 CatClose().
II.5 Xtract.
II.6 Gencat.
III Ecrire des programmes avec "Locale".
III.1 Ecrire et modifier des programmes pour qu'ils puissent
utiliser le catalogue des messages.
3.2 Ecrire des programmes qui doivent etre utilises sur des
systemes se servant de "Locale" ou non.
IV Ou sont stockes mes messages ?
V Questions les plus frequemment posees. (F.A.Q.)
-------------------------------------------------------------------------
I- Introduction a "Locale" et aux catalogues :
----------------------------------------------
I-1 : Qu'est-ce que "Locale" ?
Il y a un bon nombre de parametres necessaires pour definir les
conventions culturelles d'un pays. Ces attributs incluent la langage du
pays, le format de la date et de l'heure, la representation des nombres,
les caracteres pour la monnaie, etc. Ces "regles" locales definissent les
specification pour le milieu du pays. Le "Locale" represente la
connaissance necessaire pour supporter les specificitees d'un pays.
Il y a cinq principales zones qui risquent differer entre les
pays et les zones de milieu.
Caracteres et Codage des caracteres :
-------------------------------------
Le codage le plus repandu aux USA et dans la majorite des pays
anglo-saxons est le codage ASCII. Toutefois, il y a de nombreux caracteres
necessaires par divers pays qui ne sont pas fournis avec ce codage. Le
systeme de codage ISO 8859-1 (8 bits) possede la plupart des caracteres
necessaires pour gerer la plupart des langages europeens. Toutefois, dans
beaucoup de cas, la police de caracteres ISO-8859-1 n'est pas adequate. A
partir de la, chaque milieu aura besoin de specifier le type de codage des
caracteres dont il a besoin et devra avoir les routines de gestion de ces
caracteres pour pouvoir les utiliser.
Monnaie :
--------
Les symboles utilises changent suivant les pays comme le fait la
position de ces symboles. Les programmes doivent etre capables d'afficher les
chiffres monetaires dans leur forme exacte, suivant les differents pays.
Dates :
-------
Le format varie suivant les milieux. Le jour de noel en 1994 s'ecrit
12/25/94 aux USA mais 25/12/94 en Australie et en France. Certains milieux
desirent la date sur 24 heures plutot que le format AM/PM.
Nombres :
---------
Les nombres peuvent etre representes de manieres differentes suivant
les milieux. Par exemple, les nombres suivants sont ecrits suivant plusieurs
formats, en fonction du milieu :
12.345,67 Francais
12,345.67 Anglais/Americain
1,2345.67 Asiatique
Messages :
----------
L'application la plus evidente est sans nulle doute le support des
messages en fonction du milieu. Un mecanisme facile a utiliser doit etre
fournis pour que les developeurs et les utilisateurs puissent selectionner la
langue que le logiciel utilise.
Ce document ne se concentrera que sur le support des messages pour
les programmes. Dans une prochaine etape, il sera mis a jour pour illustrer
la facilite avec laquelle les developeurs peuvent ajouter d'autres
specificites nationales. En plus, on peut preciser que les routines et
fonctions "Locales" sont utilisees la plupart du temps par des programmes en
mode texte, c'est a dire des programmes fonctionnant sur des xterm ou des
consoles virtuelles. D'autres routines existent pour des programmes utilisant
XWindows. Elles seront decrites lors d'une prochaine mise a jour de ce
document.
I.2 Qu'est-ce que sont les catalogues ?
Les programmes communiquent avec l'utilisateur en ecrivant des
messages (du texte) a l'ecran. Ces messages peuvent etre eparpilles dans tout
le code source d'un progamme. Pour pouvoir supporter plusieurs langages, il
est necessaire de traduire ces messages dans differentes langues. Cela est
impossible a faire dans le code source pour deux raisons :
1- Pour traduire les messages dans une autre langue, les traducteurs
devraient aller a la recherche des messages dans le code source. Cela
est une pure perte de temps. De plus, bon nombre de traducteurs risquent
de ne meme pas avoir acces au code source.
2- Gerer une nouvelle langue signifie que les messages a l'interieur
du code source, doivent etre traduits, puis il faut recompiler le code source.
Ce qui devra etre fait pour tout langage.
La solution est d'avoir tous les messages enregistres dans un fichier
externe, appelle catalogue des messages. A chaque fois que le programme a
besoin d'afficher un message, le programme demande au systeme d'exploitation
de chercher le message approprie dans le catalogue et de l'afficher sur l'ecran.
Les avantages sont les suivants :
- Le catalogue peut etre traduit sans qu'il soit necessaire d'avoir
acces au code source.
- Le code source est compile une et une seule fois. Pour gerer un
nouveau langage, il suffit de traduire les messages et de l'envoyer
a l'utilisateur.
- Tous les messages sont stockes en un seul endroit.
I.3 Quel est le format d'un message du catalogue ?
Une fois que tous les messages ont ete extraits du code source, ils
sont ranges dans un fichier texte ordinaire qui est reference comme etant
le fichier des messages. Le fichier possede souvent la structure suivante :
1 Impossible d'ouvrir le fichier foo.bar
2 Impossible d'ecrire dans le fichier foo.bar
3 Impossible d'acceder au repertoire
Bien que cela soit une representation utile pour les programmeurs et
pour les traducteurs, cela se revele insuffisant pour le systeme
d'exploitaton. En effet, le systeme d'exploitation doit pouvoir y acceder
pour rechercher le message d'une maniere la plus rapide possible. Un fichier
binaire correspond a cet etat d'esprit, et c'est ce qui est fait dans la
realite.
Un catalogue de messages est une representation binaire des messages
utilises dans le logiciel. Les fichiers textes de messages sont compiles
en utilisant le programme "gencat" en un format binaire. Le catalogue ainsi
compile dans un format depend de la machine et il n'est pas portable. Il est
tres facile de recompiler les fichiers textes de messages sur d'autres
architectures (plateformes).
Les programmeurs et les traducteurs ecrivent les messages utilises
par leur programme dans des fichiers et ces fichiers sont compiles en un
catalogue de messages. Toutefois, une seule partie d'un logiciel peut
contenir des centaines d'instructions printf(), chacune etant constituee
d'un seul message. Chacun de ces messages devant etre stocke dans un fichier
de messages. Il n'est pas tres raisonnable de supposer d'avoir tous ces
messages dans un seul fichier texte de messages. Editer, modifier, detruire
et ajouter de nouveaux messages risquent de faire grossir le fichier jusqu'a
ce que cela pose des problemes.
La solution consiste a fractionner les messages en modules. Chacun de
ces modules contient les messages pour une partie specifique de
l'application. En combinant tous les modules, on obtient tous les messages
utilises. Ces modules peuvent etre compiles dans un seul catalogue de
messages. Le programme peut alors acceder a un message particulier situe
dans un module specifique a l'interieur du catalogue de messages.
Cela rend le travail du programmeur (et du traducteur) plus facile.
Le programme peut assigner des modules separes pour des routines importantes.
Alors, lorsqu'une routine est modifee, il suffit de modifier uniquement
le module dans lequel se trouve le message correspondant. Tous les autres
modules restent tels qu'il sont.
Exemple :
Pour le programme gnubar, nous avons deux endroits qui utilisent les
communications avec l'utilisateur (affichage d'erreurs et des resultats).
Les catalogues resultant sont normalement appelles : errors.m et results.m.
(Rq :Nous avons adopte la convention d'utiliser .m pour une fichier de
message)
Toutes les messages d'erreur se trouvent dans errors.m. Nous modifions
alors le programme pour que lorsqu'une erreur se produit, le programme accede au
module des messages d'erreurs et affiche le message d'erreur. De meme avec
les resultats.
Ces deux fichiers sont alors compiles pour former le catalogue des
messages pour gnubar. Le fichier resultant de la compilation est appelle
gnubar.cat. Ce catalogue est constitue de deux modules, errors et results,
chacun contenant les messages numerotes.
Pour acceder a un message particulier, le programme a besoin de
specifier quel module doit etre utilise et le numero du messsage qui doit etre
affiche.
II Quelles sont les routines appellees ?
----------------------------------------------
Les quatre routines centrales pour acceder et utiliser les catalogues
de messages dans votre code source sont setlocale(), catopen(), catgets() et
catclose().
NB: Souvenez-vous que les Catalogues de Messages ne sont qu'un element
du milieu. Les autres elements seront traites dans les versions futures de ce
document.
NB pour LINUX : Pour acceder et utiliser les fonctions "locale", vous
aurez besoin de libc.so.4.4.4c ou version ulterieure. (Je vous recommande
d'utiliser au moins libc.so.4.5.26 ou superieur etant donne qu'elle inclue
de nombreuses ameliorations dans la gestion des milieux.). Vous aurez
egalement besoin des fichiers d'entete <locale.h> et <nl_types.h>. Si vous
avez une libc qui supporte les fonctions locales, alors il est fort
probable que vous possediez les fichiers d'entete egalement. (/usr/include)
II-1. SETLOCALE() :
La premier chose qu'un programme doit faire est de positionner le milieu
dans lequel il va s'executer. Cette operation est realisee avec la fonction
setlocale(). Elle est definie comme etant :
#include <locale.h>
char *setlocale(int categorie, const char *locale);
L'argument "categorie" indique a la fonction quel attribut positionner.
Les differents attributs sont :
- LC_COLLATE : Modifie le fonctionnement de strcoll() et strxfrm()
- LC_CTYPE : Modifie le fonctionnement de la gestion des caracteres
isalpha(), islower(), isupper(), isprint(),...
- LC_MESSAGE : Positionne la langue dans laquelle les messages seront
affiches.
- LC_MONETARY : Modifie les informations retournees par localeconv()
- LC_NUMERIC : Positionne le caractere decimal pour les nombres
- LC_TIME : Modifie le fonctionnement de strftime()
- LC_ALL : Change tout !
Dans notre exemple, nous allons nous occuper uniquement des catalogues de
messages. C'est pourquoi nous allons uniquement positionner l'attribut
LC_MESSAGES avec la fonction setlocale(). L'attribut LC_ALL aurait egalement
pu etre utilise. Toutefois, il est bon d'utiliser uniquement les attributs
dont vous avez besoin dans votre programme. La raison sera expliquee
rapidement ulterieurement.
L'argument "locale" est le nom du milieu. Deux noms speciaux du milieux sont :
C Cela rend les prototypes des fonctions standard a la norme du C...
POSIX idem, mais pour la norme POSIX.
Generalement, l'argument du milieu sera : "" (guillemets vides). Cela
pour effet de selectionner le milieu par defaut de l'utilisateur. Cela est
realise par le systeme d'exploitation de cette maniere :
1- Si l'utilisateur possede la variable d'environnement LC_ALL , et
qui n'est pas nulle, alors la valeur de cette variable est utilisee comme
l'argument du milieu.
2- Si l'utilisateur possede une variable d'environnement ayant le meme
nom que l'attribut, et qui est non nul, alors cette variable est utilisee comme
l'argument du milieu.
3- Si la variable LANG est positionnee et non nulle alors cette valeur
est utilisee comme argument du milieu.
Si la valeur est valide, supportee par le milieu, alors le milieu est
modifie. Toutefois, si la valeur est non nulle et contient une valeur non
suportee par le milieu alors setlocale() revera un pointeur NULL et le
milieu ne sera pas change par rapport a la valeur du mileu par defaut "C".
Au debut du programme, le systeme d'exploitation execute la fonction
suivante :
setlocale(LC_ALL, "C");
Si votre programme n'utilise pas setlocale() ou ne peut pas changer le
milieu (en raison d'un mauvais environnement de variables), alors le
programme utilisera le milieu par defaut : "C".
Si setlocale() n'est pas capable de changer le milieu, NULL est retourne.
Une bonne methode de programmation consiste a uniquement utiliser les
attributs qui conviennent a votre logiciel. Un exemple va en illustrer les
raisons :
main()
{
setlocale( LC_ALL, "");
....
}
Le programme va maintenant positionner tous les attributs du milieu
avec soit la valeur de la variable d'environnement LC_ALL si elle est
positionnee, soit avec la valeur de la variable d'environnement LANG ou bien
dans le dernier cas, le milieu par defaut "C".
Maintenant supposez que l'utilisateur souhaite avoir tous les messages
affiches sur son ecran en Anglais, mais il desire utiliser les autres
attributs du milieu Francais. L'utilsateur realise ceci en positionnant la
variable LC_MESSAGES pour le milieu Anglais, et la variable LANG pour le
Francais.
L'exemple ci-dessus (utilise LC_ALL) ignore la varible LC_MESSAGES et
va utiliser a la place la variable LANG ... donc tous les messages seront
affiches en Francais. L'utilsateur peut soit avoir tous les attributs
positionnes pour le Francais, soit pour l'Anglais.
Admetons que cette situation est un peu exceptionnelle, mais si votre
programme a juste besoin d'avoir acces aux messages, alors il est juste
necessaire de positionner cet attribut. Si votre programme a besoin de
quatre attributs alors, vous devrez utiliser quatre fois setlocale().
C'est a l'utilisateur de bien positionner ses variables
d'environnement. Il lui est aussi tres facile de le deteriorer, simplement en
modifiant ses variables. Il est donc plus prudent d'include des informations
dans votre programme pour que l'utilisateur configure d'une maniere correcte
son environnement. Cette question sera vue dans une section ulterieure.
II-2 CATOPEN()
La fonction setlocale() etablit uniquement le bon milieu que doit
utiliser le programme. Pour acceder au catalogue, il faut d'abord l'ouvrir.
Cette fonction realise cette operation. Elle est definie de la maniere
suivante :
#include <nl_types.h>
nl_catd catopen(char *nom, int option);
catopen() ouvre le catalogue des messages et retourne un descripteur de
catalogue. Le "nom" correspond au nom du catalogue qui doit etre ouvert. Si
le nom correspond a un chemin absolu (Contient un /), le nom correspond au
chemin d'acces a ce fichier. Sinon, la variable NLSPATH est utilisee. Si
NLSPATH n'existe pas, ou si le catalogue ne peux pas etre trouve ou ouvert
dans les repertoires specifies dans NLSPATH, alors le catalogue est recherche
dans les repertoires suivants, dans l'ordre :
/usr/lib/locale/LC_MESSAGES
/usr/lib/locale/name/LC_MESSAGES
L'argument option est utilise pour indiquer le type de chargement
desire. Cela peut etre soit MCLoadBySet ou soit MCLoadAll. Le premier indique
que l'on ne charge que le module necessaire, le dernier specifie que l'on
charge TOUT le catalogue en memoire.
catopen() renvoie soir un descripteur de catalogue nl_catd, soit -1 en cas
d'erreur.
Exemple d'utilisation :
static nl_catd catdc = 0;
catdc = catopen("foo.cat", MCLoadBySet);
if ( catdc == -1)
printf(" Impossible d'ouvrir le catalogue des messages \n');
II-3 CATGETS ()
Une fois que le catalogue des messages a ete ouvert, nous avons besoin
d'une routine pour acceder au catalogue et recuperer les messages. C'est la
fonction de catgets()
#include <nl_types.h>
char *catgets(nl_catd catdc, int numero_module, int numero_message \
char *message);
catgets() lit le message "numero_message", dans "numero_module", dans
le catalogue des messages identifie par catdc. catfd est le descripteur du
catalogue (cf catopen()). Le quatrieme argument est un pointeur sur une
chaine par defaut qui sera retournee si le catalogue n'est pas ouvert, ou
s'il est endommage. Le texte du message est stocke dans un tampon interne
et doit etre copie par l'application s'il doit etre conserve ou modifie.
La chaine retournee est toujours terminee par \0.
En cas d'erreur, catgets renvoie un pointeur sur "message" si le
cataloguen'est pas ouvert. Dans le cas ou le message n'est pas trouve, catgets
retourne un pointeur sur une chaine vide.
Exemple d'utilisation :
printf( catgets( catdc, 3, 7, "Erreur d'acces au bloc No %d"),Num_Bloc );
La fonction ci-dessus essaye d'acceder au 7eme message du 3eme module du
catalogue des messages. Si ce message ne peut etre trouve pour n'importe
quelle raison, alors le message "Erreur d'acces au bloc No %d" est affiche a
la place.
II-4 CATCLOSE ()
Une fois qu'un programme a termine d'utiliser un catalogue de messages,
le catalogue doit etre ferme pour que la memoire utilisee soit liberee.
#include <nl_types.h>
void catclose(nl_catd catdc);
catclose() ferme le catalogue specifie par catdc.
catclose() retourne 0 en cas de succes, -1 en cas d'echec.
Exemple d'utilisation :
{
...
catclose(catdc);
exit(0);
}
Nous vous avons presente les quatre fonctions C dont vous avez besoin
pour utiliser les catalogues de messages dans votre logiciel. La prochaine
partie traitera les outils qui sont disponibles pour vous aider a extraire les
messages existants dans votre application, et detaillera le programme gencat
utilise pour compiler les messages.
Avant tout, voici le format du fichier de messages. Gencat a besoin
d'un fichier d'un format special pour qu'il puisse compiler les messages.
Exemple de fichier de messages :
$set 2 #chmod
$ #1 Original Message:(invalid mode)
# invalid mode
$ #2 Original Message:(virtual memory exhausted)
# virtual memory exhausted
...
La premiere ligne est utilisee pour definir le numero du module. Le mot
clef "set" doit etre present dans TOUS les fichiers de messages. Le second champ
est le numero du module et doit etre unique pour le catalogue de message.
Le troisieme est utilise pour identifier le module (Le numero peut etre
egalement utilise):
$set <No Module> #<Identificateur du Module >
La seconde ligne est l'unique identificateur du message. Il est
important de remarquer la present du signe $ et du deuxieme champ #<No>. Le
signe $ est necessaire pour permettre de differencier un message de son
identificateur. Le second champ (sans #) est l'identificateur du message. Le
reste est ignore. C'est souvent utile pour inclure le message original pour le
suivi du code, la traduction, etc
$ #<No_Message> [ Texte Ignore ]
La troisieme ligne est le texte du message. Dans ce cas, c'est le
texte pour le premier message dans le deuxieme module.
# < Texte du Message >
Lorsque vous traduisez les fichiers de messages dans d'autres langues,
il est juste necessaire de traduire les lignes de texte
( < Texte du Message > ),c'est a dire les lignes commencant par #. Aucune ligne
debutant par $ ne sera a modifier.
Le format du fichier ci-dessus correspond parfaitement aux arguments de
la fonction catgets(). La fonction catgets a besoin de No_Module et de
No_Message (deux entiers). Pour affichier le premier message du second
module,
$set 2 #chmod
+------^
| +------v
| | $ #1 Original Message:(invalid mode)
| | # invalid mode
| | $ #2 Original Message:(virtual memory exhausted)
| | # virtual memory exhausted
| |
| | printf( catgets(catdc, 2, 1, "Mode Invalide") );
| +---------------------------^
+--------------------------^
Bien que ces fonctions fonctionnent sans probleme, leur utilisation
n'est pas intuitive lors de la creation d'un programme. Par exemple, a chaque
fois qu'un programmer a besoin d'afficher un message, il doit tout d'abords
chercher le message dans le bon module, trouver son numero et le reporter
dans son code source. Cela peut rapidement devenir rapidement penible
lorsque les programmes ont besoin d'acceder a plusieurs modules ou a
plusieurs catalogues. Autant chercher une aiguille dans une bote de foin !
Au lieu d'utiliser un entier comme numero de module ou numero de
message, il serait plus facile d'utiliser des noms (texte ASCII). On peut
utiliser ce systeme si l'on emploi un #define pour faire correspondre le texte
a un entier.
Au lieu d'avoir :
$set 2 #chmod
$ #1 Original Message:(invalid mode)
# invalid mode
$ #2 Original Message:(virtual memory exhausted)
# virtual memory exhausted
...
on aurait :
$set 2 #chmod
$ #Invalid_Mode
# invalid mode
$ #VM_exhausted
# virtual memory exhausted
...
On peut remarquer qu'aucun changement n'est a effectuer pour le nom du
module car la troisieme partie de cette ligne est le nom du module.
Pour acceder au second message, on peut utiliser le code suivant :
printf( catgets(catdc, chmodSet, chmodVM_exhausted, \
"Plus de memoire virtuelle"));
L'argument No_Module de la fonction catopen() est toujours le nom du
module (chmod ici), rattache au mot "Set" => "chmodSet". Le numero du
message est toujours le nom du module (chmod) ajoute avec
l'identificateur du message (VM_exhausted) =>chmodVM_exhausted.
Pour pouvoir utiliser ces noms, le programme doit associer les noms avec
des entiers car la fonction catopen() n'accepte que des entiers comme
parametre. On realise cette association en utilsant le programme gencat qui
va generer les fichiers d'entetes qui sont utilises par le programme.
Pour les message ci-dessus, le fichier d'entete ainsi genere ressemble a :
#define chmodSet 0x2
#define chmodInvalid_Mode 0x1
#define chmodVM_exhausted 0x2
Ce fichier d'entete a ete genere a partir du fichier chmod.m. Nous avons
decide d'appeler les fichiers d'entete xxx-nls.h donc dans notre cas, le
fichier d'entete est appele : chmod-nls.h
Il ne reste plus qu'a rajouter la ligne
#include "chmod-nls.h"
au debut du programme..
II-5 XTRACT :
xtract est un programme ecrit avec yacc pour extraire les messages
d'un code source. Vous pouvez le trouver sur le site ftp :
sunsite.unc.edu:/pub/Linux/utils/nls/catalogs/locale-package.tar.gz (US)
xtract recherche dans les sources toutes les chaines de caracteres etant
entre quotes. Il affiche a l'ecran tout ce qu'il trouve.
Il s'utilise de la maniere suivante :
xtract < source.c > message.m
Le fichier message.m contient alors tous les message que xtract a trouve
dans le source. Les messages sont places dans le format habituel.
Toutefois, il est necessaire d'editer le fichier :
- les deux premieres lignes doivent etre detruites
- la ligne $set <No_Module> #<Nom_Module> doit etre ajoutee.
Exemple :
Le message original ressemble a cela :
$ #0 Message Original :(Probleme de Configuration)
# Probleme de Configuration
$ #1 Message Original :(Impossible d'ouvir le fichier)
# Impossible d'ouvir le fichier
$ #2 Message Original :(Erreur lors de l'acces au fichier)
# Erreur lors de l'acces au fichier
...
Ce n'est un format correct car il manque ke numero du module. Il suffit de
rajouter alors la ligne
$set X #Descripteur
au tout debut du fichier. X represente le numero du module.
Le fichier ressemble alors a :
$set 17 #Descripteur
$ #0 Message Original :(Probleme de Configuration)
# Probleme de Configuration
$ #1 Message Original :(Impossible d'ouvir le fichier)
# Impossible d'ouvir le fichier
$ #2 Message Original :(Erreur lors de l'acces au fichier)
# Erreur lors de l'acces au fichier
...
II-6 GENCAT
(Remarque du traducteur : Il semble qu'il y ait plusieurs versions de
gencat. Je travaille sur un systeme Linux mais aussi sur Solaris 4.2.3 et le
gencat standard sun ne possede pas les options qui vont etre expliquees
ci-dessous.)
Gencat est le programme utilise pour compiler les fichiers de messages.
Le resultat de la compilation est le catalogue des messages. Voici le detail
des options de la ligne de commande :
gencat [-new] [-lang C | C++ | ANSIC ] fic_cat fic_msg [ -h <header_file>]
Description :
-new Detruit le catalogue des message et en cree un nouveau.
Le comportement par defaut est de mettre a jour le catalogue
avec le(s) fic_msg(s).
-lang <l> Indique le format du fichier d'entete. Pour le moment, les
trois formats suportes sont les langages C, C++ et ANSIC. Les
deux derniers sont identiques. Cet argument peut etre place
n'importe ou dans la ligne de commande.
-h <hfile> Identificateurs des fichiers d'entete crees en sortie.
Cela cree un fichier d'entete avec tous les #define
appropries. Sans cela, ca serait a vous de verifier que
votre code soit coherent avec le catalogue. Le fichier
d'entete est cree a partir de tous les fic_msg de la ligne de
commande, donc l'ordre dans la ligne de commande est
important. Cela signifie que si vous mettez a la fin l'entete,
toutes les entetes seront dans un seul fichier :
gencat vodka.m pineau.m gin.m -h cuite.h
Si vous preferez conserver la dependance, vous pouvez
specifier un fichier entete par fichier de messages :
gencat vodka.m -h beurk.h pineau.m -h c1.h gin.m -h \
cuite.h
De plus, si vous executez l'instruction suivante,
gencat vodka.m -h beurk.h
le fichier beurk.h ne sera pas modifie la deuxieme fois.
Gencat verifie si le contenu a ete modifie avant de le
retraiter. Vous pouvez egalement utiliser un fichier
Makefile de ce genre:
MSGSRC=vodka.m gin.m
GENFLAGS=-new -lang C
GENCAT=gencat
NLSLIB=nlslib/OM/C
$(NLSLIB): $(MSGSRC)
@for i in $?; do cmd="$(GENCAT) $(GENFLAGS) $@$$i -h \
basename $$i .m'.H"; echo $$cmd; $$cmd; done
vodka.o: vodka.h
La boucle for/loop n'est pas tres jolie mais elle marche.
Pour chacun des fichier .m qui ont ete modifies, on
relance gencat.
Le programme gencat possede deux fonctions et il est utilise
generalement en deux fois.
La premiere fonction est de generer les fichiers d'entete a partir
des fichiers de messages pour que les programmes puissent utiliser des noms
quand ils font referenceaux modules et aux messages. La commande suivante
realise cette operation :
gencat -new /dev/null foobar.m -h foobar-nls.h
Le -new /dev/null signifie que le catalogue des messages est envoye dans
le fichier null.
La deuxieme fonction est la generation du catalogue des messages :
gencat -new foobar.cat foobar.m
Pour generer le catalogue a partir d'un Makefile :
MESSAGEFILES = toto.m titi.m tata.m
gencat -new tttt.cat $(MESSAGEFILES)
III Ecrire des programmes avec "Locale".
--------------------------------------------
III.1 Ecrire et modifier des programmes pour qu'ils puissent utiliser le
catalogue des messages.
Le probleme est donc de savoir comment modifier ou ecrire un nouveau
programme qui utilise les catalogues de messages. Voici les etapes
necessaires :
1ere etape : (Modification d'un programme existant)
La premiere chose a faire est d'extraire tous les messages du
programme existant et de les placer dans un fichier de messages. Le
programme xtract a ete concu pour cela. Cette operation est expliquee
ailleur dans ce document mais en voici un petit resume :
Code source == toto.c
Fichier de messages == toto.m
xtract < toto.c > toto.m
Il suffit d'inserer le numero du module approprie dans le fichier toto.m :
$set X #n
ou X est le numero du module
et n est le nom de la variable utilise pour acceder a ce fichier.
2eme etape : (Creation d'un nouveau fichier de messages)
Si vous debutez un nouveau fichier de messages, il est utile de
rappeler l'ordre et la structure du fichier de messages . Il est compose
de trois elements :
- L'identificateur du module
- l'identificateur du message
- le texte de chaque identifiaceur des messages
Le format a ete detaille dans un paragraphe precedent. Ce format de
fichier doit permettre de resoudre tout probleme de compilation du
catalogue de messages.
Brievement, en voici la structure :
$set 2 #chmod
$ #Invalid_Mode Message Initial : (Mode Invalide)
# Mode Invalide
$ #VM_exhausted Message Initial : (Plus de Memoire Virtuelle)
# Plus de memoire virtuelle
...
La premiere ligne est l'identificateur du module. Toutes les autres
lignes debutant par le caractere $ sont les identificateurs de message. Les
lignes suivantes sont les messages a afficher.
3eme Etape :
Pendant que vous etes en train de modifier un fichier de message
genere par la premiere etape, ou que vous etes en train de creer un nouveau
fichier, il est plus facile d'utiliser des noms pour se referencer aux
messages et aux modules plutot qu'aux nombres. Pour utiliser des noms, nous
avons besoin d'assigner un nom unique a chacun des modules, et des noms
uniques pour chacun des messages par modules.
La premiere ligne de chacun des fichiers de messages est
l'identificateur du module :
$set X #Mon_Module
X : numero du module pour ce fichier de messages
Mon_Module : Le nom utilise
X doit etre un nombre unique pour ce module. Il en va de meme pour le
nom du module. On peut alors acceder a ce module d'une maniere indiferente :
soit par le nombre, soit par le nom. C'est a vous de choisir. Toutefois, si
vous vous decidez a utiliser le nom pour acceder au module, pensez a rajouter
le mot "Set" : le mot complet devient alors : Mon_ModuleSet.
4eme Etape :
Maintenant que nous utilisons des noms comme identificateurs de
modules et de messages, nous devons creer le fichier d'entete qui va faire
correspondre les numeros avec ces noms. Le programme gencat peut etre
utilise pour generer le fichier d'entete a partir d'un fichier de message.
Cette operation a ete expliquee precedemment dans le document. Brievement,
non executons la commande suivante :
gencat -new /dev/null foobar.m -h foobar-nls.h
Gencat va donc generer le fichier d'entete. Ce fichier doit etre inclue
dans notre programme.
Il vaut mieux prendre l'habitude de nommer ses fichiers d'entete "xxx-nls.h".
Le "-nls" permet de mieux distinguer les fichier d'entete pour les messages.
5eme Etape :
Nous sommes maintenant prets a modifier le code source. La premiere
chose a faire est d'inclure les fichiers d'entete. Nous avons besoin de
trois fichiers :
#include <locale.h>
#include <nl_types.h>
#include <foobar-nls.h>
Le premier fichier (locale.h) contient plusieurs constantes
utilisees par setlocale et par d'autres fonctions : LC_*, etc...
Le second fichier contient les constantes et les prototypes pour
les fonctions catopen, catclose, etc...
Le dernier contient toutes les declarations des messages et des
modules utilises, ce qui va nous permettre d'utiliser les noms dans les
fonctions.
6eme Etape :
La prochaine etape constiste a declare une ou plusieures variables
globales pour les catalogues. Nous avons besoin d'un descripteur de
catalogue uniquement lorsque nous accedons a un catalogue de messages.
Normalement, un programme n'aura besoin d'acceder qu'a ses propres messages
de catalogue et donc, nous n'aurons besoin de definir qu'un seul descripteur
de catalogue de messages. Cela doit etre defini avant le main() :
/* Variable Globale au module : descripteur de catalogue */
static nl_catd catfd = -1;
Pour acceder au catalogue de messages nous utiliserons maintenant
la varibale catfd.
7eme Etape :
A l'interieur de la fonction main, la premiere chose a faire est de
positionner le milieu dans lequel le programme va evoluer. Cette operation
est realisee en utilisant la fonction setlocale() :
setlocale( LC_MESSAGE , "");
(Voir plus haut pour la description de la fonction setlocale)
8eme Etape :
Notre programme peut donc acceder au bon repertoire lorsqu'il a
besoin d'acceder au catalogue des messages ou a d'autre informations sur
le milieu. Nous devons maintenant ouvrir le catalogue des messages utilise
par notre programme :
catfd = catopen("foobar",MCLoadBySet);
Cette operation va ouvrir le fichier de messages foobar.cat (Rq: Ne pas
specifier .cat). Le type de chargement est specifie dans le deuxieme
arguement : chargement global en memoire ou chargement module par module.
Cette derniere option utilise moins de memoire mais elle est plus couteuse
en temps. Le choix est laisse au programmeur.
Une solution plus solide pour ouvrir et initialiser le catalogue des
messages est presentee ci-dessous. Le programme risque devoir ouvrir, fermer,
manipuler le catalogue des messages a de nombreux endroits dans le programme.
Il est donc utilise d'utiliser une fonction d'initialisation qui ouvre le
catalogue de message s'il ne l'est pas deja :
catinit()
{
if (catfd == (nl_catd) -1)
catfd = catopen("foobar", MCLoadBySet);
}
La routine verifie que le catalogue est ferme. Si c'est le cas, elle
l'ouvre. Il vous est tres facile de rajouter des fonctionnalites a cette
routine. Le premier appel a cette fonction doit etre fait apres l'appel a
setlocale() dans la fonction main(). Apres cela, vous pouvez l'appler autant
de fois que vous voulez, si vous n'etes pas sur que le catalogue soit ouvert
ou ferme.
9eme Etape
Nous sommes desormais pret pour utiliser le catalogue des messages. Nous
allons utiliser la fonction catgets.
Cette fonction possede quatre arguments :
catgets(catfd, Id_Module, Id_Message, *message);
La variable catfd represente le descripteur de catalogue retourne par la
fonction catinit ou catopen. C'est utilise par catgets pour determiner le
catalogue a utiliser(plusieurs catalogues peuvent etre ouverts en meme temps
dans un programme).
Id_Module represente le module a manipuler. Cela peut etre soit un entier,
soit le nom du module (+"Set").
Id_Message represente le numero ou le nom du message situe dans Id_Module.
Si le nom est utilise, souvenez vous que le nom du module doit etre accole au
nom du message.
*message est la chaine par defaut qui sera utilisee s'il n'est pas possible
d'acceder au message, etc.
exemple :
catgets(catfd, erreursSet, erreursVM_Exhausted, "Plus de memoire virtuelle");
Cette operation va recuperer le message VM_Exhausted dans le module erreurs
du catalogue. Dans le cas d'une erreur, la chaine "Plus de memoire virtuelle"
sera utilisee.
Nous vous recommandons de prendre l'habitude de toujours utiliser la chaine
anglaise par defaut (Note Traducteur : Bof,...pourquoi pas le francais !).
La routine catgets() retourne un pointeur sur un tampon interne termine
par un caractere NULL (\0). Nous avons besoin d'afficher la chaine :
printf("%s\n", catgets(catfd, erreursSet, erreursVM_Exhausted,
"Plus de memoire virtuelle");
Voici quelques exemples de l'ancienne approche (codage en dur), a opposer
avec la nouvelle methode :
Exemple 1 :
Avant :
printf("Incorrect read permission");
Apres :
printf("%s\n", catgets(catfd, errorsSet, errorsIncorrect_Perm,
"incorrect read permission");
Exemple 2:
Avant :
printf("Cannot change to directory %s", dir_name);
Apres :
(Extrait du catalogue des messages)
...
$ #Cant_chdir
# Cannot change to directory %s
...
printf("%s \n",
catgets(catfd, errorsSet, errorsCant_chdir,
"Cannot change to directory %s"), dir_name);
10eme Etape :
Juste avant que le programme ne se termine, nous devons fermer le catalogue
des messages. Il suffit d'utiliser la ligne suivante :
catclose(catfd);
Il est necessaire de faire quelques verifications d'erreurs. Si le
catalogue des messages ne peut etre ouvert pour n'importe quelle raison,
alors le programme utilise les messages par defauts, dans le code source.
Cela peut etre une bonne idee de reperer les erreurs pendant le debogage du
progamme. Il peut y avoir un bon nombre de raisons pour lesquelles le catalogue
ne peut etre ouvert par le systeme d'exploitation. (Repertoire incorrect,
mauvais nom, mauvais droits de fichiers, module incorrect, etc).
Voici un exemple de programme utilisant toutes les fonctionalites :
---
#include <stdio.h>
#include <nl_types.h>
#include <locale.h>
#include "foobar-nls.h"
static nl_catd catfd = -1;
void main()
{
char temp_name;
setlocale(LC_MESSAGES,"");
catinit ();
printf(catgets(catfd, foobarSet, foobarRandom_Name,
"Random text with string %s"), temp_name);
catclose(catfd);
exit(0);
}
catinit ()
{
if (catfd != (nl_catd)-1)
catfd = catopen("foobar",MCLoadBySet);
}
---
Un fichier Makefile :
-------
all: foobar catalog
foobar: foobar.o
gcc -o foobar -O2 foobar.c
foobar.o: foobar-nls.h
foobar-nls.h: foobar-nls.m
gencat -new /dev/null foobar-nls.m -h foobar-nls.h
catalog:
gencat -new foobar.cat foobar.m
install: all
install -o root -m 0755 foobar /usr/local/bin
install -o root -m 0755 foobar.cat /etc/locale/C
clean:
/bin/rm -f foobar *.o foobar-nls.h foobar.cat core
-------
C'est a vous de choisir l'endroit ou vous aller mettre les catalogues
des messages. Il est surement plus facile de les regrouper dans un repertoire
different du code source de votre programme.
III.2 Ecrire des programmes qui doivent etre utilises sur des
systemes se servant de "Locale" ou non.
Il est relativement facile soustraire les fonctions de specification
du milieu au reste du code. La methode habituelle est de definir un DEFINE :
Dans le fichier Makefile, rajoutez les lignes suivantes :
DEFINES = -DNLS
foobar.o: foobar.c
gcc $(DEFINES) foobar.c
Maintenant, dans foobar.c, nous avons :
#ifdef NLS
printf(catgets(catfd, chmodSet, chmodVM_Exausted,
"Virtual Memory Exausted");
#else
printf("Virtual Memory Exausted");
#endif
Les instructions #ifdef, #endif devront entourer chacune des fonctions
specifiques a la gestion du milieu. De meme pour les fichiers <locale.h>,
<nl_types.h>, les declaration des descripteurs de catalogues, etc
Exemple :
#ifdef NLS
#include <locale.h>
#include <nl_types.h>
extern nl_catd catfd;
void catinit ();
#endif
/* Definition des Macros utilisee */
#ifdef NLS
#define NLS_CATCLOSE(catfd) catclose (catfd);
#define NLS_CATINIT catinit ();
#define NLS_CATGETS(catfd, arg1, arg2, fmt) \
catgets ((catfd), (arg1), (arg2), (fmt))
#else
#define NLS_CATCLOSE(catfd) /* vide */
#define NLS_CATINIT /* vide */
#define NLS_CATGETS(catfd, arg1, arg2, fmt) fmt
#endif
---
Maintenant, au lieu d'ecrire cela,
#ifdef NLS
printf(catgets(catfd, chmodSet, chmodVM_exhausted, "Virtual Memory
exhausted"));
#else
printf("Virtual Memory exhausted");
#endif
on peut ecrire ceci :
printf(NLS_CATGETS(catfd, chmodSet, chmodVM_exhausted, "Virtual Memory
exhausted"));
Cela simplifie enormement les choses. Pour pouvoir supporter le milieu,
ou au contraire ne pas le supporter, il suffit de rajouter un -DNLS dans
le fichier Makefile, rajouter un #include pour le fichier des macros,
et enfin, entourer chacun des #include "xxxx-nls.h" par des #ifdef, #endif.
IV Ou sont stockes mes messages ?
--------------------------------------
Cette section peut etre consideree comme une section d'information. Elle
ne devrait etre consideree que comme etant un guide approximatif en attendant
que j'ai le temps de regarder le quatres guides standards de Portabilite
d'XOpen.
Les catalogues de messages ainsi que d'autres composantes du milieu sont
stockes dans deux repertoires :
/usr/lib/locale
/usr/local/lib/locale
Le premier est utilise par les programmes faisant partie du systeme
d'exploitation(programmes standards Unix). Le second est utilise pour des
programmes personnels, qui ne font pas partit de la distribution standard.
On peut trouver des sous-repertoires :
LC_COLLATE
LC_CTYPE
LC_MESSAGES
LC_MONETARY
LC_NUMERIC
LC_TIME
NB : Ils ne doivent pas etre confondus avec les variables du meme nom. Ce
sont les noms actuels de ces repertoires et ne risquent pas de changer.
Pour eviter les confusions, nous nous refererons a $(LC_MESSAGES), etc...
A l'interieur de ces sous repertoires, on peut y trouver les
sous-repertoires pour les pays. Par exemple, sous /usr/lib/locale/LC_MESSAGES,
on peut y trouver les repertoires suivants :
C
POSIX -> C
en_US.88591
de_DE.88591
fr_BE.88591
Dans chacun de ces sous-repertoires se trouvent les messages
specifiques a la langue. Par exemple, les messages en anglais pour
l'executable "ls" serait :
/usr/lib/locale/LC_MESSAGES/en_US.88591/ls.cat
Le format est le suivant :
/usr/lib/locale/LC_MESSAGES/xx_YY.ZZZ/mm.cat
^^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^
racine categorie langue catalogue
La racine ne change pas : c'est soit /usr/lib/locale pour les programmes
systemes soit /usr/local/lib/locale pour les autres programmes.
La categorie depend seulement des fonctions auxquelles le programmes va
faire appel. Si le programme a besoin d'informations sur la monaie, il va
chercher les informations dans :
/usr/lib/locale/LC_MONETARY/xx_YY.ZZZ/
L'attribut de la langue est probablement le plus important car il
determine quelles variables et quels repertoires vont etre utilises. Le format
de la langue est le suivant :
Langue_Pays.TypeDeCaracteres
Voici un exemple :
en_US.88591 Anglais(USA), utilisation du type de caracteres ISO 88591
de_DE.88591 Allemand(Allemagne), utilisation du type de caracteres ISO 88591
fr_BE.88591 Francais(Belgique), utilisation du type de caracteres ISO 88591
Le langue est positionnee par la variable d'environement $(LANG).
L'utilisateur devra positioner la langue, le pays et le type de caractere d'une
maniere correcte. Ainsi, le systeme d'exploitation utilisera la variable
lorsqu'il cherchera le sous-repertoire approprie pour trouver les informations
ou les catalogues de messages dont il a besoin.
Nous avons mis en evidence les deux endroits par defaut que le systeme
utilise pour stocker ses catalogues de messages et les autres attributs.
Toutefois, le systeme doit aussi etre capable de gerer les utilisateurs
qui ne peuvent installer les catalogues dans ces repertoires (Pour le faire,
ils devraient avoir les droits du super-utilisateur). Donc, il doivent
installer leurs propres catalogues dans leur repertoire personnel.
Le systeme peut gerer cette situation : il suffut d'utiliser la variable
d'environnement NLSPATH.
Cette variable contient la liste des repertoires dans lesquels le systeme
d'exploitation va rechercher les catalogues des messages.
Exemple :
NLSPATH=/usr/lib/locale/LC_MESSAGES/%L/%N:/usr/local/lib/locale/LC_MESSAGES/%L
/%N:~/messages/%N
%L => Valeur de la variable LANG
%N => Nom du catalogue
Ces deux valeurs sont substituees par le systeme d'exploitation (Note
traducteur : en fait, c'est le shell...) lors de l'evaluation. L'utilsateur
peut alors ranger ses catalogues de messages dans ses repertoires personnels.
Il peux meme courcicuiter les repertoires pat defaut en changeant
l'ordre des repertoires dans la variable d'environement NLSPATH.
V Questions les plus frequemment posees. (F.A.Q.)
-------------------------------------------------------
Question : Comment puis-je savoir si mon systeme Unix supporte l'utilisation
des fonctions de gestion du milieu ?
Reponse : Un systeme Unix qui supporte toutes les fonctionnalites de ces
fonctions doit avoir ces fichiers d'entete :
locale.h et nl_types.h
Il sont en general situes dans le repertoire /usr/include. Si un de ces
fichiers est absent, alors votre systeme risque de ne supporter qu'une
partie de ces fonctions. Ces deux fichiers sont inclus dans Linux.
============================================================================
Le sujet traite dans ce document possede plusieurs copyrights :
Alfalfa Software, Mitchum DSouza et Patrick D'Cruze - 1989-1994.
Envoyez vos suggestions, vos remarques a l'auteur de ce document a
pdcruze@orac.iinet.com.au
pdcruze@li.org
Si jamais des erreurs se sont glissees dans ce documents, envoyez un mail :
dumas@emi.u-bordeaux.fr
============================================================================