src/EventSubscriber/LocaleSubscriber.php line 21

  1. <?php
  2. namespace App\EventSubscriber;
  3. use App\Entity\User;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  6. use Symfony\Component\HttpKernel\Event\RequestEvent;
  7. use Symfony\Component\HttpKernel\KernelEvents;
  8. class LocaleSubscriber implements EventSubscriberInterface
  9. {
  10.     private array $allowedLanguages = ['fr''en'];
  11.     public function __construct(private readonly EntityManagerInterface $em, private string $defaultLocale 'fr') {
  12.     }
  13.     public function onKernelRequest(RequestEvent $event): void
  14.     {
  15.         $request $event->getRequest();
  16.         $token unserialize($request->getSession()->get('_security_main'));
  17.         // Get navigator language
  18.         $browserLang $this->detectBrowserLang($request);
  19.         // Set local by url, by session, by navigator, by default
  20.         $locale $request->get('_locale');
  21.         // @codeCoverageIgnoreStart
  22.         if ($locale && $this->languageIsAllowed($locale)) {
  23.             $request->getSession()->set('_locale'$locale);
  24.             $request->setLocale($locale);
  25.         } elseif (($locale $request->getSession()->get('_locale')) && ($this->languageIsAllowed($locale))) {
  26.             // If no explicit locale has been set on this request, use one from the session
  27.             $request->setLocale($locale);
  28.         } elseif (!empty($browserLang)) {
  29.             $request->setLocale($browserLang);
  30.         } else {
  31.             $request->setLocale($this->defaultLocale);
  32.         }
  33.         // @codeCoverageIgnoreEnd
  34.         // Save locale to user data
  35.         if ($token && $token->getUser() && ($token->getUser() instanceof User) && $token->getUser()->getLocale() != $request->getLocale()) {
  36.             /** @var User $user */
  37.             $user $this->em->getRepository(User::class)->findOneBy(['email' => $token->getUser()->getEmail()]);
  38.             $user->setLocale($request->getLocale());
  39.             $this->em->flush();
  40.         }
  41.     }
  42.     /**
  43.      * Detect Browser language and return lang
  44.      */
  45.     private function detectBrowserLang($request): string
  46.     {
  47.         $result = [];
  48.         $browserLang '';
  49.         // ex : $acceptLanguage = fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7 : languages priority separate by ','
  50.         $acceptLanguageBrowser $request->headers->get('accept-language');
  51.         foreach (explode(','$acceptLanguageBrowser) as $acceptLanguage) {
  52.             preg_match('/^(([a-zA-Z]+)(-([a-zA-Z]+)){0,1})(;q=([0-9.]+)){0,1}/'$acceptLanguage$matches);
  53.             if (isset($matches[1])) {
  54.                 $result[$matches[1]] = [
  55.                     'lng_base' => strtolower($matches[2]),
  56.                     'lng' => $matches[1],
  57.                     'priority' => (float)(isset($matches[6]) ? $matches[6] : 1)
  58.                 ];
  59.             }
  60.         }
  61.         if (!empty($result)) {
  62.             // Sort by preference
  63.             uasort($result, function ($a$b) {
  64.                 return $a['priority'] < $b['priority'];
  65.             });
  66.             // Get first occurrence
  67.             foreach ($result as $lang) {
  68.                 if ($this->languageIsAllowed($lang['lng_base'])) {
  69.                     $browserLang $lang['lng_base'];
  70.                     break;
  71.                 }
  72.             }
  73.         }
  74.         return $browserLang;
  75.     }
  76.     // @codeCoverageIgnoreStart
  77.     public static function getSubscribedEvents(): array
  78.     {
  79.         return [
  80.             // Must be registered before (i.e. with a higher priority than) the default Locale listener
  81.             KernelEvents::REQUEST => [['onKernelRequest'20]],
  82.         ];
  83.     }
  84.     // @codeCoverageIgnoreEnd
  85.     private function languageIsAllowed($lang): bool
  86.     {
  87.         return in_array($lang$this->allowedLanguages);
  88.     }
  89. }