calendario de symfony día veintitrés: Internacionalización ============================================================== Anteriormente en symfony --------------------- Ahora que ya has aprendido como transferir una aplicación symfony al host de producción, la aplicación de askeet puede correr en cualquier sitio. Pero que pasa si alguien decide usarla en un país de habla no inglesa, digamos Francia? Siendo askeet un proyecto de código abierto esperamos que gente de todo el mundo lo use pronto. Esto no sólo significa que todos los archivos del proyecto se tienen que codificar en [utf-8](http://en.wikipedia.org/wiki/UTF-8), la aplicación también tiene que proponer una interface y un contenido localizado. Piensa acerca de las compañías multinacionales que se instalarán askeet en sus Intranets para manejar la base de conocimiento. Definitivamente requerirán que los usuarios puedan cambiar el lenguaje de la interfície o el contenido en vez de instalar un askeet por idioma... Afortunadamente, las decisiones hechas durante el [día dieciocho](18.txt) de implementar universos, harán mucho más fácil nuestro trabajo, y además symfony tiene soporte nativo para las interfícies internacionalizadas. Localización ------------ Qué pasa si se llama a una dirección como: http://fr.askeet.com/ ...mostramos sólo preguntas en francés? Bien esto es muy fácil porque desde el [día dieciocho](18.txt), un URI como este se entiendo como un universo. ### Contenido Creando preguntas en un idioma universo lo etiquetará automáticamente con la etiqueta del idioma (aquí: 'fr'). Y, si navegas el universo 'fr', sólo verás las preguntas donde aparezca la etiqueta 'fr'. Por lo tanto el filtro del universo ya se preocupa del contenido localizado. Esto ha sido un paso sencillo. ### Apariencia Los universos puede tener sus propias hojas de estilo. Esto significa que la apariencia de un askeet localizado puede ser fácilmente adaptada con el mismo mecanismo. Siguiente, por favor. ### Funciones que dependen del idioma El sistema de indexación construido durante el [día veinte](http://www.symfony-project.com/askeet/22) se basa en un algoritmo de división que es dependiente del idioma. En una versión localizada, se tiene que adaptar. Por ahora no hay ninguna biblioteca de división para otros lenguajes que no sean el Inglés en PHP, pero que pasaría si hubiera alguno, o que pasaría si alguien decide portar uno de las [bibliotecas de división de Perl](http://search.cpan.org/search?query=stem&mode=all) a PHP? Luego, en el método `myTools::stemPhrase()`, deberíamos llamar a el [método factory](http://en.wikipedia.org/wiki/Factory_method_pattern) en lugar de a un simple PorterStemmer (se deja como ejercicio por ahora) ### Contenido de la base de datos Imagina un sitio web internacional que propone una lista de hoteles alrededor del mundo. Cada hotel se muestra con un texto descriptivo de las habitaciones, el servicio y el horario. Hay miles de hoteles, por lo tanto el contenido se debe de guardar en una base de datos. El problema es que debe haber tantas versiones de las descripciones como traducciones del sitio. Symfony provee una forma de estructurar los datos para manejar estas situaciones. Con el ejemplo anterior, habría una clase `Hotel` para las tasas, dirección y el contenido sin traducir, y una clase `HotelI18n` para el contenido que se deba traducir. Como los accessores de Propel abstraen esta separación, incluso si la `descripción` se encuentra en la tabla `HotelI18n`, todavía podrías acceder al contenido con un simple: [php] $description = $hotel->getDescription(); Para entender mejor como funciona esto, mira el [capítulo i18n](http://www.librosweb.es/symfony/capitulo13.html) del libro de symfony. Afortunadamente, el sistema de filtrado de los universos de askeet reemplazan la necesidad de adaptar el contenido, por lo tanto no lo usaremos aquí. Internacionalización -------------------- Como es una palabra larga, los desarrolladores, a menudo, se refieren a [internacionalización](http://en.wikipedia.org/wiki/Internationalization_and_localization) como 'i18n'. Para aquellos que no sepan porqué, sólo tenéis que contar las letras de la palabra 'internationalitzation', y también entenderéis porqué 'localización' ('localitzation') es referido como 'l10n'. En el desarrollo de aplicaciones web, i18n mayormente concierne la traducción de los contenidos del texto y el uso de formatos locales para la interfície. ### Establecer la cultura Muchas de las características de la i18n dentro de symfony se basan en el parámetro de sesión de usuario llamado la **cultura**. La cultura es la combinación del país y del idioma del usuario, y determina como el texto y la información dependiente de la cultura se mostrará. Cuando la aplicación askeet reconoce un universo como una localización, tiene que establecer la cultura correspondiente. ¿Cuándo una etiqueta permanente debe de ser reconocida cómo localización? Escogemos permitir sólo aquellas para las que la interfície está traducida (ver más abajo), por lo tanto el hecho de que un universo es una localización es determinado por la existencia de un fichero XML de traducción en el directorio `i18n/` del proyecto. Los universos se encuentran en el filtro `askeet/apps/frontend/lib/myTagFilter.class.php`, por lo tanto sólo tenemos que modificar un poco: [php] public function execute ($filterChain) { ... // is there a tag in the hostname? $request = $this->getContext()->getRequest(); $hostname = $request->getHost(); if (!preg_match($this->getParameter('host_exclude_regex'), $hostname) && $pos = strpos($hostname, '.')) { $tag = Tag::normalize(substr($hostname, 0, $pos)); // add a permanent tag constant sfConfig::set('app_permanent_tag', $tag); // add a custom stylesheet $request->setAttribute('app/tag_filter', $tag, 'helper/asset/auto/stylesheet'); // is the tag a culture? if (is_readable(sfConfig::get('sf_app_i18n_dir').'/global/messages.'.strtolower($tag).'.xml')) { $this->getContext()->getUser()->setCulture(strtolower($tag)); } else { $this->getContext()->getUser()->setCulture('en'); } } ... } >**Note**: Las etiquetas de idioma que serán reconocidas se tienen que codificar en dos caracteres en minúscula, como se describe en la [norma ISO 639-1](http://www.w3.org/WAI/ER/IG/ert/iso639.htm) (por ejemplo `fr` para el francés). Cuando se trabaja con la internacionalización, siempre es preferible códigos ISO para los países e idiomas, para que así tu código cumpla con los estándares internacionales y pueda ser comprendido por desarrolladores externos. Encontrarás más información acerca de la internacionalización y las culturas en el [capítulo de i18n](http://www.librosweb.es/symfony/capitulo13.html) del libro de symfony. ### Fechas, horas, números, moneda y mediciones La manera de mostrar la fecha en Francia no es la misma que en los Estados Unidos. Lo que un norte-americano escribiría como: Diciembre 16, 2005 9:26 PM ... es escrito por un francés 16 décembre 2005 21:26 Si recuerdas bien, cada vez que se tiene que mostrar una fecha en un template de askeet, usamos el helper `format_date()`. Este helper formatea la fecha pasada como parámetro según la cultura del usuario. Como la cultura se establece en el filtro `myTagFilter.class.php`, el formato de fecha se hará automáticamente.  Esta es otra buena práctica para los proyectos internacionales: siempre usar los helpers i18n cuando se tiene que mostrar una fecha, una hora, un número, un importe o una medición. Symfony provee helpers para la mayoría de ellos (mira el [capítulo de helpers de i18n](http://www.librosweb.es/symfony/capitulo13/traduccion_de_la_interfaz.html) del libro de symfony para más información). ### Traducción de la interfície La interfície del proyecto askeet contiene texto. En una versión localizada, el texto de la interfície se debería de mostrar en el idioma de la cultura del usuario. Para permitir la traducción de la interfície, todos lo textos de los templates de askeet, se tienen que codificar con un helper de i18n especial, `__()`. Además, el helper se debe de declara al principio del template. Por ejemplo, para permitir la traducción de la página principal, abre el template `askeet/apps/frontend/modules/question/templates/listSuccess.php` y cámbialo a [php]
$question_pager)) ?> >**Note**: En lugar de tener que añadir el helper `i18n` en el principio de cada template, lo puedes añadir una sola vez para toda la aplicación en `settings.yml`, >`askeet/apps/frontendt/config/`: > > all: > .settings: > > standard_helpers: Partial,Cache,Form,I18N > Para cada idioma en el que la interfície es traducida, se tiene que crear un fichero llamado `messages.xx.xml` en el directorio `askeet/apps/frontend/i18n/`, donde `xx` es el idioma de la traducción. Este fichero XML es un diccionario [XLIFF](http://www.xliff.org/) que muestra la versión del texto traducida desde el idioma original (Inglés para askeet). Por ejemplo, para permitir la traducción al francés, debes crear un fichero `messages.fr.xml` con el siguiente contenido: [xml]