Sign in Join
Wiki copytemporary

Translations / i18n / localizations for programs/es: what changed

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

Title: Translations / i18n / localizations for programs/es

OldNewLine
1+## Descripción general sobre internacionalización (i18n)
2+
3+   ¿Cómo hacer que los programas utilicen diferentes cadenas para diversos idiomas cómo inglés, chino, alemán, castellano, etc.?
4+
5+Básicamente funciona de la siguiente forma: Añade un recurso de cadena (*resourcestring*) para cada literal, compila para obtener los archivos .rst y/o .po (el IDE lo hace automáticamente), crea un archivo .po para cada idioma (existen herramientas gráficas para ello) y utiliza las funciones de la unidad *translations* de la LCL para cargar el archivo adecuado al iniciar el programa.
6+
7+## Formatos de fechas, horas y números
8+
9+   En Linux, BSD, Mac OS X hay diversas definiciones regionales/locales para definir el formato de la hora y de la fecha o del separador decimal. Para inicializar la RTL necesitas icluir la unidad *clocale* en la sección *use* de tu programa (archivo lpr).
10+
11+## Recursos de Cadena (*Resourcestrings*)
12+
13+   Por ejemplo
14+
15+``` pascal
16+ resourcestring
17+ Etiqueta1 = 'Algo de texto';
18+ HolaMundo1 = 'Hola Mundo';
19+```
20+
21+   Son como constantes de cadena normales, lo que significa que se pueden asignar a cualquier cadena. Por ejemplo
22+
23+``` pascal
24+ Label1.Caption := HolaMundo1;
25+```
26+
27+   Al compilar se creará un archivo .rst por cada unidad, con el mismo nombre que esta, conteniendo los resursos de cadena (nombre y contenido)
28+
29+## Archivos .po
30+
31+   Hay muchas herramientas gráficas libres (kbabel y poedit, por ejemplo) para editar archivos .po, que son texto simple al igual que los archivos .rst, pero con algunas opciones más, como una cabecera que proporciona los campos autor, la codificación, el idioma y fecha. Toda instalación de fpc proporciona la herramienta **rstconv** (windows: rstconv.exe). Esta herramienta se utiliza para convertir archivos .rst en archivos .po. El IDE realizar esta operación automáticamente.
32+
33+   Ejemplo de uso de rstconv:
34+
35+` rstconv -i unidad1.rst -o unidad1.po`
36+
37+## Traduciendo
38+
39+   Por cada idioma el archivo .po debe ser copiado y traducido. La unidad *translation* utiliza los códigos de idiomas habituales (en=inglés, de=alemán, es=español, it=italiano, ...) para buscarlos. Por ejemplo la traducción al alemán de unidad1.po se llamará unidad1.de.po. Por tanto hay que copiar el archivo unidad1.po en un archivo de nombre unidad1.it.po y así para todos los idiomas que queramos soportar y los traductores editaran el archivo .po específico de cada idioma.
40+
41+   **Nota para brasileños y portugueses:** El IDE y la LCL de Lazarus únicamente tienen traducciones de portugués de Brasil y estos archivos se denominan con la extensión 'pb.po' y no 'pt.po'.
42+
43+## Opciones del IDE para la actualización automática de archivos .po
44+
45+- La unidad que contiene los recursos de cadena debe añadirse al paquete o proyecto.
46+- Hay que proporcionar la ruta a .po si los archivos están en un directorio separado. Por ejemplo crear un directorio *idiomas* en el paquete/proyecto. Para proyectos ir a Proyecto \> Opciones de proyecto, pestaña ***i18n***. Para paquetes ir a Opciones \> Integración en IDE.
47+
48+   Cuando esta opción está activada, el IDE genera o actualiza el archivo .po base con la información contenida en los archivos .rst y .lrt (en cuyo caso la herramienta rstconv no será necesaria). El proceso de actualización se inicia mediante la recopilación de todas las entradas existentes en el .po base y en los archivos .rst y .lrt y luego aplica las siguientes características que encuentra y pone al día todo archivo .xx.po traducido.
49+
50+### Eliminación de entradas obsoletas
51+
52+   Las entradas en el archivo .po base no se encontradas en el archivo en los archivos .rst y .lrt se eliminan, posteriormente, todas las entradas que se encuentran en archivos .xx.po traducidos que no se encuentran en el archivo .po base también se quitan. De esta manera los archivos .po se limpian de las entradas obsoletas y los traductores no tienen que traducir las entradas que no se utilizan.
53+
54+### Entradas duplicadas
55+
56+   Las entradas duplicadas se producen cuando por alguna razón se utiliza el mismo texto para cadenas de recursos diferentes, un ejemplo, de esto es el archivo de Lazarus/ide/lazarusidestrconst.pas para la cadena 'Gutter';
57+
58+``` pascal
59+ dlfMouseSimpleGutterSect = 'Gutter';
60+ dlgMouseOptNodeGutter = 'Gutter';
61+ dlgGutter = 'Gutter';
62+ dlgAddHiAttrGroupGutter = 'Gutter';
63+```
64+
65+   El archivo .rst de recursos para estas cadenas A converted .rst file for this resource strings tendría un aspecto similar a este en un archivo .po:
66+
67+` #: lazarusidestrconsts.dlfmousesimpleguttersect`\
68+` msgid "Gutter"`\
69+` msgstr ""`\
70+` #: lazarusidestrconsts.dlgaddhiattrgroupgutter`\
71+` msgid "Gutter"`\
72+` msgstr ""`\
73+` etc.`
74+
75+   Donde las líneas que comienzan con "#:" se consideran como los comentarios y las herramientas empleadas para traducir estas entradas verán las repetidas líneas *msgstr "Gutter"* como entradas duplicadas y provocarán errores o advertencias al cargar o guardar. Las entradas duplicadas se consideran eventualmente normales en archivos .po y necesitan tener algo de contexto asociado. La palabra clave *msgctxt* se utiliza para agregar contexto a las entradas duplicadas y la herramienta de actualización automática de utiliza la entrada ID (el texto que sigue a "#:" ) como el contexto, para el ejemplo anterior se produciría algo así como esto:
76+
77+` #: lazarusidestrconsts.dlfmousesimpleguttersect`\
78+` msgctxt "lazarusidestrconsts.dlfmousesimpleguttersect"`\
79+` msgid "Gutter"`\
80+` msgstr ""`\
81+` #: lazarusidestrconsts.dlgaddhiattrgroupgutter`\
82+` msgctxt "lazarusidestrconsts.dlgaddhiattrgroupgutter"`\
83+` msgid "Gutter"`\
84+` msgstr ""`\
85+` etc.`
86+
87+   En los archivos traducidos .xx.po la herramienta automática hace una comprobación adicional: si la entrada duplicada fue traducida ya, la nueva entrada toma la traducción previa, por lo que aparece como traducida automáticamente.
88+
89+   La detección automática de duplicados aún no es perfecta, la detección se realiza cuando se agregan elementos a la lista y puede suceder que algunas entradas sin traducir se lean primero. Por lo tanto, puede llevar varias pasadas obtener todos los duplicados traducidos automáticamente por la herramienta.
90+
91+### Entradas confusas
92+
93+   Los cambios en las cadenas de recursos afectan a las traducciones, por ejemplo cuando en un principio una cadena de recursos se define así:
94+
95+``` pascal
96+ dlgEdColor = 'Syntax highlight';
97+```
98+
99+   esto produce una entrada en .po similar a esta
100+
101+` #: lazarusidestrconsts.dlgedcolor`\
102+` msgid "Syntax higlight"`\
103+` msgstr ""`
104+
105+   que si se han traducido al idioma castellano (este ejemplo está tomado de la historia de lazarus), puede resultar en
106+
107+` #: lazarusidestrconsts.dlgedcolor`\
108+` msgid "Syntax higlight"`\
109+` msgstr "Color"`
110+
111+   Supongamos ahora que, en un momento posterior, la cadena de recursos se ha cambiado a
112+
113+``` pascal
114+ dlgEdColor = 'Colors';
115+```
116+
117+   la entrada en .po puede ser
118+
119+` #: lazarusidestrconsts.dlgedcolor`\
120+` msgid "Colors"`\
121+` msgstr ""`
122+
123+   Tenga en cuenta que, si bien el ID sigue siendo igualmente *lazarusidestrconsts.dlgedcolor* la cadena ha cambiado de "Syntax highlight' a 'Colors', dado que la cadena se tradujo ya la traducción previa no puede coincidir con el nuevo significado. De hecho, para la nueva cadena probablemente 'Colores' puede ser una traducción mejor.    La herramienta de actualización automática avisa de esta situación y produce una entrada como esta:
124+
125+` #: lazarusidestrconsts.dlgedcolor`\
126+` #, fuzzy`\
127+` #| msgid "Syntax highlight"`\
128+` msgctxt "lazarusidestrconsts.dlgedcolor"`\
129+` msgid "Colors"`\
130+` msgstr "Color"`
131+
132+   En los términos del formato de los archivos .po, el prefijo "#," significa que la entrada tiene una marca (confusa) y los programas traductores puede presentar al usuario un igu especial para este elemento, en este caso la marca significa que la traducción en su estado actual es dudosa y hay que revisarla con más cuidado por el traductor. El prefijo "#\|" indica cuál fue la cadena anterior no traducida de esta entrada y le da al traductor una idea del porqué la entrada estaba marcada como confusa.
133+
134+## Traduciendo Formularios, Módulos de Datos y Marcos
135+
136+   Cuando la opción i18n está activada para el proyecto/paquete el IDE crea automáticamente un archivo .lrt para cada formulario, cada vez que se guarda la unidad. Por tanto cuándo se habilita la opción por primera vez hay que abrir, modificar y guardar los formularios. Por ejemplo, si guardas un formulario *unidad1 .pas* el IDE crea el archivo *unidad1.lrt*. Y al compilar el IDE reúne todas las cadenas de todos los archivos .lrt y .rst en un único archivo .po, con el nombre del proyecto o del paquete, en el directorio i18n.
137+
138+   Para los formularios que se traducen en tiempo de ejecución, hay que asignar un valor que apunte al achivo de traducción a *LRSTranslator* (definido en *LResources*) en la sección de inicio de una de las unidades del proyecto.
139+
140+``` pascal
141+ ...
142+ uses
143+ ...
144+ LResources;
145+ ...
146+ ...
147+ initialization
148+ LRSTranslator:=TPoTranslator.Create('/ruta/al/archivo/po');
149+```
150+
151+   La clase que traduce utilizando archivos *.po*, TPoTranslator, no está disponible en la LCL. Esta es una posible implementación (tomada en parte de *DefaultTranslator.pas* de la LCL) El código siguiente ya no se necesita si utilizas Lazarus 0.9.29. Sólo tienes que incluir *DefaultTranslator* en la cláusula *uses*:
152+
153+``` pascal
154+ unit PoTranslator;
155+
156+ {$mode objfpc}{$H+}
157+
158+ interface
159+
160+ uses
161+ Classes, SysUtils, LResources, typinfo, Translations;
162+
163+ type
164+
165+ { TPoTranslator }
166+
167+ TPoTranslator=class(TAbstractTranslator)
168+ private
169+ FPOFile:TPOFile;
170+ public
171+ constructor Create(POFileName:string);
172+ destructor Destroy;override;
173+ procedure TranslateStringProperty(Sender:TObject;
174+ const Instance: TPersistent; PropInfo: PPropInfo; var Content:string);override;
175+ end;
176+
177+ implementation
178+
179+ { TPoTranslator }
180+
181+ constructor TPoTranslator.Create(POFileName: string);
182+ begin
183+ inherited Create;
184+ FPOFile:=TPOFile.Create(POFileName);
185+ end;
186+
187+ destructor TPoTranslator.Destroy;
188+ begin
189+ FPOFile.Free;
190+ inherited Destroy;
191+ end;
192+
193+ procedure TPoTranslator.TranslateStringProperty(Sender: TObject;
194+ const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
195+ var
196+ s: String;
197+ begin
198+ if not Assigned(FPOFile) then exit;
199+ if not Assigned(PropInfo) then exit;
200+ {DO we really need this?}
201+ if Instance is TComponent then
202+ if csDesigning in (Instance as TComponent).ComponentState then exit;
203+ {End DO :)}
204+ if (AnsiUpperCase(PropInfo^.PropType^.Name)<>'TTRANSLATESTRING') then exit;
205+ s:=FPOFile.Translate(Content, Content);
206+ if s<>'' then Content:=s;
207+ end;
208+
209+ end.
210+```
211+
212+   Alternativamente se puede transformar el archivo *.po* en una *.mo*, utilizando msgfmt, y utilizar la unidad *DefaultTranslator*.
213+
214+``` pascal
215+ ...
216+ uses
217+ ...
218+ DefaultTranslator;
219+```
220+
221+   con ello se buscará automáticamente en varios lugares preestablecidos un archivo *.po* (primero) o un archivo *.mo*; la desventaja es que hay que mantener ambos, los archivos *.mo* para la unidad *DefaultTranslator* y los archivos *.po* para *TranslateUnitResourceStrings*.
222+
223+   Si utilizamos *DefaultTranslator*, se intentará detectar automáticamente la lengua basandose en la variable de entorno *LANG* (modificable utilizando la opción *--lang* de la línea de órdenes), buscando entonces en esos lugares la traducción (*LANG* representa la lengua deseada):
224+
225+- \<Application Directory\>/LANG/\<Application Filename\>.po (o .mo)
226+- \<Application Directory\>/languages/LANG/\<Application Filename\>.po (o .mo)
227+- \<Application Directory\>/locale/LANG/\<Application Filename\>.po (o .mo)
228+- \<Application Directory\>/locale/LC_MESSAGES/LANG/\<Application Filename\>.po (o .mo)
229+
230+   en sistemas *NIX esto se verá así
231+
232+`* /usr/share/locale/LANG/LC_MESSAGES/<Application Filename>.po (o .mo)`
233+
234+   también usará la parte corta del idioma (e.g. si es " es_ES" o " es_ES.UTF-8" y este no existe también probará con "es")
235+
236+## Seleccionando el idioma en el arranque el programa
237+
238+   Para cada fichero .po hay que hacer una llamada a la función *TranslateUnitResourceStrings* de la unidad de *translations* de la LCL. Por ejemplo:
239+
240+``` pascal
241+ {lo pimero: añadir las unidades "gettext" y "translations" en la cláusula uses}
242+ procedure TForm1.FormCreate(Sender: TObject);
243+ var
244+ PODirectorio, Idioma, IdiomaDevuelto: String;
245+ begin
246+ PODirectorio := '/ruta/a/lazarus/lcl/languages/';
247+ GetLanguageIDs(Idioma, IdiomaDevuelto); // en la unidad "gettext"
248+ TranslateUnitResourceStrings('LCLStrConsts', PODirectorio + 'lclstrconsts.%s.po', Idioma, IdiomaDevuelto);
249+ MessageDlg('Título', 'Texto', mtInformation, [mbOk, mbCancel, mbYes], 0);
250+ end;
251+```
252+
253+## Compilando los archivos .po dentro del ejecutable
254+
255+   Si no deseas instalar los archivos .po, sino poner todos los archivos de la aplicación en el ejecutable, utiliza lo siguiente:
256+
257+- Crea una nueva unidad (¡no un formulario!).
258+- Convierte los archivos .po en .lrs utilizando la utilidad lazres del directorio tools:
259+
260+ ./lazres unidad1.lrs unidad1.es.po
261+
262+   Esto creará un archivo de inclusión unit1.lrs que empieza así
263+
264+``` pascal
265+ LazarusResources.Add('unidad1.es','PO',[...
266+```
267+
268+- Añade este código:
269+
270+``` pascal
271+ uses LResources, Translations;
272+
273+ resourcestring
274+ MyCaption = 'Caption';
275+
276+ function TranslateUnitResourceStrings: boolean;
277+ var
278+ r: TLResource;
279+ POFile: TPOFile;
280+ begin
281+ r:=LazarusResources.Find('unidad1.es','PO');
282+ POFile:=TPOFile.Create;
283+ try
284+ POFile.ReadPOText(r.Value);
285+ Result:=Translations.TranslateUnitResourceStrings('unidad1',POFile);
286+ finally
287+ POFile.Free;
288+ end;
289+ end;
290+
291+ initialization
292+ {$I unidad1.lrs}
293+```
294+
295+- Llama a *TranslateUnitResourceStrings* al comienzo del programa. Puedes hacerlo en la sección de inicialización si quieres.
296+
297+## Método multiplataforma para determinar el idioma del sistema
298+
299+   Esta función suministra una cadena que representa el idioma de sistema de usuario. Funciona en Linux, Mac OS X y Windows.
300+
301+``` pascal
302+ uses
303+ Classes, SysUtils {añade aquí las unidades adionales que necesite tu código}
304+ {$IFDEF win32}
305+ , Windows
306+ {$ELSE}
307+ , Unix
308+ {$IFDEF LCLCarbon}
309+ , MacOSAll
310+ {$ENDIF}
311+ {$ENDIF}
312+ ;
313+```
314+
315+``` pascal
316+ function GetOSLanguage: string;
317+ {Método multiplataforma para determinar el idioma del sistema}
318+ var
319+ l, fbl: string;
320+ {$IFDEF LCLCarbon}
321+ theLocaleRef: CFLocaleRef;
322+ locale: CFStringRef;
323+ buffer: StringPtr;
324+ bufferSize: CFIndex;
325+ encoding: CFStringEncoding;
326+ success: boolean;
327+ {$ENDIF}
328+ begin
329+ {$IFDEF LCLCarbon}
330+ theLocaleRef := CFLocaleCopyCurrent;
331+ locale := CFLocaleGetIdentifier(theLocaleRef);
332+ encoding := 0;
333+ bufferSize := 256;
334+ buffer := new(StringPtr);
335+ success := CFStringGetPascalString(locale, buffer, bufferSize, encoding);
336+ if success then
337+ l := string(buffer^)
338+ else
339+ l := '';
340+ fbl := Copy(l, 1, 2);
341+ dispose(buffer);
342+ {$ELSE}
343+ {$IFDEF LINUX}
344+ fbl := Copy(GetEnvironmentVariable('LC_CTYPE'), 1, 2);
345+ {$ELSE}
346+ GetLanguageIDs(l, fbl);
347+ {$ENDIF}
348+ {$ENDIF}
349+ Result := fbl;
350+ end;
351+```
352+
353+## Traducciendo el IDE
354+
355+### Archivos
356+
357+   Los archivos .po del IDE están en el directorio de fuentes de Lazarus:
358+
359+- lazarus/languages cadenas para el IDE
360+- lcl/languages/ cadenas para la LCL
361+- ideintf/languages/ cadenas para el interfaz del IDE
362+
363+### Traductoress
364+
365+- La traducción al alemán está a cargo de Joerg Braun.
366+- La traducción al finés está a cargo de Seppo Suurtarla
367+- La traducción al ruso está a cargo de Maxim Ganetsky
368+
369+   Si deseas iniciar una nueva traducción, pregunta en el correo si alguien ya está trabajando en ello.
370+
371+   Por favor, lee atentamente: [[Lazarus Documentation|Traducciones]]
372+
373+## Ver también
374+
375+[[IDE Development|IDE Development: Translations, i18n, lrt, po files]]
376+
377+[[Getting translation strings right]]
378+
379+[[Lazarus Documentation|Translations]]
380+
381+---
382+*From the Free Pascal wiki, page "Translations / i18n / localizations for programs/es", as it stood on 2020-03-01 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).*
383+Category: Castellano
384+Category: Español

Back to history