Estas en: Home > caracteres

Entradas etiquetadas con caracteres

#symfony, I18N, UTF-8 y Dreamweaver

0

Supongo que ya sabrás de lo que voy a hablar, sí, codificación de caracteres y el jodío de Dreamweaver. Te cuento:

Estoy haciendo algunas pruebas con Symfony y su sistema de internacionalización ( I18N ), y para ello he hecho que el charset que muestre la plantilla sea utf-8 (además de las tablas de la BD y las conexiones desde y hacía la BD), lo curioso es que cuando he ido a verlo en el navegador el resultado me aparecía con chinos (unos cuadrados (aunque pueden ser otros símbolos) que sustituyen a las letras con tilde), total que me he tirado todo el día dándole vueltas al tema, y el problema lo tenía en Dreamweaver que, por defecto, guarda los archivos en «Europeo occidental» y no en utf-8.

Te explico todo lo que he modificado en el proyecto de Symfony, por si te pasa algo parecido puedas comparar. Con esta configuración que te voy a mostrar el sistema de internacionalización de Symfony funciona al 100%:

Antes de nada te informo que esto está pensado para Symfony 1.4, versiones superiores o inferiores pueden necesitar una configuración distinta. Cuando hablo de Dreamweaver me refiero a la versión CS3, aunque muy probablemente algo parecido haya en versiones superiores.

settings.yml

Doy por hecho que ya has creado el proyecto, al menos una aplicación y un módulo con el que hacer pruebas. Nos vamos al archivo settings.yml de la app y añadimos:

 

  1. all:
  2. .settings:
  3.  
  4. # Indicamos la cultura por defecto
  5. # Aquí poned la que os interese
  6. default_culture: es_ES
  7.  
  8. # Indicamos la codificación de caracteres
  9. charset: utf-8
  10.  
  11. # Esto lo dejo a tu elección
  12. # Puedes escribir esta línea para que el helper I18N esté
  13. # en todas las plantillas de forma global
  14. # Lo bueno de usar esta opción es que puedes añadir
  15. # más helpers:
  16. # standard_helpers: [I18N,text, etc]
  17. standard_helpers: [I18N]
  18.  
  19. # O puedes escribir esta otra línea, pero
  20. # en cada una de las plantillas tendrás que incluir
  21. # al prinicipio <?php use_helper('I18N') ?>
  22. # la decisión es tuya
  23. i18n: true

 

routing.yml

Modificamos este archivo para indicar el módulo que se mostrará como página de inicio (homepage):

 

  1. # Esto es lo que está por defecto
  2. homepage:
  3. url: /
  4. param: { module: default, action: index }
  5.  
  6. # Lo único que cambio es el nombre del módulo, que en mi caso es 'login'
  7. homepage:
  8. url: /
  9. param: { module: login, action: index }

 

 

view.yml

Este archivo creo que no hacia falta modificarlo para la internacionalización, pero por si acaso:

  1. metas:
  2. language: es

 

indexSuccess.php del módulo

Añadimos el texto que vamos a probar:

  1. // No voy a poner todo el código que tengo en mi plantilla
  2. // así que pongo solo un ejemplo
  3.  
  4. <?php echo __('Hola Mundo!') ?>
  5.  
  6. // Esto te mostrará el texto en español

Bien, como le hemos dado el valor «es_ES» a «default_culture«, Symfony no mostrará ninguna traducción sino el valor que le hemos indicado en la plantilla. Para hacer una prueba en condiciones vamos a modificar la cultura de un usuario a «en_EN«, esto mantendrá la cultura de todo el proyecto como «es_ES«.

 

actions.class.php del módulo

En la acción Index escribimos lo siguiente:

  1. class loginActions extends sfActions
  2. {
  3. /**
  4. * Executes index action
  5. *
  6. * @param sfRequest $request A request object
  7. */
  8. public function executeIndex(sfWebRequest $request)
  9. {
  10.  
  11. // Esta línea cambiará la cultura SÓLO para el usuario
  12. $this -> getUser() -> setCulture('en_EN');
  13.  
  14. return sfView::SUCCESS;
  15. }
  16. }

Obviamente nos falta crear el archivo que le indicará a Symfony el texto que debe utilizar para su sustitución. A ello voy:

 

english.en.xml

Este archivo se guarda en la carpeta «i18n» de la app, aunque también puedes crear la carpeta dentro del módulo y guardar el archivo allí. Este archivo tiene un formato especial que se debe mantener:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <xliff version="1.0">
  3. <file orginal="global" source-language="en_EN" datatype="plaintext">
  4. <body>
  5. <trans-unit id="1">
  6. <source>Hola Mundo!</source>
  7. <target>Hello World!</target>
  8. </trans-unit>
  9. <trans-unit id="2">
  10. <source>soy un texto</source>
  11. <target>I'm a text</target>
  12. </trans-unit>
  13. <trans-unit id="3">
  14. <source>Adiós</source>
  15. <target>Bye</target>
  16. </trans-unit>
  17. </body>
  18. </file>
  19. </xliff>

Cosas importantes respecto de este archivo:

  • Le puedes poner cualquier nombre pero debe acabar en *.culture.xml, por ejemplo: login.en.xml, registro.en.xml, administracion.fr.xml, comentario.it.xml.
  • También se puede poner la cultura completa, es decir, en vez de solo *.en.xml puedes nombrarlo como *.en_EN.xml.
  • El parámetro «source-language» siempre debe indicar la cultura que se va a traducir, en este caso es el ingles (en_EN).
  • Cada texto a traducir está dentro de la etiqueta <trans-unit>, que tiene el atributo id, pues bien, cada frase a traducir debe aumentar el id ( 1,2,3,4,5….500 etc)
  • No es necesario que todas las frases estén en un solo archivo, puede haber varios archivos con textos diferentes, por ejemplo, para el menú, la cabecera, el pie de página, etc.,. Importante: aunque sean archivos para idiomas diferentes el nombre del archivo siempre debe ser el mismo, variando, eso sí, la cultura.

Una vez guardado el archivo volvemos al indexSuccess.php y lo modificamos para que quede tal que así;

  1. // No voy a poner todo el código que tengo en mi plantilla
  2. // así que pongo solo un ejemplo
  3.  
  4. <?php echo __('Hola Mundo!', null, 'login') ?>
  5.  
  6. // Ahora mostrará el texto que corresponda con la cultura del usuario
  7. // Además lo buscará en un archivo concreto en este caso login.en.xml

Y así se internacionaliza un proyecto Symfony. Ahora el problemita de Dreamweaver:

El bicho, (por llamarlo de alguna manera) tiene una opción para abrir archivos que no indiquen su codificación. Normalmente está en «Europeo occidental» y debería estar en «Unicode (utf-8)«. Para cambiarlo accedemos al menú «Edición -> Preferencias…» y en «Nuevo documento» busca un desplegable que ponga «Codificación pred.«; ahí elige «Unicode (utf-8)«, activa la casilla que dice «Utilizar al abarir archivos existentes que no indiquen su codificación» y en el desplegable de abajo (Formulario de normas Unicode) selecciona «C (descomposición de compatibilidad seguida de composición canónica)«. Pulsa aceptar y prueba el en navegador.

Tal vez tengas que limpiar la cache de Symfony para ello solo tienes que escribir en la consola (que debe apuntar a la carpeta donde tienes el proyecto) symfony cc o php symfony cc.

Si sigue fallando, abre el archivo indexSuccess.php del módulo con el bloc de notas, y sin modificar nada, vete a Archivo -> Guardar como… , ahí podrás elegir la codificación, en nuestro caso, utf-8, y para evitar que se guarde como un archivo .txt elige «Todos los archivos» y así se guardará en php. Vuelve a limpiar la caché y vuelve a probar, ahora sí te debería funcionar bien.

Symfony: La vista (IV), mecanismo de escape

0

Este capítulo del libro me está resultando de lo más pesado, principalmente porque no hay mucho con lo que practicar, ya que la mayoría de la información es teoría basada en donde ubicar cada cosa en cada caso. Aunque las posibilidades que ofrece Symfony a medida que te adentras en él, son enormes.

Para acabar con la vista solo queda ver el mecanismo de escape que utiliza Symfony. Empezemos:

Este apartado del libro comienza así:

Cuando se insertan datos generados dinámicamente en una plantilla, se debe asegurar la integridad de los datos. Por ejemplo, si se utilizan datos obtenidos mediante formularios que pueden rellenar usuarios anónimos, existe un gran riesgo de que los contenidos puedan incluir scripts y otros elementos maliciosos que se encargan de realizar ataques de tipo XSS (cross-site scripting). Por tanto, se debe aplicar un mecanismo de escape a todos los datos mostrados, de forma que ninguna etiqueta HTML pueda ser peligrosa.

ACTIVANDO EL MECANISMO DE ESCAPE

Fácil. El mecanismo de escape se activa en el archivo config/settings.yml de la aplicación utilizando para ello dos parámentros:

  • scaping_strategy: Define la forma en la que las variables están disponibles en la vista.
  • scaping_method: Indica la función que se aplica a los datos.

all:
.settings:
escaping_strategy: both
escaping_method: ESC_ENTITIES

Esto de aquí arriba es lo único que tendrás que escribir para activar el mecanismo de escape. Ahora mismo, nuestra aplicación estaría utilizando el método htmlentities() de PHP para escapar los caracteres de html. Más adelante veremos más valores que pueden tener estos parámentros.

Por ejemplo, si tenemos en nuestra acción esta variable:

$this->prueba = ‘<script>alert(document.cookie)</script>’;

Al tener el mecanismo de escape activado, al mostrarla en la plantilla nos dará el siguiente resultado:

echo $prueba;
=> &gt;&lt;script&gt;alert(document.cookie)&lt;/script&gt;

Si se activa el mecanismo de escape, desde cualquier plantilla se puede acceder a una nueva variable llamada $sf_data. Se trata de un objeto contenedor que hace referencia a todas las variables que se han modificado mediante el sistema de escape. De esta forma, también es posible mostrar el contenido de la variable prueba de la siguiente manera:

echo $sf_data->get(‘prueba’);
=> &gt;&lt;script&gt;alert(document.cookie)&lt;/script&gt;

Con esta clase también podemos acceder a los datos de la variable en crudo, es decir, antes de que sus caracteres fuesen escapados:

echo $sf_data->getRaw(‘prueba’);
=> <script>alert(document.cookie)</script>

OPCIONES PARA scaping_strategy

  • backward compatible mode (o modo retrocompatible): el contenido de las variables no se modifica, pero el contenedor $sf_data almacena una versión modificada de cada variable. De esta forma, los datos de las variables se obtienen en crudo, a menos que se obtenga la versión modificada del objeto $sf_data. Se trata del valor por defecto de la opción, aunque se trata del modo que permite los ataques de tipo XSS.
  • both: a todas las variables se les aplica el mecanismo de escape. Los valores también están disponibles en el contenedor $sf_data. Se trata del valor recomendado, ya que solamente se está expuesto al riesgo si se utilizan de forma explícita los datos originales. En ocasiones, se deben utilizar los valores originales, por ejemplo para incluir código HTML de forma que se interprete en el navegador y no se muestre el código HTML. Si una aplicación se encuentra medio desarrollada y se cambia la estrategia del mecanismo de escape a este valor, algunas funcionalidades pueden dejar de funcionar como se espera. Lo mejor es seleccionar esta opción desde el principio.
  • on: los valores solamente están disponibles en el contenedor $sf_data. Se trata del método más seguro y más rapido de manejar el mecanismo de escape, ya que cada vez que se quiere mostrar el contenido de una variable, se debe elegir el método get() para los datos modificados o el método getRaw() para el contenido original. De esta forma, siempre se recuerda la posibilidad de que los datos de la variable sean corruptos.
  • off: deshabilita el mecanismo de escape. Las plantillas no pueden hacer uso del contenedor $sf_data. Si nunca se va a necesitar el sistema de escape, es mejor utilizar esta opción y no la opción por defecto bc, ya que la aplicación se ejecuta más rápidamente.

OPCIONES PARA scaping_method

  • ESC_RAW: no modifica el valor original.
  • ESC_ENTITIES: aplica la función htmlentities() de PHP al valor que se le pasa y utiliza la opción ENT_QUOTES para el estilo de las comillas.
  • ESC_JS: modifica un valor que corresponde a una cadena de JavaScript que va a ser utilizada como HTML. Se trata de una opción muy útil para escapar valores cuando se emplea JavaScript para modificar de forma dinámica el contenido HTML de la página.
  • ESC_JS_NO_ENTITIES: modifica un valor que va a ser utilizado en una cadena de JavaScript pero no le añade las entidades HTML correspondientes. Se trata de una opción muy útil para los valores que se van a mostrar en los cuadros de diálogo (por ejemplo para una variable llamada miCadena en la instrucción javascript:alert(miCadena);).

APLICANDO EL MECANISMO DE ESCAPE A ARRAYS Y OBJETOS

// Definición de la clase
class miClase
{
public function pruebaCaracterEspecial($valor = »)
{
return ‘<‘.$valor.’>’;
}
}

// En la acción
$this->array_prueba = array(‘&’, ‘<‘, ‘>’);
$this->array_de_arrays = array(array(‘&’));
$this->objeto_prueba = new miClase();

// En la plantilla
<?php foreach($array_prueba as $valor): ?>
<?php echo $valor ?>
<?php endforeach; ?>
=> &amp; &lt; &gt;
<?php echo $array_de_arrays[0][0] ?>
=> &amp;
<?php echo $objeto_prueba->pruebaCaracterEspecial(‘&’) ?>
=> &lt;&amp;&gt;

Los métodos de los objetos a los que se aplica el mecanismo de escape aceptan un parámetro adicional:

<?php echo $objeto_prueba->pruebaCaracterEspecial(‘&’) ?>
=> &lt;&amp;&gt;
// Las siguientes 3 líneas producen el mismo resultado
<?php echo $objeto_prueba->pruebaCaracterEspecial(‘&’, ESC_RAW) ?>
<?php echo $sf_data->getRaw(‘objeto_prueba’)->pruebaCaracterEspecial(‘&’) ?>
<?php echo $sf_data->get(‘objeto_prueba’, ESC_RAW)->pruebaCaracterEspecial(‘&’) ?>
=> <&>

Las variables de Symfony también se modifican al activar el mecanismo de escape. Por tanto, las variables $sf_user, $sf_request, $sf_param y $sf_context siguen funcionando, pero sus métodos devuelven sus datos modificados, a no ser que se utilice la opción ESC_RAW como último argumento de las llamadas a los métodos.

Ir arriba