I. Le projet▲
II. Les objets de GVLOGO▲
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 :
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 :
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 :
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 :
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▲
III-B-6. Boîte « À propos »▲
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 :
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 :
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 :
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 :
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 » :
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 :
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 :
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 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 :
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 :
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
entête
pForward := MemoInt[1
]; //
on
récupère
les
données
de
l'entê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 » :
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 :
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 :
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'entê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 :
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
où
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'erreurs lors des exceptions qui apparaîtront en anglais.
En fait, afin d'obtenir une traduction française de tous les messages venant de la librairie 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(1) !
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
.