简体   繁体   中英

How should one redirect locale-less URIs to locale-full ones in Symfony-CMF?

Background

We have a (fairly typical?) arrangement for a multilingual Symfony CMF website, where resource paths are prefixed by the desired locale—for example:

  • http://www.example.com/en/path/to/english-resource.html ; and
  • http://www.example.com/fr/voie/à/ressource-française.html .

We are using RoutingAutoBundle to store such routes in the content repository, and DynamicRouter to utilise them: simple and easy.

If a GET request arrives without a locale prefix, we would like to:

  1. determine the most appropriate locale for the user; and then
  2. redirect 1 the user to the same path but with locale prefix added.

Current Approach

The first part is an obvious candidate for LuneticsLocaleBundle , with router higher in its guessing order than our desired fallback methods: again, simple and easy.

However, how best to implement the second part is a little less obvious. Currently we have configured Symfony's default/static router to have a lower priority in the routing chain than DynamicRouter, and have therein configured a controller as follows:

/**
 * @Route("/{path}", requirements={"path" = "^(?!(en|fr)(/.*)?$)"})
 * @Method({"GET"})
 */
public function localeNotInUriAction()
{
    $request = this->getRequest();

    $this->redirect(
        '/'
      . $request->getLocale()     // set by Lunetics
      . $request->getRequestUri()
    );
}

But this feels rather hacky and I'm on the search for something "cleaner".

A better way?

Initially I thought to modify LuneticsLocaleBundle so that it would fire an event whenever a guesser determines the locale, thinking that if it was not the RouterLocaleGuesser then we could infer that the requested URI did not contain a locale. However this clearly isn't the case, since the RouterLocaleGuesser will only determine the locale if there was a route in the first place—so I'd not have made any progress.

I'm now a bit stuck for any other ideas. Perhaps I'm already doing the right thing after all? If so, then all I need to do is find some way to inject the permitted locales (from the config) into the requirement regex…


  1. External redirection, ie via a response with HTTP 302 status.

we use a custom 404 handler and lunetics:

exception_listener:
    class: AppBundle\EventListener\ExceptionListener
    arguments:
        container: "@service_container"
    tags:
        - { name:"kernel.event_listener", event:kernel.exception, handler:onKernelException }

and the php class

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);
}

it would be nicer to also check if the target path actually exists - as is, we will redirect /foobar to /de/foobar and display a 404 for that one, which is not that elegant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM