LCL Unicode Support/es

From Lazarus wiki
Jump to navigationJump to search

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

Introducción

En la versión 0.9.25, Lazarus tiene soporte completo para Unicode en todas las plataformas a excepción de Gtk 1. En esta página se pueden encontrar instrucciones para usuarios de La- zarus, roadmaps, descripción de conceptos básicos y detalles de implementación.

Instrucciones para usuarios

En los widgetsets unicode es importante hacer ver que no todo es unicode. La librerias Free Runtime Pascal y FCL son ANSI. Es responsabilidad del desarrollador conocer cual es la codificación de sus cadenas y realizar la conversión apropiada entre librerias que es- peran diferentes codificaciones.

Usualmente la codificación es por libreria. Cada librería esperará uniformemente un tipo de codificación, que será habitualmente o bien UNICODE (UTF-8 para Lazarus) o ANSI (lo cual significa que es la codificación del sistema, que puede ser UTF-8 o no). Tanto RTL como FCL de Free Pascal Compiler (FPC) 2.2.2 esperan cadenas ANSI (también FPC 2.3.x).

Se puede realizar la conversión entre UNICODE y ANSI utilizando las funciones UTF8ToAnsi y AnsiToUTF8 de la unit System o bien las funciones UTF8ToSys y SysToUTF8 de la unidad FileUtil. Las dos últimas son más elegantes pero insertan más código en el programa.

Ejemplos:

Digamos que tienes una cadena desde un Tedit y necesitas pasársela a alguna rutina de fichero rtl:

<delphi> var

 MyString: string; // codificación utf-8 

begin

 MyString := MyTEdit.Text;
 SomeRTLRoutine(UTF8ToAnsi(MyString));

end; </delphi>

Y en el modo inverso:

<delphi> var

 MyString: string; // ansi encoded

begin

 MyString := SomeRTLRoutine;
 MyTEdit.Text := AnsiToUTF8(MyString);

end; </delphi>

Importante: UTF8ToAnsi retornará una cadena vacía si la cadena UTF8 contiene caracteres no válidos.

Importante: AnsiToUTF8 y UTF8ToAnsi requiere un gestor de cadenas largas bajo Linux, BSD y Mac OS X. Puedes utilizar las funciones SysToUTF8 y UTF8ToSys (unit FileUtil) o añadir un gestor de cadenas largas mediante cwstring como unit en la sección uses.

Tratando con directorios y nombres de fichero

Las funciones y controles de Lazarus esperan nombres de directorios y ficheros con la codificación utf-8, pero RTL utiliza cadenas ANSI para los mismos.

For ejemplo, consideremos un pulsador (button), el cual establece la propiedad directorio de TFileListBox al valor del directorio actual. La función RTL GetCurrentDir es ANSI, y no UNICODE, lo cual precisa de una conversión:

<delphi> procedure TForm1.Button1Click(Sender: TObject); begin

 FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
 // o usar las funciones de la unit FileUtil
 FileListBox1.Directory:=GetCurrentDirUTF8;

end; </delphi>

La unit FileUtil define funciones comunes de fichero con cadenas UTF-8:

<Delphi> // funciones basicas similares a la RTL pero trabajando con UTF-8 en lugar de // codificación de sistema

// AnsiToUTF8 y UTF8ToAnsi necesitan un gestor de cadenas largas (widestring) bajo Linux, BSD, MacOSX // pero normalmente estos sistemas operativos utilizan UTF-8 como sistema de codificación // por lo que el gestor de cadenas largas no es necesario.

function NeedRTLAnsi: boolean;// true si la codificación no es UTF-8 procedure SetNeedRTLAnsi(NewValue: boolean); function UTF8ToSys(const s: string): string;// como UTF8ToAnsi pero más independiente de widestringmanager function SysToUTF8(const s: string): string;// como AnsiToUTF8 pero mas independiente de widestringmanager

// operaciones de fichero 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;

// entorno function ParamStrUTF8(Param: Integer): string; function GetEnvironmentStringUTF8(Index : Integer): String; function GetEnvironmentVariableUTF8(const EnvVar: String): String; function GetAppConfigDirUTF8(Global: Boolean): string; </Delphi>

Mac OS X

Las funciones de fichero de la unit FileUtil tienen especial cuidado en Mac OS X: OS X normaliza los nombres de fichero. Por ejemplo el nombre de fichero 'ä.txt' puede ser codificado en unicode con dos secuencias diferentes (#$C3#$A4 y 'a'#$CC#$88). Bajo Linux y BSD se puede crear un nombre de fichero con ambas codificaciones. OS X convierte automáticamente la diéresis a una secuencia de tres bytes. Esto significa:

<Delphi> if Filename1=Filename2 then ... // no es suficiente bajo OS X if AnsiCompareFileName(Filename1,Filename2)=0 then ... // no suficiente bajo fpc 2.2.2, incluso no con cwstring if CompareFilenames(Filename1,Filename2)=0 then ... // esto siempre funciona (unit FileUtil o FileProcs) </Delphi>

Lenguajes del este asiático en Windows

La fuente por defecto (Tahoma) para los controles de interface de usuario bajo Windows XP son capaces de mostrar correctamente varios lenguajes, incluyendo arabe, ruso y lenguajes del oeste, pero no lenguajes del este tales como chino, japones y coreano. Se puede escoger la fuente para el interface standard de usuario modificando la configuración regional del sistema operativo en el panel de control, seleccionando en la solapa de lenguaje el que se necesite. De esta forma ya se podría visualizar correctamente en pantalla el lenguaje deseado. Obviamente las versiones de XP para estos idiomas deben contener dicho paquete de lenguaje instalado. Ver instrucciones en: [1]


Guias de implementación

Requerimientos

El espíritu de Lazarus es: "Escribe el código una vez, compila donde quieras." Esto significa, idealmente, que una aplicación con soporte UNICODE habilitado debería tener una sola versión de código fuente soportada, sin defines condicionales en las diferentes plataformas destino.

La parte "interface" de LCL debería soportar Unicode para las plataformas destino que las soportan ya por si mismas ocultando todas las peculiaridades para el programador de aplicaciones.

En lo que concierne a Lazarus, la comunicación interna de cadenas en los límites "Código de aplicacion <--> LCL", así como "LCL <--> Widgetsets" está basada en las cadenas clásicas (orientadas al byte). Lógicamente, sus contenidos deberían codificarse de acuerdo a UTF-8.


Migración a UNICODE

Las versiones anteriores de Lazarus utilizan codificación ANSI, porque era la opción por defecto para Gtk1 y win32, hasta la versión 0.9.24. Ya con la versión 0.9.25 todos los widgetsets utilizarán UTF-8 por defecto (a excepción de gtk1, que solamente soporta UTF-8 cuando el sistema lo soporta, con sus correspondientes limitaciones). Por ello todas las aplicaciones que pasen cadenas directamente al interface (tanto las escritas en código o directamente mediante el inspector de objetos) necesitarán ser convertidas a UTF-8.

Actualmente tenemos varios grupos de widgetsets, acordes a la codificación:

  • Interfaces que utilizan codificación ANSI: gtk (1) en sistemas ANSI.
  • Interfaces que utilizan codificación UTF-8: gtk (1) en sistemas UTF-8: gtk2, qt, fpGUI, carbon
  • Interfaces que actualmente utilizan condificación ANSI, pero necesitan migrar a UTF-8: wince

Ten en cuenta que gtk 1 se encuentra en ambos grupos ANSI y UTF-8. Esto se debe a que la codificación se controla mediante una variable de entorno en Gtk 1.

Tal como es Lazarus a fecha de hoy, el software existente funcionará si se recompila para los interfaces de win32, winCE o gtk, pero presentará complicaciones de compilación para otros widgetset. Y el nuevo software utilizando UTF-8 funcionará cuando se recompile para cualquiera de los widgesets del grupo UNICODE.

El IDE se ha extendido para cargar(load)/salvar(save)/editar(edit) ficheros con las diferentes codificaciones (una codificación por fichero). Tiene una construcción heurística para determinar la codificación y de esta forma se puede cambiar la codificación del fichero en cualquier momento (Source Editor / Popup Menu / File Settings/ Encoding) por lo que el IDE puede abrir ficheros y proyectos antiguos y de esta forma utilizarse para convertir a la codificación deseada.

Recorrido

Ahora que tenemos establecida la guia, es hora de crear un recorrido y ponerlo en práctica. Por ello se ha creado el siguiente plan. Dicho plan divide las tareas en dos grupos. Uno para las tareas primarias y otro para las secundarias.

Todas las tareas primarias deben implementarse completamente antes de que se pueda decir que Lazarus tiene funcionalidad Unicode, y como tal debe prestarse especial atención a tal finalidad dedicándole el esfuerzo oportuno.

Las tareas secundarias son deseables, pero no se implementarán a menos que algunos voluntarios se dediquen a ello o se presenten muchas peticiones.

Tareas primarias

Hacer que Win32 Widgetset soporte UTF-8

Notas: en este paso nos centraremos en todas las versiones de 32 bits de Windows al mismo tiemo. Todo el código generado en este esfuerzo será aislado del actual interface win32 mediante IFDEFs, para evitar introducir errores en este interface primario. Después del tiempo de transición, los IFDEFs serán removidos y únicamente se mantendrá el código UNICODE.


Estado: totalmente implementado.


Actualiza las funciones de teclado Gtk 2 para que trabajen con UTF-8

Notas:

Estado: casi completo. Algunas características pre-editadas de gtk2 no están todavía soportadas en los controles customizados. Desconozco que lenguaje necesitan.

Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset UNICODE de Win32 y que soporte UTF-8

Notas:

Estado: completo. Excepto por el mapa de caracteres, el cual muestra solamente 255 caracteres. De todas formas todos los sistemas operativos modernos proveen mapas de caracteres manejables.

Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset de Gtk2 y de si soporta UTF-8


Notas:

Stado: completo. Hay errores gtk2 intf, pero no tienen nada que ver con UTF-8.

Tareas secundarias

Actualizar el widgetset Windows CE para que utilice UTF-8

Notas: las rutinas de conversión de rutinas están concentrados en el fichero winceproc.pp. Se necesitan algunos tests.


Stado: completado.

Actualiza las funciones de teclado de Gtk 1 para que trabajen con UTF-8


Notas:

Stado: no implementado.


RTL completa en synedit

Notas: RTL significa de derecha a izquierda como por ejemplo el utilizado en árabe.


Stado: no implementado.

Unicode essentials

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

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:

                           UTF-8 UTF-16 UTF-32
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 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 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 single 32-bit unit in UTF-32.

For more, see: Unicode FAQ - Basic questions, Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM, Wikipedia: UTF-8 [2]

Lazarus component library architecture essentials

The LCL consists of two parts:

  1. A target platform independent part, which implements a class hierarchy analogous to Delphi VCL;
  2. "Interfaces" - a part that implements the interface to APIs of each target platform.

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.

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.

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.

For more, see: Internals of the LCL

Unicode-enabling the win32 interface

Compiling LCL-Win32 with Unicode

To enable unicode on LCL for Windows go to the menu "Tools" --> "Configure Build Lazarus"

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: Since Lazarus version 0.9.25 revision 14883 this operation is not 
      needed anymore as Lazarus is unicode enabled by default.

Guidelines

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.

Windows platforms <=Win9x are based on ISO code page 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

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

Conversion example:

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

Becomes:

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

Functions that need Ansi and Wide versions

First Conversion example:

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

Becomes:

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

What should already be working with Unicode:

  • TForm, TButton, TLabel
  • Most controls
  • Menus
  • LCLIntf.ExtTextOut and most other text related winapis
  • TStrings based controls. Examples: TComboBox, TListBox, etc
  • SynEdit shows and can input UTF-8 characters correctly
  • Setting/Getting unicode strings to/from the ClipBoard
  • Setting the Application Title in the project options to (for example) 'Minha Aplicação'.
  • Double clicking words with non-ascii chars in the editor to select them

Problemas conocidos con el soporte UNICODE

  • SynEdit no soporta RTL de derecha a izquierda (Right To Left)
  • ¿Ha sido OpenFileDialogCallBack testeado con un largo número de ficheros?
    • ¿Es esto un problema específico de UNICODE? Yo creo que es un problema genérico. --Sekelsenmat 13:40, 14 February 2008 (CET)
      • Puede ser. Lo he testeado con una gran cantidad de ficheros antes de añadir la versión UNICODE. Si se trata de un problema genérico, entonces la versión no-UNICODE estaría rota cuando la versión unicode fue añadida. Vincent 21:45, 15 February 2008 (CET)
  • class function TWin32WSSelectDirectoryDialog.CreateHandle: Title, FileName and InitialDir should be made Unicode aware.

Posibles problemas con el soporte UNICODE

Basandose en la revisión de código, lo siguiente necesita ser testeado porque el código parece no estar preparado para UNICODE:

  • class procedure TWin32WSCustomComboBox.SetText
  • TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint is not Unicode aware
  • TWin32WidgetSet.MessageBox doesn't call MessageBoxW.
  • TWin32WidgetSet.TextOut: es Windows.TextOut soportado en windows 9X?
  • MessageBox buttons no muestran el UNICODE cuando son traducidos. Testeados en el IDE. Puede haber un problema en el IDE.
    • Nota: no he podido reproducirlo utilizando la traducción portuguesa. --Sekelsenmat 22:20, 12 January 2008 (CET)
  • (lista de problemas no confirmados, si se confirman pueden ser movidos al listado de arriba)

Pantallazos

Lazarus Unicode Test.png

Ver también

  • UTF-8 - Descripción de cadenas UTF-8.