instalar - Symfony2 configuración regional predeterminada en el enrutamiento
symfony español (11)
Tengo un problema con el enrutamiento y la internacionalización de mi sitio creado con Symfony2.
Si defino rutas en el archivo routing.yml, así:
example:
pattern: /{_locale}/example
defaults: { _controller: ExampleBundle:Example:index, _locale: fr }
Funciona bien con URL como:
mysite.com/en/example
mysite.com/fr/example
Pero no funciona con
mysite.com/example
¿Podría ser que los marcadores de posición opcionales están permitidos solo al final de una URL?
En caso afirmativo, ¿cuál podría ser una posible solución para mostrar una url como:
mysite.com/example
en un idioma predeterminado o redirigir al usuario a:
mysite.com/defaultlanguage/example
cuando visita:
mysite.com/example. ?
Estoy tratando de resolverlo pero sin éxito hasta ahora.
Gracias.
¡Hay mi Solución, hace que este proceso sea más rápido!
Controlador:
/**
* @Route("/change/locale/{current}/{locale}/", name="locale_change")
*/
public function setLocaleAction($current, $locale)
{
$this->get(''request'')->setLocale($locale);
$referer = str_replace($current,$locale,$this->getRequest()->headers->get(''referer''));
return $this->redirect($referer);
}
Ramita:
<li {% if app.request.locale == language.locale %} class="selected" {% endif %}>
<a href="{{ path(''locale_change'', { ''current'' : app.request.locale, ''locale'' : language.locale } ) }}"> {{ language.locale }}</a>
</li>
Creo que simplemente podría agregar una ruta como esta:
example:
pattern: /example
defaults: { _controller: ExampleBundle:Example:index }
De esta manera, la configuración regional sería la última configuración regional seleccionada por el usuario, o la configuración regional predeterminada si la configuración regional del usuario no se ha establecido. También puede agregar el parámetro "_locale" a los "valores predeterminados" en su configuración de enrutamiento si desea establecer una configuración regional específica para / ejemplo:
example:
pattern: /example
defaults: { _controller: ExampleBundle:Example:index, _locale: fr }
No sé si hay una mejor manera de hacer esto.
Esto es lo que uso para la detección y redirección automática de la configuración regional, funciona bien y no requiere largas anotaciones de ruta:
routing.yml
La ruta de locale
maneja la raíz del sitio web y luego todas las demás acciones del controlador se anexan a la configuración regional.
locale:
path: /
defaults: { _controller: AppCoreBundle:Core:locale }
main:
resource: "@AppCoreBundle/Controller"
prefix: /{_locale}
type: annotation
requirements:
_locale: en|fr
CoreController.php
Esto detecta el idioma del usuario y redirige a la ruta que elijas. Uso el hogar como predeterminado, ya que es el caso más común.
public function localeAction($route = ''home'', $parameters = array())
{
$this->getRequest()->setLocale($this->getRequest()->getPreferredLanguage(array(''en'', ''fr'')));
return $this->redirect($this->generateUrl($route, $parameters));
}
Entonces, las anotaciones de ruta pueden ser simplemente:
/**
* @Route("/", name="home")
*/
public function indexAction(Request $request)
{
// Do stuff
}
Ramita
La opción localeAction se puede usar para permitir al usuario cambiar la configuración regional sin navegar desde la página actual:
<a href="{{ path(app.request.get(''_route''), app.request.get(''_route_params'')|merge({''_locale'': targetLocale })) }}">{{ targetLanguage }}</a>
Limpio y simple!
La solución de Joseph Astrahan de LocalRewriteListener funciona a excepción de la ruta con params debido a $routePath == "/{_locale}".$path)
Ej: $routePath = "/{_locale}/my/route/{foo}"
es diferente de $path = "/{_locale}/my/route/bar"
Tuve que usar UrlMatcher (enlace a Symfony 2.7 api doc) para hacer coincidir la ruta real con la url.
Cambio isLocaleSupported para usar el código local del navegador (por ejemplo, fr -> fr_FR). Uso la configuración regional del navegador como clave y la configuración regional de la ruta como valor. Tengo una matriz como esta array([''fr_FR''] => [''fr''], [''en_GB''] => ''en''...)
(ver el archivo de parámetros a continuación para más información)
Los cambios :
- Verifique si el local dado en la solicitud es compatible. Si no, usa la configuración regional predeterminada.
- Intenta hacer coincidir la ruta con la colección de ruta de la aplicación. Si no, no hagas nada (la aplicación arroja un 404 si la ruta no existe). En caso afirmativo, redirija con la configuración regional correcta en parámetro de ruta.
Aquí está mi código. Funciona para cualquier ruta con o sin param. Esto agrega la configuración regional solo cuando se configura {_local} en la ruta.
Archivo de enrutamiento (en mi caso, el de la aplicación / config)
app:
resource: "@AppBundle/Resources/config/routing.yml"
prefix: /{_locale}/
requirements:
_locale: ''%app.locales%''
defaults: { _locale: %locale%}
El parámetro en el archivo app / config / parameters.yml
locale: fr
app.locales: fr|gb|it|es
locale_supported:
fr_FR: fr
en_GB: gb
it_IT: it
es_ES: es
services.yml
app.eventListeners.localeRewriteListener:
class: AppBundle/EventListener/LocaleRewriteListener
arguments: ["@router", "%kernel.default_locale%", "%locale_supported%"]
tags:
- { name: kernel.event_subscriber }
LocaleRewriteListener.php
<?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;
use Symfony/Component/Routing/Matcher/UrlMatcher;
use Symfony/Component/Routing/RequestContext;
class LocaleRewriteListener implements EventSubscriberInterface
{
/**
* @var Symfony/Component/Routing/RouterInterface
*/
private $router;
/**
* @var routeCollection /Symfony/Component/Routing/RouteCollection
*/
private $routeCollection;
/**
* @var urlMatcher /Symfony/Component/Routing/Matcher/UrlMatcher;
*/
private $urlMatcher;
/**
* @var string
*/
private $defaultLocale;
/**
* @var array
*/
private $supportedLocales;
/**
* @var string
*/
private $localeRouteParam;
public function __construct(RouterInterface $router, $defaultLocale = ''fr'', array $supportedLocales, $localeRouteParam = ''_locale'')
{
$this->router = $router;
$this->routeCollection = $router->getRouteCollection();
$this->defaultLocale = $defaultLocale;
$this->supportedLocales = $supportedLocales;
$this->localeRouteParam = $localeRouteParam;
$context = new RequestContext("/");
$this->matcher = new UrlMatcher($this->routeCollection, $context);
}
public function isLocaleSupported($locale)
{
return array_key_exists($locale, $this->supportedLocales);
}
public function onKernelRequest(GetResponseEvent $event)
{
//GOAL:
// Redirect all incoming requests to their /locale/route equivalent when exists.
// Do nothing if it already has /locale/ in the route to prevent redirect loops
// Do nothing if the route requested has no locale param
$request = $event->getRequest();
$baseUrl = $request->getBaseUrl();
$path = $request->getPathInfo();
//Get the locale from the users browser.
$locale = $request->getPreferredLanguage();
if ($this->isLocaleSupported($locale)) {
$locale = $this->supportedLocales[$locale];
} else if ($locale == ""){
$locale = $request->getDefaultLocale();
}
$pathLocale = "/".$locale.$path;
//We have to catch the ResourceNotFoundException
try {
//Try to match the path with the local prefix
$this->matcher->match($pathLocale);
$event->setResponse(new RedirectResponse($baseUrl.$pathLocale));
} catch (/Symfony/Component/Routing/Exception/ResourceNotFoundException $e) {
} catch (/Symfony/Component/Routing/Exception/MethodNotAllowedException $e) {
}
}
public static function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array(''onKernelRequest'', 17)),
);
}
}
Puede definir varios patrones como este:
example_default:
pattern: /example
defaults: { _controller: ExampleBundle:Example:index, _locale: fr }
example:
pattern: /{_locale}/example
defaults: { _controller: ExampleBundle:Example:index}
requirements:
_locale: fr|en
Debería poder lograr el mismo tipo de cosas con las anotaciones:
/**
* @Route("/example", defaults={"_locale"="fr"})
* @Route("/{_locale}/example", requirements={"_locale" = "fr|en"})
*/
¡Espero que ayude!
Quizás resolví esto de una manera bastante simple:
example:
path: ''/{_locale}{_S}example''
defaults: { _controller: ''AppBundle:Example:index'' , _locale="de" , _S: "/" }
requirements:
_S: "/?"
_locale: ''|de|en|fr''
Curioso sobre el juicio de los críticos ... Mis mejores deseos, Greg
Si alguien está interesado, logré poner un prefijo en mi routing.yml
sin usar otros paquetes.
Entonces, esas URL funcionan:
www.example.com/
www.example.com//home/
www.example.com/fr/home/
www.example.com/en/home/
Edite su app/config/routing.yml
:
ex_example:
resource: "@ExExampleBundle/Resources/config/routing.yml"
prefix: /{_locale}
requirements:
_locale: |fr|en # put a pipe "|" first
Luego, en tu app/config/parameters.yml
, debes configurar una configuración regional
parameters:
locale: en
Con esto, las personas pueden acceder a su sitio web sin ingresar a un lugar específico.
Symfony3
app:
resource: "@AppBundle/Controller/"
type: annotation
prefix: /{_locale}
requirements:
_locale: en|bg| # put a pipe "|" last
Tengo una solución completa a esto que descubrí después de algunas investigaciones. Mi solución asume que quieres que cada ruta tenga una configuración regional delante, incluso iniciar sesión. Esto se modifica para admitir Symfony 3, pero creo que todavía funcionará en 2.
Esta versión también asume que desea utilizar la configuración regional de los navegadores como configuración regional predeterminada si van a una ruta como / admin, pero si van a / en / admin sabrán usar en locale. Este es el caso, por ejemplo, el n. ° 2 a continuación.
Así por ejemplo:
1. User Navigates To -> "/" -> (redirects) -> "/en/"
2. User Navigates To -> "/admin" -> (redirects) -> "/en/admin"
3. User Navigates To -> "/en/admin" -> (no redirects) -> "/en/admin"
En todos los escenarios, la configuración regional se configurará correctamente de la forma que desee para su uso en todo el programa.
Puede ver la solución completa a continuación, que incluye cómo hacer que funcione con el inicio de sesión y la seguridad, de lo contrario, la versión corta probablemente funcione para usted:
Versión completa
Symfony 3 redirige todas las rutas a la versión de configuración regional actual
Version corta
Para que sea posible que el caso n. ° 2 en mis ejemplos sea necesario, debe hacerlo usando un listador httpKernal
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)),
);
}
}
Para entender cómo funciona, busque la interfaz del suscriptor del evento en la documentación de Symfony.
Para activar el listner debes configurarlo en tus servicios.yml
services.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 }
Finalmente, esto se refiere a las variables que deben definirse en su config.yml
config.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
locale_supported: [''en'',''es'',''zh'']
Finalmente, debe asegurarse de que todas sus rutas comiencen con / {locale} por el momento. Una muestra de esto está debajo en mi controller.php predeterminado
<?php
namespace AppBundle/Controller;
use Sensio/Bundle/FrameworkExtraBundle/Configuration/Route;
use Symfony/Bundle/FrameworkBundle/Controller/Controller;
use Symfony/Component/HttpFoundation/Request;
/**
* @Route("/{_locale}", requirements={"_locale" = "%app.locales%"})
*/
class DefaultController extends Controller
{
/**
* @Route("/", name="home")
*/
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")
*/
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 los requisitos de requirements={"_locale" = "%app.locales%"}
, esto hace referencia al archivo config.yml por lo que solo debe definir esos requisitos en un solo lugar para todas las rutas.
Espero que esto ayude a alguien :)
Yo uso anotaciones, y lo haré
/**
* @Route("/{_locale}/example", defaults={"_locale"=""})
* @Route("/example", defaults={"_locale"="en"}, , requirements = {"_locale" = "fr|en|uk"})
*/
Pero para la forma yml, pruebe algunos equivalentes ...
root:
pattern: /
defaults:
_controller: FrameworkBundle:Redirect:urlRedirect
path: /en
permanent: true
Cómo configurar una redirección a otra ruta sin un controlador personalizado