Difference between revisions of "LCL Unicode Support/fr"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting; deleted category included in page template)
 
(52 intermediate revisions by 7 users not shown)
Line 3: Line 3:
 
== Introduction ==
 
== Introduction ==
  
Le support avec Lazarus de la norme Unicode nécessite un développement approfondi, principalement en regard de la plate-forme Windows. Voici quelque information de base  pour ceux qui voudraient développer plus loin  le support de la norme Unicode sous Lazarus. Svp corriger, prolonger et mettre à jour cette page .
+
A 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
  
Cela aidera si vous avez déjà entendu parler de la norme Unicode et si vous aviez peut-être une certaine expérience avec les WideStrings sous Delphi. L'utilisation précédente de scripts latins non-(occidentaux)  et leurs jeux de caractères variés aidera aussi.
+
Ayant 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]]
  
Note: On discute toujours des détails d'implémentation, et le contenu de ce document peut changer.
+
== Instructions pour les utilisateurs ==
  
== Directives d'implémentation  ==
+
Mê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.
  
=== Nécessités ===
+
Habituellement, 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.
  
L'esprit de Lazarus est : "Écrire une fois, compiler partout ."
+
Vous pouvez convertir entre Unicode et ANSI en utilisant
Ceci signifie que , idéalement , une application avec la norme unicode autorisée
+
* les fonctions '''UTF8ToAnsi''' et '''AnsiToUTF8''' de l'unité System (de FPC),  
should have only one Unicode supporting source code version,
+
* ou les fonctions '''UTF8ToSys''' et '''SysToUTF8''' de l'unité FileUtil (de Lazarus).  
without any conditional defines in respect to various target
 
platforms.
 
  
The "interface" part of the LCL should support Unicode for
+
Les deux dernières sont plus intelligentes (plus rapides) mais engendrent plus de code dans votre programme.
the target platforms which support it themselves, concealing
 
at the same time all peculiarities from the application
 
programmer.
 
  
What concerns Lazarus, the internal string communication at
+
===FPC ne travaille pas en Unicode ===
the boundaries "Application code <--> LCL", as well as "LCL
+
Le 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).<br>
<--> Widgetsets" is based on the classical (byte oriented)
 
strings. Logically, their contents should be encoded according
 
to the UTF-8.
 
  
 +
Il 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]].
  
=== Migration à l'Unicode ===
+
===Convertir entre ANSI et Unicode===
 +
{{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.}}
  
Most existing Lazarus use Ansi encodings, because that´s the default for Gtk1 and win32 interfaces today. This will change in the future and all widgetsets will support UTF-8, so all applications that pass strings directly to the interface (be written on code or on the object inspector) will need to be converted to utf-8.
+
Exemples:
  
When people develop software for non-fully working widgetsets like Gtk 2, Qt, WinCE (and futurely Win32U), they use the IDE compiled for more stable widgetsets, like Gtk and win32. To avoid inconsistencies (like passing iso characters for a utf-8 widgetset), it´s necessary to use an IDE working on the same encoding as the target widgetset. This means that we will need stable UTF-8 IDE before completing the migration to Unicode.
+
Disons que vous récupérez une chaine d'un TEdit et que vous voulez la passer à une fonction de fichier de la RTL :
  
 +
<syntaxhighlight lang=pascal>var
 +
  MyString: string; // Encodé en utf-8
 +
begin
 +
  MyString := MyTEdit.Text;
 +
  SomeRTLRoutine(UTF8ToAnsi(MyString));
 +
end;</syntaxhighlight>
  
Currently we have various groups of widgetsets, according to the encoding:
+
Et pour le sens inverse :
  
*Interfaces that use ANSI encoding: win32 and gtk (1) interfaces.
+
<syntaxhighlight lang=pascal>var
 +
  MyString: string; // Encodé en ANSI
 +
begin
 +
  MyString := SomeRTLRoutine;
 +
  MyTEdit.Text := AnsiToUTF8(MyString);
 +
end;</syntaxhighlight>
  
*Interfaces that use UTF-8 encoding: gtk (1), gtk2, qt, fpGUI
+
'''Important''': UTF8ToAnsi retournera une chaine vide si la chaine UTF8 contient des caractères invalides.
  
*Interfaces that currently use ANSI encoding, but need migration to UTF-8: win32, wince
+
===Widestrings et Ansistrings===
 +
Une 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]]
  
 +
Notez 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.
  
Notice that gtk 1 is on both ANSI and UTF-8 groups. That´s becouse the encoding is controled by an enviroment variable on Gtk 1.
+
Quand vous passez de Ansistrings à Widestrings, vous devez convertir l'encodage.
  
As Lazarus is today, existing software will work, if recompiled for win32, wince or gtk interfaces, but will face encoding issues compiling for other widgetset. And new software, using UTF-8 will work when recompiled for any of the widgetsets on the Unicode group.
+
<syntaxhighlight lang=pascal>var
 +
  w: widestring;
 +
begin
 +
  w:='Über'; // Faux, car FPC convertira la page de code système vers UTF16
 +
  w:=UTF8ToUTF16('Über'); // correct
 +
  Button1.Caption:=UTF16ToUTF8(w);
 +
end;</syntaxhighlight>
  
One very important note is that you must use the IDE compiled for the same group you are targeting. This is because the IDE uses the encoding of the widgetset it was compiled to, and not the one of the target widgetset to write LFM and LRS files.
+
===Caractères et chaînes UTF8 ===
  
== Roadmap ==
+
(NDT : une fois la page UnfUTF8_strings_and_characters traduite, il faut aligner ce paragraphe et ses s/paragraphes sur la version anglaise)
  
Now that we have guidelines, it´s time to create a roadmap and put it into practice. For this, the following realistic plan was created. Our plan splits tasks in 2 groups. One for Primary tasks and another for Secondary tasks.
+
Jusqu'à 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.
  
All primary tasks must be fully implemented before we can say Lazarus is Unicode enabled, and as such will be the main attention of our effort.
+
Pour 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é.
  
Secondary tasks are desirable, but won´t be implemented unless someone volunteers for them, or posts a bounty for them.
+
Notez é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 :
  
 +
*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.
 +
*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.
  
=== Primary Tasks ===
+
====Rechercher une sous chaine ====
  
 +
Du 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 :
  
'''Make Win32 Widgetset support UTF-8'''
+
<syntaxhighlight lang=pascal>uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
 +
...
 +
procedure Where(SearchFor, aText: string);
 +
var
 +
  BytePos: LongInt;
 +
  CharacterPos: LongInt;
 +
begin
 +
  BytePos:=Pos(SearchFor,aText);
 +
  CharacterPos:=UTF8Length(PChar(aText),BytePos-1);
 +
  writeln('The substring "',SearchFor,'" is in the text "',aText,'"',
 +
    ' at byte position ',BytePos,' and at character position ',CharacterPos);
 +
end;</syntaxhighlight>
  
Notes: On this step we will target all 32-bits Windows versions at the same time. All code produced on this effort will be isolated from the current win32 interface by IFDEFs, to avoid introducing bugs on this primary interface. After the transition time, the IFDEFs will be removed and only the Unicode code will remain.
+
Du 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.
  
Details about how to support unicode on Win9x are being debated.
+
====Accéder aux caractères UTF8====
  
Status: Not implemented
+
Les 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 :
  
 +
<syntaxhighlight lang=pascal>uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
 +
...
 +
procedure DoSomethingWithString(AnUTF8String: string);
 +
var
 +
  p: PChar;
 +
  CharLen: integer;
 +
  FirstByte, SecondByte, ThirdByte: Char;
 +
begin
 +
  p:=PChar(AnUTF8String);
 +
  repeat
 +
    CharLen := UTF8CharacterLength(p);
  
'''Update Gtk 2 keyboard functions so they work with UTF-8'''
+
    // here you have a pointer to the char and it's length
 +
    // You can access the bytes of the UTF-8 Char like this:
 +
    if CharLen >= 1 then FirstByte := P[0];
 +
    if CharLen >= 2 then SecondByte := P[1];
 +
    if CharLen >= 3 then ThirdByte := P[2];
  
Notes:
+
    inc(p,CharLen);
 +
  until (CharLen=0) or (p^ = #0);
 +
end;</syntaxhighlight>
  
Status: Not implemented
+
====Accéder au Nième caractère UTF8====
  
 +
Vous pouvez également vouloir effectuer un accès direct aux caractères UTF-8.
  
'''Make sure the Lazarus IDE runs correctly with Win32 Unicode widgetset and supports UTF-8'''
+
<syntaxhighlight lang=pascal>uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
 +
...
 +
var
 +
  AnUTF8String, NthChar: string;
 +
begin
 +
  NthChar := UTF8Copy(AnUTF8String, N, 1);
 +
</syntaxhighlight>
  
Notes:
+
====Itérer les points de code en utilisant UTF8CharacterToUnicode====
  
Status: Not implemented
+
Ce qui suit montre comment itérer les valeurs des points de code 32bits de chacun des caractères dans une chaine UTF-8:
  
 +
<syntaxhighlight lang=pascal>uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
 +
...
 +
procedure IterateUTF8Characters(const AnUTF8String: string);
 +
var
 +
  p: PChar;
 +
  unicode: Cardinal;
 +
  CharLen: integer;
 +
begin
 +
  p:=PChar(AnUTF8String);
 +
  repeat
 +
    unicode:=UTF8CharacterToUnicode(p,CharLen);
 +
    writeln('Unicode=',unicode);
 +
    inc(p,CharLen);
 +
  until (CharLen=0) or (unicode=0);
 +
end;</syntaxhighlight>
  
'''Make sure the Lazarus IDE runs correctly with Gtk 2 widgetset and supports UTF-8'''
+
====Copie, longueur, mise en minuscule, etc des chaines UTF-8====
  
Notes:  
+
Quasiment 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:
  
Status: Not implemented
+
<syntaxhighlight lang=pascal>
 +
function UTF8CharacterLength(p: PChar): integer;
 +
function UTF8Length(const s: string): PtrInt;
 +
function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
 +
function UTF8CharacterToUnicode(p: PChar; out CharLen: integer): Cardinal;
 +
function UnicodeToUTF8(u: cardinal; Buf: PChar): integer; inline;
 +
function UnicodeToUTF8SkipErrors(u: cardinal; Buf: PChar): integer;
 +
function UnicodeToUTF8(u: cardinal): shortstring; inline;
 +
function UTF8ToDoubleByteString(const s: string): string;
 +
function UTF8ToDoubleByte(UTF8Str: PChar; Len: PtrInt; DBStr: PByte): PtrInt;
 +
function UTF8FindNearestCharStart(UTF8Str: PChar; Len: integer;
 +
                                  BytePos: integer): integer;
 +
// find the n-th UTF8 character, ignoring BIDI
 +
function UTF8CharStart(UTF8Str: PChar; Len, CharIndex: PtrInt): PChar;
 +
// find the byte index of the n-th UTF8 character, ignoring BIDI (byte len of substr)
 +
function UTF8CharToByteIndex(UTF8Str: PChar; Len, CharIndex: PtrInt): PtrInt;
 +
procedure UTF8FixBroken(P: PChar);
 +
function UTF8CharacterStrictLength(P: PChar): integer;
 +
function UTF8CStringToUTF8String(SourceStart: PChar; SourceLen: PtrInt) : string;
 +
function UTF8Pos(const SearchForText, SearchInText: string): PtrInt;
 +
function UTF8Copy(const s: string; StartCharIndex, CharCount: PtrInt): string;
 +
procedure UTF8Delete(var s: String; StartCharIndex, CharCount: PtrInt);
 +
procedure UTF8Insert(const source: String; var s: string; StartCharIndex: PtrInt);
  
=== Secondary Tasks ===
+
function UTF8LowerCase(const AInStr: string; ALanguage: string=''): string;
 +
function UTF8UpperCase(const AInStr: string; ALanguage: string=''): string;
 +
function FindInvalidUTF8Character(p: PChar; Count: PtrInt;
 +
                                  StopOnNonASCII: Boolean = false): PtrInt;
 +
function ValidUTF8String(const s: String): String;
  
 +
procedure AssignUTF8ListToAnsi(UTF8List, AnsiList: TStrings);
  
'''Update Windows CE widgetset so it uses UTF-8'''
+
//compare functions
  
Notes: String conversion routines are concentrated on the winceproc.pp file. Many tests are needed.
+
function UTF8CompareStr(const S1, S2: string): Integer;
 +
function UTF8CompareText(const S1, S2: string): Integer;
 +
</syntaxhighlight>
  
Status: Not implemented
+
===Travailler avec les répertoires et les noms de fichier===
  
 +
Les 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.
  
'''Update Gtk 1 keyboard functions so they work with UTF-8'''
+
Par 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:
  
Notes:
+
<syntaxhighlight lang=pascal>procedure TForm1.Button1Click(Sender: TObject);
 +
begin
 +
  FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
 +
  // or use the functions from the FileUtil unit
 +
  FileListBox1.Directory:=GetCurrentDirUTF8;
 +
end;</syntaxhighlight>
 +
 
 +
L'unité FileUtil définit les fonctions classiques de fichiers travaillant avec des chaines UTF-8:
 +
 
 +
<syntaxhighlight lang=pascal>// basic functions similar to the RTL but working with UTF-8 instead of the
 +
// system encoding
 +
 
 +
// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, Mac OS X
 +
// but normally these OS use UTF-8 as system encoding so the widestringmanager
 +
// is not needed.
 +
function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8
 +
procedure SetNeedRTLAnsi(NewValue: boolean);
 +
function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager
 +
function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager
 +
 
 +
// file operations
 +
function FileExistsUTF8(const Filename: string): boolean;
 +
function FileAgeUTF8(const FileName: string): Longint;
 +
function DirectoryExistsUTF8(const Directory: string): Boolean;
 +
function ExpandFileNameUTF8(const FileName: string): string;
 +
function ExpandUNCFileNameUTF8(const FileName: string): string;
 +
{$IFNDEF VER2_2_0}
 +
function ExtractShortPathNameUTF8(Const FileName : String) : String;
 +
{$ENDIF}
 +
function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
 +
function FindNextUTF8(var Rslt: TSearchRec): Longint;
 +
procedure FindCloseUTF8(var F: TSearchrec);
 +
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
 +
function FileGetAttrUTF8(const FileName: String): Longint;
 +
function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
 +
function DeleteFileUTF8(const FileName: String): Boolean;
 +
function RenameFileUTF8(const OldName, NewName: String): Boolean;
 +
function FileSearchUTF8(const Name, DirList : String): String;
 +
function FileIsReadOnlyUTF8(const FileName: String): Boolean;
 +
function GetCurrentDirUTF8: String;
 +
function SetCurrentDirUTF8(const NewDir: String): Boolean;
 +
function CreateDirUTF8(const NewDir: String): Boolean;
 +
function RemoveDirUTF8(const Dir: String): Boolean;
 +
function ForceDirectoriesUTF8(const Dir: string): Boolean;
 +
 
 +
// environment
 +
function ParamStrUTF8(Param: Integer): string;
 +
function GetEnvironmentStringUTF8(Index : Integer): String;
 +
function GetEnvironmentVariableUTF8(const EnvVar: String): String;
 +
function GetAppConfigDirUTF8(Global: Boolean): string;</syntaxhighlight>
  
Status: Not implemented
+
====Mac OS X====
  
 +
(NDT : A reporter dans UTF8_strings_and_characters une fois traduite)
  
 +
Les 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:
  
 +
<syntaxhighlight lang=pascal>if Filename1 = Filename2 then ... // n'est pas suffisant sous OS X
 +
if AnsiCompareFileName(Filename1, Filename2) = 0 then ... // insuffisant sous fpc 2.2.2, même avec cwstring
 +
if CompareFilenames(Filename1, Filename2) = 0 then ... // Ceci fonctionne toujours (unité FileUtil ou FileProcs)</syntaxhighlight>
  
== Unicode essentials ==
+
===Langages Est-asiatiques sous Windows===
  
Unicode standard maps integers from 0 to 10FFFF(h) to characters. Each such mapping is called a code point. In other words, Unicode characters are in principle defined for code points from U+000000 to U+10FFFF (0 to 1 114 111).
+
La 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 [http://newton.uor.edu/Departments&Programs/AsianStudiesDept/Language/asianlanguageinstallation_XP.html ici].
  
There are three schemes for representing Unicode code points as unique byte sequences. These schemes are called Unicode transformation formats: UTF-8, UTF-16 and UTF-32. The conversions between all of them are possible. Here are their basic properties:
+
Il est supposé que les versions ultérieures de Windows ont un support direct (out-of-the-box) pour ces langages.
  
                            UTF-8 UTF-16 UTF-32
+
=== Usage of UTF-8 ===
Smallest code point [hex] 000000 000000 000000
 
Largest code point  [hex] 10FFFF 10FFFF 10FFFF
 
Code unit size [bits]          8    16    32
 
Minimal bytes/character        1      2      4
 
Maximal bytes/character        4      4      4
 
  
'''UTF-8''' has several important and useful properties: It is
+
Voir les détails dans [[UTF8_strings_and_characters]]
interpreted as a sequence of bytes, so that the concept of
 
lo- and hi-order byte does not exist. Unicode
 
characters U+0000 to U+007F (ASCII) are encoded simply as
 
bytes 00h to 7Fh (ASCII compatibility). This means that
 
files and strings which contain only 7-bit ASCII characters
 
have the same encoding under both ASCII and UTF-8. All
 
characters >U+007F are encoded as a sequence of several
 
bytes, each of which has the two most significant bits set. No
 
byte sequence of one character is contained within a longer
 
byte sequence of another character. This allows easy search for substrings. The first byte of a
 
multibyte sequence that represents a non-ASCII character is
 
always in the range C0h to FDh and it indicates how many
 
bytes follow for this character. All further bytes in a
 
multibyte sequence are in the range 80h to BFh. This allows
 
easy resynchronization and robustness.
 
  
'''UTF-16''' has the following most important properties: It uses a
+
== Particularités de Free Pascal ==
single 16-bit word to encode characters from U+0000
 
to U+d7ff, and a pair of 16-bit words to encode any of the
 
remaining Unicode characters.
 
  
Finally, any Unicode character can be represented as a
+
===UTF8 et les fichiers sources - le BOM manquant===
single 32-bit unit in '''UTF-32'''.
 
  
For more, see:
+
Quand 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.
[http://www.unicode.org/faq/basic_q.html Unicode FAQ - Basic questions],
+
 
[http://www.unicode.org/faq/utf_bom.html Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM],
+
Note: 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.
[http://en.wikipedia.org/wiki/UTF-8 Wikipedia: UTF-8]
+
Par exemple:
[http://en.wikipedia.org/wiki/ISO-8859]
+
 
 +
<syntaxhighlight lang=pascal>Button1.Caption := 'Über';</syntaxhighlight>
 +
 
 +
Quand 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.  
 +
 
 +
<syntaxhighlight lang=pascal>// source file saved as UTF without BOM
 +
if FileExists('Über.txt') then ; // wrong, because FileExists expects system encoding
 +
if FileExistsUTF8('Über.txt') then ; // correct</syntaxhighlight>
 +
 
 +
== L'essentiel d'Unicode==
 +
 
 +
Le 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).
  
== Lazarus component library architecture essentials ==
+
Il 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:
  
The LCL consists of two parts:
+
                                UTF-8 UTF-16 UTF-32
# A target platform independent part, which implements a class hierarchy analogous to Delphi VCL;
+
Plus petit point de code [hex]  000000 000000 000000
# "Interfaces" - a part that implements the interface to APIs of each target platform.
+
Plus grand point de code [hex]  10FFFF 10FFFF 10FFFF
 +
Taille unitaire de code [bits]  8    16    32
 +
Octets minimum/caractère        1      2      4
 +
Octets maximum/caractère        4      4      4
  
The communication between the two parts is done by an abstract class TWidgetset. Each widgetset is implemented by its own class derived from TWidgetset.
+
'''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.
  
The GTK 1 widgetset is the oldest. In this widgetset the string encoding is determined by the LANG environment variable, which is usually a iso The ISO-8859-n group of single byte encodings. Recently (as of Mandriva 2007, for example), many distributions have being shipping Gtk 1 configured for UTF-8. Our Gtk 1 interface lacks proper support for UTF-8 on the keyboard handling routines, so this is a big problem, that increases the need for Lazarus to implement cross-platform Unicode support.
+
'''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.
  
Gtk2 widgetset only works with UTF-8 encoding, but the keyboard code of the interface is still based on the old gtk 1 code, so it does not support UTF-8 completely.
+
Pour finir, chaque caractère Unicode peut être représenté comme un simple mot de 32 bits en '''UTF-32'''.
  
The win32 interface is setup with ansi widgets, so it is currently not possible to use Unicode with win32.
+
Pour plus d'informations, voir:
 +
[http://www.unicode.org/faq/basic_q.html Unicode FAQ - Questions basiques],
 +
[http://www.unicode.org/faq/utf_bom.html Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM],
 +
[http://en.wikipedia.org/wiki/UTF-8 Wikipedia: UTF-8]
 +
[http://en.wikipedia.org/wiki/ISO-8859]
  
Qt interface is prepared for UTF-8. Qt itself uses UTF-16 as native encoding, but the lazarus interface for Qt converts from UTF-8 to UTF-16.
+
= Détails d'implementation =
  
Windows CE only support UTF-16 as character encoding, but our interface for it currently converts strings from ISO to UTF-16 before calling the Windows API. This is very easy to fix, as all conversion code is concentrated on a few routines on the winceproc.pp file.
+
== Lazarus/LCL utilisent généralement uniquement UTF-8 ==
  
For more, see: [[LCL Internals#Internals of the LCL|Internals of the LCL]]
+
Depuis 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.
  
== Unicode-enabling the win32 interface ==
+
== Activer Unicode pour l'interface win32  ==
  
=== Compiling LCL-Win32 with Unicode ===
+
==== Vue d'ensemble ====
  
To enable unicode on LCL for Windows go to the menu "Tools" --> "Configure Build Lazarus"
+
Premiè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.
  
Put -dWindowsUnicodeSupport on the "Options" field. Select all targets to NONE, and only LCL to Clean+Build. Select win32 as target widgetset. Click on "Build".
+
==== Pas de support d'Unicode sur Win9x ====
  
Now you can recompile your existing applications and they will have Unicode mode enabled. Note that at the moment only a few parts of the software will be really unicode enabled and you may find bugs on those parts.
+
Les 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.  
  
=== Guidelines ===
+
Win 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.
  
First, and most importantly, all Unicode patches for the Win32 interface must be enclosed by IFDEF WindowsUnicodeSupport, to avoid breaking the existing ANSI interface. After this stabilizes, all ifdefs will be removed and only the Unicode part will remain. At this moment all existing programs that use ANSI characters will need migration to Unicode.
+
Les 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.
  
Windows platforms <=Win9x are based on ISO code page
+
Windows CE utilise uniquement les API Wide.
standards and only partially support Unicode. Windows platforms
 
starting with WinNT and Windows CE fully support Unicode. Win 9x and NT offer two parallel sets of API functions: the old ANSI enabled *A and the new, Unicode enabled *W. *W
 
functions accept wide strings, i.e. UTF-16 encoded strings, as parameters. Windows CE only uses Wide API functions.
 
  
====Wide functions present on Windows 9x====
+
====Fonctions Wide présentes sur Windows 9x====
  
Some Wide API functions are present on Windows 9x. Here is a list of such functions: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp
+
Certaines 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
  
Conversion example:
+
Exemple de conversion:
  
<pre>
+
<syntaxhighlight lang=pascal>GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
  GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
+
Length(ButtonCaption), TextSize);</syntaxhighlight>
Length(ButtonCaption), TextSize);
 
</pre>
 
  
Becomes:
+
Devient:
  
<pre>
+
<syntaxhighlight lang=pascal>{$ifdef WindowsUnicodeSupport}
  {$ifdef WindowsUnicodeSupport}
+
  GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
    GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
+
{$else}
  {$else}
+
  GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
    GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
+
{$endif}</syntaxhighlight>
  {$endif}
 
</pre>
 
  
====Functions that need Ansi and Wide versions====
+
====Fonctions qui nécessitent des versions Ansi et Wide ====
  
First Conversion example:
+
Premier exemple de Conversion:
  
<pre>
+
<syntaxhighlight lang=pascal>function TGDIWindow.GetTitle: String;
function TGDIWindow.GetTitle: String;
 
 
var
 
var
 
  l: Integer;
 
  l: Integer;
Line 229: Line 367:
 
   SetLength(Result, l);
 
   SetLength(Result, l);
 
   Windows.GetWindowText(Handle, @Result[1], l);
 
   Windows.GetWindowText(Handle, @Result[1], l);
end;
+
end;</syntaxhighlight>
</pre>
 
  
Becomes:
+
Devient:
  
<pre>
+
<syntaxhighlight lang=pascal>function TGDIWindow.GetTitle: String;
function TGDIWindow.GetTitle: String;
 
 
var
 
var
l: Integer;
+
  l: Integer;
AnsiBuffer: string;
+
  AnsiBuffer: string;
WideBuffer: WideString;
+
  WideBuffer: WideString;
 
begin
 
begin
  
 
{$ifdef WindowsUnicodeSupport}
 
{$ifdef WindowsUnicodeSupport}
  
if UnicodeEnabledOS then
+
if UnicodeEnabledOS then
begin
+
begin
  l := Windows.GetWindowTextLengthW(Handle);
+
  l := Windows.GetWindowTextLengthW(Handle);
  SetLength(WideBuffer, l);
+
  SetLength(WideBuffer, l);
  l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
+
  l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
  SetLength(WideBuffer, l);
+
  SetLength(WideBuffer, l);
  Result := Utf8Encode(WideBuffer);
+
  Result := Utf8Encode(WideBuffer);
end
+
end
else
+
else
begin
+
begin
  l := Windows.GetWindowTextLength(Handle);
+
  l := Windows.GetWindowTextLength(Handle);
  SetLength(AnsiBuffer, l);
+
  SetLength(AnsiBuffer, l);
  l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
+
  l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
  SetLength(AnsiBuffer, l);
+
  SetLength(AnsiBuffer, l);
  Result := AnsiToUtf8(AnsiBuffer);
+
  Result := AnsiToUtf8(AnsiBuffer);
end;
+
end;
  
 
{$else}
 
{$else}
Line 269: Line 405:
 
{$endif}
 
{$endif}
  
end;
+
end;</syntaxhighlight>
 +
 
 +
 
 +
 
 +
=== Captures d'écran ===
 +
 
 +
[[Image:Lazarus Unicode Test.png]]
 +
 
 +
= Pages de code FPC =
 +
 
 +
Pourquoi la LCL n'utilise pas les pages de code (codepage) pour les sources ?
 +
 
 +
Les -Fcutf8 et {$codepage utf8} existent depuis longtemps.
 +
 
 +
Le programme suivant nécessite FPC 2.7.1. Le problème suivant existe aussi sur les plus vieux compilateurs.
 +
 
 +
Il 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.
 +
 
 +
Par 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 :
 +
 
 +
Compilez ceci avec -Fcutf8 et lancez-le sur un linux avec LANG paramétré à utf-8:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
program project1;
 +
{$mode objfpc}{$H+}
 +
begin
 +
  writeln(DefaultSystemCodePage,' ',CP_UTF8);
 +
  writeln('ä');
 +
end.
 +
</syntaxhighlight>
 +
 
 +
Résultat :
 +
<pre>
 +
0 65001
 +
ä
 +
</pre>
 +
 
 +
La 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 :
 +
 
 +
<syntaxhighlight lang=pascal>
 +
program project1;
 +
{$mode objfpc}{$H+}
 +
uses cwstring;
 +
begin
 +
  writeln(DefaultSystemCodePage,' ',CP_UTF8);
 +
  writeln('ä');
 +
end.
 +
</syntaxhighlight>
 +
 
 +
Résultat :
 +
<pre>
 +
65001 65001
 +
ä
 +
</pre>
 +
 
 +
Ce qui précède est toutefois une simplification, un mensonge pour enfants.
 +
Prenons le programme suivant :
 +
 
 +
<syntaxhighlight lang=pascal>
 +
program project1;
 +
{$mode objfpc}{$H+}
 +
uses cwstring;
 +
var
 +
  a,b,c: string;
 +
begin
 +
  writeln(DefaultSystemCodePage,' ',CP_UTF8);
 +
  a:='ä'; b:='='#$C3#$A4; // #$C3#$A4 is UTF-8 for ä
 +
  c:=      'ä='#$C3#$A4;
 +
  writeln(a,b); // writes ä=ä
 +
  writeln(c);  // writes ä=ä
 +
end.
 +
</syntaxhighlight>
 +
 
 +
<pre>
 +
65001 65001
 +
ä=ä
 +
ä=ä
 +
</pre>
 +
 
 +
Vous 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.
 +
 
 +
Alors qu'est-ce qui cloche ?
 +
 
 +
Le compilateur traite toute constante chaine non ASCII (ici : le "ä") comme des widestring (UCS2, pas UTF-16).
 +
 
 +
Vous ne pouvez pas tromper le compilateur avec 'ä='+#$C3#$A4. Vous devez définir deux constantes chaînes différentes.
 +
 
 +
Utiliser n'importe quel caractère hors de la plage UCS-2 résulte en un
 +
<pre>
 +
Fatal: illegal character "'�'" ($F0)
 +
</pre>
 +
 
 +
Vous 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.
  
 +
Maintenant, compilez le même programme sans -Fcutf8:
 +
<pre>
 +
65001 65001
 +
ä=ä
 +
ä=ä
 
</pre>
 
</pre>
  
=== Roadmap ===
+
Ouah, 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.
  
What should already be working with Unicode:
+
C'est une des raisons pour lesquelles les applications LCL n'utilisent pas les drapeaux de page de code (codepage flags).
  
* TForm.Caption
+
msegui a implémenté un écosystème de widestrings, qui marche mieux avec un code de page.
* TButton.Caption
 
* TLabel.Caption
 
  
List of units to be checked:
+
= Voir aussi =
  
*"win32callback.inc"
+
* [[Character and string types/fr|Types caractère et chaîne]]
*"win32def.pp"
+
* [[UTF-8/fr|UTF-8]] - Description des chaines UTF-8
*"win32int.pp"
+
* [[FPC Unicode support/fr|Support d'Unicode par FPC]]
*"win32lclintf.inc"
+
* [[for-in_loop/fr#Traversée_de_chaînes_UTF-8]] - Fragment de code qui montre comment traverser les chaînes UTF8
*"win32lclintfh.inc"
+
* [[unicode use cases/fr|cas d'utilisation d'Unicode]]
*"win32listsl.inc"
 
*"win32listslh.inc"
 
*"win32memostrings.inc"
 
*"win32object.inc"
 
*"win32proc.pp"
 
*"win32winapi.inc"
 
*"win32winapih.inc"
 
*"win32wsactnlist.pp"
 
*"win32wsarrow.pp"
 
*"win32wsbuttons.pp"
 
*"win32wscalendar.pp"
 
*"win32wschecklst.pp"
 
*"win32wsclistbox.pp"
 
*"win32wscomctrls.pp"
 
*"win32wscontrols.pp"
 
*"win32wscustomlistview.inc"
 
*"win32wsdbctrls.pp"
 
*"win32wsdbgrids.pp"
 
*"win32wsdialogs.pp"
 
*"win32wsdirsel.pp"
 
*"win32wseditbtn.pp"
 
*"win32wsextctrls.pp"
 
*"win32wsextdlgs.pp"
 
*"win32wsfilectrl.pp"
 
*"win32wsforms.pp"
 
*"win32wsgrids.pp"
 
*"win32wsimglist.pp"
 
*"win32wsmaskedit.pp"
 
*<s>"win32wsmenus.pp"</s> - Felipe
 
*<s>"win32wspairsplitter.pp"</s> - Felipe
 
*<s>"win32wsspin.pp"</s> - Felipe
 
*<s>"win32wsstdctrls.pp"</s> - Felipe
 
*<s>"win32wstoolwin.pp"</s> - Felipe
 
*<s>"winext.pas"</s> - Felipe
 

Latest revision as of 01:20, 19 February 2020

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) 한국어 (ko) русский (ru) 中文(中国大陆)‎ (zh_CN) 中文(台灣)‎ (zh_TW)

Introduction

A 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

Ayant 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

Instructions pour les utilisateurs

Mê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.

Habituellement, 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.

Vous pouvez convertir entre Unicode et ANSI en utilisant

  • les fonctions UTF8ToAnsi et AnsiToUTF8 de l'unité System (de FPC),
  • ou les fonctions UTF8ToSys et SysToUTF8 de l'unité FileUtil (de Lazarus).

Les deux dernières sont plus intelligentes (plus rapides) mais engendrent plus de code dans votre programme.

FPC ne travaille pas en Unicode

Le 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).

Il 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.

Convertir entre ANSI et Unicode

Light bulb  Remarque: 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.

Exemples:

Disons que vous récupérez une chaine d'un TEdit et que vous voulez la passer à une fonction de fichier de la RTL :

var
  MyString: string; // Encodé en utf-8
begin
  MyString := MyTEdit.Text;
  SomeRTLRoutine(UTF8ToAnsi(MyString));
end;

Et pour le sens inverse :

var
  MyString: string; // Encodé en ANSI
begin
  MyString := SomeRTLRoutine;
  MyTEdit.Text := AnsiToUTF8(MyString);
end;

Important: UTF8ToAnsi retournera une chaine vide si la chaine UTF8 contient des caractères invalides.

Widestrings et Ansistrings

Une 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

Notez 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.

Quand vous passez de Ansistrings à Widestrings, vous devez convertir l'encodage.

var 
  w: widestring;
begin
  w:='Über'; // Faux, car FPC convertira la page de code système vers UTF16
  w:=UTF8ToUTF16('Über'); // correct
  Button1.Caption:=UTF16ToUTF8(w);
end;

Caractères et chaînes UTF8

(NDT : une fois la page UnfUTF8_strings_and_characters traduite, il faut aligner ce paragraphe et ses s/paragraphes sur la version anglaise)

Jusqu'à 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.

Pour 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é.

Notez é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 :

  • 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.
  • 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.

Rechercher une sous chaine

Du 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 :

uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
...
procedure Where(SearchFor, aText: string);
var
  BytePos: LongInt;
  CharacterPos: LongInt;
begin
  BytePos:=Pos(SearchFor,aText);
  CharacterPos:=UTF8Length(PChar(aText),BytePos-1);
  writeln('The substring "',SearchFor,'" is in the text "',aText,'"',
    ' at byte position ',BytePos,' and at character position ',CharacterPos);
end;

Du 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.

Accéder aux caractères UTF8

Les 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 :

uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
...
procedure DoSomethingWithString(AnUTF8String: string);
var
  p: PChar;
  CharLen: integer;
  FirstByte, SecondByte, ThirdByte: Char;
begin
  p:=PChar(AnUTF8String);
  repeat
    CharLen := UTF8CharacterLength(p);

    // here you have a pointer to the char and it's length
    // You can access the bytes of the UTF-8 Char like this:
    if CharLen >= 1 then FirstByte := P[0];
    if CharLen >= 2 then SecondByte := P[1];
    if CharLen >= 3 then ThirdByte := P[2];

    inc(p,CharLen);
  until (CharLen=0) or (p^ = #0);
end;

Accéder au Nième caractère UTF8

Vous pouvez également vouloir effectuer un accès direct aux caractères UTF-8.

uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
...
var
  AnUTF8String, NthChar: string;
begin
  NthChar := UTF8Copy(AnUTF8String, N, 1);

Itérer les points de code en utilisant UTF8CharacterToUnicode

Ce qui suit montre comment itérer les valeurs des points de code 32bits de chacun des caractères dans une chaine UTF-8:

uses lazutf8; // LCLProc for Lazarus 0.9.30 or inferior
...
procedure IterateUTF8Characters(const AnUTF8String: string);
var
  p: PChar;
  unicode: Cardinal;
  CharLen: integer;
begin
  p:=PChar(AnUTF8String);
  repeat
    unicode:=UTF8CharacterToUnicode(p,CharLen);
    writeln('Unicode=',unicode);
    inc(p,CharLen);
  until (CharLen=0) or (unicode=0);
end;

Copie, longueur, mise en minuscule, etc des chaines UTF-8

Quasiment 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:

function UTF8CharacterLength(p: PChar): integer;
function UTF8Length(const s: string): PtrInt;
function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
function UTF8CharacterToUnicode(p: PChar; out CharLen: integer): Cardinal;
function UnicodeToUTF8(u: cardinal; Buf: PChar): integer; inline;
function UnicodeToUTF8SkipErrors(u: cardinal; Buf: PChar): integer;
function UnicodeToUTF8(u: cardinal): shortstring; inline;
function UTF8ToDoubleByteString(const s: string): string;
function UTF8ToDoubleByte(UTF8Str: PChar; Len: PtrInt; DBStr: PByte): PtrInt;
function UTF8FindNearestCharStart(UTF8Str: PChar; Len: integer;
                                  BytePos: integer): integer;
// find the n-th UTF8 character, ignoring BIDI
function UTF8CharStart(UTF8Str: PChar; Len, CharIndex: PtrInt): PChar;
// find the byte index of the n-th UTF8 character, ignoring BIDI (byte len of substr)
function UTF8CharToByteIndex(UTF8Str: PChar; Len, CharIndex: PtrInt): PtrInt;
procedure UTF8FixBroken(P: PChar);
function UTF8CharacterStrictLength(P: PChar): integer;
function UTF8CStringToUTF8String(SourceStart: PChar; SourceLen: PtrInt) : string;
function UTF8Pos(const SearchForText, SearchInText: string): PtrInt;
function UTF8Copy(const s: string; StartCharIndex, CharCount: PtrInt): string;
procedure UTF8Delete(var s: String; StartCharIndex, CharCount: PtrInt);
procedure UTF8Insert(const source: String; var s: string; StartCharIndex: PtrInt);

function UTF8LowerCase(const AInStr: string; ALanguage: string=''): string;
function UTF8UpperCase(const AInStr: string; ALanguage: string=''): string;
function FindInvalidUTF8Character(p: PChar; Count: PtrInt;
                                  StopOnNonASCII: Boolean = false): PtrInt;
function ValidUTF8String(const s: String): String;

procedure AssignUTF8ListToAnsi(UTF8List, AnsiList: TStrings);

//compare functions

function UTF8CompareStr(const S1, S2: string): Integer;
function UTF8CompareText(const S1, S2: string): Integer;

Travailler avec les répertoires et les noms de fichier

Les 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.

Par exemple, considérez un bouton, qui positionne la propriété Directory d'une TFileListBox au répertoire courant. La fonction RTL GetCurrentDir travaille en ansi, et non en unicode, donc une conversion est nécessaire:

procedure TForm1.Button1Click(Sender: TObject);
begin
  FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
  // or use the functions from the FileUtil unit
  FileListBox1.Directory:=GetCurrentDirUTF8;
end;

L'unité FileUtil définit les fonctions classiques de fichiers travaillant avec des chaines UTF-8:

// basic functions similar to the RTL but working with UTF-8 instead of the
// system encoding

// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, Mac OS X
// but normally these OS use UTF-8 as system encoding so the widestringmanager
// is not needed.
function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8
procedure SetNeedRTLAnsi(NewValue: boolean);
function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager
function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager

// file operations
function FileExistsUTF8(const Filename: string): boolean;
function FileAgeUTF8(const FileName: string): Longint;
function DirectoryExistsUTF8(const Directory: string): Boolean;
function ExpandFileNameUTF8(const FileName: string): string;
function ExpandUNCFileNameUTF8(const FileName: string): string;
{$IFNDEF VER2_2_0}
function ExtractShortPathNameUTF8(Const FileName : String) : String;
{$ENDIF}
function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
function FindNextUTF8(var Rslt: TSearchRec): Longint;
procedure FindCloseUTF8(var F: TSearchrec);
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
function FileGetAttrUTF8(const FileName: String): Longint;
function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
function DeleteFileUTF8(const FileName: String): Boolean;
function RenameFileUTF8(const OldName, NewName: String): Boolean;
function FileSearchUTF8(const Name, DirList : String): String;
function FileIsReadOnlyUTF8(const FileName: String): Boolean;
function GetCurrentDirUTF8: String;
function SetCurrentDirUTF8(const NewDir: String): Boolean;
function CreateDirUTF8(const NewDir: String): Boolean;
function RemoveDirUTF8(const Dir: String): Boolean;
function ForceDirectoriesUTF8(const Dir: string): Boolean;

// environment
function ParamStrUTF8(Param: Integer): string;
function GetEnvironmentStringUTF8(Index : Integer): String;
function GetEnvironmentVariableUTF8(const EnvVar: String): String;
function GetAppConfigDirUTF8(Global: Boolean): string;

Mac OS X

(NDT : A reporter dans UTF8_strings_and_characters une fois traduite)

Les 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:

if Filename1 = Filename2 then ... // n'est pas suffisant sous OS X
if AnsiCompareFileName(Filename1, Filename2) = 0 then ... // insuffisant sous fpc 2.2.2, même avec cwstring
if CompareFilenames(Filename1, Filename2) = 0 then ... // Ceci fonctionne toujours (unité FileUtil ou FileProcs)

Langages Est-asiatiques sous Windows

La 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.

Il est supposé que les versions ultérieures de Windows ont un support direct (out-of-the-box) pour ces langages.

Usage of UTF-8

Voir les détails dans UTF8_strings_and_characters

Particularités de Free Pascal

UTF8 et les fichiers sources - le BOM manquant

Quand 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.

Note: 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:

Button1.Caption := 'Über';

Quand 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.

// source file saved as UTF without BOM
if FileExists('Über.txt') then ; // wrong, because FileExists expects system encoding
if FileExistsUTF8('Über.txt') then ; // correct

L'essentiel d'Unicode

Le 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).

Il 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:

                                UTF-8 UTF-16 UTF-32
Plus petit point de code [hex]  000000 000000 000000
Plus grand point de code [hex]  10FFFF 10FFFF 10FFFF
Taille unitaire de code [bits]  8     16     32
Octets minimum/caractère        1      2      4
Octets maximum/caractère        4      4      4

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.

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.

Pour finir, chaque caractère Unicode peut être représenté comme un simple mot de 32 bits en UTF-32.

Pour plus d'informations, voir: Unicode FAQ - Questions basiques, Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM, Wikipedia: UTF-8 [1]

Détails d'implementation

Lazarus/LCL utilisent généralement uniquement UTF-8

Depuis 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.

Activer Unicode pour l'interface win32

Vue d'ensemble

Premiè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.

Pas de support d'Unicode sur Win9x

Les 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.

Win 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.

Les 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.

Windows CE utilise uniquement les API Wide.

Fonctions Wide présentes sur Windows 9x

Certaines 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

Exemple de conversion:

GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
Length(ButtonCaption), TextSize);

Devient:

{$ifdef WindowsUnicodeSupport}
  GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
{$else}
  GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
{$endif}

Fonctions qui nécessitent des versions Ansi et Wide

Premier exemple de Conversion:

function TGDIWindow.GetTitle: String;
var
 l: Integer;
begin
   l := Windows.GetWindowTextLength(Handle);
   SetLength(Result, l);
   Windows.GetWindowText(Handle, @Result[1], l);
end;

Devient:

function TGDIWindow.GetTitle: String;
var
  l: Integer;
  AnsiBuffer: string;
  WideBuffer: WideString;
begin

{$ifdef WindowsUnicodeSupport}

if UnicodeEnabledOS then
begin
  l := Windows.GetWindowTextLengthW(Handle);
  SetLength(WideBuffer, l);
  l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
  SetLength(WideBuffer, l);
  Result := Utf8Encode(WideBuffer);
end
else
begin
  l := Windows.GetWindowTextLength(Handle);
  SetLength(AnsiBuffer, l);
  l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
  SetLength(AnsiBuffer, l);
  Result := AnsiToUtf8(AnsiBuffer);
end;

{$else}

   l := Windows.GetWindowTextLength(Handle);
   SetLength(Result, l);
   Windows.GetWindowText(Handle, @Result[1], l);

{$endif}

end;


Captures d'écran

Lazarus Unicode Test.png

Pages de code FPC

Pourquoi la LCL n'utilise pas les pages de code (codepage) pour les sources ?

Les -Fcutf8 et {$codepage utf8} existent depuis longtemps.

Le programme suivant nécessite FPC 2.7.1. Le problème suivant existe aussi sur les plus vieux compilateurs.

Il 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.

Par 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 :

Compilez ceci avec -Fcutf8 et lancez-le sur un linux avec LANG paramétré à utf-8:

program project1;
{$mode objfpc}{$H+}
begin
  writeln(DefaultSystemCodePage,' ',CP_UTF8);
  writeln('ä');
end.

Résultat :

0 65001
ä

La 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 :

program project1;
{$mode objfpc}{$H+}
uses cwstring;
begin
  writeln(DefaultSystemCodePage,' ',CP_UTF8);
  writeln('ä');
end.

Résultat :

65001 65001
ä

Ce qui précède est toutefois une simplification, un mensonge pour enfants. Prenons le programme suivant :

program project1;
{$mode objfpc}{$H+}
uses cwstring;
var
  a,b,c: string;
begin
  writeln(DefaultSystemCodePage,' ',CP_UTF8);
  a:='ä'; b:='='#$C3#$A4; // #$C3#$A4 is UTF-8 for ä
  c:=       'ä='#$C3#$A4;
  writeln(a,b); // writes ä=ä
  writeln(c);   // writes ä=ä
end.
65001 65001
ä=ä
ä=ä

Vous 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.

Alors qu'est-ce qui cloche ?

Le compilateur traite toute constante chaine non ASCII (ici : le "ä") comme des widestring (UCS2, pas UTF-16).

Vous ne pouvez pas tromper le compilateur avec 'ä='+#$C3#$A4. Vous devez définir deux constantes chaînes différentes.

Utiliser n'importe quel caractère hors de la plage UCS-2 résulte en un

Fatal: illegal character "'�'" ($F0)

Vous 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.

Maintenant, compilez le même programme sans -Fcutf8:

65001 65001
ä=ä
ä=ä

Ouah, 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.

C'est une des raisons pour lesquelles les applications LCL n'utilisent pas les drapeaux de page de code (codepage flags).

msegui a implémenté un écosystème de widestrings, qui marche mieux avec un code de page.

Voir aussi