¡La versión 2.0 de Play ya está lista! Ayúdanos a traducir la documentación de la útlima versión y sigue nuestro progreso.

Manuales, tutoriales & referencias

Consulte

Contenidos

Elija la versión

Buscar

Busque con google

Libros

Internacionalización y localización

Dado que ya hemos completado la funcionalidad de nuestro sistema de blog, ahora le aportaremos un valor agregado adicional: internacionalización y localización del idioma de la aplicación web. Aunque podríamos haber hecho esto desde el principio, es más realista construir la primera versión de una aplicación en un solo idioma, y luego agregar múltiples idiomas.

Internacionalización y localización

Para agregar soporte para múltiples idiomas, debemos llevar adelante dos operaciones distintas: internacionalización y localización. Ambos principalmente se refieren al texto de la aplicación.

Internacionalización, en términos de programación, es una reestructuración (refactoring) de nuestra aplicación para eliminar código específico a un idioma en particular. En una aplicación web, esto consiste casi exclusivamente en reemplazar los textos de la interfaz de usuario en las plantillas de la capa vista con referencias a mensajes internacionalizados. También incluye el formato de tipos de datos que no son textos, como fechas, moneda y otros números, de acuerdo al correspondiente idioma.

Localización consiste en hacer una versión específica de la aplicación para una determinada localidad. Si la aplicación está internacionalizada, quiere decir que tiene una o más versiones para varias localidades entre las cuales se puede elegir. En una aplicación web, esta localización principalmente se refiere a la traducción del texto de la interfaz de usuario al idioma seleccionado. La selección del idioma se realiza normalmente como una combinación del conjunto de preferencias de idioma del navegador web, y la selección de idioma efectuada desde la interfaz de usuario de la aplicación en sí.

En la práctica, estas dos operaciones van juntas: usted internacionaliza y localiza una parte de la aplicación al mismo tiempo.

Yet Another Blog Engine (Otro sistema de blogs más)

El punto de partida de esta sección es el código terminado del tutorial, el cual puede encontrar en el directorio de instalación de Play samples-and-tests/yabe. El objetivo es internacionalizar completamente la aplicación, y agregar localización para los idiomas Francés, Alemán y, como no podía ser de otra forma, también español.

Para empezar, primero edite el archivo conf/application.conf y quite el caracter de comentario (en el archivo de configuración por defecto) o agregue una línea con los tres idiomas soportados:

# Localizaciones para Inglés, Alemán, Francés y Español. 
application.langs=en,nl,fr,es 

Ahora si abre una página de la aplicación, la consola Play mostrará cuatro advertencias porque aún no ha definido ningún archivo de mensajes localizados:

16:19:04,728 WARN ~ Messages file missing for locale en 
16:19:04,729 WARN ~ Messages file missing for locale nl 
16:19:04,729 WARN ~ Messages file missing for locale fr
16:19:04,729 WARN ~ Messages file missing for locale es  

Archivos de mensajes UTF-8

Las advertencias anteriores quieren decir que necesita reemplazar el archivo conf/messages existente con un archivo de mensajes para cada idioma:

  • messages.en
  • messages.nl
  • messages.fr
  • messages.es

En este punto encontramos la primera mejora respecto a la forma en que Java hace normalmente las cosas. Estos archivos usan la misma sintaxis de los archivos de propiedades (properties) de Java, pero no son archivos de propiedades porque ellos deben usar la codificación UTF-8. Los archivo de Propiedades Java, por otro lado, utilizan la codificación de caracteres ISO-8859-1 ‘Latin-1’ para transferencia de datos desde y hacia archivos de texto.

Usar archivos de mensajes UTF-8 para la localización en un idioma es una gran ventaja, ya que le permite escribir los mensajes localizados en un idioma en ‘texto plano’. Por ejemplo, esto significa que para una localización en Griego, en vez de usar:

hello.morning = \u0152\u222b\u0152\u00b1\u0152\u00aa\u0152\u2211\u0152\u00ba\u0152\u2260\u0153\u00c5\u0152\u00b1
hello.informal = \u0152\u2265\u0152\u00b5\u0152\u03c0\u0152\u00b1 \u0153\u00c9\u0152\u00f8\u0153\u00d6 

usted puede usar las letras Griegas en lugar de esos códigos de caracteres Unicode:

hello.morning = καλημέρα 
hello.informal = γεια σου 

En el resto de este tutorial los ejemplos de código definirán los mensajes en uno de estos archivos, o mostrarán código HTML internacionalizado en alguna de las plantillas HTML de la vista.

Mensajes simples

El caso simple es una cadena de texto que no cambia, y que no es interrumpida por otro tag HTML. Por ejemplo, el primero de tales textos está en la plantilla yabe/app/views/main.html, en la lista tools:

<ul id="tools"> 
<li> 
<a href="@{Admin.index()}">Log in to write something</a> 
</li> 
</ul> 

Para internacionalizar esto, reemplazamos el texto con una búsqueda de mensaje, usando la sintanxis &{'clave'}:

<ul id="tools"> 
<li> 
<a href="@{Admin.index()}">&{'views.main.tools.login'}</a> 
</li> 
</ul> 

Para agregar la localización, agregue la línea respectiva en cada uno de los cuatro archivos de mensajes. En conf/messages.en

views.main.tools.login = Log in to write something 

En conf/messages.nl

views.main.tools.login = Inloggen om iets te schrijven 

En conf/messages.fr

views.main.tools.login = Connectez-vous pour écrire quelque chose 

En conf/messages.es

views.main.tools.login = Inicie sesión para escribir algún mensaje 

La clave del mensaje puede ser cualquiera de su preferencia; en este ejemplo he utilizado una clave para indicar la ubicación views/main.html#tools

Una vez que haya guardado estos cambios, puede ver las versiones de los diferentes idiomas en su navegador web cambiando la configuración que modifica el valor del encabezado Accept-Language del pedido HTTP. En Firefox, seleccione Opciones » Contenido » Idiomas » Seleccionar…, agregue Francés [fr], Alemán [nl] y Español [es] si es que ya no están en la lista, cambie el que está primero, cierre el cuadro de diálogo y recargue la página.

Localización del modelo de la aplicación

Si usa ese enlace para ingresar a las páginas ‘admin’ del blog, puede acceder a la lista de posts (artículos), tags (etiquetas), comments (comentarios) y users (usarios). Estas páginas son proporcionadas por el módulo CRUD. Para cada una de estas páginas, el título (rosado claro) y el encabezado de las columnas son términos del modelo de la aplicación, por ejemplo: clases JavaBeans y nombres de propiedades.

El módulo CRUD internacionaliza estos nombres usando la clase JavaBeans o el nombre de la propiedad como la clave del mensaje, lo cual significa que usted puede localizarlos con mensajes como los que se muestran a continuación:

En conf/messages.nl

post = artikel 
Post = Artikel 
posts = artikelen 
Posts = Artikelen 
comment = reactie 
Comment = Reactie 
comments = reacties 
Comments = Reacties 
user = gebruiker 
User = Gebruiker 
users = gebruikers 
Users = Gebruikers 

En conf/messages.fr

post = article 
Post = Article 
posts = articles 
Posts = Articles 
comment = commentaire 
Comment = Commentaire 
comments = commentaires 
Comments = Commentaires 
user = utilisateur 
User = Utilisateur 
users = utilisateur 
Users = Utilisateurs 

En conf/messages.es

post = artículo
Post = Artículo
posts = artículos 
Posts = Artículo 
comment = comentario 
Comment = Comentario 
comments = comentarios 
Comments = Comentarios 
user = usuario 
User = Usuario 
users = usuarios 
Users = Usuarios

Notará que esto no cambia el enlace redondeado y púrpura del panel de navegación:

Estos están definidos en el archivo views/admin.html el cual usted puede internacionalizar para que usen las mismas localizaciones simplemente rodeando el texto existente con &{'…'} tal como se muestra a continuación:

<a href="@{Posts.list()}">&{'Posts'}</a> 
… 
<a href="@{Tags.list()}">&{'Tags'}</a> 
… 
<a href="@{Comments.list()}">&{'Comments'}</a> 
… 
<a href="@{Users.list()}">&{'Users'}</a> 

Mensajes parametrizados

Además de los mensajes simples, nuestra aplicación también incluye mensajes que contienen una variable, tales como Posts tagged with Play

Para localizar un mensaje que contiene un solo parámetro, use una cadena de formato Java para intsertar el valor del parámetro en el mensaje:

views.Application.listTagged.title = Posts tagged with %s 

y en la plantilla, agregue el parámetro así:

&{'views.Application.listTagged.title', tag} 

Cuando un mensaje contiene múltiples parámetros, agregue un índice a la cadena de formato para permitir un orden diferente de las palabras en otros idiomas:

views.Admin.index.welcome = Welcome %1$s, <span>you have written %2$s posts so far</span> 

… con una lista en la plantilla:

&{'views.Admin.index.welcome', user, posts.size()} 

En este ejemplo, incluso nos gustaría usar la forma plural correcta para la palabra ‘post’, así que hagamos de esa palabra un parámetro también:

views.Admin.index.welcome = Welcome %1$s, <span>you have written %2$s %3$s so far</span> 

… y utilice la extensión pluralize en la plantilla

&{'views.Admin.index.welcome', user, posts.size(), posts.pluralize(messages.get('post'), messages.get('posts'))} 

Note que hemos usado messages.get para buscar el singular y el plural localizados.

Localización de módulos Play

La localización de módulos Play funciona de la misma manera que la localización dentro de su aplicación. Esta aplicación usa los módulos CRUD y Secure, lo cual significa que debemos localizar los mensajes en play/modules/crud/conf/messages y play/modules/secure/conf/messages que nuestra aplicación usa.

En conf/messages.nl

# play/modules/crud (administration) 
crud.title = Beheer 
crud.home = Home 
crud.blank = Nieuw 
crud.index.title = Kies het te bewerken object 
crud.index.objectType = Type object 
crud.index.action = 
crud.index.add = Voeg toe 
crud.add = &{%s} toevoegen 
crud.list.title = &{%s} 
crud.list.size = %d &{%s} 
crud.list.totalSize = %d totaal 
crud.pagination.previous = « Vorige 
crud.pagination.next = Volgende » 
crud.pagination.last = Laatste »» 
crud.pagination.first = «« Eerste 
crud.show.title = &{%s} bewerken 
crud.save = Opslaan 
crud.saveAndContinue = Opslaan en verder bewerken 
crud.cancel = Annuleren 
crud.hasErrors = Corrigeer fouten a.u.b. 
crud.blank.title = &{%s} toevoegen 
crud.saveAndAddAnother = Opslaan en nogmaals creëren 
crud.delete = &{%s} verwijderen 
crud.created = &{%s} is aangemaakt 
crud.saved = &{%s} is opgeslagen 
crud.deleted = &{%s} is verwijderd 
crud.delete.error = Kan dit object niet verwijderen 
crud.search = Zoeken 
crud.none = (Geen) 
crud.help.required = Verplicht. 
crud.help.minlength = Min. lengte is %d. 
crud.help.maxlength = Max. lengte is %d. 
crud.help.email = Geldig e-mailadres 
crud.help.dateformat = In de vorm YYYY-MM-DD. 
crud.help.numeric = Numeriek. 
crud.help.min = Moet groter daan %d zijn. 
crud.help.future = In de toekomst. 
crud.help.past = In het verleden. 
crud.help.after = Na %s. 
crud.help.before = Voor %s. 
crud.help.range = Tussen %d en %d 
 
# play/modules/secure 
secure.username = Uw e-mailadres: 
secure.password = Uw wachtwoord: 
secure.signin = Nu inloggen 

En conf/messages.fr

# play/modules/crud (administration) 
crud.title = Administration 
crud.home = Home 
crud.blank = Nouveau 
crud.index.title = Choisissez l'objet à modifier 
crud.index.objectType = Type objet 
crud.index.action = XXX 
crud.index.add = Ajouter 
crud.add = Ajouter &{%s} 
crud.list.title = &{%s} 
crud.list.size = %d &{%s} 
crud.list.totalSize = %d total 
crud.pagination.previous = « Précédent 
crud.pagination.next = Suivant » 
crud.pagination.last = Dernier »» 
crud.pagination.first = «« Premier 
crud.show.title = Modifier &{%s} 
crud.save = Enregistrer 
crud.saveAndContinue = Enregistrer et continuez à modifier 
crud.cancel = Annuler 
crud.hasErrors = Corrigez les erreurs s.v.p. 
crud.blank.title = Ajouter &{%s} 
crud.saveAndAddAnother = Enregistrer et ajouter un autre 
crud.delete = Supprimer &{%s} 
crud.created = &{%s} a été crée 
crud.saved = &{%s} est enregistré 
crud.deleted = &{%s} est supprimé 
crud.delete.error = Ne peut pas supprimer l’objet 
crud.search = Chercher 
crud.none = (aucun) 
crud.help.required = Obligatoire. 
crud.help.minlength = Longeur minimum est %d. 
crud.help.maxlength = Longeur maximum est %d. 
crud.help.email = Adresse e-mail valide 
crud.help.dateformat = En format YYYY-MM-DD. 
crud.help.numeric = Numerique. 
crud.help.min = Doit être plus grand que %d. 
crud.help.future = Dans le futur. 
crud.help.past = Dans le passé. 
crud.help.after = Après %s. 
crud.help.before = Avant %s. 
crud.help.range = Entre %d et %d 
 
# play/modules/secure 
secure.username = Votre adresse e-mail: 
secure.password = Votre mot de passe: 
secure.signin = Connectez-vous maintenant 

En conf/messages.es

# play/modules/crud (administration) 
crud.title = Administración 
crud.home = Página principal
crud.blank = Nuevo
crud.index.title = Seleccione el objeto a modificar 
crud.index.objectType = Tipo de objeto
crud.index.action = XXX 
crud.index.add = Agregar
crud.add = Agregar &{%s} 
crud.list.title = &{%s} 
crud.list.size = %d &{%s} 
crud.list.totalSize = %d en total 
crud.pagination.previous = « Anterior 
crud.pagination.next = Siguiente » 
crud.pagination.last = Último »» 
crud.pagination.first = «« Primero 
crud.show.title = Modificar &{%s} 
crud.save = Guardar
crud.saveAndContinue = Guardar y seguir modificando 
crud.cancel = Cancelar
crud.hasErrors = Por favor, corrija los errores. 
crud.blank.title = Agregar &{%s} 
crud.saveAndAddAnother = Guardar y agregar uno nuevo 
crud.delete = Eliminar &{%s} 
crud.created = &{%s} ha sido creado 
crud.saved = &{%s} ha sido guardado
crud.deleted = &{%s} ha sido eliminado
crud.delete.error = No puede eliminar el objeto 
crud.search = Buscar
crud.none = (algún) 
crud.help.required = Requerido. 
crud.help.minlength = La longitud mínima es %d. 
crud.help.maxlength = La longitud máxima es %d. 
crud.help.email = Una dirección de correo válida 
crud.help.dateformat = En formado YYYY-MM-DD. 
crud.help.numeric = Numérico. 
crud.help.min = Debe ser mayor a %d. 
crud.help.future = En el futuro. 
crud.help.past = En el pasado. 
crud.help.after = Después de %s. 
crud.help.before = Antes de %s. 
crud.help.range = Entre %d y %d 
 
# play/modules/secure 
secure.username = Su dirección de correo: 
secure.password = Su clave: 
secure.signin = Entrar 

Por supuesto, una vez que haya hecho esto es también una buena idea contribuir con el desarrollo del módulo agregando estas localizaciones.

Casos Especiales

Cuando está localizando una aplicación web, hay algunos casos especiales que pueden ser difíciles de implementar si está usando un framework de aplicación web basado en componentes, tales como JavaServer Faces:

  1. Mensajes parametrizados utilizados en el valor de un atributo
  2. Parámetros de mensajes con formato
  3. Enlaces dentro de los mensajes

Estos tres casos resultan ser muy simples en Play.

El primer caso ocurre cuando quiere usar una frase con un parámetro dentro del valor de un atributo en la plantilla, tal como:

<a href="@{Application.show(_post.id)}" title="By Bob"> 

Esto es un problema en JSF, porque normalmente usted usaría un tag XML para desarrollar el reemplazo del parámetro, lo cual no puede hacer en el valor de un atributo. La sintáxis Play sencillamente evita estos problemas, y usted puede simplemente hacer esto:

<a href="@{Application.show(_post.id)}" title="&{'views.tags.display.author', _post.author.fullname}"> 

El segundo caso es cuando quiere dar formato a un valor, tal vez una fecha, para usar como un parámetro del mensaje en una frase como By Bob on 2009-06-14. Una vez más, el problema en JSF ocurre al tener que usar un tag XML para dar formato al valor, y al mismo tiempo se necesita ser capaz de usar el resultado en el valor de un atributo XML. En Play, las extensiones para dar formato a los objetos conviven naturalmente con la sintáxis para pasar parámetros a los mensajes localizados, de manera que puede hacer lo siguiente:

<span>&{'views.tags.display.author', _post.author.fullname, comment.postedAt.format('yyyy-MM-dd')}"}</span> 

También puede, por supuesto, localizar el patrón de formato:

<span>&{'views.tags.display.author', _post.author.fullname, comment.postedAt.format(messages.get('views.dateFormat'))}"}</span> 

El tercer caso típicamente ocurre cuando quiere que parte del mensaje localizado sea un enlace, como en el mensaje Log in to write something. Esto es un problema en JSF porque el enlace es un componente JSF que es interpretado de una forma tal que el tag HTML del enlace no puede estar dentro del archivo de mensajes. Play, por otro lado, le permite usar HTML plano en sus plantillas, así que usted puede colocar el tag HTML del enlace directamente en su mensaje, agregándole un parámetro para la URL:

logIn = <a href="%s">Log in</a> to write something
&{'logIn', '/admin'} 

Nuestra aplicación estaba usando la sintáxis

<a href="@{Admin.index()}">

en el enlace para conseguir que el framework genere la URL basada en el archivo routes. Para hacer esto en el parámetro del mensaje, escriba:

&{'logIn', actionBridge.Admin.index()} 

El ejemplo localizado 'Yet Another Blog Engine'

El resultado final de aplicar los pasos anteriores es una versión localizada del ejemplo ‘Yet Another Blog Engine’ que funciona en Inglés, Alemán, Francés y Español.

La interfaz de administración de ‘Yet Another Blog Engine’ in Alemán (arriba) y Francés (abajo).

Ahora que ha terminado el tutorial, continúe con La Documentación Esencial - Conceptos principales.

Por Peter Hilton, originalmente publicado en el blog Lunatech Research.