У нас есть (довольно типичное?) Соглашение для многоязычного веб-сайта Symfony CMF, где пути к ресурсам начинаются с префикса желаемой локали, например:
http://www.example.com/en/path/to/english-resource.html
; а такжеhttp://www.example.com/fr/voie/à/ressource-française.html
,Мы используем RoutingAutoBundle хранить такие маршруты в хранилище контента, и DynamicRouter использовать их: просто и легко.
Если GET
запрос приходит без префикс локали, мы хотели бы:
Первая часть является очевидным кандидатом на LuneticsLocaleBundle, с router
выше в своем порядке угадывания, чем наши желаемые методы отступления: опять же, просто и легко.
Однако то, как лучше реализовать вторую часть, немного менее очевидно. В настоящее время мы настроили статический / статический маршрутизатор Symfony на более низкий приоритет в цепочке маршрутизации, чем DynamicRouter, и настроили контроллер следующим образом:
/**
* @Route("/{path}", requirements={"path" = "^(?!(en|fr)(/.*)?$)"})
* @Method({"GET"})
*/
public function localeNotInUriAction()
{
$request = this->getRequest();
$this->redirect(
'/'
. $request->getLocale() // set by Lunetics
. $request->getRequestUri()
);
}
Но это кажется довольно хакерским, и я нахожусь в поиске чего-то более «чистого».
Первоначально я думал изменить LuneticsLocaleBundle так, чтобы он вызывал событие всякий раз, когда гадатель определяет локаль, думая, что если бы это не было RouterLocaleGuesser
тогда мы можем сделать вывод, что запрошенный URI не содержит локали. Однако это явно не так, так как RouterLocaleGuesser
определит локаль только в том случае, если сначала был маршрут, поэтому я бы не продвинулся.
Я сейчас немного застрял на любых других идеях. Возможно, я уже все делаю правильно? Если так, то все, что мне нужно сделать, — это найти способ вставить разрешенные локали (из конфига) в регулярное выражение требования…
мы используем пользовательский обработчик 404 и лунетик:
exception_listener:
class: AppBundle\EventListener\ExceptionListener
arguments:
container: "@service_container"tags:
- { name:"kernel.event_listener", event:kernel.exception, handler:onKernelException }
и класс php
class ExceptionListener
{
/**
* @var ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
if ($this->container->getParameter('kernel.debug')) {
// do not interfere with error handling while debugging
return;
}
$exception = $event->getException();
if ($exception instanceof NotFoundHttpException) {
$this->handle404($event);
return;
}
// ...
}
public function handle404(GetResponseForExceptionEvent $event)
{
$request = $event->getRequest();
if (preg_match('#^\/(de|fr|en)\/#', $request->getPathInfo())) {
// a real 404, these are nicely handled by Twig
return;
}
// i *think* that the locale is not set on the request, as lunetics comes after routing, and the routing will raise the 404
$bestLang = $this->container->get('lunetics_locale.guesser_manager')->runLocaleGuessing($request);
if (! $bestLang) {
$bestLang = 'de';
}
$qs = $request->getQueryString();
if (null !== $qs) {
$qs = '?'.$qs;
}
$url = $request->getSchemeAndHttpHost() . $request->getBaseUrl() . '/' . $bestLang . $request->getPathInfo() . $qs;
$this->redirect($event, $url);
}
было бы лучше также проверить, существует ли целевой путь на самом деле — как мы перенаправим / foobar в / de / foobar и отобразим 404 для этого, что не так уж и элегантно.
Других решений пока нет …