Réaliser un interpréteur en Pascal

Avec Free Pascal sous Lazarus


précédentsommairesuivant

III. Récréation : EasyTurtle (logiciel de dessin)

Avant d'aborder les outils de programmation et de rentrer dans le cœur de l'interpréteur, une récréation s'impose avec la création d'un petit logiciel de dessin baptisé EasyTurtle.

III-A. Le projet EasyTurtle

Il s'agit de mettre en œuvre l'unité GVTurtles en permettant à un enfant de dessiner avec la tortue, de rejouer l'ensemble des ordres qu'il lui aura donnés, ou encore de charger et de sauvegarder ses réalisations.

Quant au programmeur, il pourra utiliser quelques outils particulièrement utiles, en particulier les listes d'actions.

III-B. B - Mode d'emploi rapide

III-B-1. L'écran d'accueil

L'écran d'accueil se présente comme ceci :

Image non disponible

Sur l'essentiel de la fenêtre se situe la zone d'affichage de la tortue : c'est ici que seront réalisés les dessins. Afin de faciliter sa manipulation, la zone de dessin est en mode « clos », ce qui signifie que la tortue ne peut pas être perdue de vue, car tout ordre tendant à la faire disparaître conduira à la faire buter contre le bord de son champ comme s'il y avait une barrière.

À droite de cette zone d'affichage, on aperçoit toute une série de boutons : ce sont les actions mises à disposition de l'utilisateur.

III-B-2. La tortue

On distingue ainsi les actions concernant la tortue :

Image non disponible

La tortue peut donc avancer, reculer, tourner à gauche et à droite. Chacune de ces opérations se fait selon une valeur par défaut modifiable grâce à une fenêtre de réglage des préférences.

Si elle est de forme triangulaire, la tortue peut aussi grossir et rapetisser. Dans le cas contraire, ces boutons sont grisés, et par conséquent inactifs.

L'utilisateur peut effacer l'écran, renvoyer la tortue à son origine, l'autoriser ou lui interdire d'écrire, la rendre visible ou invisible.

Un bouton indique toujours l'action à réaliser, et non l'état de la tortue. Ainsi, si le bouton comporte le message « N'écris plus », c'est que la tortue laisse actuellement une trace et qu'une pression sur le bouton lui demandera de ne plus écrire.

III-B-3. Couleurs et formes

Le panneau suivant s'occupe des couleurs et des formes :

Image non disponible

Le premier bouton permute l'apparence de la tortue, entre le triangle et le dessin au format png. Le second ouvre une fenêtre de choix de la couleur du crayon, tout comme le suivant pour la couleur de fond. Les deux derniers dessinent respectivement un carré et un cercle à l'emplacement de la tortue.

III-B-4. Ordres généraux

Le panneau suivant regroupe les ordres généraux concernant le travail effectué par la tortue :

Image non disponible Image non disponible

EasyTurtle propose un enregistrement des ordres donnés à la tortue. C'est à partir de cet enregistrement qu'opéreront les boutons, « Charge », « Sauve », « RAZ » et « Défais ».

L'utilisateur peut sauvegarder et charger son travail. Il peut ouvrir des fenêtres spécialisées : « Outils », « À Propos » et « Aide ». Il peut aussi rejouer une séquence enregistrée et interrompre cette répétition : ce sont les deux boutons sans légende qui apparaissent en bas à gauche de la copie d'écran. Il peut encore annuler le dernier ordre donné à la tortue (bouton « Défais ») ou remettre à zéro toute la séquence (bouton « RAZ »). Enfin, il peut quitter le logiciel.

Le fait de charger une suite d'ordres l'exécute immédiatement après le chargement.

Suivant l'état du logiciel, certains boutons seront désactivés : par exemple, les boutons « Défais », « Sauve » et « Rejoue » ne seront activés que si des ordres ont été enregistrés. Le bouton « Stop » ne sera activé que si le bouton « Rejoue » a été pressé et seulement le temps de la répétition. Lorsque le bouton « Rejoue » a été pressé, tous les boutons, sauf « Quitter » et « Stop », sont désactivés.

Pour réinitialiser la séquence d'ordres, plusieurs possibilités sont offertes : chargement d'un nouveau fichier d'ordres, pression sur le bouton « RAZ » et modification du fond de l'écran. Contrairement au bouton « Efface l'écran », le bouton « RAZ » conserve la couleur d'écriture et celle du fond de l'écran.

III-B-5. L'aide

Une aide peut être demandée :

Image non disponible

III-B-6. Boîte « À propos »

De même, on peut accéder à une boîte « À propos » :

Image non disponible

III-B-7. Boîte des préférences

Enfin, une boîte d'outils permet de modifier les valeurs par défaut de certains ordres concernant le dessin effectué par la tortue :

Image non disponible

Les modifications apportées grâce à la boîte des préférences sont enregistrées avec le fichier des ordres. Les valeurs sont données en pixels et en degrés.

III-B-8. Autres éléments

Tous les boutons décrits ci-dessus ont leur double dans la barre d'outils située en haut de la fenêtre principale :

Image non disponible

Les pictogrammes sont évidemment les mêmes, afin de renforcer la cohérence du logiciel. De plus, la plupart des ordres peuvent être donnés par une combinaison de touches indiquée en aide ponctuelle près du bouton avant de le presser et dans la barre de statut.

Pour terminer cette présentation rapide, il faut noter des indicateurs fournis par la barre de statut et par deux disques situés en bas à droite de la fenêtre principale :

Image non disponible

On prend ainsi connaissance d'une aide succincte concernant le bouton survolé par la souris, des principales valeurs associées à la tortue et des valeurs attribuées à la couleur du crayon de la tortue (premier disque) et au fond de l'écran (second disque), ainsi que l'état du logiciel : « enregistrement en cours », « sauvegarde en cours », « chargement en cours » et « répétition des ordres ».

III-C. La programmation

La suite de cette partie décrit le fonctionnement d'EasyTurtle. Contrairement aux autres logiciels d'exemples, celui-ci fonctionne à partir de plusieurs fiches :

Image non disponible

L'unité principale s'appelle Main.pas. GVAbout.pas contient la boîte « À propos », Help.pas l'aide et GVTools.pas la boîte des préférences.

L'appel d'une fiche externe se fait selon un modèle courant depuis la fiche « Main ». Voici, par exemple, l'appel de la boîte « À propos » :

 
Sélectionnez
procedure TMainForm.ActionAboutExecute(Sender: TObject);
// *** boîte à propos ***
begin
  GVAbout.AboutForm := TAboutForm.Create(Self); // on crée la fiche
  try
    GVAbout.AboutForm.ShowModal; // on l'affiche
  finally
    GVAbout.AboutForm.Free; // on la libère
  end;
end;

III-C-1. La fiche principale

La fiche « Main » contient toutes les méthodes nécessaires au fonctionnement d'EasyTurtle. Les éléments les plus complexes sont ceux relatifs à la mémorisation des ordres donnés à la tortue : le logiciel utilise à cette fin un tableau ouvert géré par une méthode nommée Memorize :

 
Sélectionnez
procedure TMainForm.Memorize(const Value: Integer);
// *** mémorisation d'une action ***
begin
  SetLength(MemoInt, Length(MemoInt) + 1); // on augmente la taille du tableau
  MemoInt[Length(MemoInt) - 1] := Value; // on enregistre
  pSaved := False; // séquence non enregistrée
end;

Cette méthode ajuste la taille du tableau avant d'enregistrer la nouvelle donnée. Elle indique aussi que la séquence a été modifiée en vue d'un futur enregistrement.

La plupart des ordres sont gérés de manière identique. Voici par exemple l'ordre ActionForwardExecute qui fait avancer la tortue :

 
Sélectionnez
procedure TMainForm.ActionForwardExecute(Sender: TObject);
// *** la tortue avance ***
begin
  GVTurtle.Move(pForward); // la tortue bouge
  Memorize(CT_Forward); // mémorisation
end;

On exécute l'ordre et on l'enregistre, rien de plus facile ! Simplement, afin de permettre aux boutons, à la barre d'outils et aux combinaisons de touches de fonctionner de la même manière sans dupliquer le code, on utilise un composant TActionList qui centralise les actions.

En plus de l'exécution, on a prévu une mise à jour (disponibilité, visibilité, affichage) de chaque fonction suivant l'état du logiciel :

 
Sélectionnez
procedure TMainForm.ActionForwardUpdate(Sender: TObject);
// actions actives/inactives
begin
  // seulement si enregistrement
  (Sender as TAction).Enabled := (pState = stRecording);
end;

Ici, l'objet appelant (qui doit être une action) n'est activé que si le mode est celui de l'enregistrement. En effet, il ne faut pas continuer à dessiner au cours de la sauvegarde, du chargement ou si l'on est en train de rejouer toute la séquence.

On aurait pu indiquer directement ActionForward dans la méthode, mais le transtypage (Sender as TAction) permet de partager le même gestionnaire avec d'autres actions au comportement identique (ActionBackward, par exemple).

Rejouer la séquence exige de dispatcher les ordres en fonction de leur enregistrement :

 
Sélectionnez
procedure TMainForm.Replay;
// *** rejoue les actions ***
begin
  GVTurtle.ReInit; // réinitialisation de la tortue
  GVTurtle.Speed := TbSpeed.Position; // vitesse selon barre
  GVTurtle.Screen := teGate; // écran clos
  fCmd := C_MinCmds; // on pointe sur le premier élément du tableau hors en-tête
  pForward := MemoInt[1]; // on récupère les données de l'en-tête
  pBackward := MemoInt[2];
  pLeft := MemoInt[3];
  pRight := MemoInt[4];
  pLength := MemoInt[5];
  GVTurtle.ScreenColor := MemoInt[6];
  // on boucle tant qu'il y a des ordres et qu'un arrêt n'a pas été demandé
  while (fCmd < Length(MemoInt)) and (pState = stPlaying) do  // on balaie le tableau
  begin
     //images adaptées pour les roues
    tbReplay.ImageIndex := (fCmd mod 31) + 15;
    ImageListBigWait.GetBitmap(fCmd mod 31 ,ImgRound.Picture.Bitmap);
    // on permet aux messages d'être traités
    Application.ProcessMessages;
    // on répartit le travail suivant les ordres enregistrés
    case MemoInt[fCmd] of
      CT_Forward : GVturtle.Move(pForward); // avance
      CT_Backward : GVturtle.Move(pBackward); // recule
      CT_Left : GVturtle.Turn(pLeft); // à gauche
      CT_Right : GVturtle.Turn(pRight); // à droite
      CT_Bigger : GVturtle.Size := GVTurtle.Size + 2;  // taille + 2
      CT_Minus : GVturtle.Size := GVTurtle.Size - 2; // taille - 2
      CT_Home :  begin
        GVTurtle.Home; // maison
        Inc(fCmd, 3); // ordre suivant
      end;
      CT_UpDown : GVTurtle.PenDown := not GVTurtle.PenDown; // crayon baissé
      // visibilité
      CT_SeeSaw : GVTurtle.TurtleVisible := not GVTurtle.TurtleVisible;
      CT_Kind : if GVTurtle.Kind <> tkTriangle then // type
                  GVTurtle.Kind := tkTriangle
                else
                  GVTurtle.Kind := tkPng;
      CT_Pen : begin  // couleur crayon
        Inc(fCmd,2); // ordre suivant
        GVTurtle.PenColor := MemoInt[fCmd];
      end;
      CT_Square: GVTurtle.Square(pLength); // carré
      CT_Circle: GVTurtle.Circle(pLength); // cercle
    end;
    Inc(fCmd); // ordre suivant
  end;
  Refresh; // remet à jour les voyants
  pState := stRecording; // on repasse en mode enregistrement
end;

Le champ privé fCmd est le pointeur utilisé sur l'ordre en cours. Avant de rejouer les ordres, on s'occupe de l'en-tête qui contient les valeurs modifiables depuis la fenêtre des paramètres. Ces valeurs sont enregistrées avec l'éventuel fichier de sauvegarde.

On remarquera la présence de Application.ProcessMessages qui permet à l'application de réagir aux événements tels que l'appui sur le bouton « Stop ». On profite aussi de cette boucle pour animer certains boutons : ainsi, deux roues seront animées. Certaines données ne sont utiles qu'en cas d'action de correction, en remontant dans le temps : pour rejouer la séquence, on les ignore simplement (voir le traitement de CT_Home, par exemple).

La partie la plus complexe de cette unité est celle relative à la fonction « Défaire » :

 
Sélectionnez
procedure TMainForm.ActionUndoExecute(Sender: TObject);
// *** défaire la dernière action ***
begin
  if (Length(MemoInt) > (C_MinCmds + 4)) and // ordre le plus long = HOME
    (MemoInt[Length(MemoInt) - 4] = CT_Home) then
  begin
    GVTurtle.PenRubber := True;  // on efface
    GVTurtle.SetPos(Round(MemoInt[Length(MemoInt) - 2])
        , Round(MemoInt[Length(MemoInt) - 1])); // on se repositionne
    GVTurtle.Heading := MemoInt[Length(MemoInt) - 3]; // direction de la tortue
    GVTurtle.PenRubber := False; // on écrit normalement
    SetLength(MemoInt, Length(MemoInt) - 4); // on réajuste la mémorisation
  end
  else
  if (Length(MemoInt) > (C_MinCmds + 3)) and (MemoInt[Length(MemoInt) - 3]
    = CT_Pen) then // couleur de crayon
  begin
    GVTurtle.PenColor := MemoInt[Length(MemoInt) - 2]; // couleur récupérée
    SetLength(MemoInt, Length(MemoInt) - 3); // on ajuste
  end
  else
  begin
    case MemoInt[Length(MemoInt) - 1] of
      CT_Forward: begin  // on a avancé
        GVTurtle.PenRubber := True; // on efface
        GVTurtle.Move(-pForward); // donc on recule
        GVTurtle.PenRubber := False; // on écrit normalement
      end;
      CT_Backward: begin // on a reculé
        GVTurtle.PenRubber := True; // on efface
        GVTurtle.Move(-pBackward); // donc on avance
        GVTurtle.PenRubber := False; // on écrit normalement
      end;
      CT_Left: GVTurtle.Turn(-pLeft); // gauche devient droite
      CT_Right: GVTurtle.Turn(-pRight); // droite devient gauche
      CT_SeeSaw: GVTurtle.TurtleVisible := not GVTurtle.TurtleVisible; // visibilité
      CT_UpDown: GVTurtle.PenDown:= not GVTurtle.PenDown; // écriture ou non
      CT_Kind: if GVTurtle.Kind = tkTriangle then // type de tortue
        GVTurtle.Kind := tkPNG
      else
        GVTurtle.Kind := tkTriangle;
      CT_Bigger: GVTurtle.Size := GVTurtle.Size - 2; // on a grossi
      CT_Minus: GVTurtle.Size := GVTurtle.Size + 2; // on a rapetissé
      CT_Circle: begin  // un cercle
        GVTurtle.PenRubber:= True;
        GVTurtle.Circle(pLength+1);
        GVTurtle.PenRubber:= False;
      end;
      CT_Square: begin  // un carré
        GVTurtle.PenRubber:= True;
        GVTurtle.Square(pLength+1);
        GVTurtle.PenRubber:= False;
      end;
    end;
    SetLength(MemoInt, Length(MemoInt) - 1); // on ajuste la mémorisation
  end;
  pSaved := not (Length(MemoInt) > C_MinCmds); // drapeau d'enregistrement
end;

En effet, pour être annulées, certaines fonctions exigent que l'état de la tortue soit enregistré au préalable : ainsi, pour annuler un retour à l'origine, il faut se souvenir de l'ancien emplacement de la tortue, mais aussi de son orientation. En amont, il faut donc que chaque ordre mémorise ce qu'attendra une éventuelle correction.

La solution adoptée ici est de repasser sur le dernier trait en l'effaçant. Cette méthode est très rapide, mais elle peut effacer un trait qui devrait être conservé parce qu'il est recouvrant.

La méthode ActionSaveExecute d'enregistrement de la séquence doit tenir compte des remarques précédentes :

 
Sélectionnez
procedure TMainForm.ActionSaveExecute(Sender: TObject);
// *** sauvegarde des ordres de la tortue ***
var
  F: TextFile;
  OK: Boolean;
  I: Integer;
begin
  OK := False; // abandon par défaut
  repeat
    Ok := SaveDialog.Execute; // dialogue de sauvegarde
    if Ok then
    begin
      // confirmation si le fichier existe
      if FileExists(SaveDialog.FileName) then  // boîte de dialogue si existe
        // demande de remplacement si le fichier existe
        case MessageDlg(Format(ME_Replace,[ExtractFileName(SaveDialog.FileName)]),
               mtConfirmation, mbYesNoCancel,0) of
          mrYes: Ok := True; // on écrase l'ancien fichier
          mrNo: Ok := False; // on recommence
          mrCancel: Exit; // abandon si le fichier existe
        end;
    end
    else
      Exit; // abandon dès la boîte de dialogue
  until Ok;
  try
    AssignFile(F,SaveDialog.FileName); // fichier assigné
    pState := stSaving; // mode sauvegarde indiqué
    try
      Rewrite(F); // on remet à zéro le fichier
      for I := 0 to Length(MemoInt) - 1 do // on balaie les ordres enregistrés
      begin
        // images qui tournent pendant le chargement
        tbReplay.ImageIndex := (I mod 31) + 15;
        ImageListBigWait.GetBitmap(I mod 31 ,ImgRound.Picture.Bitmap);
        Application.ProcessMessages; // on traite les messages
        writeln(F, MemoInt[I]); // on écrit dans le fichier
      end;
      pSaved := True; // sauvegarde effectuée
    except
      // erreur de sauvegarde
      MessageDlg(Format(ME_SaveError,[ExtractFileName(SaveDialog.FileName)]),
        mtError, [mbOk],0);
    end;
  finally
    CloseFile(F); // fermeture du fichier
    pState := stRecording;
    Refresh;  // remet à jour les voyants
  end;
end;

Cette méthode vérifie l'existence du fichier avant d'éventuellement l'écraser puis enregistre les données dans l'ordre du tableau des commandes. Elle anime aussi deux roues en permettant à l'application de gérer les événements grâce à la méthode Application.ProcessMessages.

Le chargement d'un fichier est un brin plus complexe :

 
Sélectionnez
procedure TMainForm.ActionLoadExecute(Sender: TObject);
// ouverture d'un fichier de commandes
var
  F: TextFile;
  Num, I: Integer;
begin
  if OpenDialog.Execute then // boîte de dialogue d'ouverture de fichier
  begin
    try
      AssignFile(F,OpenDialog.FileName); // fichier assigné
      pState := stLoading;  // statut signifié
      try
        Reset(F); // fichier réinitialisé
        readln(F, Num);
        if Num <> CT_Version then
        begin
          MessageDlg(Format(ME_VersionError,[ExtractFileName(SaveDialog.FileName)]),
          mtError, [mbOk], 0); // signale une erreur de version
          Exit; // sortie
        end;
        readln(F, Num); // récupère les données de l'en-tête
        pForward := Num;
        readln(F, Num);
        pBackward := Num;
        readln(F, Num);
        pLeft := Num;
        readln(F, Num);
        pRight := Num;
        readln(F, Num);
        pLength := Num;
        readln(F, Num);
        GVTurtle.ScreenColor := Num;
        ActionRAZExecute(Sender); // initialisation
        I := -1; // pointeur de travail pour l'affichage
        repeat
          Inc(I); // élément suivant
          // images des roues mises à jour
          tbReplay.ImageIndex := (I mod 31) + 15;
          ImageListBigWait.GetBitmap(I mod 31 ,ImgRound.Picture.Bitmap);
          Application.ProcessMessages; // on permet les messages
          readln(F, Num); // on lit une donnée
          Memorize(Num); // qu'on enregistre
        until EOF(F) ; // jusqu'à la fin du fichier
      except
        MessageDlg(Format(ME_LoadError,[ExtractFileName(SaveDialog.FileName)]),
          mtError, [mbOk], 0); // signale une erreur de lecture
        ActionEraseExecute(Sender); // on remet à zéro
      end;
    finally
      CloseFile(F); // ferme le fichier
      pState := stRecording; // on enregistre de nouveau
      Refresh;  // remet à jour les voyants
    end;
    ActionReplayExecute(Sender); // rejoue immédiatement la séquence
    pSaved := True; // enregistrement déjà fait
  end;
end;

Le premier élément du fichier doit contenir le numéro de version actuelle, soit 100. Il est suivi de l'en-tête puis des données proprement dites. Au fur et à mesure de la lecture, on fait tourner les roues habituelles et on enregistre les ordres grâce à la méthode Memorize. À la fin de la méthode, on lance l'exécution de la séquence afin d'avoir un écran à jour : sans ce dernier point, la fonction « Défaire » serait erronée puisqu'elle corrigerait un écran non dessiné !

On peut enfin mentionner la méthode CloseQuery :

 
Sélectionnez
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
// *** demande de fermeture ***
begin
  // fermeture à confirmer
  if not pSaved then // si séquence non enregistrée
  begin  // demande d'enregistrement
    case MessageDlg(ME_NotSaved, mtConfirmation, mbYesNoCancel,0) of
      mrYes: begin // oui
        ActionSaveExecute(Sender); // sauvegarde
        CanClose := pSaved; // on sort si c'est fait
      end;
      mrNo: CanClose := True; // on sort
      mrCancel: CanClose := False; // on ne sort pas
    end;
  end
  else // cas  il n'y a rien à enregistrer
    CanClose := (MessageDlg(ME_Close, mtConfirmation, mbYesNo,0) = mrYes);
  // on arrête le dessin si nécessaire
 if CanClose then
   pState := stRecording;
end;

Lors d'une demande de fermeture du logiciel, on vérifie préalablement si la séquence en cours, lorsqu'elle a été modifiée, doit être enregistrée. Les messages diffèrent suivant les cas.

Le test de CanClose qui modifie si nécessaire pState s'explique par le fait que l'utilisateur peut avoir demandé la fermeture du logiciel alors qu'on rejoue une séquence. On interrompt alors cette répétition et l'on ferme le logiciel.

III-C-2. Les autres fiches

La boîte des préférences comprise dans l'unité GVTools est d'une simplicité enfantine : elle modifie ou non les données qu'elle doit gérer suivant le bouton pressé.

L'unité Help permet d'introduire quelques animations très simples : une image suit le déplacement de la souris en modifiant son champ Left, les composants TLabel ont leur texte en gras ou non suivant l'entrée/la sortie de la souris de leur surface…

L'unité GVAbout comprend aussi une animation aléatoire de la tortue.

III-C-3. Un logiciel tout en français…

Un programmeur non averti peut être surpris par le comportement de Lazarus dès qu'il s'agit de quitter les sentiers de l'anglais.

En effet, un programme aussi simple que celui-ci pose problème :

  • créer une nouvelle application ;
  • ajouter un TBitBtn à la fiche ;
  • modifier sa propriété Kind pour qu'elle soit sur bkClose ;
  • lancer le programme…

    Alors que le bouton affichait correctement « Fermer » dans l'EDI, il affiche sa version anglaise « Close » dans notre programme ! Qui plus est, ce comportement qui se répète pour tous les éléments qui ont besoin d'une traduction est indétectable lors de la réalisation de l'interface.

    Une solution partielle consiste à forcer la réécriture des éléments à traduire en les modifiant dans l'inspecteur d'objets. Malheureusement, cette solution ne règle pas les messages d'erreur lors des exceptions qui apparaîtront en anglais.

    En fait, afin d'obtenir une traduction française de tous les messages venant de la bibliothèque principale LCL, il est absolument nécessaire de modifier le fichier de projet. Pour cela :

  • choisir dans le menu « projet », l'élément « inspecteur de projet » afin de faire apparaître la fenêtre du projet ; cliquer sur le fichier <nom_du_projet>.lpr qui contient le code source du projet ;

  • repérer le chemin qui mène vers le fichier lclstrconsts.fr.po (normalement, pour Windows : <emplacement_de_Lazarus>\lcl\languages\) ;

  • modifier le projet en utilisant le listing qui suit(19) !
 
Sélectionnez
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, bgrabitmappack, Main, GVAbout, Help, GVTools, GVConsts, GVTurtles,
  SysUtils, GetText, Translations; // traduction française de la LCL

{$R *.res}

procedure TranslateLCL;
// *** traduction ***
var
  Lang, DefLang: string;
begin
  Lang := EmptyStr;
  Deflang := EmptyStr;
  GetLanguageIDs({%H-}Lang, {%H-}DefLang);
  // utilisation du fichier corrigé
  TranslateUnitResourceStrings('LCLStrConsts',
      '..\..\3rdparty\lclstrconsts.fr.po', Lang, DefLang);
end;

begin
  RequireDerivedFormResource := True;
  TranslateLCL; // traduction
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);
  Application.Run;
end.

précédentsommairesuivant
Dans le programme de test fourni, le fichier utilisé est sur le chemin du dossier GVLOGO. J'espère que la reproduction du fichier est fiable puisque c'est moi qui ai fourni cette traduction à l'équipe de Lazarus !

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2015 Gilles Vasseur. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.