Difference between revisions of "LCL Unicode Support/de"

From Lazarus wiki
Jump to navigationJump to search
m (Text replace - "delphi>" to "syntaxhighlight>")
m (Text replace - "Delphi>" to "syntaxhighlight>")
Line 49: Line 49:
 
Wegen der besonderen Natur von UTF8 können Sie einfach die normalen Stringfunktionen verwenden:
 
Wegen der besonderen Natur von UTF8 können Sie einfach die normalen Stringfunktionen verwenden:
  
<Delphi>procedure Where(SearchFor, aText: string);
+
<syntaxhighlight>procedure Where(SearchFor, aText: string);
 
var
 
var
 
   BytePos: LongInt;
 
   BytePos: LongInt;
Line 58: Line 58:
 
   writeln('The substring "',SearchFor,'" is in the text "',aText,'"',
 
   writeln('The substring "',SearchFor,'" is in the text "',aText,'"',
 
     ' at byte position ',BytePos,' and at character position ',CharacterPos);
 
     ' at byte position ',BytePos,' and at character position ',CharacterPos);
end;</Delphi>
+
end;</syntaxhighlight>
  
  
Line 69: Line 69:
 
Das Folgende demonstriert wie Sie die 32bit Unicode-Werte von Zeichen in einem UTF8-String durchlaufen:
 
Das Folgende demonstriert wie Sie die 32bit Unicode-Werte von Zeichen in einem UTF8-String durchlaufen:
  
<Delphi>uses LCLProc;
+
<syntaxhighlight>uses LCLProc;
 
...
 
...
 
procedure IterateUTF8Characters(const AnUTF8String: string);
 
procedure IterateUTF8Characters(const AnUTF8String: string);
Line 83: Line 83:
 
     inc(p,CharLen);
 
     inc(p,CharLen);
 
   until (CharLen=0) or (unicode=0);
 
   until (CharLen=0) or (unicode=0);
end;</Delphi>
+
end;</syntaxhighlight>
  
 
===Der Umgang mit Verzeichnis- und Dateinamen===
 
===Der Umgang mit Verzeichnis- und Dateinamen===
Line 100: Line 100:
 
Die Unit 'FileUtil' definiert allgemeine Dateifunktionen mit UTF-8 Strings:
 
Die Unit 'FileUtil' definiert allgemeine Dateifunktionen mit UTF-8 Strings:
  
<Delphi>// Basisfunktionen ähnlich zur RTL, aber arbeiten mit UTF-8 anstelle der
+
<syntaxhighlight>// Basisfunktionen ähnlich zur RTL, aber arbeiten mit UTF-8 anstelle der
 
// Systemkodierung
 
// Systemkodierung
  
Line 140: Line 140:
 
function GetEnvironmentStringUTF8(Index : Integer): String;
 
function GetEnvironmentStringUTF8(Index : Integer): String;
 
function GetEnvironmentVariableUTF8(const EnvVar: String): String;
 
function GetEnvironmentVariableUTF8(const EnvVar: String): String;
function GetAppConfigDirUTF8(Global: Boolean): string;</Delphi>
+
function GetAppConfigDirUTF8(Global: Boolean): string;</syntaxhighlight>
  
 
==== Mac OS X ====
 
==== Mac OS X ====
Line 146: Line 146:
 
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:
 
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:
  
<Delphi>if Filename1=Filename2 then ... // reicht nicht aus unter OS X
+
<syntaxhighlight>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 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)</Delphi>
+
if CompareFilenames(Filename1,Filename2)=0 then ... // dies funktioniert immer (Unit FileUtil oder FileProcs)</syntaxhighlight>
  
 
==UTF8 und Quelltextdateien - die fehlende BOM==
 
==UTF8 und Quelltextdateien - die fehlende BOM==
Line 158: Line 158:
 
Zum Beispiel:
 
Zum Beispiel:
  
<Delphi>Button1.Caption := 'Über';</Delphi>
+
<syntaxhighlight>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.  
 
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.  
  
<Delphi>// Quelltextdatei gespeichert als UTF ohne BOM
+
<syntaxhighlight>// Quelltextdatei gespeichert als UTF ohne BOM
 
if FileExists('Über.txt') then ; // falsch, weil 'FileExists' Systemkodierung erwartet
 
if FileExists('Über.txt') then ; // falsch, weil 'FileExists' Systemkodierung erwartet
if FileExistsUTF8('Über.txt') then ; // richtig</Delphi>
+
if FileExistsUTF8('Über.txt') then ; // richtig</syntaxhighlight>
  
 
==Widestrings und Ansistrings==
 
==Widestrings und Ansistrings==
Line 170: Line 170:
 
Wenn Sie Ansistrings an Widestrings übergeben, müssen Sie die Kodierung konvertieren.
 
Wenn Sie Ansistrings an Widestrings übergeben, müssen Sie die Kodierung konvertieren.
  
<Delphi>var  
+
<syntaxhighlight>var  
 
   w: widestring;
 
   w: widestring;
 
begin
 
begin
Line 176: Line 176:
 
   w:=UTF8ToUTF16('Über'); // richtig
 
   w:=UTF8ToUTF16('Über'); // richtig
 
   Button1.Caption:=UTF16ToUTF8(w);
 
   Button1.Caption:=UTF16ToUTF8(w);
end;</Delphi>
+
end;</syntaxhighlight>
  
 
==Quelltextdatei mit UTF8 BOM==
 
==Quelltextdatei mit UTF8 BOM==

Revision as of 23:49, 24 March 2012

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ültioge 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 UTF( 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