Logiciels    Wiki     Programmation     Electronique     Projets     Auteur 
Retour : Accueil > Programmation > Conseils

Créer une application multilingue

Introduction

Je vais décrire dans cette article une manière (il y en a d'autres) de réaliser une application multilingue. Cette méthode repose sur des fichiers ayant la même structure que les fichiers INI qui servaient jusqu'à Windows 95 aux applications à enregistrer leur configuration (ces fichiers INI ont été remplacés par la base de registre).

Cette description s'applique toujours à BORLAND C++Builder et Delphi, qui proposent, même dans les dernières versions, des routines rendant l'utilisation de tels fichiers extrêmement simple. Les exemples de codes seront comme d'habitude en vert foncé pour C++Builder et en rouge foncé pour Delphi.

Connaissances nécessaires

Les fichiers INI

Les fichiers INI sont de simples fichiers textes modifiables dans n'importe quel éditeur, et portant l'extension .ini. La structure des fichiers INI est constituée de sections placée entre crochets et de valeurs de données stockées dans des clés, comme dans cet extrait de WIN.INI :
[Desktop]
Wallpaper=(None)
TileWallpaper=0
WallpaperStyle=2
Pattern=(None)
Pour notre utilisation cela ressemblera plus à :
[Principal]
Ouvrir=Open
Enregistrer=Save
Les valeurs de donnée peuvent contenir plusieurs types de valeurs mais seules les chaînes nous intéressent pour cette application. Pour pouvoir accéder à de tels fichiers avec C++ Builder et Delphi il faut inclure une bibliothèque spéciale :
#include 
uses IniFiles;
Il faut ensuite créer un objet TIniFiles :
TIniFile *FichierIni = new TIniFile(cheminFichier);
ou
TIniFile *FichierIni;
FichierIni = new TIniFile(cheminFichier);
Var FichierIni: TIniFile;
FichierIni := TIniFile.Create(cheminFichier);
Et pour finir il faut accéder aux valeur enregistrées :
FichierIni->ReadString(nomSection, nomClé, valeurDéfaut);
FichierIni.ReadString(nomSection, nomClé, valeurDéfaut);

Recherche de fichiers dans un dossier

L'un des avantages de cette méthode est que l'on peut ajouter n'importe quel fichier langue dans le dossier de l'exécutable et mettre directement à la disposition de l'application cette langue, sans installer quoi que ce soit. Il faut pour cela à la création de la fiche principale détecter tous les fichiers langues disponibles pour les rendre accessibles à l'utilisateur.

C++Builder et Delphi proposent des routines de recherche de fichiers qui vont nous servir. On commence tout d'abord par déclarer un objet TSearchRec qui est une structure qui définit les informations de fichier recherchées :

TSearchRec SR;
Var SR: TSearchRec;
On utilise ensuite les routines FindFirst et FindNext qui permettent de traiter les fichiers un par un :
if (FindFirst(fichiersRecherchés, faAnyFile, SR) == 0)
do
{
  // Traiter les fichiers
} while (FindNext(SR) == 0);
If FindFirst(fichiersRecherchés, faAnyFile, SR) = 0 Then
Repeat
  // Traiter le premier fichier
Until FindNext(SR) <> 0;
fichiersRecherchés est une chaîne constituée du chemin du dossier dans lequel on recherche les fichiers suivi des caractéristiques des fichiers. Par exemple pour rechercher touts les fichier on mettra *.*, pour les fichiers d'extension .lng on mettra *.lng. Pour chaque traitement d'un fichier on récupèrera ses caractéristiques dans SR, avec par exemple le nom dans la propriété Name. Il ne faut pas non plus oublier de libérer les ressources allouées à SR à la fin :
FindClose(SR);
FindClose(SR);
Il est même conseillé de protèger toutes les opérations sur SR par un bloc try et de le libérer dans finally.

Créer des éléments de menu à l'exécution

Puisqu'il est possible d'ajouter des fichiers langue sans modifier l'exécutable, il est nécessaire de pouvoir adapter le menu au nombre de langues disponibles.
TMenuItem *EMenu = new TMenuItem(Form1);
PopupMenu1->Items->Add(EMenu);
Var EMenu: TMenuItem;
EMenu := TMenuItem.Create(Form1);
PopupMenu1.Items.Add(EMenu);
Il faut bien sûr avoir placé un composant TPopupMenu nomé PopupMenu1 et la fiche nommée Form1. On peut également faire la même chose avec un composant TMainMenu. Par exemple pour ajouter un sous-menu au 2° sous-menu du 3° sous-menu :
MainMenu1->Items->Items[2]->Items[1]->Add(EMenu);
MainMenu1.Items.Items[2].Items[1].Add(EMenu);
NB : Quand on place un Menu général, on crée des sous-menus (qui apparaissent en ligne en haut de la fiche), puis un 2° niveau de sous-menus (qui apparaissent en colonne quand on clique sur un sous-menu de 1° niveau), puis autant de sous-niveaux que l'on désire en faisant un clic droit sur un sous-menu puis en sélectionnant Créer un sous-menu, qui apparaîtront à en colonnes à droite du niveau précédent. Il est également possible de faire la même chose avec les PopupMenu. Il faut ensuite configurer l'élément de menu, principalement le nom (propriété Name), le texte (propriété Caption), la propriété RadioItem à True et surtout lui affecter l'événement OnClick.

Implémentation finale

Les fichiers langues

Les fichiers langues peuvent être créés dans n'importe quel éditeur. Vous pouvez définir leur structure comme vous le désirez, à condition de le répercuter dans votre code. Vous pouvez choisir l'extension que vous voulez mais l'extension *.lng est la plus répandue. Vous pouvez identifier chaque traduction par un nom ou par un numéro, les classer dans plusieurs sections ou tout mettre dans la même. Il faut faire un compromis entre la taille du fichier et sa lisibilité, et là tout dépend de la quantité de traductions que vous avez à enregistrer et si vous désirez réaliser vous même tous les fichiers ou si vous voulez laissez la possibilité aux utilisateurs de les créer.

Exemple de code complet

void __fastcall ChangerLangue(AnsiString Langue)
{
  TIniFile *FichierIni = new TIniFile(CheminApp + Langue + ".lng");
  try
  {
    Form1->tbCrypter->Caption = FichierIni->ReadString("Menu", "Ouvrir", "&Ouvrir");
    Msg[0] = FichierIni->ReadString("Messages", "Erreur", "Il s'est produit une erreur");
    ...
  } __finally { delete FichierIni; }
}

void __fastcall TForm1::miFrancaisClick(TObject *Sender)
{
  ((TMenuItem*)Sender)->Checked = true;
  ChangerLangue(((TMenuItem*)Sender)->Caption);
}

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  TSearchRec SR;
  TRegistry *Registre = new TRegistry;
  Registre->RootKey = HKEY_LOCAL_MACHINE;
  Registre->OpenKey("Software\\CR-TEKnologies\\CR-KryptX", true);
  AnsiString Langue = Registre->ReadString("Langue");
  delete Registre;
  AnsiString Recherche = CheminApp + "\\*.lng";
  miFrancais->Checked = true;
  try
  {
    int i = 0;
    if (FindFirst(Recherche, faAnyFile, SR) == 0)
    do
    {
      i++;
      AnsiString nomFichier = ExtractFileName(SR.Name);
      nomFichier.Delete(nomFichier.Length() - 3, 4);
      TMenuItem *EMenu = new TMenuItem(Form1);
      EMenu->Name = "miAuto" + IntToStr(i);
      EMenu->Caption = nomFichier;
      EMenu->RadioItem = true;
      EMenu->OnClick = TForm1::miFrancaisClick;
      PopupMenu->Items->Add(EMenu);
      if (nomFichier == Langue) EMenu->Checked = true;
    } while (FindNext(SR) == 0);
  } __finally { FindClose(SR); }
}

Procedure ChangerLangue(Langue: String);
Var
  FichierIni: TIniFile;
Begin
  FichierIni := TIniFile.Create(CheminApp + Langue + '.lng');
  Try
    Form1.tbCrypter.Caption := FichierIni.ReadString('Menu', 'Ouvrir', '&Ouvrir');
    Msg[0] := FichierIni.ReadString('Messages', 'Erreur', 'Il s''est produit une erreur');
    ...
  Finally FichierIni.Free; End;
End;

procedure TForm1.miFrancaisClick(Sender: TObject);
Begin
  (Sender As TMenuItem).Checked := True;
  ChangerLangue((Sender As TMenuItem).Caption);
End;

procedure TForm1.FormCreate(Sender: TObject);
Var
  i: Integer;
  SR: TSearchRec;
  EMenu: TMenuItem;
  Recherche, nomFichier: String;
  Registre: TRegistry;
Begin
  CheminApp := ExtractFilePath(ParamStr(0));
  Registre := TRegistry.Create();
  Registre.RootKey := HKEY_LOCAL_MACHINE;
  Registre.OpenKey('Software\CR-TEKnologies\CR-KryptX', True);
  Langue := Registre.ReadString('Langue');
  Registre.Free;
  Recherche := CheminApp + '\*.lng';
  miFrancais.Checked := True;
  Try
    i := 0;
    If (FindFirst(Recherche, faAnyFile, SR) = 0) Then
    Repeat
      Inc(i);
      nomFichier := ExtractFileName(SR.Name);
      Delete(nomFichier, Length(SR.Name) - 3, 4);
      EMenu := TMenuItem.Create(Form1);
      EMenu.Name := 'miAuto' + IntToStr(i);
      EMenu.Caption := nomFichier;
      EMenu.RadioItem := True;
      EMenu.OnClick := miFrancaisClick;
      PopupMenu.Items.Add(EMenu);
      if (nomFichier = Langue) Then EMenu.Checked := True;
    Until (FindNext(SR) <> 0);
  Finally FindClose(SR); End;
End;

Conclusion

Vous devez maintenant être capable de réaliser une application entièrement multilingue très facilement.
Si vous avez un problème ou une question, n'hésitez pas à me contacter : contact@crteknologies.fr


Retour : Accueil > Programmation > Conseils > [haut]
 Logiciels    Wiki     Programmation     Electronique     Projets     Auteur 


Vie privée