Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save thibaut-decherit/1ad9d66237152041d1d2ace3102cf8c9 to your computer and use it in GitHub Desktop.
Save thibaut-decherit/1ad9d66237152041d1d2ace3102cf8c9 to your computer and use it in GitHub Desktop.
Symfony - Locale Redirect To Client Preference

Symfony - LocaleRedirectToClientPreference

Redirects to browser preferred locale if this locale is supported by the application.

config/services.yml

App\EventListener\LocaleRedirectToClientPreference:
  arguments:
    $router: '@router'
    $defaultLocale: '%kernel.default_locale%' # framework.default_locale in config/packages/translation.yaml
    $supportedLocales: '%app.supported_locales%'
  tags:
    - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

config/services.yaml

parameters:
  app.supported_locales:
    - en
    - fr

src/EventListener/LocaleRedirectToClientPreference.php

<?php

namespace App\EventListener;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouterInterface;

/**
 * Class LocaleRedirectToClientPreference
 * @package AppBundle\EventListener
 *
 * Redirects to browser preferred locale if this locale is supported by the application.
 */
class LocaleRedirectToClientPreference
{
    /**
     * @var string
     */
    private $defaultLocale;

    /**
     * @var RouterInterface
     */
    private $router;

    /**
     * @var RouteCollection
     */
    private $routeCollection;

    /**
     * @var array
     */
    private $supportedLocales;

    /**
     * LocaleRedirectToClientPreference constructor.
     * @param RouterInterface $router
     * @param string $defaultLocale
     * @param array $supportedLocales
     */
    public function __construct(RouterInterface $router, string $defaultLocale, array $supportedLocales)
    {
        $this->router = $router;
        $this->routeCollection = $router->getRouteCollection();
        $this->defaultLocale = $defaultLocale;
        $this->supportedLocales = $supportedLocales;
    }

    /*
     * Checks client preferred locales against locales supported by the application and returns first match.
     * Returns empty string if no match is found.
     */ 
    private function localeResolver(array $clientPreferredLocales): string
    {
        foreach ($clientPreferredLocales as $value) {
            $locale = substr($value, 0, 2);

            if (in_array($locale, $this->supportedLocales)) {
                return $locale;
            }
        }

        return '';
    }

    /*
     * Redirects all incoming requests to their /locale/route equivalent as long as the route supports _locale element.
     * Does nothing if it already has /locale/ in the route to prevent redirect loops or forcing a locale unto an user
     * whom already chose one.
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        if ($request->isMethod('get') === false) {
            return;
        }

        $path = $request->getPathInfo();

        $routeExists = false;

        // Checks if requested route exists.
        foreach ($this->routeCollection as $routeObject) {
            $routePath = $routeObject->getPath();
            if ($routePath === '/{_locale}' . $path) {
                $routeExists = true;
                break;
            }
        }

        if ($routeExists === false) {
            return;
        }

        // Gets the preferred locales from client.
        $clientPreferredLocales = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);

        $resolvedLocale = $this->localeResolver($clientPreferredLocales);

        // If client preferred locale is not in the list of supported locales then sets to defaultLocale from config.yml.
        if (empty($resolvedLocale)) {
            $resolvedLocale = $request->getDefaultLocale();
        }

        $event->setResponse(new RedirectResponse('/' . $resolvedLocale . $path));
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment