Your SlideShare is downloading. ×
0
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
I18n   DeSymfony
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

I18n DeSymfony

3,456

Published on

35 en 1, internacionalización al límite con sf2

35 en 1, internacionalización al límite con sf2

1 Comment
2 Likes
Statistics
Notes
No Downloads
Views
Total Views
3,456
On Slideshare
0
From Embeds
0
Number of Embeds
10
Actions
Shares
0
Downloads
62
Comments
1
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. ¿Quién soy?• Cristina Quintana (@jautu)• Soy de Córdoba• Desarrolladora web en Acilia
  • 2. Repasando conceptosInternacionalización y localización
  • 3. Repasando conceptosSe podría decir: I18n = Contenedor y L10n = contenido
  • 4. Nuestro proyecto de internacionalizaciónEn sus orígenesServidores WindowsTecnología .NET Con AciliaSQL Server Servidores Linux Tecnología Open Source MySQL
  • 5. Nuestro proyecto deinternacionalización
  • 6. Nuestro proyecto deinternacionalización
  • 7. Nuestro proyecto deinternacionalización
  • 8. Nuestro proyecto deinternacionalizaciónGrecia
  • 9. Nuestro proyecto deinternacionalizaciónGrecia Reino Unido
  • 10. Nuestro proyecto deinternacionalizaciónGrecia Reino Unido Turquía
  • 11. Nuestro proyecto deinternacionalizaciónGrecia Reino Unido Turquía
  • 12. Nuestro proyecto deinternacionalización Israel
  • 13. Nuestro proyecto de internacionalizaciónIsrael Israel Italia
  • 14. Nuestro proyecto de internacionalizaciónIsrael Israel Italia E. Árabes
  • 15. Nuestro proyecto de internacionalización <html dir=""> •  ltr (left-to-right) •  rtl (right-to-left)Por supuesto, cada caso necesita unas cssdistintas.
  • 16. Planificación del proyecto•  Migración de la base de datos•  Construcción del backend•  Construcción del frontend•  Nueva infraestructura
  • 17. Planificación del proyecto•  Migración de la base de datos•  Construcción del backend•  Construcción del frontend (sf2)•  Nueva infraestructura
  • 18. ¿Cómo hicimos el frontend? Mode translation: On
  • 19. Flujo de trabajoPetición Controlador Respuesta Vista
  • 20. Flujo de trabajo_locale SIexiste SI Procesar Construir ruta acción vista
  • 21. Flujo de trabajo_locale SIexiste SI Procesar Construir ruta acción vista
  • 22. Buscando el paísUtilizamos un listener: ¿Qué hace?•  Ejecutar sólo en peticiones maestras.•  Parsear url.•  Chequear país.•  Establecer valores predeterminados del idioma (l10n).
  • 23. Buscando el país src/bndl/Resources/config/services.xml<services> ... <service id="bndl.lang_listener" class="%bndl.lang_listener.class%"> <tag name="kernel.event_listener" event="kernel.request" method="onUrlParse"/> <argument type="service" id="router"/> <argument type="service" id="doctrine.orm.entity_manager"/> </service> ...</services> Declaración del listener
  • 24. Buscando el país src/bnd/Listener/LangListener.phpclass LanguageListener{ public function __construct(RouterInterface $router, EntityManager $em) { $this->_router = $router; $this->_em = $em; } public function onUrlParse(Event $event) { ... }} Contenido del listener
  • 25. public function onUrlParse(Event $event) { // Ejecutar la lógica sólo si es una petición maestra if ($event->getRequestType() !== SymfonyComponentHttpKernelHttpKernel::MASTER_REQUEST) { return; } ... // Busqueda del código de país en la url $parameters = $this->router->match($request->getPathInfo()); ... // Si existe el código del país ... // Si el código del país no exite ... } Contenido del listener
  • 26. Detección de IP de usuarioLibrería Maxmind: •  Ofrece una detección más que razonable en su versión gratuita (GeoLite)X-Forwarded-For header: •  Al pasar por Varnish, load balancers…. La IP original del usuario se pierde.Necesitamos que nuestra aplicación utilice esa IP Curiosidades
  • 27. Detección de IP de usuario SymfonyComponentHttpFoundationRequest.php// Consulta el parámetro REMOTE_ADDR$this->request->getClientIp();// Consulta tanto HTTP_CLIENT_IP// como HTTP_X_FORWARDED_FOR$this->request->getClientIp( true ); Curiosidades
  • 28. Flujo de trabajo_locale SIexiste SI Procesar Construir ruta acción vista
  • 29. Flujo de trabajo_locale SIexiste SI Procesar Construir ruta acción vista
  • 30. ¿Rutas i18n?
  • 31. ¿Qué rutas i18n podemos encontrar?Peticiones de los usuarios: http://natgeotv.com/uk/listings http://natgeotv.com/fr/grille http://natgeotv.com/nl/weekoverzichtAcción de esas peticiones: Controlador: Schedule Acción: listings Análisis del problema
  • 32. ¿Cómo podemos generar esas rutas? src/mybundle/Resources/config/routing.ymlBndl_listings_uk: pattern: /{_locale}/listings defaults: { _controller: "Bndl:Schedule:listings" } requirements: _locale: ukBndl_listings_nl: pattern: /{_locale}/weekoverzicht defaults: { _controller: "Bndl:Schedule:listings" } requirements: _locale: nl ¿Generación manual de rutas?
  • 33. ¿Cómo podemos generar esas rutas? src/mybundle/Resources/config/routing.ymlBndl_listings_uk: pattern: /{_locale}/listings defaults: { _controller: "Bndl:Schedule:listings" } requirements: _locale: uk ¿Estamos locos?Bndl_listings_nl: pattern: /{_locale}/weekoverzicht defaults: { _controller: "Bndl:Schedule:listings" } requirements: _locale: nl ¿Generación manual de rutas?
  • 34. Generación dinámica de rutas1.  Definir la ruta genérica: Bndl_listings: pattern: /{_locale}/listings defaults: { _controller: "Bndl:Schedule:listings" }2.  Modificar el routing para la generación automática de dicha ruta para todos los países. Plan de acción
  • 35. Generación dinámica de rutasCOUNTRYid URL_WORD_TRANSnameurl id... country_idtime_zone url_word_id valueURL_WORDidvalue Un poquito de base de datos
  • 36. Generación dinámica de rutas COUNTRYid url15 uk URL_WORD_TRANS6 fr id country_id url_word_id value25 nl 1 15 7 listings 2 6 7 grilleURL_WORD 3 25 7 weekoverzichtid value7 listings Un poquito de base de datos
  • 37. Generación dinámica de rutas COUNTRYid url15 uk URL_WORD_TRANS6 fr id country_id url_word_id value25 nl 1 15 7 listings 2 6 7 grilleURL_WORD 3 25 7 weekoverzichtid value7 listings Un poquito de base de datos
  • 38. ¿Qué rutas i18n podemos encontrar?Peticiones de los usuarios: http://natgeotv.com/uk/listings http://natgeotv.com/fr/grille http://natgeotv.com/nl/weekoverzichtAcción de esas peticiones: Controlador: Schedule Acción: listings Análisis del problema
  • 39. Modificando la generación de rutasInterceptar el momento en el que se lee la configuración de las rutas:<services> ... <service id="bndl.routing.loader.yml" class="%bndl.routing.loader.yml.class%" public="false"> <tag name="routing.loader" /> <argument type="service" id="service_container" /> <argument type="service" id="bndl.file_locator" /> </service> <service id="routing.loader.yml" alias="bndl.routing.loader.yml" /> ...</services> Adaptación de la estructura
  • 40. Modificando la generación de rutasInterceptar el momento en el que se lee la configuración de las rutas:<services> ... <service id="bndl.routing.loader.yml" class="%bndl.routing.loader.yml.class%" public="false"> <tag name="routing.loader" /> <argument type="service" id="service_container" /> <argument type="service" id="bndl.file_locator" /> </service> <service id="routing.loader.yml" alias="bndl.routing.loader.yml" /> ...</services> Adaptación de la estructura
  • 41. Modificando la generación de rutas¿Qué hace la clase original?namespace SymfonyComponentRoutingLoader;class YamlFileLoader extends FileLoader{ protected function parseRoute(RouteCollection $collection, $name, $config, $file) { ... $route = new Route($config[pattern], $defaults, $requirements, $options); $collection->add($name, $route); }} Aprendiendo de sf2
  • 42. Modificando la generación de rutasExtendemos el servicio original:use SymfonyComponentRoutingLoaderYamlFileLoader as BaseYamlFileLoader;class YamlFileLoader extends BaseYamlFileLoader{ protected function parseRoute(RouteCollection $collection, $name, $config, $file) { $url_words_trans = ...; // Consulta a la tabla url_words $country = ...; // Consulta a la tabla country ... $route = new I18nRoute(..., $url_words_trans, $country); $collection->addCollection($route->getCollection()); }} Adaptando la arquitectura
  • 43. Modificando la generación de rutasclass I18nRoute { public function __construct($original_pattern, $defaults = array(), $requirements = array(), $options = array(), $translations = array(), $regions = array()) { // Construccion a partir del patrón original, sus equivalentes por país $patterns = ...; // Formato: array(_locale => pattern) ... //Construcción de una ruta para cada patrón nuevo foreach ($patterns as $country_url => $i18n_pattern) { $requirements[_locale] = $country_url; $this->collection->add($name . _ . country_url, new Route($i18n_pattern, $defaults, $requirements, $options, true)); } $this->collection->add($name, new Route($original_pattern, $defaults, $requirements, $options, true)); }} Adaptando la arquitectura
  • 44. ResultadoBndl_listings_uk: pattern: /{_locale}/listings defaults: { _controller: ”Bndl:Schedule:listings" } requirements: _locale: ukBndl_listings_nl: pattern: /{_locale}/weekoverzicht defaults: { _controller: ”Bndl:Schedule:listings" } requirements: _locale: nlBndl_listings: pattern: /{_locale}/listings defaults: { _controller: ”Bndl:Schedule:listings" } Rutas disponibles
  • 45. Flujo de trabajo_locale SIexiste SI Procesar Construir ruta acción vista
  • 46. Flujo de trabajo_locale SIexiste SI Procesar Construir ruta acción vista
  • 47. Flujo de trabajo_locale SIexiste SI Procesar Construir ruta acción vista
  • 48. ¿Vistas i18n?
  • 49. ¿Dónde pueden estar mis vistas? app/Resources/Bndl/views src/Bndl/Resources/views¿Qué hacer si tenemos requisitos específicos para un país?: Llenar el código de {% if %} Análisis del problema
  • 50. ¿Dónde pueden estar mis vistas? app/Resources/Bndl/views src/Bndl/Resources/views¿Qué hacer si tenemos requisitos específicos para un país?: Llenar el código de ¡Acaben ya conmigo! {% if %} Análisis del problema
  • 51. ¿Dónde pueden estar mis vistas?¿Alguna idea mejor?:•  Se puede utilizar la plantilla que se encuentre primero en este nuevo árbol de directorios src/Bndl/Resources/views/{_locale} src/Bndl/Resources/views/%DIR_BASE% src/Bndl/Resources/views Análisis del problema
  • 52. Modificando la localización de las vistasInterceptar el momento cuando crear el árbol de directorios:<services> ... <service id="bnbl.file_locator" class="%bnbl.file_locator.class%"> <argument type="service" id="kernel" /> <argument>%kernel.root_dir%/Resources</argument> </service> <service id="file_locator" alias="bnbl.file_locator" /> ...</services> Adaptación de la estructura
  • 53. Modificando la localización de las vistasInterceptar el momento cuando crear el árbol de directorios:<services> ... <service id="bnbl.file_locator" class="%bnbl.file_locator.class%"> <argument type="service" id="kernel" /> <argument>%kernel.root_dir%/Resources</argument> </service> <service id="file_locator" alias="bnbl.file_locator" /> ...</services> Adaptación de la estructura
  • 54. Modificando la localización de las vistas¿Qué hace la clase original?:namespace SymfonyComponentHttpKernelConfig;class FileLocator extends BaseFileLocator{ public function locate($file, $currentPath = null, $first = true) { ... return $this->kernel->locateResource($file, $this->path, $first); ... }} Adaptando la arquitectura
  • 55. Modificando la localización de las vistasExtendemos el servicio original:use SymfonyComponentHttpKernelConfigFileLocator as BaseFileLocator;class FileLocator extends BaseFileLocator{ public function locate($file, $currentPath = null, $first = true) { ... return $this->locateResource($file, $this->path, $first); ... }} Adaptando la arquitectura
  • 56. Modificando la localización de las vistasExtendemos el servicio original:use SymfonyComponentHttpKernelConfigFileLocator as BaseFileLocator;class FileLocator extends BaseFileLocator{ public function locate($file, $currentPath = null, $first = true) { ... return $this->locateResource($file, $this->path, $first); ... }} Adaptando la arquitectura
  • 57. Modificando la localización de las vistasclass FileLocator extends BaseFileLocator{ // Lógica de la clase SymfonyComponentHttpKernelKernel public function locateResource($name, $dir = null, $first = true) { $myDir = ...; // Nombre del directorio para las vistas del país foreach ( $bundles as $bundle ) { $checkPaths = ...; // Rutas relativas a chequear foreach ( $checkPaths as $checkPath) { if ( file_exists($file = $checkPath)) { // Verificación de la plantilla } } } ... }} Adaptando la arquitectura
  • 58. ResultadoEl árbol de directorios en el que se chequean la existencia de las plantillas es: app/Resources/Bndl/views/{_locale} app/Resources/Bndl/views/%DIR_BASE% app/Resources/Bndl/views src/Bndl/Resources/views/{_locale} src/Bndl/Resources/views/%DIR_BASE% src/Bndl/Resources/views Rutas de directorios disponibles
  • 59. Resultado Hong KongRutas de directorios disponibles
  • 60. Resultado Hong Kong Reino UnidoRutas de directorios disponibles
  • 61. Resultado Hong Kong Reino UnidoRutas de directorios disponibles
  • 62. ¿Quebraderos de cabeza?
  • 63. Lo que no hay que dejar pasar•  Establecer la zona horaria correcta.•  Formatos de fechas.•  Conversión de texto a mayúscula.•  Cachear lo no cacheable.•  Especificar el encoding en la configuración de los servicios que acceden a base de datos.•  ... Pequeñas soluciones
  • 64. Lo que no hay que dejar pasarConversión de texto a mayúscula: {{ | upper }} Pequeñas soluciones
  • 65. Lo que no hay que dejar pasarConversión de texto a mayúscula: {{ | upper }}Debería imprimir: Pequeñas soluciones
  • 66. Lo que no hay que dejar pasarConversión de texto a mayúscula: {{ | upper }}Debería imprimir: ¡PERO NO! Imprime: SIBER SIRLAR Pequeñas soluciones
  • 67. Lo que no hay que dejar pasarConversión de texto a mayúscula: {{ | upper }}Debería imprimir: ¡PERO NO! Imprime: SIBER SIRLAR Pequeñas soluciones
  • 68. Lo que no hay que dejar pasarSolución: Sobreescribir el método upper de twig.class TextExtension extends Twig_Extension{ public function upper(Twig_Environment $env, $sentence) { $value = str_replace(ı, I, str_replace(i, İ, $sentence)); if ( null !== ( $charset = $env->getCharset() ) ) { return mb_strtoupper($value, $charset); } return strtoupper($value); }} Pequeñas soluciones
  • 69. Infraestructura
  • 70. Load Balancer Varnish (n)Media Frontend (n) Backend (n) | BBDD
  • 71. Deploy y más deploy
  • 72. ¿Cómo manejamos los entornos?•  Tenemos múltiples servidores que coordinar y monitorizar.•  Necesitamos una herramienta que actúe sobre todos.•  Nuestra elección Magallanes, que sería como el “Capistrano de PHP”, creado por @andres_montanez, miembro del equipo. Análisis de la situación
  • 73. Magallanes, ¡hay que probarlo!•  Posibilidad de configurar múltiples “targets”•  Posibilidad de crear “pre-tasks”•  Integración con git•  Rollback instantáneo•  Posibilidad de post-tasks•  Configuración de número de releases a archivar.•  Posibilidad de sobreescribir realeases. ¡Feliz deploy a todos!
  • 74. Muchas gracias @jautu quintana.cano@gmail.comhttp://es.linkedin.com/in/cristinaquintanacano Contacto

×