<?php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Based on https://symfony.com/doc/4.4/session/locale_sticky_session.html
*
*
*/
class LocaleSubscriber implements EventSubscriberInterface {
private $defaultLocale;
public function __construct($defaultLocale = 'es') {
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(RequestEvent $event) {
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// try to get language from the HTTP_ACCEPT_LANGUAGE
$locale = $this->detect($request);
if ($locale) {
$request->getSession()->set('_locale', $locale);
}
//Retrieve locale value from session, if not, the default will be used
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
setLocale(LC_TIME, $request->getSession()->get('_locale', $this->defaultLocale));
}
/**
*
* @return type
*/
public static function getSubscribedEvents(): array {
return [
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::REQUEST => [['onKernelRequest', 20]],
];
}
/**
* Copied and adapted from golfandalucia.com
*
* @param type $request
* @return type
*/
private function detect($request): string {
$httpAcceptLanguageHeader = $this->getHttpAcceptLanguageHeader($request);
if ($httpAcceptLanguageHeader == null) {
return 'es';
}
$locales = $this->getWeightedLocales($httpAcceptLanguageHeader);
$sortedLocales = $this->sortLocalesByWeight($locales);
$mappedLocales = array_map(function ($weightedLocale) {
return $weightedLocale['locale'];
}, $sortedLocales);
return (str_contains($mappedLocales[0], 'es')) ? 'es' : 'en';
}
private function getHttpAcceptLanguageHeader($request) {
if (null !== ($request->server->get('HTTP_ACCEPT_LANGUAGE'))) {
return trim($request->server->get('HTTP_ACCEPT_LANGUAGE'));
} else {
return null;
}
}
private function getWeightedLocales($httpAcceptLanguageHeader) {
if (strlen($httpAcceptLanguageHeader) == 0) {
return [];
}
$weightedLocales = [];
// We break up the string 'en-CA,ar-EG;q=0.5' along the commas,
// and iterate over the resulting array of individual locales. Once
// we're done, $weightedLocales should look like
// [['locale' => 'en-CA', 'q' => 1.0], ['locale' => 'ar-EG', 'q' => 0.5]]
foreach (explode(',', $httpAcceptLanguageHeader) as $locale) {
// separate the locale key ("ar-EG") from its weight ("q=0.5")
$localeParts = explode(';', $locale);
$weightedLocale = ['locale' => $localeParts[0]];
if (count($localeParts) == 2) {
// explicit weight e.g. 'q=0.5'
$weightParts = explode('=', $localeParts[1]);
// grab the '0.5' bit and parse it to a float
$weightedLocale['q'] = floatval($weightParts[1]);
} else {
// no weight given in string, ie. implicit weight of 'q=1.0'
$weightedLocale['q'] = 1.0;
}
$weightedLocales[] = $weightedLocale;
}
return $weightedLocales;
}
/**
* Sort by high to low `q` value
*/
private function sortLocalesByWeight($locales) {
usort($locales, function ($a, $b) {
// usort will cast float values that we return here into integers,
// which can mess up our sorting. So instead of subtracting the `q`,
// values and returning the difference, we compare the `q` values and
// explicitly return integer values.
if ($a['q'] == $b['q']) {
return 0;
}
if ($a['q'] > $b['q']) {
return -1;
}
return 1;
});
return $locales;
}
}