tutoriales tutorial symfony2 libros bootstrap and agregar php locale symfony symfony-routing

php - symfony2 - tutoriales symfony 3



Symfony 3 redirige todas las rutas a la versiĆ³n de configuraciĆ³n regional actual (2)

Estoy trabajando en una aplicación de Symfony en la que mi objetivo es que, independientemente de la página en la que se encuentre el usuario, vaya a la versión de la página de la configuración regional.

Por ejemplo, si el usuario navega hacia "/" la página de inicio, se redirigirá a "/ en /"

Si están en la página "/ admin" se redireccionará a "/ en / admin" , de tal manera que la propiedad _locale se establece desde la ruta.

También necesita determinar la configuración regional si visitan / admin desde el navegador de los usuarios, ya que no se determinó la configuración regional para saber a qué página redirigir.

Actualmente mi controlador predeterminado se ve a continuación desde que estoy probando. Estoy usando el modo de desarrollo y el perfil para probar que las traducciones funcionan correctamente.

<?php namespace AppBundle/Controller; use Sensio/Bundle/FrameworkExtraBundle/Configuration/Route; use Symfony/Bundle/FrameworkBundle/Controller/Controller; use Symfony/Component/HttpFoundation/Request; class DefaultController extends Controller { /** * @Route("/", name="homepage") * @Route("/{_locale}/", name="homepage_locale") */ public function indexAction(Request $request) { $translated = $this->get(''translator'')->trans(''Symfony is great''); // replace this example code with whatever you need return $this->render(''default/index.html.twig'', [ ''base_dir'' => realpath($this->container->getParameter(''kernel.root_dir'').''/..''), ''translated'' => $translated ]); } }

Este método actual mantendrá al usuario en "/" si navega allí, pero quiero que lo redireccione a "/ en /". Esto debería funcionar también para otras páginas, como / admin, o / somepath / pathagain / article1 (/ en / admin, / en / somepath / pathagain / article1)

¿Cómo haría esto?

Referencias que he leído que no ayudaron:

Symfony2 Usar configuración regional predeterminada en el enrutamiento (una URL para un idioma)

Symfony2 configuración regional predeterminada en el enrutamiento

::Actualizar::

No he resuelto mi problema pero me he acercado y he aprendido algunos trucos para ser más eficiente.

DefaultController.php

<?php namespace AppBundle/Controller; use Sensio/Bundle/FrameworkExtraBundle/Configuration/Route; use Symfony/Bundle/FrameworkBundle/Controller/Controller; use Symfony/Component/HttpFoundation/Request; class DefaultController extends Controller { /** * @Route("/", name="home", defaults={"_locale"="en"}, requirements={"_locale" = "%app.locales%"}) * @Route("/{_locale}/", name="home_locale", requirements={"_locale" = "%app.locales%"}) */ public function indexAction(Request $request) { $translated = $this->get(''translator'')->trans(''Symfony is great''); // replace this example code with whatever you need return $this->render(''default/index.html.twig'', [ ''base_dir'' => realpath($this->container->getParameter(''kernel.root_dir'').''/..''), ''translated'' => $translated ]); } /** * @Route("/admin", name="admin", defaults={"_locale"="en"}, requirements={"_locale" = "%app.locales%"}) * @Route("/{_locale}/admin", name="admin_locale", requirements={"_locale" = "%app.locales%"}) */ public function adminAction(Request $request) { $translated = $this->get(''translator'')->trans(''Symfony is great''); // replace this example code with whatever you need return $this->render(''default/index.html.twig'', [ ''base_dir'' => realpath($this->container->getParameter(''kernel.root_dir'').''/..''), ''translated'' => $translated ]); } } ?>

Config.yml

imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.yml } # Put parameters here that don''t need to change on each machine where the app is deployed # http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration parameters: locale: en app.locales: en|es|zh framework: #esi: ~ translator: { fallbacks: ["%locale%"] } secret: "%secret%" router: resource: "%kernel.root_dir%/config/routing.yml" strict_requirements: ~ form: ~ csrf_protection: ~ validation: { enable_annotations: true } #serializer: { enable_annotations: true } templating: engines: [''twig''] #assets_version: SomeVersionScheme default_locale: "%locale%" trusted_hosts: ~ trusted_proxies: ~ session: # handler_id set to null will use default session handler from php.ini handler_id: ~ save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%" fragments: ~ http_method_override: true assets: ~ # Twig Configuration twig: debug: "%kernel.debug%" strict_variables: "%kernel.debug%" # Doctrine Configuration doctrine: dbal: driver: pdo_mysql host: "%database_host%" port: "%database_port%" dbname: "%database_name%" user: "%database_user%" password: "%database_password%" charset: UTF8 # if using pdo_sqlite as your database driver: # 1. add the path in parameters.yml # e.g. database_path: "%kernel.root_dir%/data/data.db3" # 2. Uncomment database_path in parameters.yml.dist # 3. Uncomment next line: # path: "%database_path%" orm: auto_generate_proxy_classes: "%kernel.debug%" naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: true # Swiftmailer Configuration swiftmailer: transport: "%mailer_transport%" host: "%mailer_host%" username: "%mailer_user%" password: "%mailer_password%" spool: { type: memory }

Observe bajo parámetros el valor app.locales: en|es|zh . Este es ahora un valor al que puedo hacer referencia cada vez que creo mis rutas si planeo admitir más configuraciones regionales en el futuro, lo cual hago. Esas rutas son inglesas, españolas, chinas en ese orden para los curiosos. En DefaultController en las anotaciones, "%app.locales%" es la parte que hace referencia al parámetro de configuración.

El problema con mi método actual es / admin, por ejemplo, no redirecciona al usuario a / {navegadores locale} / admin, que sería la solución más elegante para mantener todo organizado ... pero al menos las rutas funcionan. Todavía estoy buscando una mejor solución.

****Actualizar****

Creo que posiblemente haya encontrado la respuesta aquí como la respuesta más baja dada ( Agregue la configuración regional y los requisitos para todas las rutas - Symfony2 ), la respuesta de Athlan. Simplemente no estoy seguro de cómo implementar esto en Symfony 3 ya que sus instrucciones no eran lo suficientemente claras para mí.

Creo que este artículo también podría ser útil ( http://symfony.com/doc/current/components/event_dispatcher/introduction.html )


Después de 12 horas de investigar esto, finalmente encontré una solución aceptable. Por favor, publique versiones revisadas de esta solución si puede hacerlo más eficiente.

Algunas cosas a tener en cuenta, mi solución es particular a mi necesidad. Lo que hace es forzar cualquier URL para ir a una versión localizada si existe.

Esto requiere algunas convenciones que se deben seguir cuando crea rutas.

DefaultController.php

<?php namespace AppBundle/Controller; use Sensio/Bundle/FrameworkExtraBundle/Configuration/Route; use Symfony/Bundle/FrameworkBundle/Controller/Controller; use Symfony/Component/HttpFoundation/Request; class DefaultController extends Controller { /** * @Route("/{_locale}/", name="home_locale", requirements={"_locale" = "%app.locales%"}) */ public function indexAction(Request $request) { $translated = $this->get(''translator'')->trans(''Symfony is great''); // replace this example code with whatever you need return $this->render(''default/index.html.twig'', [ ''base_dir'' => realpath($this->container->getParameter(''kernel.root_dir'').''/..''), ''translated'' => $translated ]); } /** * @Route("/{_locale}/admin", name="admin_locale", requirements={"_locale" = "%app.locales%"}) */ public function adminAction(Request $request) { $translated = $this->get(''translator'')->trans(''Symfony is great''); // replace this example code with whatever you need return $this->render(''default/index.html.twig'', [ ''base_dir'' => realpath($this->container->getParameter(''kernel.root_dir'').''/..''), ''translated'' => $translated ]); } } ?>

Tenga en cuenta que ambas rutas siempre comienzan con "/ {_ locale} /". Para que esto funcione, todas las rutas de tu proyecto deben tener esto. Usted acaba de poner el nombre de la ruta real después. Para mí, estaba bien con este escenario. Puede modificar mi solución para satisfacer sus necesidades con la suficiente facilidad.

El primer paso es crear una escucha en httpKernal para interceptar las solicitudes antes de que vayan a los enrutadores para procesarlas.

LocaleRewriteListener.php

<?php //src/AppBundle/EventListener/LocaleRewriteListener.php namespace AppBundle/EventListener; use Symfony/Component/HttpFoundation/RedirectResponse; use Symfony/Component/Routing/RouterInterface; use Symfony/Component/HttpKernel/Event/GetResponseEvent; use Symfony/Component/HttpKernel/KernelEvents; use Symfony/Component/EventDispatcher/EventSubscriberInterface; use Symfony/Component/HttpFoundation/Session/Session; use Symfony/Component/Routing/RouteCollection; class LocaleRewriteListener implements EventSubscriberInterface { /** * @var Symfony/Component/Routing/RouterInterface */ private $router; /** * @var routeCollection /Symfony/Component/Routing/RouteCollection */ private $routeCollection; /** * @var string */ private $defaultLocale; /** * @var array */ private $supportedLocales; /** * @var string */ private $localeRouteParam; public function __construct(RouterInterface $router, $defaultLocale = ''en'', array $supportedLocales = array(''en''), $localeRouteParam = ''_locale'') { $this->router = $router; $this->routeCollection = $router->getRouteCollection(); $this->defaultLocale = $defaultLocale; $this->supportedLocales = $supportedLocales; $this->localeRouteParam = $localeRouteParam; } public function isLocaleSupported($locale) { return in_array($locale, $this->supportedLocales); } public function onKernelRequest(GetResponseEvent $event) { //GOAL: // Redirect all incoming requests to their /locale/route equivlent as long as the route will exists when we do so. // Do nothing if it already has /locale/ in the route to prevent redirect loops $request = $event->getRequest(); $path = $request->getPathInfo(); $route_exists = false; //by default assume route does not exist. foreach($this->routeCollection as $routeObject){ $routePath = $routeObject->getPath(); if($routePath == "/{_locale}".$path){ $route_exists = true; break; } } //If the route does indeed exist then lets redirect there. if($route_exists == true){ //Get the locale from the users browser. $locale = $request->getPreferredLanguage(); //If no locale from browser or locale not in list of known locales supported then set to defaultLocale set in config.yml if($locale=="" || $this->isLocaleSupported($locale)==false){ $locale = $request->getDefaultLocale(); } $event->setResponse(new RedirectResponse("/".$locale.$path)); } //Otherwise do nothing and continue on~ } public static function getSubscribedEvents() { return array( // must be registered before the default Locale listener KernelEvents::REQUEST => array(array(''onKernelRequest'', 17)), ); } }

Finalmente, configura services.yml para iniciar el escucha.

Servicios.yml

# Learn more about services, parameters and containers at # http://symfony.com/doc/current/book/service_container.html parameters: # parameter_name: value services: # service_name: # class: AppBundle/Directory/ClassName # arguments: ["@another_service_name", "plain_value", "%parameter_name%"] appBundle.eventListeners.localeRewriteListener: class: AppBundle/EventListener/LocaleRewriteListener arguments: ["@router", "%kernel.default_locale%", "%locale_supported%"] tags: - { name: kernel.event_subscriber }

También en config.yml deseará agregar lo siguiente debajo de los parámetros:

config.yml

parameters: locale: en app.locales: en|es|zh locale_supported: [''en'',''es'',''zh'']

Quería que hubiera un solo lugar donde definieras las configuraciones regionales, pero terminé teniendo que hacer 2 ... pero al menos están en el mismo lugar y son tan fáciles de cambiar.

app.locales se usa en el controlador predeterminado (requirements={"_locale" = "%app.locales%"}) y locale_supported se usa en LocaleRewriteListener. Si detecta una configuración regional que no está en la lista, recurrirá a la configuración regional predeterminada, que en este caso es el valor de la configuración regional: en.

app.locales es agradable con el comando de requisitos porque generará un 404 para cualquier configuración regional que no coincida.

Si está utilizando formularios y tiene un inicio de sesión, deberá hacer lo siguiente a su security.yml

Security.yml

# To get started with security, check out the documentation: # http://symfony.com/doc/current/book/security.html security: encoders: Symfony/Component/Security/Core/User/User: algorithm: bcrypt cost: 12 AppBundle/Entity/User: algorithm: bcrypt cost: 12 role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] providers: # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers database: entity: { class: AppBundle:User } #property: username # if you''re using multiple entity managers # manager_name: customer firewalls: # disables authentication for assets and the profiler, adapt it according to your needs dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: pattern: ^/ anonymous: true form_login: check_path: login_check login_path: login_route provider: database csrf_token_generator: security.csrf.token_manager remember_me: secret: ''%secret%'' lifetime: 604800 # 1 week in seconds path: / httponly: false #httponly false does make this vulnerable in XSS attack, but I will make sure that is not possible. logout: path: /logout target: / access_control: # require ROLE_ADMIN for /admin* #- { path: ^/login, roles: ROLE_ADMIN } - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/(.*?)/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/, roles: ROLE_USER }

El cambio importante a tener en cuenta aquí es que (.*?)/login se autenticará de forma anónima para que sus usuarios aún puedan iniciar sesión. Esto significa que las rutas como ...dogdoghere / login pueden activarse, pero los requisitos que le mostraré en breve en las rutas de inicio de sesión evitan esto y arrojarán errores 404. Me gusta esta solución con el (.*?) Frente a [az]{2} caso de que desee utilizar locales en_US.

SecurityController.php

<?php // src/AppBundle/Controller/SecurityController.php namespace AppBundle/Controller; use Symfony/Bundle/FrameworkBundle/Controller/Controller; use Symfony/Component/HttpFoundation/Request; use Sensio/Bundle/FrameworkExtraBundle/Configuration/Route; class SecurityController extends Controller { /** * @Route("{_locale}/login", name="login_route", defaults={"_locale"="en"}, requirements={"_locale" = "%app.locales%"}) */ public function loginAction(Request $request) { $authenticationUtils = $this->get(''security.authentication_utils''); // get the login error if there is one $error = $authenticationUtils->getLastAuthenticationError(); // last username entered by the user $lastUsername = $authenticationUtils->getLastUsername(); return $this->render( ''security/login.html.twig'', array( // last username entered by the user ''last_username'' => $lastUsername, ''error'' => $error, ) ); } /** * @Route("/{_locale}/login_check", name="login_check", defaults={"_locale"="en"}, requirements={"_locale" = "%app.locales%"}) */ public function loginCheckAction() { // this controller will not be executed, // as the route is handled by the Security system } /** * @Route("/logout", name="logout") */ public function logoutAction() { } } ?>

Tenga en cuenta que incluso estas rutas usan {_locale} al frente. Sin embargo, esto me gusta, así que puedo dar acceso personalizado a diferentes configuraciones regionales. Solo ten eso en cuenta. La única ruta que no necesita la configuración regional es la desconexión, que funciona bien, ya que realmente es solo una ruta de interceptación para el sistema de seguridad. También tenga en cuenta que utiliza los requisitos que se establecen desde config.yml, por lo que solo debe editarlos en un solo lugar para todas las rutas en sus proyectos.

Espero que esto ayude a alguien a tratar de hacer lo que estaba haciendo!

NOTA :: Para probar esto fácilmente, uso la extensión ''Quick Language Switcher'' para Google Chrome, que cambia el encabezado accept-language en todas las solicitudes.


No tengo suficiente reputación para agregar un comentario a la solución correcta. Así que estoy agregando una nueva respuesta

Puede agregar "prefix: / {_ locale}" en app / config / routing.yml de esta manera:

app: resource: "@AppBundle/Controller/" type: annotation prefix: /{_locale}

Por lo tanto, no es necesario que lo agregue a todas las rutas de cada acción. Para los siguientes pasos. Muchas gracias, es perfecto.