src/EventSubscriber/LocaleSubscriber.php line 65

  1. <?php
  2. namespace App\EventSubscriber;
  3. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  4. use Symfony\Component\HttpFoundation\RedirectResponse;
  5. use Symfony\Component\HttpKernel\Event\RequestEvent;
  6. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  7. use Symfony\Component\HttpKernel\KernelEvents;
  8. use Symfony\Component\Routing\RouteCollection;
  9. use Symfony\Component\Routing\RouterInterface;
  10. class LocaleSubscriber implements EventSubscriberInterface
  11. {
  12.     /**
  13.      * @var RouterInterface
  14.      */
  15.     private $router;
  16.     /**
  17.      * @var RouteCollection
  18.      */
  19.     private $routeCollection;
  20.     /**
  21.      * @var string
  22.      */
  23.     private $defaultLocale;
  24.     /**
  25.      * @var array
  26.      */
  27.     private $supportedLocales;
  28.     /**
  29.      * @var string
  30.      */
  31.     private $localeRouteParam;
  32.     /**
  33.      * @param RouterInterface $router
  34.      * @param $defaultLocale
  35.      * @param array $supportedLocales
  36.      */
  37.     public function __construct($defaultLocaleRouterInterface $router, array $supportedLocales = array('fr''en'), $localeRouteParam '_locale')
  38.     {
  39.         $this->router $router;
  40.         $this->routeCollection $router->getRouteCollection();
  41.         $this->defaultLocale $defaultLocale;
  42.         $this->supportedLocales $supportedLocales;
  43.         $this->localeRouteParam $localeRouteParam;
  44.     }
  45.     public function isLocaleSupported($locale)
  46.     {
  47.         return in_array($locale$this->supportedLocales);
  48.     }
  49.     /**
  50.      * Redirect to correct locale route if omitted or not concordant with route requirements.
  51.      *
  52.      * @param ResponseEvent $event
  53.      * @return void
  54.      */
  55.     public function onKernelResponse(ResponseEvent $event): void
  56.     {
  57.         //--------------------------------------------------------------------------------------------------------------
  58.         // GOAL:
  59.         //--------------------------------------------------------------------------------------------------------------
  60.         // Redirect all incoming requests to their /locale/route equivalent as long as the route will exists when we do so.
  61.         // If the route required a specific locale so we force it.
  62.         // Do nothing if it already has /locale/ in the route to prevent redirect loops
  63.         //--------------------------------------------------------------------------------------------------------------
  64.         $request $event->getRequest();
  65.         $path $request->getPathInfo();
  66.         $pathWithoutLocale null;
  67.         if (preg_match('#^/([a-zA-Z]{2,3})/#'$path$parts)) {
  68.             $pathWithoutLocale substr($pathstrlen($parts[0]) -1);
  69.         }
  70.         $route_exists false// By default, assume route does not exist.
  71.         $force_locale false// By default, assume not forcing locale.
  72.         foreach ($this->routeCollection as $routeObject){
  73.             $routePath $routeObject->getPath();
  74.             if ($routePath == '/{'.$this->localeRouteParam.'}'.$path || $routePath == '/{'.$this->localeRouteParam.'}'.$pathWithoutLocale){
  75.                 $route_requirements $routeObject->getRequirements();
  76.                 $route_exists true;
  77.                 // Checks if route has a required locale.
  78.                 if (is_array($route_requirements) && array_key_exists($this->localeRouteParam$route_requirements)
  79.                     && !empty($route_requirements[$this->localeRouteParam])) {
  80.                     $routeLocales explode('|'$route_requirements[$this->localeRouteParam]);
  81.                     $force_locale count($routeLocales) > false $route_requirements[$this->localeRouteParam];
  82.                 }
  83.                 break;
  84.             }
  85.         }
  86.         // If the route does indeed exist then lets redirect there.
  87.         if ($route_exists == true){
  88.             // Get the locale from the users browser.
  89.             $locale $request->getPreferredLanguage();
  90.             // If no locale from browser or locale not in list of known locales supported then set to defaultLocale.
  91.             if (!(!empty($locale) && $this->isLocaleSupported($locale) === true)) {
  92.                 $locale $request->getDefaultLocale();
  93.             }
  94.             // If a locale was detected in route and current locale is different of required locale.
  95.             if(!is_bool($force_locale) && $locale !== $force_locale) {
  96.                 $locale $force_locale;
  97.             }
  98.             $targeted_url '/'.$locale.($pathWithoutLocale ?? $path);
  99.             if ($targeted_url === $path) {
  100.                 return;
  101.             }
  102.             $event->setResponse(new RedirectResponse($targeted_url));
  103.         }
  104.         elseif ($request->get($this->localeRouteParam) === null && $path === '/'
  105.             $event->setResponse(new RedirectResponse('/'.$this->defaultLocale.'/'));
  106.         
  107.         //Otherwise do nothing and continue on~
  108.     }
  109.     public function onKernelRequest(RequestEvent $event): void
  110.     {
  111.         $request $event->getRequest();
  112.         if (!$request->hasPreviousSession()) {
  113.             return;
  114.         }
  115.         // try to see if the locale has been set as a _locale routing parameter
  116.         if ($locale $request->attributes->get($this->localeRouteParam)) {
  117.             $request->getSession()->set($this->localeRouteParam$locale);
  118.         } else {
  119.             // if no explicit locale has been set on this request, use one from the session
  120.             $request->setLocale($request->getSession()->get($this->localeRouteParam$this->defaultLocale));
  121.         }
  122.     }
  123.     public static function getSubscribedEvents()
  124.     {
  125.         return [
  126.             KernelEvents::RESPONSE => [['onKernelResponse'17]],
  127.             // must be registered before (i.e. with a higher priority than) the default Locale listener
  128.             KernelEvents::REQUEST => [['onKernelRequest'20]],
  129.         ];
  130.     }
  131. }