Sign in Join
Wiki copytemporary

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

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

OldNewLine
11## Descripción general sobre internacionalización (i18n)
22
33   ¿Cómo hacer que los programas utilicen diferentes cadenas para diversos idiomas cómo inglés, chino, alemán, castellano, etc.?
44
55Bá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.
66
77## Formatos de fechas, horas y números
88
99   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).
1010
1111## Recursos de Cadena (*Resourcestrings*)
1212
1313   Por ejemplo
1414
1515``` pascal
1616 resourcestring
1717 Etiqueta1 = 'Algo de texto';
1818 HolaMundo1 = 'Hola Mundo';
1919```
2020
2121   Son como constantes de cadena normales, lo que significa que se pueden asignar a cualquier cadena. Por ejemplo
2222
2323``` pascal
2424 Label1.Caption := HolaMundo1;
2525```
2626
2727   Al compilar se creará un archivo .rst por cada unidad, con el mismo nombre que esta, conteniendo los resursos de cadena (nombre y contenido)
2828
2929## Archivos .po
3030
3131   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.
3232
3333   Ejemplo de uso de rstconv:
3434
35-` rstconv -i unidad1.rst -o unidad1.po`
35+```text
36+ rstconv -i unidad1.rst -o unidad1.po
37+```
3638
3739## Traduciendo
3840
3941   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.
4042
4143   **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'.
4244
4345## Opciones del IDE para la actualización automática de archivos .po
4446
4547- La unidad que contiene los recursos de cadena debe añadirse al paquete o proyecto.
4648- 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.
4749
4850   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.
4951
5052### Eliminación de entradas obsoletas
5153
5254   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.
5355
5456### Entradas duplicadas
5557
5658   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';
5759
5860``` pascal
5961 dlfMouseSimpleGutterSect = 'Gutter';
6062 dlgMouseOptNodeGutter = 'Gutter';
6163 dlgGutter = 'Gutter';
6264 dlgAddHiAttrGroupGutter = 'Gutter';
6365```
6466
6567   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:
6668
67-` #: lazarusidestrconsts.dlfmousesimpleguttersect`\
68-` msgid "Gutter"`\
69-` msgstr ""`\
70-` #: lazarusidestrconsts.dlgaddhiattrgroupgutter`\
71-` msgid "Gutter"`\
72-` msgstr ""`\
73-` etc.`
69+```text
70+ #: lazarusidestrconsts.dlfmousesimpleguttersect
71+ msgid "Gutter"
72+ msgstr ""
73+ #: lazarusidestrconsts.dlgaddhiattrgroupgutter
74+ msgid "Gutter"
75+ msgstr ""
76+ etc.
77+```
7478
7579   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:
7680
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.`
81+```text
82+ #: lazarusidestrconsts.dlfmousesimpleguttersect
83+ msgctxt "lazarusidestrconsts.dlfmousesimpleguttersect"
84+ msgid "Gutter"
85+ msgstr ""
86+ #: lazarusidestrconsts.dlgaddhiattrgroupgutter
87+ msgctxt "lazarusidestrconsts.dlgaddhiattrgroupgutter"
88+ msgid "Gutter"
89+ msgstr ""
90+ etc.
91+```
8692
8793   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.
8894
8995   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.
9096
9197### Entradas confusas
9298
9399   Los cambios en las cadenas de recursos afectan a las traducciones, por ejemplo cuando en un principio una cadena de recursos se define así:
94100
95101``` pascal
96102 dlgEdColor = 'Syntax highlight';
97103```
98104
99105   esto produce una entrada en .po similar a esta
100106
101-` #: lazarusidestrconsts.dlgedcolor`\
102-` msgid "Syntax higlight"`\
103-` msgstr ""`
107+```text
108+ #: lazarusidestrconsts.dlgedcolor
109+ msgid "Syntax higlight"
110+ msgstr ""
111+```
104112
105113   que si se han traducido al idioma castellano (este ejemplo está tomado de la historia de lazarus), puede resultar en
106114
107-` #: lazarusidestrconsts.dlgedcolor`\
108-` msgid "Syntax higlight"`\
109-` msgstr "Color"`
115+```text
116+ #: lazarusidestrconsts.dlgedcolor
117+ msgid "Syntax higlight"
118+ msgstr "Color"
119+```
110120
111121   Supongamos ahora que, en un momento posterior, la cadena de recursos se ha cambiado a
112122
113123``` pascal
114124 dlgEdColor = 'Colors';
115125```
116126
117127   la entrada en .po puede ser
118128
119-` #: lazarusidestrconsts.dlgedcolor`\
120-` msgid "Colors"`\
121-` msgstr ""`
129+```text
130+ #: lazarusidestrconsts.dlgedcolor
131+ msgid "Colors"
132+ msgstr ""
133+```
122134
123135   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:
124136
125-` #: lazarusidestrconsts.dlgedcolor`\
126-` #, fuzzy`\
127-` #| msgid "Syntax highlight"`\
128-` msgctxt "lazarusidestrconsts.dlgedcolor"`\
129-` msgid "Colors"`\
130-` msgstr "Color"`
137+```text
138+ #: lazarusidestrconsts.dlgedcolor
139+ #, fuzzy
140+ #| msgid "Syntax highlight"
141+ msgctxt "lazarusidestrconsts.dlgedcolor"
142+ msgid "Colors"
143+ msgstr "Color"
144+```
131145
132146   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.
133147
134148## Traduciendo Formularios, Módulos de Datos y Marcos
135149
136150   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.
137151
138152   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.
139153
140154``` pascal
141155 ...
142156 uses
143157 ...
144158 LResources;
145159 ...
146160 ...
147161 initialization
148162 LRSTranslator:=TPoTranslator.Create('/ruta/al/archivo/po');
149163```
150164
151165   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*:
152166
153167``` pascal
154168 unit PoTranslator;
155169
156170 {$mode objfpc}{$H+}
157171
158172 interface
159173
160174 uses
161175 Classes, SysUtils, LResources, typinfo, Translations;
162176
163177 type
164178
165179 { TPoTranslator }
166180
167181 TPoTranslator=class(TAbstractTranslator)
168182 private
169183 FPOFile:TPOFile;
170184 public
171185 constructor Create(POFileName:string);
172186 destructor Destroy;override;
173187 procedure TranslateStringProperty(Sender:TObject;
174188 const Instance: TPersistent; PropInfo: PPropInfo; var Content:string);override;
175189 end;
176190
177191 implementation
178192
179193 { TPoTranslator }
180194
181195 constructor TPoTranslator.Create(POFileName: string);
182196 begin
183197 inherited Create;
184198 FPOFile:=TPOFile.Create(POFileName);
185199 end;
186200
187201 destructor TPoTranslator.Destroy;
188202 begin
189203 FPOFile.Free;
190204 inherited Destroy;
191205 end;
192206
193207 procedure TPoTranslator.TranslateStringProperty(Sender: TObject;
194208 const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
195209 var
196210 s: String;
197211 begin
198212 if not Assigned(FPOFile) then exit;
199213 if not Assigned(PropInfo) then exit;
200214 {DO we really need this?}
201215 if Instance is TComponent then
202216 if csDesigning in (Instance as TComponent).ComponentState then exit;
203217 {End DO :)}
204218 if (AnsiUpperCase(PropInfo^.PropType^.Name)<>'TTRANSLATESTRING') then exit;
205219 s:=FPOFile.Translate(Content, Content);
206220 if s<>'' then Content:=s;
207221 end;
208222
209223 end.
210224```
211225
212226   Alternativamente se puede transformar el archivo *.po* en una *.mo*, utilizando msgfmt, y utilizar la unidad *DefaultTranslator*.
213227
214228``` pascal
215229 ...
216230 uses
217231 ...
218232 DefaultTranslator;
219233```
220234
221235   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*.
222236
223237   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):
224238
225239- \<Application Directory\>/LANG/\<Application Filename\>.po (o .mo)
226240- \<Application Directory\>/languages/LANG/\<Application Filename\>.po (o .mo)
227241- \<Application Directory\>/locale/LANG/\<Application Filename\>.po (o .mo)
228242- \<Application Directory\>/locale/LC_MESSAGES/LANG/\<Application Filename\>.po (o .mo)
229243
230244   en sistemas *NIX esto se verá así
231245
232-`* /usr/share/locale/LANG/LC_MESSAGES/<Application Filename>.po (o .mo)`
246+```text
247+* /usr/share/locale/LANG/LC_MESSAGES/<Application Filename>.po (o .mo)
248+```
233249
234250   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")
235251
236252## Seleccionando el idioma en el arranque el programa
237253
238254   Para cada fichero .po hay que hacer una llamada a la función *TranslateUnitResourceStrings* de la unidad de *translations* de la LCL. Por ejemplo:
239255
240256``` pascal
241257 {lo pimero: añadir las unidades "gettext" y "translations" en la cláusula uses}
242258 procedure TForm1.FormCreate(Sender: TObject);
243259 var
244260 PODirectorio, Idioma, IdiomaDevuelto: String;
245261 begin
246262 PODirectorio := '/ruta/a/lazarus/lcl/languages/';
247263 GetLanguageIDs(Idioma, IdiomaDevuelto); // en la unidad "gettext"
248264 TranslateUnitResourceStrings('LCLStrConsts', PODirectorio + 'lclstrconsts.%s.po', Idioma, IdiomaDevuelto);
249265 MessageDlg('Título', 'Texto', mtInformation, [mbOk, mbCancel, mbYes], 0);
250266 end;
251267```
252268
253269## Compilando los archivos .po dentro del ejecutable
254270
255271   Si no deseas instalar los archivos .po, sino poner todos los archivos de la aplicación en el ejecutable, utiliza lo siguiente:
256272
257273- Crea una nueva unidad (¡no un formulario!).
258274- Convierte los archivos .po en .lrs utilizando la utilidad lazres del directorio tools:
259275
260276 ./lazres unidad1.lrs unidad1.es.po
261277
262278   Esto creará un archivo de inclusión unit1.lrs que empieza así
263279
264280``` pascal
265281 LazarusResources.Add('unidad1.es','PO',[...
266282```
267283
268284- Añade este código:
269285
270286``` pascal
271287 uses LResources, Translations;
272288
273289 resourcestring
274290 MyCaption = 'Caption';
275291
276292 function TranslateUnitResourceStrings: boolean;
277293 var
278294 r: TLResource;
279295 POFile: TPOFile;
280296 begin
281297 r:=LazarusResources.Find('unidad1.es','PO');
282298 POFile:=TPOFile.Create;
283299 try
284300 POFile.ReadPOText(r.Value);
285301 Result:=Translations.TranslateUnitResourceStrings('unidad1',POFile);
286302 finally
287303 POFile.Free;
288304 end;
289305 end;
290306
291307 initialization
292308 {$I unidad1.lrs}
293309```
294310
295311- Llama a *TranslateUnitResourceStrings* al comienzo del programa. Puedes hacerlo en la sección de inicialización si quieres.
296312
297313## Método multiplataforma para determinar el idioma del sistema
298314
299315   Esta función suministra una cadena que representa el idioma de sistema de usuario. Funciona en Linux, Mac OS X y Windows.
300316
301317``` pascal
302318 uses
303319 Classes, SysUtils {añade aquí las unidades adionales que necesite tu código}
304320 {$IFDEF win32}
305321 , Windows
306322 {$ELSE}
307323 , Unix
308324 {$IFDEF LCLCarbon}
309325 , MacOSAll
310326 {$ENDIF}
311327 {$ENDIF}
312328 ;
313329```
314330
315331``` pascal
316332 function GetOSLanguage: string;
317333 {Método multiplataforma para determinar el idioma del sistema}
318334 var
319335 l, fbl: string;
320336 {$IFDEF LCLCarbon}
321337 theLocaleRef: CFLocaleRef;
322338 locale: CFStringRef;
323339 buffer: StringPtr;
324340 bufferSize: CFIndex;
325341 encoding: CFStringEncoding;
326342 success: boolean;
327343 {$ENDIF}
328344 begin
329345 {$IFDEF LCLCarbon}
330346 theLocaleRef := CFLocaleCopyCurrent;
331347 locale := CFLocaleGetIdentifier(theLocaleRef);
332348 encoding := 0;
333349 bufferSize := 256;
334350 buffer := new(StringPtr);
335351 success := CFStringGetPascalString(locale, buffer, bufferSize, encoding);
336352 if success then
337353 l := string(buffer^)
338354 else
339355 l := '';
340356 fbl := Copy(l, 1, 2);
341357 dispose(buffer);
342358 {$ELSE}
343359 {$IFDEF LINUX}
344360 fbl := Copy(GetEnvironmentVariable('LC_CTYPE'), 1, 2);
345361 {$ELSE}
346362 GetLanguageIDs(l, fbl);
347363 {$ENDIF}
348364 {$ENDIF}
349365 Result := fbl;
350366 end;
351367```
352368
353369## Traducciendo el IDE
354370
355371### Archivos
356372
357373   Los archivos .po del IDE están en el directorio de fuentes de Lazarus:
358374
359375- lazarus/languages cadenas para el IDE
360376- lcl/languages/ cadenas para la LCL
361377- ideintf/languages/ cadenas para el interfaz del IDE
362378
363379### Traductoress
364380
365381- La traducción al alemán está a cargo de Joerg Braun.
366382- La traducción al finés está a cargo de Seppo Suurtarla
367383- La traducción al ruso está a cargo de Maxim Ganetsky
368384
369385   Si deseas iniciar una nueva traducción, pregunta en el correo si alguien ya está trabajando en ello.
370386
371387   Por favor, lee atentamente: [[Lazarus Documentation|Traducciones]]
372388
373389## Ver también
374390
375391[[IDE Development|IDE Development: Translations, i18n, lrt, po files]]
376392
377393[[Getting translation strings right]]
378394
379395[[Lazarus Documentation|Translations]]
380396
381397---
382398*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).*
383399Category: Castellano
384400Category: Español

Back to history