Sign in Join
Wiki copytemporary

LCL Unicode Support/es: what changed

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

OldNewLine
11## Introducción
22
33   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 Lazarus, roadmaps, descripción de conceptos básicos y detalles de implementación.
44
55## Instrucciones para usuarios
66
77   En los widgetsets unicode es importante hacer ver que no todo es Unicode. Las librerías Runtime Free 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 librerías que esperan diferentes codificaciones.
88
99   Usualmente la codificación es por librería. 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).
1010
1111   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.
1212
1313   Ejemplos:
1414
1515   Digamos que tienes una cadena desde un TEdit y necesitas pasársela a alguna rutina de fichero rtl:
1616
1717``` pascal
1818 var
1919 miCadena: string; // codificación utf-8
2020 begin
2121 miCadena := miTEdit.Text;
2222 unaRutinaRTL(UTF8ToAnsi(miCadena));
2323 end;
2424```
2525
2626   Y en el modo inverso:
2727
2828``` pascal
2929 var
3030 miCadena: string; // codificada ansi
3131 begin
3232 miCadena := unaRutinaRTL;
3333 miTEdit.Text := AnsiToUTF8(miCadena);
3434 end;
3535```
3636
3737   **Importante**: UTF8ToAnsi retornará una cadena vacía si la cadena UTF8 contiene caracteres no válidos.
3838
3939   **Importante**: AnsiToUTF8 y UTF8ToAnsi requiere un gestor de cadenas largas bajo Linux, BSD y Mac OS X. Puedes utilizar las funciones SysToUTF8 y UTF8ToSys (unidad FileUtil) o añadir un gestor de cadenas largas mediante cwstring como unidad en la sección *uses*.
4040
4141### Tratamiento de cadenas UTF-8 y de caracteres
4242
4343   Si deseamos actuar sobre los caracteres de una cadena UTF8, básicamente hay dos maneras.
4444
4545- actuar sobre los bytes - útil para buscar una subcadena o cuando se busca sólo en los caracteres ASCII de la cadena UTF8. Por ejemplo, al analizar archivos xml.
4646- actuar a través de los caracteres - útil para componentes gráficos como SynEdit. Por ejemplo cuando se desea conocer el tercer carácter que aparece en la pantalla.
4747
4848#### Buscando una subcadena
4949
5050   Debido a la naturaleza especial de UTF8 podemos simplemente usar las funciones normales de cadena:
5151
5252``` pascal
5353 procedure Where(SearchFor, aText: string);
5454 var
5555 BytePos: LongInt;
5656 CharacterPos: LongInt;
5757 begin
5858 BytePos:=Pos(SearchFor,aText);
5959 CharacterPos:=UTF8Length(PChar(aText),BytePos-1);
6060 writeln('La subcadena "',SearchFor,'" está en el texto "',aText,'"',
6161 ' en la posición de byte ',BytePos,' y en la posición de caracter ',CharacterPos);
6262 end;
6363```
6464
6565#### Accediendo a los caracteres UTF8
6666
6767   Los caracteres Unicode pueden variar en longitud, por lo que la mejor solución para acceder a ellos es usar una iteración cuando queremos acceder a los caracteres en la secuencia en la que se encuentran. Para ello podemos utilizar este código:
6868
6969``` pascal
7070 uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
7171 ...
7272 procedure DoSomethingWithString(AnUTF8String: string);
7373 var
7474 p: PChar;
7575 CharLen: integer;
7676 FirstByte, SecondByte, ThirdByte: Char;
7777 begin
7878 p:=PChar(AnUTF8String);
7979 repeat
8080 CharLen := UTF8CharacterLength(p);
8181
8282 // Ahora tenemos un puntero al caracter y su longitud
8383 // Podemos acceder a los bytes del caracter UTF-8 así:
8484 if CharLen >= 1 then FirstByte := P[0];
8585 if CharLen >= 2 then SecondByte := P[1];
8686 if CharLen >= 3 then ThirdByte := P[2];
8787
8888 inc(p,CharLen);
8989 until (CharLen=0) or (p^ = #0);
9090 end;
9191```
9292
9393#### Acceder al enésimo caracter UTF8
9494
9595   Además de la iteración, podemos querer tambien realizar un acceso aleatorio a los caracteres UTF-8.
9696
9797``` pascal
9898 uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
9999 ...
100100 var
101101 AnUTF8String, NthChar: string;
102102 begin
103103 NthChar := UTF8Copy(AnUTF8String, N, 1);
104104```
105105
106106#### Iterar sobre los puntos de código usando UTF8CharacterToUnicode
107107
108108   A continuación se muestra cómo recorrer en iteración el valor del código de 32 bits de punto para cada carácter de una cadena UTF8:
109109
110110``` pascal
111111 uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
112112 ...
113113 procedure IterateUTF8Characters(const AnUTF8String: string);
114114 var
115115 p: PChar;
116116 unicode: Cardinal;
117117 CharLen: integer;
118118 begin
119119 p:=PChar(AnUTF8String);
120120 repeat
121121 unicode:=UTF8CharacterToUnicode(p,CharLen);
122122 writeln('Unicode=',unicode);
123123 inc(p,CharLen);
124124 until (CharLen=0) or (unicode=0);
125125 end;
126126```
127127
128128#### UTF-8: Copiar cadena, Longitud, Minúsculas, etc
129129
130130   Casi todas las operaciones que podríamos querer ejecutar con cadenas UTF-8 están cubiertas por las rutinas en la unidad de lazutf8 (unidad LCLProc en Lazarus 0.9.30 o inferior). Consulte la siguiente lista de las rutinas de tomada de lazutf8.pas:
131131
132132``` pascal
133133 function UTF8CharacterLength(p: PChar): integer;
134134 function UTF8Length(const s: string): PtrInt;
135135 function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
136136 function UTF8CharacterToUnicode(p: PChar; out CharLen: integer): Cardinal;
137137 function UnicodeToUTF8(u: cardinal; Buf: PChar): integer; inline;
138138 function UnicodeToUTF8SkipErrors(u: cardinal; Buf: PChar): integer;
139139 function UnicodeToUTF8(u: cardinal): shortstring; inline;
140140 function UTF8ToDoubleByteString(const s: string): string;
141141 function UTF8ToDoubleByte(UTF8Str: PChar; Len: PtrInt; DBStr: PByte): PtrInt;
142142 function UTF8FindNearestCharStart(UTF8Str: PChar; Len: integer;
143143 BytePos: integer): integer;
144144 // encontrar el caracter enésimo UTF8, ignorando BIDI
145145 function UTF8CharStart(UTF8Str: PChar; Len, CharIndex: PtrInt): PChar;
146146 // encontrar el ínidice de bytedel enésimo caracter UTF8, ignorando BIDI (longitud en bytes de la subcadena)
147147 function UTF8CharToByteIndex(UTF8Str: PChar; Len, CharIndex: PtrInt): PtrInt;
148148 procedure UTF8FixBroken(P: PChar);
149149 function UTF8CharacterStrictLength(P: PChar): integer;
150150 function UTF8CStringToUTF8String(SourceStart: PChar; SourceLen: PtrInt) : string;
151151 function UTF8Pos(const SearchForText, SearchInText: string): PtrInt;
152152 function UTF8Copy(const s: string; StartCharIndex, CharCount: PtrInt): string;
153153 procedure UTF8Delete(var s: String; StartCharIndex, CharCount: PtrInt);
154154 procedure UTF8Insert(const source: String; var s: string; StartCharIndex: PtrInt);
155155
156156 function UTF8LowerCase(const AInStr: string; ALanguage: string=''): string;
157157 function UTF8UpperCase(const AInStr: string; ALanguage: string=''): string;
158158 function FindInvalidUTF8Character(p: PChar; Count: PtrInt;
159159 StopOnNonASCII: Boolean = false): PtrInt;
160160 function ValidUTF8String(const s: String): String;
161161
162162 procedure AssignUTF8ListToAnsi(UTF8List, AnsiList: TStrings);
163163
164164 //funciones de comparación
165165
166166 function UTF8CompareStr(const S1, S2: string): Integer;
167167 function UTF8CompareText(const S1, S2: string): Integer;
168168```
169169
170170### Tratando con directorios y nombres de fichero
171171
172172   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.
173173
174174   Por ejemplo, consideremos un botón, que establece la propiedad directorio de TFileListBox al valor del directorio actual. La función RTL [[doc:rtl/sysutils/getcurrentdir.html|GetCurrentDir]] es ANSI, y no UNICODE, lo cual precisa de una conversión:
175175
176176``` pascal
177177 procedure TForm1.Boton1Click(Sender: TObject);
178178 begin
179179 FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
180180 // o usar las funciones de la unidad FileUtil
181181 FileListBox1.Directory:=GetCurrentDirUTF8;
182182 end;
183183```
184184
185185   La unidad FileUtil define funciones comunes de fichero con cadenas UTF-8:
186186
187187``` pascal
188188 // funciones básicas similares a la RTL pero trabajando con UTF-8 en lugar de
189189 // codif cación de sistema
190190 // AnsiToUTF8 y UTF8ToAnsi necesitan un gestor de cadenas largas (widestring) bajo Linux, BSD, MacOSX
191191 // pero normalmente estos sistemas operativos utilizan UTF-8 como sistema de codificación
192192 // por lo que el gestor de cadenas largas no es necesario.
193193
194194 function NeedRTLAnsi: boolean;// true si la codificación no es UTF-8
195195 procedure SetNeedRTLAnsi(NewValue: boolean);
196196 function UTF8ToSys(const s: string): string;// como UTF8ToAnsi pero más independiente de widestringmanager
197197 function SysToUTF8(const s: string): string;// como AnsiToUTF8 pero mas independiente de widestringmanager
198198
199199 // operaciones de fichero
200200 function FileExistsUTF8(const Filename: string): boolean;
201201 function FileAgeUTF8(const FileName: string): Longint;
202202 function DirectoryExistsUTF8(const Directory: string): Boolean;
203203 function ExpandFileNameUTF8(const FileName: string): string;
204204 function ExpandUNCFileNameUTF8(const FileName: string): string;
205205 {$IFNDEF VER2_2_0}
206206 function ExtractShortPathNameUTF8(Const FileName : String) : String;
207207 {$ENDIF}
208208 function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
209209 function FindNextUTF8(var Rslt: TSearchRec): Longint;
210210 procedure FindCloseUTF8(var F: TSearchrec);
211211 function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
212212 function FileGetAttrUTF8(const FileName: String): Longint;
213213 function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
214214 function DeleteFileUTF8(const FileName: String): Boolean;
215215 function RenameFileUTF8(const OldName, NewName: String): Boolean;
216216 function FileSearchUTF8(const Name, DirList : String): String;
217217 function FileIsReadOnlyUTF8(const FileName: String): Boolean;
218218 function GetCurrentDirUTF8: String;
219219 function SetCurrentDirUTF8(const NewDir: String): Boolean;
220220 function CreateDirUTF8(const NewDir: String): Boolean;
221221 function RemoveDirUTF8(const Dir: String): Boolean;
222222 function ForceDirectoriesUTF8(const Dir: string): Boolean;
223223
224224 // entorno
225225 function ParamStrUTF8(Param: Integer): string;
226226 function GetEnvironmentStringUTF8(Index : Integer): String;
227227 function GetEnvironmentVariableUTF8(const EnvVar: String): String;
228228 function GetAppConfigDirUTF8(Global: Boolean): string;
229229```
230230
231231   Aunque la mayor parte de los parámetros pasados a estas funciones tienen unos tipos más simples otros tienen más complejidad, por eso añado aquí contenido para no tener que ir a buscar a otro sitio:    Descripción de recursos utilizados:
232232
233233``` pascal
234234 function FindFirstUTF8(
235235 filtro:string; // le indicamos el filtro a utilizar por ejemplo *.*, *.txt, *.exe....
236236 Attr: LongInt; // atributos de búsqueda
237237 out Rslt: TSearchRec // devuelve el resultado buscado con respecto a path y Attr
238238 ):LongInt;
239239
240240 // Para establecer la trayectoria donde empezar a buscar se lo indicamos con SetCurrentDir()
241241 // por ejemplo SetCurrentDir ('c:\').
242242 // Para saber que trayectoria ha establecido realmente se lo podemos preguntar con GetCurrentDir
243243 // claro que podemos recurrir al resultado de la llamada para saber si tuvo exito pero de todos
244244 // modos definimos una variable string directorio_actual:string y luego directorio_actual:=GetCurrentDir();
245245 //
246246 // Para encontrar sucesivas entradas se necesitaría utilizar FindNextUTF8.
247247 // Para liberar los recursos utilizados podemos emplear FindCloseUTF8.
248248
249249 // Attr: LongInt donde los atributos pueden ser los siguientes:
250250 //
251251 // faReadOnly = $00000001;
252252 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fareadonly.html
253253 // const faHidden = $00000002;
254254 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fahidden.html
255255 //
256256 // const faSysFile = $00000004;
257257 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fasysfile.html
258258 // const faVolumeId = $00000008;
259259 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/favolumeid.html
260260 // const faDirectory = $00000010;
261261 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fadirectory.html
262262 // const faArchive = $00000020;
263263 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/faarchive.html
264264 // const faAnyFile = $0000003f;
265265 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/faanyfile.html
266266 // const faSymLink = $00000040;
267267 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fasymlink.html
268268 // Son constantes que se pueden usar tal cual o por su valor numérico (longint)
269269 // como puede ser $00000008 para obtener el volumen de la unidad de disco.
270270 // He recogido la información basándome en FindFirst no FindFirstUTF si me equivoco
271271 // que me corrijan, además para sistemas Linux he encontrado esto:
272272 // It is not recommended to use findfirst/findnext on Linux.
273273 // It is an (ineffective) emulation. Instead try using the linux unit
274274 // and using 'Glob' instead.
275275 //
276276 // I have plans for a better findfirst/findnext algorithm on Linux,
277277 // but I need some time to implement it.
278278 //
279279 //-- Michael Van Canneyt, 18 octubre 2000 15:10
280280 // De eso ya hace tiempo, yo aún no he podido practicar el equivalente en Linux
281281 // pero lo dejo de referencia.
282282```
283283
284284``` pascal
285285 type TSearchRec = record
286286 Time: LongInt; // Timestamp del fichero.
287287 Size: Int64; // Tamaño del fichero.
288288 Attr: LongInt; // Atributos del fichero.
289289 Name: TFilename; // Nombre del fichero (no parte de directorio).
290290 ExcludeAttr: LongInt; // Atributos a excluir de la búsqueda (no usar).
291291 FindHandle: Pointer; // Manejador (handle) interno del sistema operativo(no usar).
292292 Mode: TMode; //Modo de fichero UNIX. Solo usado en sistemas UNIX.
293293 PathOnly: AnsiString; // trayecto de un fichero.
294294 end;
295295 // donde type TFilename = String;
296296 //
297297```
298298
299299   El siguiente es un pequeño ejemplo aplicado a un botón:    Creamos varias etiquetas (TLabel) en las que por medio de su propiedad caption visualizaremos los resultados
300300
301301``` pascal
302302 procedure TForm1.Button1Click(Sender: TObject);
303303 var
304304 lista_ficheros:TSearchRec;
305305 directorio_actual:string;
306306 trayecto: string;
307307 begin
308308 lista_ficheros.name:=' ';
309309 SetCurrentDir('c:\');
310310 label1.caption:='El directorio actual es: '+GetCurrentDir();
311311 if findfirstutf8 ('*.*',faAnyFile,lista_ficheros) = 0 then
312312 begin
313313 if findnextutf8 (lista_ficheros) = 0 then
314314 label2.caption:=lista_ficheros.name;
315315 if findnextutf8 (lista_ficheros) = 0 then;
316316 label3.caption:=lista_ficheros.name;
317317 if findnextutf8 (lista_ficheros) = 0 then;
318318 label4.caption:=lista_ficheros.name;
319319 if findnextutf8 (lista_ficheros) = 0 then;
320320 label5.caption:=lista_ficheros.name;
321321 findcloseutf8 (lista_ficheros);
322322 end
323323 else
324324 label1.caption:='Hay un error en alguna parte';
325325 end;
326326```
327327
328328#### Mac OS X
329329
330330   Las funciones de fichero de la unidad 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:
331331
332332``` pascal
333333 if Filename1=Filename2 then ... // no es suficiente bajo OS X
334334 if AnsiCompareFileName(Filename1,Filename2)=0 then ... // no suficiente bajo fpc 2.2.2, incluso no con cwstring
335335 if CompareFilenames(Filename1,Filename2)=0 then ... // esto siempre funciona (unit FileUtil o FileProcs)
336336```
337337
338338## UTF8 y archivos fuente - el BOM desaparecido
339339
340340   Al crear archivos de código fuente con Lazarus y escribir algunos caracteres no ASCII el archivo se guarda en UTF8. **No utiliza BOM** (marca de orden de byte). Puedes cambiar la codificación en el menú contextual sobre el editor con la opción *Ajustes de archivo... / Codificación*. La razón de la omisión de BOM es cómo trata Ansistrings FPC. Por compatibilidad la LCL usa Ansistrings y para la portabilidad del LCL utiliza UTF8.
341341
342342   Nota:Algunos editores de texto de MS Windows podrían tratar los archivos como con codificación del sistema y mostrar caracteres no válidos. No agregue BOM. Si se agrega BOM tenemos que cambiar todas las asignaciones de cadena.
343343
344344   Por ejemplo:
345345
346346``` pascal
347347 Button1.Caption:='Über';
348348```
349349
350350   Cuando no hay BOM (marca de orden de byte) y no nos pasan un parámetro de página de códigos el compilador trata la cadena como sistema de codificación y copia cada byte no convertido a la cadena. Así es como el LCL espera cadenas de texto.
351351
352352``` pascal
353353 // el archivo fuente está guardado como UTF sin BOM
354354 if FileExists('Über.txt') then ; // erróneo, porque FileExists espera codificación del sistema
355355 if FileExistsUTF8('Über.txt') then ; // correcto
356356```
357357
358358## Widestrings y Ansistrings
359359
360360   al pasar de Ansistrings a Widestrings hay que convertir la codificación.
361361
362362``` pascal
363363 var
364364 w: widestring;
365365 begin
366366 w:='Über'; // erróneo, porque FPC convertirá la codificación del sistema a UTF16
367367 w:=UTF8ToUTF16('Über'); // correcto
368368 Button1.Caption:=UTF16ToUTF8(w);
369369 end;
370370```
371371
372372## Archivos fuente con UTF8 BOM
373373
374374   Si haces una gran cantidad de conversiones widestring en una unidad, el código podría ser más legible guardando el fuente como UTF8 con BOM. El compilador permite una codificación por unidad.
375375
376376### Lenguajes del este asiático en Windows
377377
378378   La fuente por defecto (Tahoma) para los controles de interface de usuario bajo Windows XP son capaces de mostrar correctamente varios lenguajes, incluyendo árabe, ruso y lenguajes del oeste, pero no lenguajes del este tales como chino, japones y coreano.    Se puede escoger la fuente para la interfaz estándar 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](http://newton.uor.edu/Departments&Programs/AsianStudiesDept/Language/asianlanguageinstallation_XP.html)
379379
380380## Guias de implementación
381381
382382### Requerimientos
383383
384384   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.
385385
386386   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.
387387
388388   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]].
389389
390390### Migración a UNICODE
391391
392392   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.
393393
394394   Actualmente tenemos varios grupos de widgetsets, acordes a la codificación:
395395
396396- Interfaces que utilizan codificación ANSI: gtk (1) en sistemas ANSI.
397397
398398- Interfaces que utilizan codificación UTF-8: gtk (1) en sistemas UTF-8: gtk2, qt, fpGUI, carbon, win32, wince, all others.
399399
400400   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.
401401
402402   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.
403403
404404   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.
405405
406406## Recorrido
407407
408408   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.
409409
410410   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.
411411
412412   Las tareas secundarias son deseables, pero no se implementarán a menos que algunos voluntarios se dediquen a ello o se presenten muchas peticiones.
413413
414414### Tareas primarias
415415
416416   **Hacer que Win32 Widgetset soporte UTF-8**
417417
418418   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.
419419
420420   Estado: totalmente implementado.
421421
422422   **Actualiza las funciones de teclado Gtk 2 para que trabajen con UTF-8**
423423
424424   Notas:
425425
426426   Estado: casi completo. Algunas características pre-editadas de gtk2 no están todavía soportadas en los controles customizados. Desconozco que lenguaje necesitan.
427427
428428   **Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset UNICODE de Win32 y que soporte UTF-8**
429429
430430   Notas:
431431
432432   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.
433433
434434   **Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset de Gtk2 y de si soporta UTF-8**
435435
436436   Notas:
437437
438438   Estado: completo. Hay errores gtk2 intf, pero no tienen nada que ver con UTF-8.
439439
440440### Tareas secundarias
441441
442442   **Actualizar el widgetset Windows CE para que utilice UTF-8**
443443
444444   Notas: las rutinas de conversión de rutinas están concentrados en el fichero winceproc.pp. Se necesitan algunos tests.
445445
446446   Estado: completado.
447447
448448   **Actualiza las funciones de teclado de Gtk 1 para que trabajen con UTF-8**
449449
450450   Notas:
451451
452452   Estado: no implementado.
453453
454454   **RTL completa en synedit**
455455
456456   Notas: RTL significa de derecha a izquierda como por ejemplo el utilizado en árabe.
457457
458458   Estado: no implementado.
459459
460460## Esenciales de UNICODE
461461
462462   UNICODE estándar mapea enteros (integers) de 0 a 10FFFF (h) a caracteres. Cada mapeo es llamado un punto de codigo. En otras palabras, los caracteres UNICODE están en principio definidos por puntos de código desde U+000000 a U+10FFFF (0 a 1 114 111). 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 en hexadecimal (0 to 1.114.111 en decimal).
463463
464464   Existen tres esquemas para representar punto de código UNICODE como una única secuencia de bytes. Estos esquemas se llaman formatos de transformación UNICODE: UTF-8, UTF-16 y UTF-32. La conversión entre todos ellos es posible. Aquí están sus propiedades básicas:
465465
466466`                            UTF-8 UTF-16 UTF-32`\
467467` Smallest code point [hex] 000000 000000 000000`\
468468` Largest code point  [hex] 10FFFF 10FFFF 10FFFF`\
469469` Code unit size [bits]          8     16     32`\
470470` Minimal bytes/character        1      2      4`\
471471` Maximal bytes/character        4      4      4`
472472
473473   **UTF-8** tiene varias propiedades importantes y útiles: es interpretado como una secuencia de bytes, por lo que el concepto de orden bajo(lo-) y alto (hi-) no existe. Los caracteres U+0000 a U+007F (ASCII)se codifican simplemente como bytes 00h a 7Fh (por compatibilidad ASCII). Esto significa que los ficheros y cadenas que contienen solamente caracteres de 7-bit ASCII tienen la misma codificación en ambos formatos (ASCII y UTF-8). Todos los caracteres \>U+007F se codifican como una secuencia de varios bytes, cada uno de los cuales tiene los dos bits más significativos puestos a 1 (bits set). No se contiene una secuencia de bytes de un carácter dentro de la larga secuencia de bytes del otro. Esto permite la búsqueda sencilla de subcadenas. El primer byte de una secuencia multibyte que representa un carácter no-ASCII está siempre en el rango C0h a FDh y esto indica cuantos bytes siguen a este carácter. Todos los caracteres que que siguen en una secuencia multibyte se encuentran en el rango 80h a BFh. Esto permite una fácil resincronización y robustez.
474474
475475   **UTF-16** tiene las siguientes propiedades más destacadas. Utiliza una sola palabra (words) de 16-bit para codificar caracteres desde U+0000 a U+d7ff, y un par de palabras de 16 bits para codificar cualquiera de los caracteres UNICODE restantes. remaining Unicode characters.
476476
477477   Finalmente, cualquier caracter UNICODE puede ser representado como una unidad de 32 bits en **UTF-32**.
478478
479479   Para más información, ver:
480480
481481   [Unicode FAQ - Basic questions](http://www.unicode.org/faq/basic_q.html),    [Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM](http://www.unicode.org/faq/utf_bom.html),    [Wikipedia: UTF-8](http://en.wikipedia.org/wiki/UTF-8)    [2](http://en.wikipedia.org/wiki/ISO-8859)
482482
483483## Lo esencial de la arquitectura de la librería de componentes de Lazarus
484484
485485   La LCL consta de dos partes:
486486
4874871. Una parte independiente de la plataforma de destino, que implementa un jerarquía de clases análoga a la VCL de Delphi;
4884882. "Interfaces" - la parte que implementa la interfaz a las APIs de cada plataforma de destino.
489489
490490   La comunicación entre las dos partes se realiza mediante una clase abstracta TWidgetset. Cada widgetset se implementa por su propia clase derivada de TWidgetset.
491491
492492   El widgetset de GTK 1 es el más antiguo. En este widgetset se determina la codificación de los textos mediante la variable de entorno LANG, que suele ser un grupo iso ISO-8859-n de un solo byte de codificación. Recientemente (a partir de Mandriva 2007, por ejemplo), muchas distribuciones han distribuido Gtk 1 configurado para UTF-8. Nuestra interfaz Gtk 1 carece de un apoyo eficaz a UTF-8 en las rutinas de manejo del teclado, así que esto es un gran problema, que aumenta la necesidad de Lazarus de implementar soporte Unicode multiplataforma.
493493
494494   El widgetset de Gtk2 únicamente funciona con codificación UTF-8 y suporta UTF-8 completamente.
495495
496496   La interfaz win32 dispone de widgets ansi y el soporte UTF-8 se ha iniciado, pero no interface is setup with ansi widgets and UTF-8 support is started, pero no esta acabado y está deshabilitado. Por lo tanto, actualmente no es posible utilizar Unicode con win32.
497497
498498   La interfaz Qt está preparada para UTF-8. El propio Qt usa UTF-16 como codificación nativa, pero la interfaz de Lazarus para Qt convierte de UTF-8 a UTF-16.
499499
500500   Windows CE únicamente soporta UTF-16 pra la codificación de caracteres, pero nuestra interfaz para él ahora convierte las cadenas de ISO a UTF-16 antes de llamar a la API de Windows. Esto es muy fácil de solucionar, ya que todo el código de conversión se concentra en unas pocas rutinas en el archivo winceproc.pp.
501501
502502   For more, see: [[LCL Internals|Internals of the LCL]]
503503
504504## Habilitando UNICODE para el interface win32
505505
506506### Compilando LCL-Win32 con UNICODE
507507
508508   Para habilitar UNICODE en LCL para Windows ir al menú, seleccionar entrada "Herramientas" --\> "Configurar "Construir Lazarus..." poner dWindowsUnicodeSupport en el campo "Opciones". Selecccionar todos los objetivos a NONE, y solo LCL a Clean+Build. Selecciona win32 como objetivo wdgetset. Finalmente pulsar en "Construir".
509509
510510   Ahora ya puedes recompilar tus aplicaciones pre-existentes y tendrán el modo UNICODE habilitado.
511511
512512   **Nota**: Desde la versión 0.9.25 revisión 14883 esta operación no es necesaria debido a que Lazarus tiene el soporte UNICODE habilitado por defecto.
513513
514514### Directrices
515515
516516####
517517
518518   Primera, y más importante, todos los parches UNICODE para el interface Win32 deben ser incluidos por **{\$ifdef WindowsUnicodeSupport}**, para evitar romper el interface ANSI existente. Cuándo el código sea estable, todos los *ifdef* se quitaran y solamente permanecerá la parte UNICODE. En este momento todos los programas existentes que usan caracteres ANSI necesitaran migrar a UNICODE.
519519
520520   Las plantaformas windows \<=Win9x se basan en códigos de página de la norma ISO y soportan Unicode de forma parcial. Las plataformas *win* a partir de WinNT y Windows CE suportan completamente UNICODE. Win 9x y NT ofrecen dos conjuntos paralelos de funciones API: el antiguo ANSI demoninado *A y el nuevo, UNICODE demonimado *W. Las funciones *W aceptan *wide strings*, por ejemplo cadenas codificadas UTF-16 como parámetros. Windows CE solamente utiliza funciones Wide API.
521521
522522#### Funciones Wide presentes en Windows 9x
523523
524524   Algunas funciones Wide API están presentes en Windows 9x. La siguiente es una lista de las mismas: <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp>
525525
526526   Ejemplo de conversión:
527527
528528``` pascal
529529 GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
530530 Length(ButtonCaption), TextSize);
531531```
532532
533533   Viene a ser:
534534
535535``` pascal
536536 {$ifdef WindowsUnicodeSupport}
537537 GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
538538 {$else}
539539 GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
540540 {$endif}
541541```
542542
543543#### Funciones que necesitan versiones ANSI y Wide
544544
545545   Primer ejemplo de conversión:
546546
547547``` pascal
548548 function TGDIWindow.GetTitle: String;
549549 var
550550 l: Integer;
551551 begin
552552 l := Windows.GetWindowTextLength(Handle);
553553 SetLength(Result, l);
554554 Windows.GetWindowText(Handle, @Result[1], l);
555555 end;
556556```
557557
558558   Viene a ser así:
559559
560560``` pascal
561561 function TGDIWindow.GetTitle: String;
562562 var
563563 l: Integer;
564564 AnsiBuffer: string;
565565 WideBuffer: WideString;
566566 begin
567567
568568 {$ifdef WindowsUnicodeSupport}
569569
570570 if UnicodeEnabledOS then
571571 begin
572572 l := Windows.GetWindowTextLengthW(Handle);
573573 SetLength(WideBuffer, l);
574574 l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
575575 SetLength(WideBuffer, l);
576576 Result := Utf8Encode(WideBuffer);
577577 end
578578 else
579579 begin
580580 l := Windows.GetWindowTextLength(Handle);
581581 SetLength(AnsiBuffer, l);
582582 l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
583583 SetLength(AnsiBuffer, l);
584584 Result := AnsiToUtf8(AnsiBuffer);
585585 end;
586586
587587{$else}
588588
589589 l := Windows.GetWindowTextLength(Handle);
590590 SetLength(Result, l);
591591 Windows.GetWindowText(Handle, @Result[1], l);
592592
593593{$endif}
594594
595595 end;
596596```
597597
598598### Recorrido
599599
600600#### Que debería estar ya funcionando con UNICODE:
601601
602602- TForm, TButton, TLabel.
603603- La mayor parte de los controles.
604604- Menús.
605605- LCLIntf.ExtTextOut y muchas otras winapis de texto relacionadas.
606606- Controles basados en TStrings. Ejemplos: TComboBox, TListBox, etc
607607- SynEdit muestra y permite la introducción de caracteres UTF-8 correctamente.
608608- Estableciendo(setting)/Obteniendo(Getting) cadenas UNICODE hacia/desde el portapapeles (ClipBoard).
609609- Estableciendo el título de la aplicación en las opciones de proyecto a (por ejemplo) 'Mi aplicación'.
610610- Haciendo doble ckick en palabras con caracteres no-ascii en el editor para seleccionarlos.
611611
612612#### Problemas conocidos con el soporte UNICODE
613613
614614- SynEdit no soporta RTL de derecha a izquierda (Right To Left)
615-- <s> ¿Ha sido OpenFileDialogCallBack testeado con un largo número de ficheros?
615+- ¿Ha sido OpenFileDialogCallBack testeado con un largo número de ficheros?
616616 - ¿Es esto un problema específico de UNICODE? Yo creo que es un problema genérico. --Sekelsenmat 13:40, 14 February 2008 (CET)
617617 - 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)
618- - Elemento asociado del seguidor de errores: <http://bugs.freepascal.org/view.php?id=10918></s> Corregido e implementado.
619-- <s> Función de clase TWin32WSSelectDirectoryDialog.CreateHandle: Title, nombre de fichero(FileName) y directorio inicial (InitialDir) debería ser convertido a UNICODE.
620- - Associated bugtracker item: <http://bugs.freepascal.org/view.php?id=10919></s> Implementado.
618+ - Elemento asociado del seguidor de errores: <http://bugs.freepascal.org/view.php?id=10918> Corregido e implementado.
619+- Función de clase TWin32WSSelectDirectoryDialog.CreateHandle: Title, nombre de fichero(FileName) y directorio inicial (InitialDir) debería ser convertido a UNICODE.
620+ - Associated bugtracker item: <http://bugs.freepascal.org/view.php?id=10919> Implementado.
621621
622622   **Posibles problemas con el soporte UNICODE**
623623
624624   Basandose en la revisión de código, lo siguiente necesita ser testeado porque el código parece no estar preparado para UNICODE:
625625
626-- <s>Procedimiento de clase TWin32WSCustomComboBox.SetText
626+- Procedimiento de clase TWin32WSCustomComboBox.SetText
627627- TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint no soporta UNICODE
628-- TWin32WidgetSet.MessageBox doesn't call MessageBoxW.</s>
628+- TWin32WidgetSet.MessageBox doesn't call MessageBoxW.
629629- TWin32WidgetSet.TextOut: es Windows.TextOut soportado en windows 9X?
630630 - Si, por favor ver [[LCL Unicode Support/es|LCL_Unicode_Support/es#Wide_functions_present_on_Windows_9x]]. Althought I never tested this.
631631- MessageBox buttons no muestran el UNICODE cuando son traducidos. Testeados en el IDE. Puede haber un problema en el IDE.
632632 - Nota: no he podido reproducirlo utilizando la traducción portuguesa. --Sekelsenmat 22:20, 12 January 2008 (CET)
633633- (lista de problemas no confirmados, si se confirman pueden ser movidos al listado de arriba)
634634
635635### Pantallazos
636636
637637   ![](/wiki-images/4/49/Lazarus_Unicode_Test.png)
638638
639639## Ver también
640640
641641- [[UTF-8]] - Descripción de cadenas UTF-8.
642642
643643---
644644*From the Free Pascal wiki, page "LCL Unicode Support/es", as it stood on 2020-02-19 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
645645Category: Castellano
646646Category: Español
647647Category: LCL/es

Back to history