Difference between revisions of "LCL Unicode Support/de"

From Free Pascal wiki
Jump to navigationJump to search
m (Fixed syntax highlighting; deleted category included in page template)
 
(27 intermediate revisions by 9 users not shown)
Line 3: Line 3:
 
== Einleitung ==
 
== Einleitung ==
  
Der Lazarus Support für den Unicode-Standard ist noch nicht vollständig, vor allem, was die Windows-Plattform angeht. Auf dieser Seite finden Sie einige grundsätzliche Informationen , auch wenn Sie den Lazarus Support für Unicode weiter entwickeln wollen. Bitte korrigieren Sie gegebenenfalls diese Seite, wenn Ihnen etwas auffällt (Auch Erweiterungen sind willkommen!)
+
Mit der Version 0.9.25 unterstützt Lazarus Unicode auf allen Plattformen außer auf Gtk 1. Diese Seite liefert Informationen für Anwender, die Strategie der Entwickler und eine Beschreibung der Konzepte und deren Anwendungsdetails.
  
Für eine Mitarbeit ist es sicher hilfreich, wenn Sie schon etwas über den Unicode-Standard wissen oder sogar schon einige Erfahrungen mit Unicode in Delphi gemacht haben. Hilfreich ist auch, wenn Sie bereits Scripts verwendet haben, die die verschiedenen Character Sets, z. B. Non-Western/Latin Character Sets bearbeitet haben.
+
== Anweisungen für Benutzer ==
  
Anmerkung: Die Details der Implementierung sind noch im Fluss und werden auch noch diskutiert. So kann sich auch der Inhalt dieser Seite noch ändern.
+
In Unicode-Interfaces - das anzumerken ist wichtig - ist nicht alles in Unicode. Die Free Pascal Laufzeitbibliothek (RTL) und die Free Pascal FCL-Bibliothek sind ANSI. Es liegt in der Verantwortung des Entwicklers zu wissen, welche Kodierung die Zeichenketten verwenden, und eine saubere Umwandlung durchzuführen zwischen Bibliotheken mit unterschiedlichen Kodierungen.
  
== Implementierungsrichtlinien ==
+
Normalerweise erfolgt eine Kodierung pro Bibliothek (d. h. eine dynamische Bibliothek 'dll' oder ein Lazarus-Package). Jede Bibliothek erwartet 1 einheitliche Art von Kodierung, also üblicherweise entweder Unicode (UTF-8 für Lazarus) oder ANSI (das bedeutet die Systemkodierung, und kann utf-8 sein oder nicht). Die RTL und die FCL von FPC 2.4 erwarten ANSI-Zeichenketten. FPC 2.5.x derzeit auch.
 +
 
 +
Sie können zwischen Unicode und ANSI konvertieren mittels der Funktionen '''UTF8ToAnsi''' und '''AnsiToUTF8''' aus der Unit 'System'  oder mittels '''UTF8ToSys''' und '''SysToUTF8''' aus der Unit 'FileUtil'. Die beiden letzteren sind eleganter, stellen aber mehr Code in Ihr Programm.
 +
 
 +
Beispiele:
 +
 
 +
Angenommen Sie haben eine Zeichenkette aus einem TEdit und wollen diese an eine Routine aus der RTL übergeben:
 +
 
 +
<syntaxhighlight lang=pascal>var
 +
  MyString: string; // utf-8 encoded
 +
begin
 +
  MyString := MyTEdit.Text;
 +
  SomeRTLRoutine(UTF8ToAnsi(MyString));
 +
end;</syntaxhighlight>
 +
 
 +
Und in der umgekehrten Richtung:
 +
 
 +
<syntaxhighlight lang=pascal>var
 +
  MyString: string; // ansi encoded
 +
begin
 +
  MyString := SomeRTLRoutine;
 +
  MyTEdit.Text := AnsiToUTF8(MyString);
 +
end;</syntaxhighlight>
 +
 
 +
'''Wichtig''': UTF8ToAnsi gibt eine leere Zeichenkette zurück, wenn der UTF8-String ungültige Zeichen enthält.
 +
 
 +
'''Wichtig''': AnsiToUTF8 und UTF8ToAnsi brauchen einen Widestring-Manager unter Linux, BSD und Mac OS X. Sie können die Funktionen SysToUTF8 und UTF8ToSys (aus der Unit FileUtil) einsetzen oder einen Widestring-Manager, indem Sie 'cwstring' als eine der ersten Units zum uses-Abschnitt in Ihrem Programm hinzufügen.
 +
 
 +
 
 +
===Der Umgang mit UTF8 Zeichenketten und Zeichen===
 +
 
 +
Wenn Sie die Zeichen einer UTF8-Zeichenkette durchlaufen wollen, gibt es dafür zwei unterschiedliche Arten:
 +
 
 +
*durchlaufen Sie die Bytes - ist nützlich zum Suchen eines Teilstrings oder wenn Sie nur die ASCII-Zeichen eines UTF8-Strings betrachten wollen. Zum Beispiel beim Parsen einer xml-Datei.
 +
*durchlaufen Sie die Zeichen - ist nützlich für graphische Komponenten wie Synedit. Zum Beispiel wenn Sie wissen wollen, welches das dritte am Bildschirm dargestellte Zeichen ist.
 +
 
 +
====Suchen eines Teilstrings====
 +
 
 +
Wegen der besonderen Natur von UTF8 können Sie einfach die normalen Stringfunktionen verwenden:
 +
 
 +
<syntaxhighlight lang=pascal>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>
 +
 
 +
 
 +
====Zugriff auf einen UTF8-String als Array====
 +
 
 +
Ein Array besteht aus aus Elementen gleicher Größe. Ein UTF8-Zeichen kann aber 1 bis 4 Bytes haben. Deshalb müssen Sie den UTF8-String konvertieren, entweder in ein Array aus Unicode-Werten (longwords) oder in ein Array aus PChar mit einem Zeiger auf jedes Zeichen.
 +
 
 +
====Durchlaufen von Zeichen mittels UTF8CharacterToUnicode====
 +
 
 +
Das Folgende demonstriert wie Sie die 32bit Unicode-Werte von Zeichen in einem UTF8-String durchlaufen:
 +
 
 +
<syntaxhighlight lang=pascal>uses LCLProc;
 +
...
 +
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>
 +
 
 +
===Der Umgang mit Verzeichnis- und Dateinamen===
 +
 
 +
Die Steuerelemente und Funktionen in Lazarus erwarten Dateinamen und Verzeichnisnamen in UTF-8 Kodierung, aber die RTL benutzt ANSI-Strings für Verzeichnisse und Dateinamen.
 +
 
 +
Betrachten Sie beispielsweise eine Schaltfläche, die die Eigenschaft 'Directory' einer TFileListBox auf das aktuelle Verzeichnis setzt. Die RTL-Funktion [[doc:rtl/sysutils/getcurrentdir.html|GetCurrentDir]] ist ANSI, und nicht Unicode, deswegen muss man konvertieren:
 +
 
 +
<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>
 +
 
 +
Die Unit 'FileUtil' definiert allgemeine Dateifunktionen mit UTF-8 Strings:
 +
 
 +
<syntaxhighlight lang=pascal>// Basisfunktionen ähnlich zur RTL, aber arbeiten mit UTF-8 anstelle der
 +
// Systemkodierung
 +
 
 +
// AnsiToUTF8 und UTF8ToAnsi brauchen einen Widestring-Manager unter Linux, BSD, Mac OS X,
 +
// aber normalerweise nehmen diese Betriebssysteme UTF-8 als Systemkodierung, dann ist
 +
// ein Widestring-Manager nicht nötig.
 +
function NeedRTLAnsi: boolean;// wahr, wenn die Systemkodierung nicht UTF-8 ist
 +
procedure SetNeedRTLAnsi(NewValue: boolean);
 +
function UTF8ToSys(const s: string): string;// wie UTF8ToAnsi, aber unabhängig von einem Widestring-Manager
 +
function SysToUTF8(const s: string): string;// wie AnsiToUTF8, aber unabhängig von einem Widestring-Manager
 +
 
 +
// Dateioperationen
 +
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;
 +
 
 +
// Umgebung
 +
function ParamStrUTF8(Param: Integer): string;
 +
function GetEnvironmentStringUTF8(Index : Integer): String;
 +
function GetEnvironmentVariableUTF8(const EnvVar: String): String;
 +
function GetAppConfigDirUTF8(Global: Boolean): string;</syntaxhighlight>
 +
 
 +
==== Mac OS X ====
 +
 
 +
Die Dateifunktionen der Unit 'FileUtil' berücksichtigen eine Besonderheit von Mac OS X: OS X normalisiert Dateiname. Zum Beispiel kann der Dateiname 'ä.txt' in Unicode mit zwei unterschiedlichen Sequenzen (#$C3#$A4 und 'a'#$CC#$88) kodiert werden. Unter Linux und BSD können Sie einen Dateinamen mit beiden Kodierungen erzeugen. OS X konvertiert automatisch den a-Umlaut in die drei Byte Sequenz. Das bedeutet:
 +
 
 +
<syntaxhighlight lang=pascal>if Filename1=Filename2 then ... // reicht nicht aus unter OS X
 +
if AnsiCompareFileName(Filename1,Filename2)=0 then ... // reicht nicht aus unter fpc 2.2.2, nicht einmal mit 'cwstring'
 +
if CompareFilenames(Filename1,Filename2)=0 then ... // dies funktioniert immer (Unit FileUtil oder FileProcs)</syntaxhighlight>
 +
 
 +
==UTF8 und Quelltextdateien - die fehlende BOM==
 +
 
 +
Wenn Sie mit Lazarus Quelltextdateien erzeugen und einige Nicht-ASCII-Zeichen eintippen, dann wird diese Datei mit UTF8 gespeichert. Es '''verwendet keine BOM''' (Byte-Ordnungs-Markierung). Sie können die Kodierung ändern mittels eines Rechtsklicks im Quelltexteditor / Dateieinstellungen / Zeichenkodierung. Der Grund für die fehlende BOM ist die Art, wie FPC ANSI-Strings behandelt. Für die Kompatibilität benutzt die LCL ANSI-Strings und für die Portabilität benutzt die LCL UTF8.
 +
 
 +
Anmerkung: Einige MS Windows Texteditoren verarbeiten diese Dateien mit der System-Codepage und zeigen dann ungültige Zeichen an. Fügen Sie '''keine''' BOM hinzu. Wenn Sie eine BOM einfügen, müssen Sie alle Stringzuweisungen ändern.
 +
 
 +
Zum Beispiel:
 +
 
 +
<syntaxhighlight lang=pascal>Button1.Caption := 'Über';</syntaxhighlight>
 +
 
 +
Wenn keine BOM vorliegt (und kein Codepage-Parameter übergeben wurde), dann behandelt der Compiler die Zeichenkette wie systemkodiert und kopiert jedes Byte unkonvertiert in die Zeichenkette. Genau so erwartet die LCL die Zeichenketten.
 +
 
 +
<syntaxhighlight lang=pascal>// Quelltextdatei gespeichert als UTF ohne BOM
 +
if FileExists('Über.txt') then ; // falsch, weil 'FileExists' Systemkodierung erwartet
 +
if FileExistsUTF8('Über.txt') then ; // richtig</syntaxhighlight>
 +
 
 +
==Widestrings und Ansistrings==
 +
 
 +
Wenn Sie Ansistrings an Widestrings übergeben, müssen Sie die Kodierung konvertieren.
 +
 
 +
<syntaxhighlight lang=pascal>var
 +
  w: widestring;
 +
begin
 +
  w:='Über'; // falsch, weil der FPC die System-Codepage zu UTF16 konvertiert
 +
  w:=UTF8ToUTF16('Über'); // richtig
 +
  Button1.Caption:=UTF16ToUTF8(w);
 +
end;</syntaxhighlight>
 +
 
 +
==Quelltextdatei mit UTF8 BOM==
 +
 
 +
Wenn Sie in einer Unit zahlreiche Konvertierungen von Widestrings durchführen, könnte der Code dadurch lesbarer werden, dass Sie den Quelltext als UTF8 mit BOM abspeichern. Der Compiler erlaubt pro Unit eine Kodierung.
  
=== Voraussetzungen ===
 
  
Die Vision von Lazarus ist: "Write once, compile everywhere." Das bedeutet im Idealfall, eine Applikation, die Unicode eingeschaltet hat, hat nur einen Source-Code, der für alle Plattformen gültig ist, ohne irgendwelche konditionalen Definitionen.
 
  
Der "Interface" Teil der LCL sollte Unicode für die Zielplattform bereitstellen, die Zielplattform sollte Unicode selber unterstützen, ohne den Anwendungsprogrammierer mit Details zu belästigen.
+
=== Ostasiatische Sprachen unter Windows ===
  
Was Lazarus betrifft ist die interne String-Kommunikation an den Schnittstellen "Anwendungscode <--> LCL" und "LCL <--> Widgetsets". Beide basieren nämlich auf den klassischen byte-orientierten Strings. Logisch sollten diese String konvertiert werden zu dem Code [[UTF-8]].
+
Die Vorgabeschriftart (Tahoma) für Benutzerinterface-Steuerelemente unter Windows XP kann verschiedene Sprachen korrekt anzeigen, darunter Arabisch, Russisch und westliche Sprachen, aber nicht ostasiatische Sprachen, wie Chinesisch, Japanisch oder Koreanisch. Gehen Sie zur Systemsteuerung, wählen Sie 'Region und Sprache' aus, klicken Sie auf die Sprachformate und installieren Sie das East Asia Language Pack. Dadurch wird die Vorgabeschriftart ab jetzt diese Sprachen korrekt anzeigen. Natürlich haben Windows XP Versionen, die für diese Sprachen lokalisiert wurden, dieses Sprachpaket bereits installiert. Genauere Instruktionen gibt es [http://newton.uor.edu/Departments&Programs/AsianStudiesDept/Language/asianlanguageinstallation_XP.html hier].
  
=== Migration to Unicode ===
+
== Implementierungsrichtlinien ==
  
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.
+
=== Voraussetzungen ===
  
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.
+
Die Vision von Lazarus ist: "Write once, compile everywhere." Das bedeutet im Idealfall, eine Applikation, die Unicode eingeschaltet hat, hat nur einen Source-Code, der für alle Plattformen gültig ist, ohne irgendwelche konditionalen Definitionen.  
  
 +
Der "Interface" Teil der LCL sollte Unicode für die Zielplattform bereitstellen, die Zielplattform sollte Unicode selber unterstützen, ohne den Anwendungsprogrammierer mit Details zu belästigen.
  
Currently we have various groups of widgetsets, according to the encoding:
+
Was Lazarus betrifft, ist die interne String-Kommunikation an den Schnittstellen "Anwendungscode <--> LCL" und "LCL <--> Widgetsets". Beide basieren nämlich auf den klassischen byte-orientierten Strings. Logisch sollten diese String konvertiert werden zu dem Code [[UTF-8]].
  
*Interfaces that use ANSI encoding: win32 and gtk (1) interfaces.
+
=== Migration zu Unicode ===
  
*Interfaces that use UTF-8 encoding: gtk (1), gtk2, qt, fpGUI, carbon
+
Die meisten existierenden Versionen von Lazarus benutzen die ANSI-Kodierung, weil dies der Standard war für die Gtk1 und win32 Interfaces bis zur Version 0.9.24. Ab 0.9.25 verwenden alle Interfaces [[UTF-8]] als Standard (mit Ausnahme von gtk1, das [[UTF-8]] nur dann unterstützt, wenn auch die Systemkodierung [[UTF-8]] ist, und selbst damit gibt es Einschränkungen). Deshalb müssen alle Anwendungen, die Zeichenketten direkt an das Interface übergeben (sei es geschrieben im Code oder im Objektinspektor) diese Zeichenketten zu UTF-8 konvertieren.
  
*Interfaces that currently use ANSI encoding, but need migration to UTF-8: win32, wince
+
Derzeit haben wir verschiedene Gruppen von Interfaces, je nach Kodierung:
  
 +
*Interfaces die die ANSI-Kodierung verwenden: gtk (1) auf ANSI Systemen.
  
Notice that gtk 1 is on both ANSI and UTF-8 groups. That´s because the encoding is controlled by an environment variable on Gtk 1.
+
*Interfaces die die [[UTF-8]]-Kodierung verwenden: gtk (1) auf UTF-8 Systemen, gtk2, qt, [[fpGUI]], carbon, win32, wince, alle anderen
  
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.
+
Beachten Sie, dass gtk (1) sowohl in den Gruppen ANSI als auch UTF-8 aufscheint. Das ist, weil die Kodierung von einer Umgebungsvariablen unter Gtk 1 kontrolliert wird.
  
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.
+
Die IDE wurde erweitert für das Laden/Speichern/Bearbeiten von Dateien mit unterschiedlichen Kodierungen (1 Kodierung pro Datei). Sie hat eine eingebaute Heuristik um die Kodierung zu ermitteln und Sie können die Kodierung einer Datei zu jedem Zeitpunkt ändern (Quelltexteditor / Popup-Menü / Dateieinstellungen / Kodierung). So kann die IDE alte Dateien und Projekte öffnen und auch zum Konvertieren der Kodierung benutzt werden.
  
 
== Roadmap ==
 
== Roadmap ==
  
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.
+
Da wir nunmehr Richtlinien haben, ist es an der Zeit eine "Roadmap" auszuarbeiten und diese Realität werden zu lassen. Vor diesem Hintergrund wurde der nachfolgend dargestellte Plan aufgestellt. Dieser Plan teilt die Aufgaben in zwei Gruppen ein. Eine Gruppe für primäre, eine für sekundäre Aufgaben.
  
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.
+
Alle primären Aufgaben müssen vollständig umgesetzt sein, bevor wir davon sprechen können, dass Lazarus Unicode-fähig ist. Diesen primären Aufgaben gilt daher unser Hauptaugenmerk. An ihnen werden wir bevorzugt arbeiten.
  
Secondary tasks are desirable, but won´t be implemented unless someone volunteers for them, or posts a bounty for them.
+
Sekundäre Aufgaben sind zwar wünschenswert, werden aber erst umgesetzt, wenn sich für die Aufgabe jemand findet, der sie umsetzt.
  
  
Line 53: Line 224:
  
  
'''Make Win32 Widgetset support UTF-8'''
+
'''Ziel: Win32 Widgetset unterstützt UTF-8'''
  
 
Anmerkungen: 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.
 
Anmerkungen: 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.
  
Status: teilweise implementiert
+
Status: vollständig implementiert
  
  
Line 71: Line 242:
 
Anmerkungen:  
 
Anmerkungen:  
  
Status: vollendet. Except for the character map, which still shows only 255 characters. But all modern OS provide nice unicode character maps anyway.
+
Status: vollendet. Mit Ausnahme der Zeichentabelle ("Character-Map"), sie zeigt weiterhin lediglich 255 Zeichen. Aber alle modernen Betriebssysteme bieten ohnehin schöne Unicode-Zeichentabellen.
  
  
Line 83: Line 254:
  
  
'''Update Windows CE widgetset so it uses UTF-8'''
+
'''Aktualisieren des Windows CE Interfaces, damit es UTF-8 benutzt'''
  
Anmerkungen: String conversion routines are concentrated on the winceproc.pp file. Many tests are needed.
+
Anmerkungen: String-Umwandlungsroutinen konzentrieren sich auf die winceproc.pp file. Hier sind noch viele Tests notwendig.
  
Status: nicht implementiert
+
Status: vollendet
  
  
Line 94: Line 265:
 
Anmerkungen:
 
Anmerkungen:
  
Status: nicht implementiert
+
Status: nicht implementiert.
  
  
'''Complete RTL in synedit'''
+
'''RTL in synedit vervollständigen'''
  
 
Anmerkungen: RTL meint von rechts nach links (right to left) wie es z.B. im Arabischen verwendet wird
 
Anmerkungen: RTL meint von rechts nach links (right to left) wie es z.B. im Arabischen verwendet wird
Line 103: Line 274:
 
Status: nicht implementiert.
 
Status: nicht implementiert.
  
== Unicode essentials ==
+
== Grundlagen von Unicode ==
  
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).
+
Der Unicode-Standard weist den Integerzahlen von 0 bis 10FFFF(h) Zeichen zu. Jede solche Zuweisung wird auch "Codepoint" genannt. Mit anderen Worten, die Unicode-Zeichen sind im Prinzip definiert für die Codepoints von U+000000 bis U+10FFFF (0 to 1 114 111).
  
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:
+
Es gibt drei Schemata, um Unicode-Codepoints als eindeutige Bytesequenzen abzubilden. Diese Schemata nennt man Unicode-Transformationsformate: UTF-8, UTF-16 und UTF-32. Die Umwandlung untereinander ist möglich. Hier sind ihre grundlegenden Eigenschaften:
  
                            UTF-8 UTF-16 UTF-32
+
                              UTF-8 UTF-16 UTF-32
  Smallest code point [hex] 000000 000000 000000
+
  Kleinster Codepoint    [hex] 000000 000000 000000
  Largest code point  [hex] 10FFFF 10FFFF 10FFFF
+
  Größter Codepoint      [hex] 10FFFF 10FFFF 10FFFF
  Code unit size [bits]         8    16    32
+
  Größe der Codeeinheit [Bits]     8    16    32
  Minimal bytes/character        1      2      4
+
  Minimale Bytes/Zeichen            1      2      4
  Maximal bytes/character        4      4      4
+
  Maximale Bytes/Zeichen            4      4      4
  
'''UTF-8''' has several important and useful properties: It is
+
'''UTF-8''' hat einige wichtige und nützliche Eigenschaften: Es wird
interpreted as a sequence of bytes, so that the concept of
+
interpretiert als Sequenz von Bytes, darum existiert das
lo- and hi-order byte does not exist. Unicode
+
Konzept von nieder- und hochwertigem (lo- and hi-order) Byte nicht. Die Unicode-
characters U+0000 to U+007F (ASCII) are encoded simply as
+
Zeichen U+0000 bis U+007F (ASCII) sind einfach als
bytes 00h to 7Fh (ASCII compatibility). This means that
+
Bytes 00h bis 7Fh kodiert (ASCII Kompatibilität). Dies bedeutet, dass
files and strings which contain only 7-bit ASCII characters
+
Dateien und Zeichenketten die nur 7-Bit ASCII-Zeichen enthalten
have the same encoding under both ASCII and UTF-8. All
+
die gleiche Kodierung sowohl unter ASCII als auch unter UTF-8 haben. Alle
characters >U+007F are encoded as a sequence of several
+
Zeichen > U+007F sind kodiert als Sequenz mehrerer
bytes, each of which has the two most significant bits set. No
+
Bytes, deren zwei höchste Bits gesetzt sind. Nie
byte sequence of one character is contained within a longer
+
ist eine Bytesequenz eines Zeichens in einer längeren Bytesequenz
byte sequence of another character. This allows easy search for substrings. The first byte of a
+
eines anderen Zeichens enthalten. Dies erlaubt eine Suche nach Teilstrings. Das erste Byte einer
multibyte sequence that represents a non-ASCII character is
+
Multibyte-Sequenz, die ein nicht-ASCII-Zeichen darstellt ist
always in the range C0h to FDh and it indicates how many
+
immer im Bereic C0h bis FDh und es zeigt, wie viele Bytes
bytes follow for this character. All further bytes in a
+
für dieses Zeichen noch folgen. Alle weiteren Bytes in einer
multibyte sequence are in the range 80h to BFh. This allows
+
Multibyte-Sequenz sind im Bereich 80h bis BFh. Dadurch werden
easy resynchronization and robustness.
+
leichte Resynchronisation und Robustheit möglich.
  
'''UTF-16''' has the following most important properties: It uses a
+
'''UTF-16''' hat die folgenden wichtigen Eigenschaften: Es benutzt ein
single 16-bit word to encode characters from U+0000  
+
einzelnes 16-Bit Wort zum Kodieren der Zeichen von U+0000  
to U+d7ff, and a pair of 16-bit words to encode any of the
+
bis U+d7ff, und ein Paar von 16-Bit Worten, um jedes der restlichen Unicode-Zeichen zu kodieren.
remaining Unicode characters.
 
  
Finally, any Unicode character can be represented as a
+
Zuletzt kann jedes Unicode-Zeichen auch als einzelne
single 32-bit unit in '''UTF-32'''.
+
32-Bit Einheit in '''UTF-32''' dargestellt werden.
  
 
Für weitere Informationen siehe:  
 
Für weitere Informationen siehe:  
Line 147: Line 317:
 
[http://en.wikipedia.org/wiki/ISO-8859]
 
[http://en.wikipedia.org/wiki/ISO-8859]
  
== Lazarus component library architecture essentials ==
+
== Grundlagen der Lazarus-Komponentenbibliotheks-Architektur ==
  
 
Die LCL besteht aus zwei Teilen:  
 
Die LCL besteht aus zwei Teilen:  
# A target platform independent part, which implements a class hierarchy analogous to Delphi VCL;  
+
# Einem von der Zielplattform unabhängigen Teil, der eine Klassenhierarchie analog zur Delphi VCL implementiert;  
# "Interfaces" - a part that implements the interface to APIs of each target platform.
+
# "Interfaces" - einem Teil der das Interface zu den APIs einer jeden Zielplattform implementiert.
 
 
The communication between the two parts is done by an abstract class TWidgetset. Each widgetset is implemented by its own class derived from TWidgetset.
 
 
 
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.
 
 
 
Gtk2 widgetset only works with UTF-8 encoding and supports UTF-8 completely.
 
  
The win32 interface is setup with ansi widgets and UTF-8 support is started, but not yet complete and therefore disabled by default. So it is currently not possible to use Unicode with win32.
+
Die Kommunikation zwischen den beiden Teilen erfolgt durch eine abstrakte Klasse 'TWidgetset'. Jedes Interface ist implementiert durch seine eigene, von TWidgetset abgeleitete Klasse.
  
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.
+
Das GTK 1 Interface ist das älteste. In diesem Widgetset ist die Zeichenkettenkodierung bestimmt durch die Umgebungsvariable 'LANG' (???--- , which is usually a iso The ISO-8859-n group of single byte encodings---???). Zuletzt (zum Beispiel bei Mandriva 2007), haben viele Distributionen ein Gtk 1 ausgeliefert, das für UTF-8 konfiguriert war. Unserem Gtk 1 Interface fehlt die vollständige Unterstützung für UTF-8 in den Tastaturverarbeitungsroutinen. Dieses große Problem zeigt, wie wichtig es für Lazarus ist, eine plattformübergreifende Unicodeunterstützung zu implementieren.
  
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.
+
Das Gtk2 Interface arbeitet nur mit UTF-8 Kodierung und unterstützt UTF-8 vollständig.
  
For more, see: [[LCL Internals#Internals of the LCL|Internals of the LCL]]
+
Ab Lazarus 0.9.28 unterstützen das Windows und Windows CE Interface Unicode vollständig.
  
== Unicode-enabling the win32 interface  ==
+
Das Qt Interface ist für UTF-8 vorbereitet. Qt selbst benutzt UTF-16 als eingebaute Kodierung, aber das Lazarus Interface für Qt konvertiert von UTF-8 zu UTF-16.
  
=== Compiling LCL-Win32 with Unicode ===
+
Für weitere Informationen siehe: [[LCL Internals#Internals of the LCL|Internals of the LCL]]
  
To enable unicode on LCL for Windows go to the menu "Tools" --> "Configure Build Lazarus"
+
== Vorbereitung des Win32-Interfaces für 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".
 
 
 
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.
 
  
 
=== Richtlinien ===
 
=== Richtlinien ===
  
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.
+
Als erstes, und auch wichtigstes, müssen alle Unicode-Patches für das Win32-Interface in Anweisungen für bedingte Kompilierung (IFDEF WindowsUnicodeSupport) eingeschlossen sein, um zu verhindern, dass das existierende ANSI-Interface zerbricht. Nachdem sich dies stabilisiert hat, werden alle IFDEFs entfernt und nur der Unicodeteil bleibt bestehen. Zu diesem Zeitpunkt brauchen alle Programme die ANSI-Zeichen verwenden eine Migration hin zu Unicode.
  
Windows platforms <=Win9x are based on ISO code page
+
Windows-Plattformen <= Win9x basieren auf den Standards der ISO Codeseiten und unterstützen Unicode nur teilweise. Die Windows-Plattformen beginnend mit WinNT und Windows CE unterstützen Unicode vollständig. Win 9x und NT bieten deshalb zwei parallele Mengen von API-Funktionen: die alten ANSI-fähigen *A und die neuen Unicode-fähigen *W. *W Funktionen akzeptieren Widestrings, d.h. UTF-16 kodierte Strings, als Parameter. Windows CE verwendet nur die Wide API Funktionen.
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 ====
+
====Wide-Funktionen, die es unter Windows 9x gibt====
  
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
+
Einzelne Wide API Funktionen gibt es unter Windows 9x. Hier ist eine Liste solcher Funktionen: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp
  
Conversion example:
+
Konvertierungsbeispiel:
  
 
<pre>
 
<pre>
  GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
+
GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
 
Length(ButtonCaption), TextSize);
 
Length(ButtonCaption), TextSize);
 
</pre>
 
</pre>
  
Becomes:
+
wird zu:
  
<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>
 
  
==== Funktionen die Ansi und Wide Versionen benötigen ====
+
==== Funktionen die ANSI- und Wide- Versionen benötigen ====
  
First Conversion example:
+
Erstes Konvertierungsbeispiel:
  
<delphi>
+
<syntaxhighlight lang=pascal>function TGDIWindow.GetTitle: String;
function TGDIWindow.GetTitle: String;
 
 
var
 
var
l: Integer;
+
  l: Integer;
 
begin
 
begin
  l := Windows.GetWindowTextLength(Handle);
+
  l := Windows.GetWindowTextLength(Handle);
  SetLength(Result, l);
+
  SetLength(Result, l);
  Windows.GetWindowText(Handle, @Result[1], l);
+
  Windows.GetWindowText(Handle, @Result[1], l);
end;
+
end;</syntaxhighlight>
</delphi>
 
  
Becomes:
+
wird zu:
  
<delphi>
+
<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}
  
  l := Windows.GetWindowTextLength(Handle);
+
  l := Windows.GetWindowTextLength(Handle);
  SetLength(Result, l);
+
  SetLength(Result, l);
  Windows.GetWindowText(Handle, @Result[1], l);
+
  Windows.GetWindowText(Handle, @Result[1], l);
  
 
{$endif}
 
{$endif}
  
end;
+
end;</syntaxhighlight>
 
 
</delphi>
 
  
 
=== Roadmap ===
 
=== Roadmap ===
  
Was sollte bereits mit Unicode funktionieren:
+
====Was bereits mit Unicode funktionieren sollte:====
  
 
* TForm, TButton, TLabel
 
* TForm, TButton, TLabel
 
* die meisten Controls
 
* die meisten Controls
 
* Menüs
 
* Menüs
* LCLIntf.ExtTextOut and most other text related winapis
+
* LCLIntf.ExtTextOut und die meisten anderen textbezogenen WinAPIs
* TStrings based controls. Examples: TComboBox, TListBox, etc
+
* TStrings-basierte Steuerelemente. Beispiele: TComboBox, TListBox, etc...
* SynEdit shows and can input UTF-8 characters correctly
+
* SynEdit kann UTF-8 Zeichen korrekt anzeigen und als Eingabe entgegennehmen
 +
* Unicode-Zeichenketten zur/von der Zwischenablage schicken/empfangen
 +
* Den Anwendungstitel in den Projekteinstellungen setzen. Zum Beispiel auf: 'Minha Aplicação'.
 +
* Doppelklick im Editor auf Wörter mit nicht-ASCII Zeichen wählt diese Wörter aus
  
Bekannte Probleme bei der Unicode Unterstützung:
+
====Bekannte Probleme bei der Unicode-Unterstützung:====
  
 +
* Viele Komponenten unterstützen RTL nicht oder nur fehlerhaft
 
* SynEdit unterstützt nicht RTL (right to left, von rechts nach links)
 
* SynEdit unterstützt nicht RTL (right to left, von rechts nach links)
* MessageBox buttons don't show unicode correctly when they are translated. Tested on the IDE. Could be a problem on the IDE however.
+
* <s>Is OpenFileDialogCallBack tested with selection large numbers of files?
 +
** Is this problem unicode specific? I think it's a generic problem. --[[User:Sekelsenmat|Sekelsenmat]] 13:40, 14 February 2008 (CET)
 +
*** Maybe. I know I tested it with large number of files before the Unicode version was added. If it is a generic problem, then the the non-Unicode version got broken, when the Unicode version was added. [[User:Vincent|Vincent]] 21:45, 15 February 2008 (CET)
 +
**** Associated bugtracker item: http://bugs.freepascal.org/view.php?id=10918</s> Fixed and implemented.
 +
* <s>class function TWin32WSSelectDirectoryDialog.CreateHandle: Title, FileName and InitialDir should be made Unicode aware.
 +
** Associated bugtracker item: http://bugs.freepascal.org/view.php?id=10919</s> Implemented.
  
Liste der zu prüfenden Units:
+
'''Mögliche Probleme bei der Unicode-Unterstützung'''
  
*"win32callback.inc"
+
Basierend auf einer Durchsicht des Codes, sollten folgende Punkte getestet werden, weil der dortige Code nicht Unicode-fähig zu sein scheint:
*"win32def.pp"
+
* <s>class procedure TWin32WSCustomComboBox.SetText</s>
*"win32int.pp"
+
* <s>TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint is not Unicode aware</s>
*"win32lclintf.inc"
+
* <s>TWin32WidgetSet.MessageBox doesn't call MessageBoxW.</s>
*"win32lclintfh.inc"
+
* TWin32WidgetSet.TextOut: Is Windows.TextOut supported on windows 9X?
*"win32listsl.inc"
+
** Yes, please see [[LCL_Unicode_Support#Wide_functions_present_on_Windows_9x]]. Althought I never tested this.
*"win32listslh.inc"
+
* MessageBox buttons don't show unicode correctly when they are translated. Tested on the IDE. Could be a problem on the IDE however.
*"win32memostrings.inc"
+
** Note: I couldn't reproduce using the portuguese translation --[[User:Sekelsenmat|Sekelsenmat]] 22:20, 12 January 2008 (CET)
*"win32object.inc"
+
* (list of unconfirmed problems, if confirmed can be moved to the list above)
*"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"
 
*<s>"win32wsdirsel.pp"</s> - Felipe
 
*<s>"win32wseditbtn.pp"</s> - Felipe
 
*<s>"win32wsextctrls.pp"</s> - Felipe
 
*<s>"win32wsextdlgs.pp"</s> - Felipe
 
*<s>"win32wsfilectrl.pp"</s> - Felipe
 
*<s>"win32wsforms.pp"</s> - Felipe
 
*<s>"win32wsgrids.pp"</s> - Felipe
 
*<s>"win32wsimglist.pp"</s> - Felipe
 
*<s>"win32wsmaskedit.pp"</s> - Felipe
 
*<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
 
  
 
=== Screenshots ===
 
=== Screenshots ===

Latest revision as of 02:19, 19 February 2020

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

Einleitung

Mit der Version 0.9.25 unterstützt Lazarus Unicode auf allen Plattformen außer auf Gtk 1. Diese Seite liefert Informationen für Anwender, die Strategie der Entwickler und eine Beschreibung der Konzepte und deren Anwendungsdetails.

Anweisungen für Benutzer

In Unicode-Interfaces - das anzumerken ist wichtig - ist nicht alles in Unicode. Die Free Pascal Laufzeitbibliothek (RTL) und die Free Pascal FCL-Bibliothek sind ANSI. Es liegt in der Verantwortung des Entwicklers zu wissen, welche Kodierung die Zeichenketten verwenden, und eine saubere Umwandlung durchzuführen zwischen Bibliotheken mit unterschiedlichen Kodierungen.

Normalerweise erfolgt eine Kodierung pro Bibliothek (d. h. eine dynamische Bibliothek 'dll' oder ein Lazarus-Package). Jede Bibliothek erwartet 1 einheitliche Art von Kodierung, also üblicherweise entweder Unicode (UTF-8 für Lazarus) oder ANSI (das bedeutet die Systemkodierung, und kann utf-8 sein oder nicht). Die RTL und die FCL von FPC 2.4 erwarten ANSI-Zeichenketten. FPC 2.5.x derzeit auch.

Sie können zwischen Unicode und ANSI konvertieren mittels der Funktionen UTF8ToAnsi und AnsiToUTF8 aus der Unit 'System' oder mittels UTF8ToSys und SysToUTF8 aus der Unit 'FileUtil'. Die beiden letzteren sind eleganter, stellen aber mehr Code in Ihr Programm.

Beispiele:

Angenommen Sie haben eine Zeichenkette aus einem TEdit und wollen diese an eine Routine aus der RTL übergeben:

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

Und in der umgekehrten Richtung:

var
  MyString: string; // ansi encoded
begin
  MyString := SomeRTLRoutine;
  MyTEdit.Text := AnsiToUTF8(MyString);
end;

Wichtig: UTF8ToAnsi gibt eine leere Zeichenkette zurück, wenn der UTF8-String ungültige Zeichen enthält.

Wichtig: AnsiToUTF8 und UTF8ToAnsi brauchen einen Widestring-Manager unter Linux, BSD und Mac OS X. Sie können die Funktionen SysToUTF8 und UTF8ToSys (aus der Unit FileUtil) einsetzen oder einen Widestring-Manager, indem Sie 'cwstring' als eine der ersten Units zum uses-Abschnitt in Ihrem Programm hinzufügen.


Der Umgang mit UTF8 Zeichenketten und Zeichen

Wenn Sie die Zeichen einer UTF8-Zeichenkette durchlaufen wollen, gibt es dafür zwei unterschiedliche Arten:

  • durchlaufen Sie die Bytes - ist nützlich zum Suchen eines Teilstrings oder wenn Sie nur die ASCII-Zeichen eines UTF8-Strings betrachten wollen. Zum Beispiel beim Parsen einer xml-Datei.
  • durchlaufen Sie die Zeichen - ist nützlich für graphische Komponenten wie Synedit. Zum Beispiel wenn Sie wissen wollen, welches das dritte am Bildschirm dargestellte Zeichen ist.

Suchen eines Teilstrings

Wegen der besonderen Natur von UTF8 können Sie einfach die normalen Stringfunktionen verwenden:

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;


Zugriff auf einen UTF8-String als Array

Ein Array besteht aus aus Elementen gleicher Größe. Ein UTF8-Zeichen kann aber 1 bis 4 Bytes haben. Deshalb müssen Sie den UTF8-String konvertieren, entweder in ein Array aus Unicode-Werten (longwords) oder in ein Array aus PChar mit einem Zeiger auf jedes Zeichen.

Durchlaufen von Zeichen mittels UTF8CharacterToUnicode

Das Folgende demonstriert wie Sie die 32bit Unicode-Werte von Zeichen in einem UTF8-String durchlaufen:

uses LCLProc;
...
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;

Der Umgang mit Verzeichnis- und Dateinamen

Die Steuerelemente und Funktionen in Lazarus erwarten Dateinamen und Verzeichnisnamen in UTF-8 Kodierung, aber die RTL benutzt ANSI-Strings für Verzeichnisse und Dateinamen.

Betrachten Sie beispielsweise eine Schaltfläche, die die Eigenschaft 'Directory' einer TFileListBox auf das aktuelle Verzeichnis setzt. Die RTL-Funktion GetCurrentDir ist ANSI, und nicht Unicode, deswegen muss man konvertieren:

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

Die Unit 'FileUtil' definiert allgemeine Dateifunktionen mit UTF-8 Strings:

// Basisfunktionen ähnlich zur RTL, aber arbeiten mit UTF-8 anstelle der
// Systemkodierung

// AnsiToUTF8 und UTF8ToAnsi brauchen einen Widestring-Manager unter Linux, BSD, Mac OS X,
// aber normalerweise nehmen diese Betriebssysteme UTF-8 als Systemkodierung, dann ist
// ein Widestring-Manager nicht nötig.
function NeedRTLAnsi: boolean;// wahr, wenn die Systemkodierung nicht UTF-8 ist
procedure SetNeedRTLAnsi(NewValue: boolean);
function UTF8ToSys(const s: string): string;// wie UTF8ToAnsi, aber unabhängig von einem Widestring-Manager
function SysToUTF8(const s: string): string;// wie AnsiToUTF8, aber unabhängig von einem Widestring-Manager

// Dateioperationen
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;

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

Mac OS X

Die Dateifunktionen der Unit 'FileUtil' berücksichtigen eine Besonderheit von Mac OS X: OS X normalisiert Dateiname. Zum Beispiel kann der Dateiname 'ä.txt' in Unicode mit zwei unterschiedlichen Sequenzen (#$C3#$A4 und 'a'#$CC#$88) kodiert werden. Unter Linux und BSD können Sie einen Dateinamen mit beiden Kodierungen erzeugen. OS X konvertiert automatisch den a-Umlaut in die drei Byte Sequenz. Das bedeutet:

if Filename1=Filename2 then ... // reicht nicht aus unter OS X
if AnsiCompareFileName(Filename1,Filename2)=0 then ... // reicht nicht aus unter fpc 2.2.2, nicht einmal mit 'cwstring'
if CompareFilenames(Filename1,Filename2)=0 then ... // dies funktioniert immer (Unit FileUtil oder FileProcs)

UTF8 und Quelltextdateien - die fehlende BOM

Wenn Sie mit Lazarus Quelltextdateien erzeugen und einige Nicht-ASCII-Zeichen eintippen, dann wird diese Datei mit UTF8 gespeichert. Es verwendet keine BOM (Byte-Ordnungs-Markierung). Sie können die Kodierung ändern mittels eines Rechtsklicks im Quelltexteditor / Dateieinstellungen / Zeichenkodierung. Der Grund für die fehlende BOM ist die Art, wie FPC ANSI-Strings behandelt. Für die Kompatibilität benutzt die LCL ANSI-Strings und für die Portabilität benutzt die LCL UTF8.

Anmerkung: Einige MS Windows Texteditoren verarbeiten diese Dateien mit der System-Codepage und zeigen dann ungültige Zeichen an. Fügen Sie keine BOM hinzu. Wenn Sie eine BOM einfügen, müssen Sie alle Stringzuweisungen ändern.

Zum Beispiel:

Button1.Caption := 'Über';

Wenn keine BOM vorliegt (und kein Codepage-Parameter übergeben wurde), dann behandelt der Compiler die Zeichenkette wie systemkodiert und kopiert jedes Byte unkonvertiert in die Zeichenkette. Genau so erwartet die LCL die Zeichenketten.

// Quelltextdatei gespeichert als UTF ohne BOM
if FileExists('Über.txt') then ; // falsch, weil 'FileExists' Systemkodierung erwartet
if FileExistsUTF8('Über.txt') then ; // richtig

Widestrings und Ansistrings

Wenn Sie Ansistrings an Widestrings übergeben, müssen Sie die Kodierung konvertieren.

var 
  w: widestring;
begin
  w:='Über'; // falsch, weil der FPC die System-Codepage zu UTF16 konvertiert
  w:=UTF8ToUTF16('Über'); // richtig
  Button1.Caption:=UTF16ToUTF8(w);
end;

Quelltextdatei mit UTF8 BOM

Wenn Sie in einer Unit zahlreiche Konvertierungen von Widestrings durchführen, könnte der Code dadurch lesbarer werden, dass Sie den Quelltext als UTF8 mit BOM abspeichern. Der Compiler erlaubt pro Unit eine Kodierung.


Ostasiatische Sprachen unter Windows

Die Vorgabeschriftart (Tahoma) für Benutzerinterface-Steuerelemente unter Windows XP kann verschiedene Sprachen korrekt anzeigen, darunter Arabisch, Russisch und westliche Sprachen, aber nicht ostasiatische Sprachen, wie Chinesisch, Japanisch oder Koreanisch. Gehen Sie zur Systemsteuerung, wählen Sie 'Region und Sprache' aus, klicken Sie auf die Sprachformate und installieren Sie das East Asia Language Pack. Dadurch wird die Vorgabeschriftart ab jetzt diese Sprachen korrekt anzeigen. Natürlich haben Windows XP Versionen, die für diese Sprachen lokalisiert wurden, dieses Sprachpaket bereits installiert. Genauere Instruktionen gibt es hier.

Implementierungsrichtlinien

Voraussetzungen

Die Vision von Lazarus ist: "Write once, compile everywhere." Das bedeutet im Idealfall, eine Applikation, die Unicode eingeschaltet hat, hat nur einen Source-Code, der für alle Plattformen gültig ist, ohne irgendwelche konditionalen Definitionen.

Der "Interface" Teil der LCL sollte Unicode für die Zielplattform bereitstellen, die Zielplattform sollte Unicode selber unterstützen, ohne den Anwendungsprogrammierer mit Details zu belästigen.

Was Lazarus betrifft, ist die interne String-Kommunikation an den Schnittstellen "Anwendungscode <--> LCL" und "LCL <--> Widgetsets". Beide basieren nämlich auf den klassischen byte-orientierten Strings. Logisch sollten diese String konvertiert werden zu dem Code UTF-8.

Migration zu Unicode

Die meisten existierenden Versionen von Lazarus benutzen die ANSI-Kodierung, weil dies der Standard war für die Gtk1 und win32 Interfaces bis zur Version 0.9.24. Ab 0.9.25 verwenden alle Interfaces UTF-8 als Standard (mit Ausnahme von gtk1, das UTF-8 nur dann unterstützt, wenn auch die Systemkodierung UTF-8 ist, und selbst damit gibt es Einschränkungen). Deshalb müssen alle Anwendungen, die Zeichenketten direkt an das Interface übergeben (sei es geschrieben im Code oder im Objektinspektor) diese Zeichenketten zu UTF-8 konvertieren.

Derzeit haben wir verschiedene Gruppen von Interfaces, je nach Kodierung:

  • Interfaces die die ANSI-Kodierung verwenden: gtk (1) auf ANSI Systemen.
  • Interfaces die die UTF-8-Kodierung verwenden: gtk (1) auf UTF-8 Systemen, gtk2, qt, fpGUI, carbon, win32, wince, alle anderen

Beachten Sie, dass gtk (1) sowohl in den Gruppen ANSI als auch UTF-8 aufscheint. Das ist, weil die Kodierung von einer Umgebungsvariablen unter Gtk 1 kontrolliert wird.

Die IDE wurde erweitert für das Laden/Speichern/Bearbeiten von Dateien mit unterschiedlichen Kodierungen (1 Kodierung pro Datei). Sie hat eine eingebaute Heuristik um die Kodierung zu ermitteln und Sie können die Kodierung einer Datei zu jedem Zeitpunkt ändern (Quelltexteditor / Popup-Menü / Dateieinstellungen / Kodierung). So kann die IDE alte Dateien und Projekte öffnen und auch zum Konvertieren der Kodierung benutzt werden.

Roadmap

Da wir nunmehr Richtlinien haben, ist es an der Zeit eine "Roadmap" auszuarbeiten und diese Realität werden zu lassen. Vor diesem Hintergrund wurde der nachfolgend dargestellte Plan aufgestellt. Dieser Plan teilt die Aufgaben in zwei Gruppen ein. Eine Gruppe für primäre, eine für sekundäre Aufgaben.

Alle primären Aufgaben müssen vollständig umgesetzt sein, bevor wir davon sprechen können, dass Lazarus Unicode-fähig ist. Diesen primären Aufgaben gilt daher unser Hauptaugenmerk. An ihnen werden wir bevorzugt arbeiten.

Sekundäre Aufgaben sind zwar wünschenswert, werden aber erst umgesetzt, wenn sich für die Aufgabe jemand findet, der sie umsetzt.


Primäre Aufgaben

Ziel: Win32 Widgetset unterstützt UTF-8

Anmerkungen: 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.

Status: vollständig implementiert


Aktualisieren der GTK2 Tastaturfunktionen, damit sie mit UTF-8 funktionieren

Anmerkungen:

Status: fast vollendet. Some pre-editing features of the gtk2 are not yet supported in custom controls. I don't know, which language needs them.


Make sure the Lazarus IDE runs correctly with Win32 Unicode widgetset and supports UTF-8

Anmerkungen:

Status: vollendet. Mit Ausnahme der Zeichentabelle ("Character-Map"), sie zeigt weiterhin lediglich 255 Zeichen. Aber alle modernen Betriebssysteme bieten ohnehin schöne Unicode-Zeichentabellen.


Make sure the Lazarus IDE runs correctly with Gtk 2 widgetset and supports UTF-8

Anmerkungen:

Status: vollendet. Es gibt noch GTK2 intf bugs, aber diese haben nichts mit UTF-8 zu tun.

Sekundäre Aufgaben

Aktualisieren des Windows CE Interfaces, damit es UTF-8 benutzt

Anmerkungen: String-Umwandlungsroutinen konzentrieren sich auf die winceproc.pp file. Hier sind noch viele Tests notwendig.

Status: vollendet


Aktualisieren der GTK1 Tastaturfunktionen, damit sie mit UTF-8 funktionieren

Anmerkungen:

Status: nicht implementiert.


RTL in synedit vervollständigen

Anmerkungen: RTL meint von rechts nach links (right to left) wie es z.B. im Arabischen verwendet wird

Status: nicht implementiert.

Grundlagen von Unicode

Der Unicode-Standard weist den Integerzahlen von 0 bis 10FFFF(h) Zeichen zu. Jede solche Zuweisung wird auch "Codepoint" genannt. Mit anderen Worten, die Unicode-Zeichen sind im Prinzip definiert für die Codepoints von U+000000 bis U+10FFFF (0 to 1 114 111).

Es gibt drei Schemata, um Unicode-Codepoints als eindeutige Bytesequenzen abzubilden. Diese Schemata nennt man Unicode-Transformationsformate: UTF-8, UTF-16 und UTF-32. Die Umwandlung untereinander ist möglich. Hier sind ihre grundlegenden Eigenschaften:

                              UTF-8 UTF-16 UTF-32
Kleinster Codepoint    [hex] 000000 000000 000000
Größter Codepoint      [hex] 10FFFF 10FFFF 10FFFF
Größe der Codeeinheit [Bits]      8     16     32
Minimale Bytes/Zeichen            1      2      4
Maximale Bytes/Zeichen            4      4      4

UTF-8 hat einige wichtige und nützliche Eigenschaften: Es wird interpretiert als Sequenz von Bytes, darum existiert das Konzept von nieder- und hochwertigem (lo- and hi-order) Byte nicht. Die Unicode- Zeichen U+0000 bis U+007F (ASCII) sind einfach als Bytes 00h bis 7Fh kodiert (ASCII Kompatibilität). Dies bedeutet, dass Dateien und Zeichenketten die nur 7-Bit ASCII-Zeichen enthalten die gleiche Kodierung sowohl unter ASCII als auch unter UTF-8 haben. Alle Zeichen > U+007F sind kodiert als Sequenz mehrerer Bytes, deren zwei höchste Bits gesetzt sind. Nie ist eine Bytesequenz eines Zeichens in einer längeren Bytesequenz eines anderen Zeichens enthalten. Dies erlaubt eine Suche nach Teilstrings. Das erste Byte einer Multibyte-Sequenz, die ein nicht-ASCII-Zeichen darstellt ist immer im Bereic C0h bis FDh und es zeigt, wie viele Bytes für dieses Zeichen noch folgen. Alle weiteren Bytes in einer Multibyte-Sequenz sind im Bereich 80h bis BFh. Dadurch werden leichte Resynchronisation und Robustheit möglich.

UTF-16 hat die folgenden wichtigen Eigenschaften: Es benutzt ein einzelnes 16-Bit Wort zum Kodieren der Zeichen von U+0000 bis U+d7ff, und ein Paar von 16-Bit Worten, um jedes der restlichen Unicode-Zeichen zu kodieren.

Zuletzt kann jedes Unicode-Zeichen auch als einzelne 32-Bit Einheit in UTF-32 dargestellt werden.

Für weitere Informationen siehe: Unicode FAQ - Basic questions, Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM, Wikipedia: UTF-8 [1]

Grundlagen der Lazarus-Komponentenbibliotheks-Architektur

Die LCL besteht aus zwei Teilen:

  1. Einem von der Zielplattform unabhängigen Teil, der eine Klassenhierarchie analog zur Delphi VCL implementiert;
  2. "Interfaces" - einem Teil der das Interface zu den APIs einer jeden Zielplattform implementiert.

Die Kommunikation zwischen den beiden Teilen erfolgt durch eine abstrakte Klasse 'TWidgetset'. Jedes Interface ist implementiert durch seine eigene, von TWidgetset abgeleitete Klasse.

Das GTK 1 Interface ist das älteste. In diesem Widgetset ist die Zeichenkettenkodierung bestimmt durch die Umgebungsvariable 'LANG' (???--- , which is usually a iso The ISO-8859-n group of single byte encodings---???). Zuletzt (zum Beispiel bei Mandriva 2007), haben viele Distributionen ein Gtk 1 ausgeliefert, das für UTF-8 konfiguriert war. Unserem Gtk 1 Interface fehlt die vollständige Unterstützung für UTF-8 in den Tastaturverarbeitungsroutinen. Dieses große Problem zeigt, wie wichtig es für Lazarus ist, eine plattformübergreifende Unicodeunterstützung zu implementieren.

Das Gtk2 Interface arbeitet nur mit UTF-8 Kodierung und unterstützt UTF-8 vollständig.

Ab Lazarus 0.9.28 unterstützen das Windows und Windows CE Interface Unicode vollständig.

Das Qt Interface ist für UTF-8 vorbereitet. Qt selbst benutzt UTF-16 als eingebaute Kodierung, aber das Lazarus Interface für Qt konvertiert von UTF-8 zu UTF-16.

Für weitere Informationen siehe: Internals of the LCL

Vorbereitung des Win32-Interfaces für Unicode

Richtlinien

Als erstes, und auch wichtigstes, müssen alle Unicode-Patches für das Win32-Interface in Anweisungen für bedingte Kompilierung (IFDEF WindowsUnicodeSupport) eingeschlossen sein, um zu verhindern, dass das existierende ANSI-Interface zerbricht. Nachdem sich dies stabilisiert hat, werden alle IFDEFs entfernt und nur der Unicodeteil bleibt bestehen. Zu diesem Zeitpunkt brauchen alle Programme die ANSI-Zeichen verwenden eine Migration hin zu Unicode.

Windows-Plattformen <= Win9x basieren auf den Standards der ISO Codeseiten und unterstützen Unicode nur teilweise. Die Windows-Plattformen beginnend mit WinNT und Windows CE unterstützen Unicode vollständig. Win 9x und NT bieten deshalb zwei parallele Mengen von API-Funktionen: die alten ANSI-fähigen *A und die neuen Unicode-fähigen *W. *W Funktionen akzeptieren Widestrings, d.h. UTF-16 kodierte Strings, als Parameter. Windows CE verwendet nur die Wide API Funktionen.

Wide-Funktionen, die es unter Windows 9x gibt

Einzelne Wide API Funktionen gibt es unter Windows 9x. Hier ist eine Liste solcher Funktionen: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp

Konvertierungsbeispiel:

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

wird zu:

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

Funktionen die ANSI- und Wide- Versionen benötigen

Erstes Konvertierungsbeispiel:

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

wird zu:

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;

Roadmap

Was bereits mit Unicode funktionieren sollte:

  • TForm, TButton, TLabel
  • die meisten Controls
  • Menüs
  • LCLIntf.ExtTextOut und die meisten anderen textbezogenen WinAPIs
  • TStrings-basierte Steuerelemente. Beispiele: TComboBox, TListBox, etc...
  • SynEdit kann UTF-8 Zeichen korrekt anzeigen und als Eingabe entgegennehmen
  • Unicode-Zeichenketten zur/von der Zwischenablage schicken/empfangen
  • Den Anwendungstitel in den Projekteinstellungen setzen. Zum Beispiel auf: 'Minha Aplicação'.
  • Doppelklick im Editor auf Wörter mit nicht-ASCII Zeichen wählt diese Wörter aus

Bekannte Probleme bei der Unicode-Unterstützung:

  • Viele Komponenten unterstützen RTL nicht oder nur fehlerhaft
  • SynEdit unterstützt nicht RTL (right to left, von rechts nach links)
  • Is OpenFileDialogCallBack tested with selection large numbers of files?
    • Is this problem unicode specific? I think it's a generic problem. --Sekelsenmat 13:40, 14 February 2008 (CET)
      • Maybe. I know I tested it with large number of files before the Unicode version was added. If it is a generic problem, then the the non-Unicode version got broken, when the Unicode version was added. Vincent 21:45, 15 February 2008 (CET)
  • class function TWin32WSSelectDirectoryDialog.CreateHandle: Title, FileName and InitialDir should be made Unicode aware.

Mögliche Probleme bei der Unicode-Unterstützung

Basierend auf einer Durchsicht des Codes, sollten folgende Punkte getestet werden, weil der dortige Code nicht Unicode-fähig zu sein scheint:

  • class procedure TWin32WSCustomComboBox.SetText
  • TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint is not Unicode aware
  • TWin32WidgetSet.MessageBox doesn't call MessageBoxW.
  • TWin32WidgetSet.TextOut: Is Windows.TextOut supported on windows 9X?
  • MessageBox buttons don't show unicode correctly when they are translated. Tested on the IDE. Could be a problem on the IDE however.
    • Note: I couldn't reproduce using the portuguese translation --Sekelsenmat 22:20, 12 January 2008 (CET)
  • (list of unconfirmed problems, if confirmed can be moved to the list above)

Screenshots

Lazarus Unicode Test.png

Siehe auch

  • UTF-8 - Beschreibung von UTF-8 Zeichenketten