Sign in Join
Wiki copytemporary

LCL Unicode Support/fr: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Introduction
22
33A partir de la version 0.9.25, Lazarus supporte pleinement Unicode pour toutes les plateformes, excepté Gtk 1. Cette page couvre l'Unicode dans Lazarus jusqu'à la version 1.4 qui utilise FPC 2.6.4
44
55Ayant commmençé avec la version 2.0, Lazarus aura amélioré le support Unicode en utilisant les caractéristiques de FPC 3.0+. Voir les détails ici: [[Unicode Support in Lazarus]]
66
77## Instructions pour les utilisateurs
88
99Même si Lazarus possède des ensembles de widgets Unicode, il est important de noter que tout n'est pas en Unicode. Il est de la responsabilité du développeur de connaître l'encodage de ses chaînes de caractère, et d'effectuer la conversion appropriée entre les bibliothèques qui attendent des encodages différents.
1010
1111Habituellement, l'encodage est défini bibliothèque par bibliothèque (une bibliothèque dynamique (dll) ou un package Lazarus). Chaque bibliothèque attendra uniformément un type d'encodage, qui sera habituellement soit Unicode (UTF-8 pour Lazarus), soit ANSI (qui signifie l'encodage du système, et peut être UTF-8 ou non). La RTL et la FCL de FPC \<= 2.6 attendent des chaînes ANSI.
1212
1313Vous pouvez convertir entre Unicode et ANSI en utilisant
1414
1515- les fonctions **UTF8ToAnsi** et **AnsiToUTF8** de l'unité System (de FPC),
1616- ou les fonctions **UTF8ToSys** et **SysToUTF8** de l'unité FileUtil (de Lazarus).
1717
1818Les deux dernières sont plus intelligentes (plus rapides) mais engendrent plus de code dans votre programme.
1919
2020### FPC ne travaille pas en Unicode
2121
2222Le Runtime Free Pascal (RTL), et la bibliothèque de composants Free Pascal (FCL), dans les versions actuelles de FPC (jusqu'à la 2.6.x) sont ANSI, vous devrez donc convertir les chaines venant des bibliothèques Unicode, ou allant vers des bibliothèques Unicode (comme la LCL).\
2323Il y a des améliorations significatives dans les branches de développement FPC 2.7.1 en ce qui concerne les chaînes. Voir RawByteString et UTF8String dans [[FPC Unicode support]].
2424
2525### Convertir entre ANSI et Unicode
2626
2727> **Note:** AnsiToUTF8 et UTF8ToAnsi nécessitent un gestionnaire de widestring sous Linux, BSD et Mac OS X. Vous pouvez utiliser les fonctions SysToUTF8 et UTF8ToSys (unité FileUtil) ou ajouter le gestionnaire de widestring en ajoutant cwstring comme l'une des première unités dans la section uses de votre programme.
2828
2929Exemples:
3030
3131Disons que vous récupérez une chaine d'un TEdit et que vous voulez la passer à une fonction de fichier de la RTL :
3232
3333``` pascal
3434var
3535 MyString: string; // Encodé en utf-8
3636begin
3737 MyString := MyTEdit.Text;
3838 SomeRTLRoutine(UTF8ToAnsi(MyString));
3939end;
4040```
4141
4242Et pour le sens inverse :
4343
4444``` pascal
4545var
4646 MyString: string; // Encodé en ANSI
4747begin
4848 MyString := SomeRTLRoutine;
4949 MyTEdit.Text := AnsiToUTF8(MyString);
5050end;
5151```
5252
5353**Important**: UTF8ToAnsi retournera une chaine vide si la chaine UTF8 contient des caractères invalides.
5454
5555### Widestrings et Ansistrings
5656
5757Une widestring est un type de chaine dont les éléments ont une taille de 2 octets. Les widestrings stockent pratiquement toujours leurs données dans l'encodage UTF-16. Voir [[Widestrings/fr|Widestrings]]
5858
5959Notez que chaque point de donnée accédé à la manière d'un tableau sur une widestring tient sur 2 octets. En UTF-16, un caractère peut utiliser 1 ou 2 points de données, et occupe donc 2 ou 4 octets. Cela signifie qu'acccéder à une widestring en tant que tableau et espérer obtenir un caractère UTF-16 est **complètement faux** et échouera lorsqu'un caractère sur 4 octets est présent dans la chaine. Notez également que UTF-16, comme UTF-8, peut avoir des caractères composés. Le caractère "Á" par exemple peut être encodé soit comme un seul caractère, soit comme 2 caractères : "A" + un modificateur d'accent. Donc en Unicode, un texte qui contient des caractères accentués peut souvent être encodé de plusieurs façons, et ni Lazarus ni FPC gèrent ça automatiquement.
6060
6161Quand vous passez de Ansistrings à Widestrings, vous devez convertir l'encodage.
6262
6363``` pascal
6464var
6565 w: widestring;
6666begin
6767 w:='Über'; // Faux, car FPC convertira la page de code système vers UTF16
6868 w:=UTF8ToUTF16('Über'); // correct
6969 Button1.Caption:=UTF16ToUTF8(w);
7070end;
7171```
7272
7373### Caractères et chaînes UTF8
7474
7575(NDT : une fois la page UnfUTF8_strings_and_characters traduite, il faut aligner ce paragraphe et ses s/paragraphes sur la version anglaise)
7676
7777Jusqu'à Lazarus 0.9.30, les routines de gestion de l'UTF-8 étaient dans la LCL dans l'unité LCLProc. Dans Lazarus 0.9.31 (et supérieur), les routines de LCLProc sont toujours disponibles pour la compatibilité ascendante, mais le vrai code qui gère UTF-8 est localisé dans le package lazutils, dans l'unité lazutf8.
7878
7979Pour exécuter des opérations sur des chaines UTF-8, il est préférable d'utiliser les routines de l'unité lazutf8 plutôt que celles de SysUtils de Free Pascal, car SysUtils n'est pas encore prêt à travailler avec Unicode, alors que lazutf8 l'est. Substituez simplement les routines de SysUtils avec leur équivalent lazutf8, qui ont toujours le même nom exception faite du préfixe "UTF8" ajouté.
8080
8181Notez également que simplement itérer sur les caractères comme si la chaine était un tableau ne fonctionne pas en Unicode. Ceci n'est pas quelque chose de spécifique à UTF-8 et vous ne pouvez simplement pas supposer qu'un caractère aura une taille fixe en Unicode. Si vous voulez itéré sur les caractères d'une chaine UFT-8, il y a basiquement deux moyens :
8282
8383- itérer sur les octets - utile pour chercher une sous chaine ou quand vous ne vous intéressez qu'aux caractères ASCII de la chaine UTF8. Par exemple pour analyser (parser) des fichier xml.
8484- itérer sur les caractères - utile pour les composants graphiques tel synedit. Par exemple lorsque vous souhaitez connaître le troisième caractère affiché à l'écran.
8585
8686#### Rechercher une sous chaine
8787
8888Du fait de la nature particulière d'UTF8, vous pouvez simplement utiliser les fonctions de manipulations de chaine classiques. Même si UTF-8 est un encodage multi-octets, le premier octet (d'un caractère multi-octets) ne peut pas être pris pour les suivants. Donc chercher une chaine UTF-8 valide avec Pos retournera toujours une position UTF-8 valide :
8989
9090``` pascal
9191uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
9292...
9393procedure Where(SearchFor, aText: string);
9494var
9595 BytePos: LongInt;
9696 CharacterPos: LongInt;
9797begin
9898 BytePos:=Pos(SearchFor,aText);
9999 CharacterPos:=UTF8Length(PChar(aText),BytePos-1);
100100 writeln('The substring "',SearchFor,'" is in the text "',aText,'"',
101101 ' at byte position ',BytePos,' and at character position ',CharacterPos);
102102end;
103103```
104104
105105Du fait de l’ambiguïté d'Unicode, Pos() (comme n'importe quelle comparaison) peut montrer un comportement inattendu, quand par exemple l'une des chaines contient des caractères composés, alors que l'autre utilise le code direct pour la même lettre. Ceci n'est pas automatiquement géré par la RTL.
106106
107107#### Accéder aux caractères UTF8
108108
109109Les caractères Unicode peuvent varier en longueur, donc la meilleure solution pour y accéder lorsque vous avez l'intention de le faire dans l'ordre dans lequel ils sont consiste à utiliser une itération. Pour itérer les caractères, utilisez ce code :
110110
111111``` pascal
112112uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
113113...
114114procedure DoSomethingWithString(AnUTF8String: string);
115115var
116116 p: PChar;
117117 CharLen: integer;
118118 FirstByte, SecondByte, ThirdByte: Char;
119119begin
120120 p:=PChar(AnUTF8String);
121121 repeat
122122 CharLen := UTF8CharacterLength(p);
123123
124124 // here you have a pointer to the char and it's length
125125 // You can access the bytes of the UTF-8 Char like this:
126126 if CharLen >= 1 then FirstByte := P[0];
127127 if CharLen >= 2 then SecondByte := P[1];
128128 if CharLen >= 3 then ThirdByte := P[2];
129129
130130 inc(p,CharLen);
131131 until (CharLen=0) or (p^ = #0);
132132end;
133133```
134134
135135#### Accéder au Nième caractère UTF8
136136
137137Vous pouvez également vouloir effectuer un accès direct aux caractères UTF-8.
138138
139139``` pascal
140140uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
141141...
142142var
143143 AnUTF8String, NthChar: string;
144144begin
145145 NthChar := UTF8Copy(AnUTF8String, N, 1);
146146```
147147
148148#### Itérer les points de code en utilisant UTF8CharacterToUnicode
149149
150150Ce qui suit montre comment itérer les valeurs des points de code 32bits de chacun des caractères dans une chaine UTF-8:
151151
152152``` pascal
153153uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
154154...
155155procedure IterateUTF8Characters(const AnUTF8String: string);
156156var
157157 p: PChar;
158158 unicode: Cardinal;
159159 CharLen: integer;
160160begin
161161 p:=PChar(AnUTF8String);
162162 repeat
163163 unicode:=UTF8CharacterToUnicode(p,CharLen);
164164 writeln('Unicode=',unicode);
165165 inc(p,CharLen);
166166 until (CharLen=0) or (unicode=0);
167167end;
168168```
169169
170170#### Copie, longueur, mise en minuscule, etc des chaines UTF-8
171171
172172Quasiment toutes les opérations que vous pouvez vouloir exécuter avec les chaines UTF-8 sont couvertes par les routines de l'unité lazutf8 (unité LCLProc pour Lazarus 0.9.30 ou inférieur). Regardez la liste suivante de routines prises de lazutf8.pas:
173173
174174``` pascal
175175function UTF8CharacterLength(p: PChar): integer;
176176function UTF8Length(const s: string): PtrInt;
177177function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
178178function UTF8CharacterToUnicode(p: PChar; out CharLen: integer): Cardinal;
179179function UnicodeToUTF8(u: cardinal; Buf: PChar): integer; inline;
180180function UnicodeToUTF8SkipErrors(u: cardinal; Buf: PChar): integer;
181181function UnicodeToUTF8(u: cardinal): shortstring; inline;
182182function UTF8ToDoubleByteString(const s: string): string;
183183function UTF8ToDoubleByte(UTF8Str: PChar; Len: PtrInt; DBStr: PByte): PtrInt;
184184function UTF8FindNearestCharStart(UTF8Str: PChar; Len: integer;
185185 BytePos: integer): integer;
186186// find the n-th UTF8 character, ignoring BIDI
187187function UTF8CharStart(UTF8Str: PChar; Len, CharIndex: PtrInt): PChar;
188188// find the byte index of the n-th UTF8 character, ignoring BIDI (byte len of substr)
189189function UTF8CharToByteIndex(UTF8Str: PChar; Len, CharIndex: PtrInt): PtrInt;
190190procedure UTF8FixBroken(P: PChar);
191191function UTF8CharacterStrictLength(P: PChar): integer;
192192function UTF8CStringToUTF8String(SourceStart: PChar; SourceLen: PtrInt) : string;
193193function UTF8Pos(const SearchForText, SearchInText: string): PtrInt;
194194function UTF8Copy(const s: string; StartCharIndex, CharCount: PtrInt): string;
195195procedure UTF8Delete(var s: String; StartCharIndex, CharCount: PtrInt);
196196procedure UTF8Insert(const source: String; var s: string; StartCharIndex: PtrInt);
197197
198198function UTF8LowerCase(const AInStr: string; ALanguage: string=''): string;
199199function UTF8UpperCase(const AInStr: string; ALanguage: string=''): string;
200200function FindInvalidUTF8Character(p: PChar; Count: PtrInt;
201201 StopOnNonASCII: Boolean = false): PtrInt;
202202function ValidUTF8String(const s: String): String;
203203
204204procedure AssignUTF8ListToAnsi(UTF8List, AnsiList: TStrings);
205205
206206//compare functions
207207
208208function UTF8CompareStr(const S1, S2: string): Integer;
209209function UTF8CompareText(const S1, S2: string): Integer;
210210```
211211
212212### Travailler avec les répertoires et les noms de fichier
213213
214214Les contrôles et fonctions de Lazarus attendent des noms de fichier et de répertoire encodés en utf-8, mais la RTL utilise des chaines ansi pour ces noms de fichier et répertoire.
215215
216216Par exemple, considérez un bouton, qui positionne la propriété Directory d'une TFileListBox au répertoire courant. La fonction RTL [[doc:rtl/sysutils/getcurrentdir.html|GetCurrentDir]] travaille en ansi, et non en unicode, donc une conversion est nécessaire:
217217
218218``` pascal
219219procedure TForm1.Button1Click(Sender: TObject);
220220begin
221221 FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
222222 // or use the functions from the FileUtil unit
223223 FileListBox1.Directory:=GetCurrentDirUTF8;
224224end;
225225```
226226
227227L'unité FileUtil définit les fonctions classiques de fichiers travaillant avec des chaines UTF-8:
228228
229229``` pascal
230230// basic functions similar to the RTL but working with UTF-8 instead of the
231231// system encoding
232232
233233// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, Mac OS X
234234// but normally these OS use UTF-8 as system encoding so the widestringmanager
235235// is not needed.
236236function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8
237237procedure SetNeedRTLAnsi(NewValue: boolean);
238238function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager
239239function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager
240240
241241// file operations
242242function FileExistsUTF8(const Filename: string): boolean;
243243function FileAgeUTF8(const FileName: string): Longint;
244244function DirectoryExistsUTF8(const Directory: string): Boolean;
245245function ExpandFileNameUTF8(const FileName: string): string;
246246function ExpandUNCFileNameUTF8(const FileName: string): string;
247247{$IFNDEF VER2_2_0}
248248function ExtractShortPathNameUTF8(Const FileName : String) : String;
249249{$ENDIF}
250250function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
251251function FindNextUTF8(var Rslt: TSearchRec): Longint;
252252procedure FindCloseUTF8(var F: TSearchrec);
253253function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
254254function FileGetAttrUTF8(const FileName: String): Longint;
255255function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
256256function DeleteFileUTF8(const FileName: String): Boolean;
257257function RenameFileUTF8(const OldName, NewName: String): Boolean;
258258function FileSearchUTF8(const Name, DirList : String): String;
259259function FileIsReadOnlyUTF8(const FileName: String): Boolean;
260260function GetCurrentDirUTF8: String;
261261function SetCurrentDirUTF8(const NewDir: String): Boolean;
262262function CreateDirUTF8(const NewDir: String): Boolean;
263263function RemoveDirUTF8(const Dir: String): Boolean;
264264function ForceDirectoriesUTF8(const Dir: string): Boolean;
265265
266266// environment
267267function ParamStrUTF8(Param: Integer): string;
268268function GetEnvironmentStringUTF8(Index : Integer): String;
269269function GetEnvironmentVariableUTF8(const EnvVar: String): String;
270270function GetAppConfigDirUTF8(Global: Boolean): string;
271271```
272272
273273#### Mac OS X
274274
275275(NDT : A reporter dans UTF8_strings_and_characters une fois traduite)
276276
277277Les fonctions de fichier de l'unité FileUtil font également attention à une particularité de Mac OS X: OS X normalise les noms de fichier. Par exemple, le nom de fichier 'ä.txt' peut être encodé en unicode avec deux séquences différentes (#\$C3#\$A4 et 'a'#\$CC#\$88). Sous Linux et BSD, vous pouvez utiliser les deux représentations. OS X convertit automatiquement le a tréma dans la séquence de trois octets. Ce qui signifie:
278278
279279``` pascal
280280if Filename1 = Filename2 then ... // n'est pas suffisant sous OS X
281281if AnsiCompareFileName(Filename1, Filename2) = 0 then ... // insuffisant sous fpc 2.2.2, même avec cwstring
282282if CompareFilenames(Filename1, Filename2) = 0 then ... // Ceci fonctionne toujours (unité FileUtil ou FileProcs)
283283```
284284
285285### Langages Est-asiatiques sous Windows
286286
287287La police par défaut (Tahoma) utilisée pour les contrôles d'interface utilisateur sous Windows XP est capable d'afficher correctement plusieurs langages, incluant l'arable, le russe et les langages occidentaux, mais pas les langages d'Asis de l'est, comme le chinois, le japonais et le koréen. En allant dans le Panneau de Configuration, dans les Options régionales, sur l'onglet Langages et en installant le East Asia Language Pack, la police standard de l'interface utilisateur commencera simplement à afficher correctement ces langages. Bien évidemment, les versions de Windows XP localisées pour ces langages contiendront déjà ce pack de langage. Instructions détaillées [ici](http://newton.uor.edu/Departments&Programs/AsianStudiesDept/Language/asianlanguageinstallation_XP.html).
288288
289289Il est supposé que les versions ultérieures de Windows ont un support direct (out-of-the-box) pour ces langages.
290290
291291### Usage of UTF-8
292292
293293Voir les détails dans [[UTF8 strings and characters]]
294294
295295## Particularités de Free Pascal
296296
297297### UTF8 et les fichiers sources - le BOM manquant
298298
299299Quand vous créez des fichiers source avec Lazarus et que vous saisissez des caractères non ascii, le fichier est sauvegardé en UTF-8. Il n**'utilise pas de BOM** (Byte Order Mark). Vous pouvez changer l'encodage via un clic droit dans source editor / File Settings / Encoding. La raison de l'absence de ce BOM vient de comment FPC traite les Ansistrings. Pour la compatibilité, la LCL utilise des Ansistrings, et pour la portabilité, la LCL utilise UTF8.
300300
301301Note: Certains éditeurs de texte windows peuvent interpréter les fichiers comme étant dans l'encodage système et les afficher avec des caractères invalides. N'ajoutez pas le BOM. Si vous ajoutez le BOM, vous devrez changer toutes les assignations de chaine. Par exemple:
302302
303303``` pascal
304304Button1.Caption := 'Über';
305305```
306306
307307Quand aucun BOM n'est présent (et aucun paramètre de codepage n'est passé), le compilateur considère la chaine comme étant dans l'encodage du système et copie chaque octet dans la chaine sans le convertir. C'est ainsi que la LCL attend les chaines.
308308
309309``` pascal
310310// source file saved as UTF without BOM
311311if FileExists('Über.txt') then ; // wrong, because FileExists expects system encoding
312312if FileExistsUTF8('Über.txt') then ; // correct
313313```
314314
315315## L'essentiel d'Unicode
316316
317317Le standard Unicode fait correspondre les entiers de 0 à 10FFFF(h) à des caractères. Chaque correspondance individuelle est appelée un point de code. En d'autres termes, les caractères Unicode sont en principe définis pour les points de codes de U+000000 à U+10FFFF (0 à 1 114 111).
318318
319319Il existe trois schémas pour représenter les points de code Unicode en séquences d'octets uniques. Ces schémas sont appelés formats de transformation Unicode : UTF-8, UTF-16 et UTF-32. Les conversions entre chacun d'eux sont possible. Voici leur propriétés basiques:
320320
321-`                                UTF-8 UTF-16 UTF-32`\
322-`Plus petit point de code [hex]  000000 000000 000000`\
323-`Plus grand point de code [hex]  10FFFF 10FFFF 10FFFF`\
324-`Taille unitaire de code [bits]  8     16     32`\
325-`Octets minimum/caractère        1      2      4`\
326-`Octets maximum/caractère        4      4      4`
321+```text
322+ UTF-8 UTF-16 UTF-32
323+Plus petit point de code [hex] 000000 000000 000000
324+Plus grand point de code [hex] 10FFFF 10FFFF 10FFFF
325+Taille unitaire de code [bits] 8 16 32
326+Octets minimum/caractère 1 2 4
327+Octets maximum/caractère 4 4 4
328+```
327329
328330**UTF-8** a plusieurs propriétés importantes et utiles: Il est interprété comme une séquence d'octets, entrainant que le concept d'octet de poids fort ou faible n'existe pas. Les caractères Unicode de U+0000 à U+007F (ASCII) sont simplement encodés comme des octets de 00h à 7Fh (compatibilité ASCII). Cela signifie que les noms de fichier et les chaines de caractères qui ne contiennent que des caractères ASCII 7 bits ont le même encodage en ASCII et en UTF-8. Tous les caractères \>U+007F sont encodés dans une séquence de plusieurs octets, pour chacun desquels les deux bits de poids forts sont positionnés. Aucune séquence d'octet d'un caractère n'est contenue à l'intérieur d'une séquence plus longue d'un autre caractère. Ceci permet des recherches faciles pour les sous chaine. Le premier octet d'une séquence multioctets qui représente un caractère non ASCII est toujours dans la plage C0h à FDh et il indique combien d'octets suivent pour ce caractère. Tous les octest suivant dans la séquence multioctet sont dans la plage 80h à BFh. Ceci permet facilement la resynchronisation et la robustesse.
329331
330332**UTF-16** possède la propriété la plus importante suivante: il utilise un simple mot de 16 bits pour encoder les caractères de U+0000 à U+d7ff, et une paire de mots de 16 bits chacun pour encoder n'importe quel autre caractère Unicode.
331333
332334Pour finir, chaque caractère Unicode peut être représenté comme un simple mot de 32 bits en **UTF-32**.
333335
334336Pour plus d'informations, voir: [Unicode FAQ - Questions basiques](http://www.unicode.org/faq/basic_q.html), [Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM](http://www.unicode.org/faq/utf_bom.html), [Wikipedia: UTF-8](http://en.wikipedia.org/wiki/UTF-8) [1](http://en.wikipedia.org/wiki/ISO-8859)
335337
336338# Détails d'implementation
337339
338340## Lazarus/LCL utilisent généralement uniquement UTF-8
339341
340342Depuis que l'interface gtk1 a été rendue obsolète dans Lazarus 0.9.31, toutes les interfaces LCL peuvent gérer Unicode, et la LCL utilise et accepte seulement des chaînes encodées en UTF-8, sauf pour les routines explicitement marquées comme acceptant d'autres encodages.
341343
342344## Activer Unicode pour l'interface win32
343345
344346#### Vue d'ensemble
345347
346348Premièrement, et le plus important, tous les patches Unicode pour l'interface Win32 doivent être inclus dans des IFDEF WindowsUnicodeSupport, pour éviter de casser l'interface ANSI existante. Une fois que ce sera stabilisé, tous les ifdefs seront supprimés et seule la partie Unicode restera. A ce moment tous les programmes existants qui utilisent des caractères ANSI nécessiteront une migration vers Unicode.
347349
348350#### Pas de support d'Unicode sur Win9x
349351
350352Les plateformes Windows \<=Win9x sont basées sur des code page standard ISO et supportent seulement partiellement Unicode. Les plateformes Windows à partir de Windows NT (c'est-à-dire Windows 2000, XP, Vista, 7, 8) et Windows CE supportent pleinement Unicode.
351353
352354Win 9x et NT offrent deux ensembles parallèles d'API : les anciennes ANSI *A et les nouvelles Unicode *W. Les fonctions *W acceptent les widestrings - encodées en UTF-16- comme paramètres.
353355
354356Les Windows 9x possèdent toutes les fonctions *W mais la plupart ont des implémentations vides, donc elle ne font rien. Seules certaines fonctions *W sont pleinement implémentées sous Windows 9x. Elles sont listées ci-dessous dans la section "Fonctions Wide présentes sur Windows 9x". Cette propriété est importante car elle permet d'avoir une seule application à la fois pour Win9x et WinNT, et détecter au runtime quel ensemble d'API utiliser.
355357
356358Windows CE utilise uniquement les API Wide.
357359
358360#### Fonctions Wide présentes sur Windows 9x
359361
360362Certaines fonctions des API Wide sont présentes sur Windows 9x. Vous trouverez ici une liste de ces fonctions: <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp>
361363
362364Exemple de conversion:
363365
364366``` pascal
365367GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
366368Length(ButtonCaption), TextSize);
367369```
368370
369371Devient:
370372
371373``` pascal
372374{$ifdef WindowsUnicodeSupport}
373375 GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
374376{$else}
375377 GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
376378{$endif}
377379```
378380
379381#### Fonctions qui nécessitent des versions Ansi et Wide
380382
381383Premier exemple de Conversion:
382384
383385``` pascal
384386function TGDIWindow.GetTitle: String;
385387var
386388 l: Integer;
387389begin
388390 l := Windows.GetWindowTextLength(Handle);
389391 SetLength(Result, l);
390392 Windows.GetWindowText(Handle, @Result[1], l);
391393end;
392394```
393395
394396Devient:
395397
396398``` pascal
397399function TGDIWindow.GetTitle: String;
398400var
399401 l: Integer;
400402 AnsiBuffer: string;
401403 WideBuffer: WideString;
402404begin
403405
404406{$ifdef WindowsUnicodeSupport}
405407
406408if UnicodeEnabledOS then
407409begin
408410 l := Windows.GetWindowTextLengthW(Handle);
409411 SetLength(WideBuffer, l);
410412 l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
411413 SetLength(WideBuffer, l);
412414 Result := Utf8Encode(WideBuffer);
413415end
414416else
415417begin
416418 l := Windows.GetWindowTextLength(Handle);
417419 SetLength(AnsiBuffer, l);
418420 l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
419421 SetLength(AnsiBuffer, l);
420422 Result := AnsiToUtf8(AnsiBuffer);
421423end;
422424
423425{$else}
424426
425427 l := Windows.GetWindowTextLength(Handle);
426428 SetLength(Result, l);
427429 Windows.GetWindowText(Handle, @Result[1], l);
428430
429431{$endif}
430432
431433end;
432434```
433435
434436### Captures d'écran
435437
436438![](/wiki-images/4/49/Lazarus_Unicode_Test.png)
437439
438440# Pages de code FPC
439441
440442Pourquoi la LCL n'utilise pas les pages de code (codepage) pour les sources ?
441443
442444Les -Fcutf8 et {\$codepage utf8} existent depuis longtemps.
443445
444446Le programme suivant nécessite FPC 2.7.1. Le problème suivant existe aussi sur les plus vieux compilateurs.
445447
446448Il y a quelques pièges avec -Fcutf8 et{\$codepage utf8}. Ca marche seulement si le DefaultSystemCodePage de la RTL est CP_UTF8. Sinon, vos chaines sont converties par le compilateur.
447449
448450Par exemple, sous Linux, le paramétrage par défaut est CP_ACP, qui devient ISO_8859-1. La RTL **ne** lit **pas** toute seule le langage de votre environnement. Donc l'encodage par défaut est ISO_8859-1. Cela signifie que vos constantes chaîne UTF-8 sont converties par le compilateur :
449451
450452Compilez ceci avec -Fcutf8 et lancez-le sur un linux avec LANG paramétré à utf-8:
451453
452454``` pascal
453455program project1;
454456{$mode objfpc}{$H+}
455457begin
456458 writeln(DefaultSystemCodePage,' ',CP_UTF8);
457459 writeln('ä');
458460end.
459461```
460462
461463Résultat :
462464
463465 0 65001
464466 ä
465467
466468La LCL utilise un gestionnaire de widestring (cwstring pour le moment), qui paramètre le DefaultSystemCodePage. Vous pouvez faire de même dans vos programmes non-LCL :
467469
468470``` pascal
469471program project1;
470472{$mode objfpc}{$H+}
471473uses cwstring;
472474begin
473475 writeln(DefaultSystemCodePage,' ',CP_UTF8);
474476 writeln('ä');
475477end.
476478```
477479
478480Résultat :
479481
480482 65001 65001
481483 ä
482484
483485Ce qui précède est toutefois une simplification, un mensonge pour enfants. Prenons le programme suivant :
484486
485487``` pascal
486488program project1;
487489{$mode objfpc}{$H+}
488490uses cwstring;
489491var
490492 a,b,c: string;
491493begin
492494 writeln(DefaultSystemCodePage,' ',CP_UTF8);
493495 a:='ä'; b:='='#$C3#$A4; // #$C3#$A4 is UTF-8 for ä
494496 c:= 'ä='#$C3#$A4;
495497 writeln(a,b); // writes ä=ä
496498 writeln(c); // writes ä=ä
497499end.
498500```
499501
500502 65001 65001
501503 ä=ä
502504 ä=ä
503505
504506Vous pouvez voir que les constantes chaîne en UTF-8 marchent, que les constantes chaînes avec des codes UTF-8 marchent aussi, mais que la combinaison ne marche pas. Ca a été compilé avec -Fcutf8 et utilise cwstring pour fixer le DefaultSystemCodePage à CP_UTF8.
505507
506508Alors qu'est-ce qui cloche ?
507509
508510Le compilateur traite toute constante chaine non ASCII (ici : le "ä") comme des widestring (UCS2, pas UTF-16).
509511
510512Vous ne pouvez pas tromper le compilateur avec 'ä='+#\$C3#\$A4. Vous devez définir deux constantes chaînes différentes.
511513
512514Utiliser n'importe quel caractère hors de la plage UCS-2 résulte en un
513515
514516 Fatal: illegal character "'�'" ($F0)
515517
516518Vous pouvez les spécifier avec des codes UTF-16 : *\#\$D834#\$DD1E*. Oui, vous avez bien lu. Spécifier une page de code avec -Fcutf8 ou {\$codepage utf8} défini en fait un mélange d'UTF-8 et d'UTF-16.
517519
518520Maintenant, compilez le même programme sans -Fcutf8:
519521
520522 65001 65001
521523 ä=ä
522524 ä=ä
523525
524526Ouah, tout marche comme prévu. Vous pouvez même mixer des constantes chaînes ASCII et non-ASCII. Sans page de code, le compilateur stocke les constantes chaine comme des séquences d'octets. C'est exactement ce qu'est UTF-8.
525527
526528C'est une des raisons pour lesquelles les applications LCL n'utilisent pas les drapeaux de page de code (codepage flags).
527529
528530msegui a implémenté un écosystème de widestrings, qui marche mieux avec un code de page.
529531
530532# Voir aussi
531533
532534- [[Character and string types/fr|Types caractère et chaîne]]
533535- [[UTF-8/fr|UTF-8]] - Description des chaines UTF-8
534536- [[FPC Unicode support/fr|Support d'Unicode par FPC]]
535537- [[for-in loop/fr|for-in_loop/fr#Traversée_de_chaînes_UTF-8]] - Fragment de code qui montre comment traverser les chaînes UTF8
536538- [[unicode use cases/fr|cas d'utilisation d'Unicode]]
537539
538540---
539541*From the Free Pascal wiki, page "LCL Unicode Support/fr", as it stood on 2020-02-19 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history