<?php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouterInterface;
class LocaleSubscriber implements EventSubscriberInterface
{
/**
* @var RouterInterface
*/
private $router;
/**
* @var RouteCollection
*/
private $routeCollection;
/**
* @var string
*/
private $defaultLocale;
/**
* @var array
*/
private $supportedLocales;
/**
* @var string
*/
private $localeRouteParam;
/**
* @param RouterInterface $router
* @param $defaultLocale
* @param array $supportedLocales
*/
public function __construct($defaultLocale, RouterInterface $router, array $supportedLocales = array('fr'), $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);
}
/**
* Redirect to correct locale route if omitted or not concordant with route requirements.
*
* @param ResponseEvent $event
* @return void
*/
public function onKernelResponse(ResponseEvent $event): void
{
//--------------------------------------------------------------------------------------------------------------
// GOAL:
//--------------------------------------------------------------------------------------------------------------
// Redirect all incoming requests to their /locale/route equivalent as long as the route will exists when we do so.
// If the route required a specific locale so we force it.
// 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.
$force_locale = false; // By default, assume not forcing locale.
foreach ($this->routeCollection as $routeObject){
$routePath = $routeObject->getPath();
if ($routePath == '/'.$this->localeRouteParam.$path){
$route_requirements = $routeObject->getRequirements();
$route_exists = true;
// Checks if route has a required locale.
if (is_array($route_requirements) && array_key_exists($this->localeRouteParam, $route_requirements)
&& !empty($route_requirements[$this->localeRouteParam])) {
$force_locale = $route_requirements[$this->localeRouteParam];
}
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.
if (!(!empty($locale) && $this->isLocaleSupported($locale) === true)) {
$locale = $request->getDefaultLocale();
}
// If a locale was detected in route and current locale is different of required locale.
if(!is_bool($force_locale) && $locale !== $force_locale) {
$locale = $force_locale;
}
$event->setResponse(new RedirectResponse("/".$locale.$path));
}
elseif ($request->get($this->localeRouteParam) === null && $path === '/')
$event->setResponse(new RedirectResponse('/'.$this->defaultLocale.'/'));
//Otherwise do nothing and continue on~
}
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $request->attributes->get($this->localeRouteParam)) {
$request->getSession()->set($this->localeRouteParam, $locale);
} else {
// if no explicit locale has been set on this request, use one from the session
$request->setLocale($request->getSession()->get($this->localeRouteParam, $this->defaultLocale));
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => [['onKernelResponse', 17]],
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::REQUEST => [['onKernelRequest', 20]],
];
}
}