Setting Locale based on URI & subdomain in Symfony2

Path vs Domain

For the URL you’ve got a few options, that I’m going to simplify down to two. You can set the url based on the domain. e.x. jp.maxvandervelde.com/blog/symfony2-subdomain-locale. Or you can set it based on the path URI. e.x. maxvandervelde.com/jp/blog/symfony2-subdomain-locale. There are ups and downs to both. If you decide to use the path option, you may run into trouble with the Routing system. Because of the way routes are currently generated in Symfony2, it’s not possible to make the path prefix that specifies the locale optional. So if you’re going to do this, that prefix slug must be on all of your pages, you can’t default to a language if it’s not there.

Setting locale based Path

This option is built into the routing system pretty easily. All you have to do is specify the _locale parameter somewhere in the URL. This can be in the prefix, like below:

max_vandervelde_blog:
    resource: "@MaxVanderveldeBlogBundle/Controller/"
    type: annotation
    prefix: /{_locale}/

or it can be in the controller route, even available to specify with the annotation syntax:

/**
 * @Route("/{_locale}/blog/{slug}")
*/

If you want to restrict what can be passed in you can specify requirements as well. However, as I mentioned earlier, you cannot specify a default here.

max_vandervelde_blog:
    resource: "@MaxVanderveldeBlogBundle/Controller/"
    type: annotation
    prefix: /{_locale}/
    requirements:
        _locale: "en|jp"

or

/**
 * @Route("/{_locale}/blog/{slug}", requirements={"_locale": "en|jp"})
*/

Setting locale based on subdomain

If you want to set your locale based on the subdomain or TLD, you’ll have a few different problems. If you do a simple subdomain at the beginning for the locale, you can use the routing system, but you’ll run into the same problem as before, where you will not be able to specify a default easily. However, if that is not a problem for you, as of Symfony 2.4 you can specify the host in the route like so:

max_vandervelde_blog:
    host: {_locale}.maxvandervelde.com
    resource: "@MaxVanderveldeBlogBundle/Controller/"
    type: annotation

If that is a problem for you, or you would like to specify based on the TLD, you’re going to have to write an event listener to set the locale manually. Don’t panic, it’s not that hard. Let me show you. There’s a bunch of different ways you could implement this, I’ll try to keep it simple here.

You’ll need to subscribe to the KernelEvents::REQUEST event posted by Symfony2. From there, you’ll determine your locale based on the request, and set the locale.

In the example below we, somewhat crudely, pop off the first part of the URL and check if it’s in a list of supported locales. If it is, we set that locale to the request. Here, you could potentially skip the checking in the supporting array if we don’t want to maintain the list of supported locales.

<?php

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class LocalePrefixListener implements EventSubscriberInterface
{
    private static $locales = ['jp', 'en'];

    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        $domain = parse_url($request->getUri())['host'];
        $domainPieces = explode('.', $domain);

        if (count($domainPieces) !== 3) {
            return;
        }

        if (in_array($domainPieces[0], self::$locales)) {
            $request->setLocale($domainPieces[0]);
        }
    }

    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::REQUEST => [['onKernelRequest', 255]],
        ];
    }
}

From there all you have to do is subscribe to the event to the kernel event subscriber. If you’re using Yaml for your service configuration it will look something like this:

  locale_prefix_listener:
      class: MaxVandervelde\BlogBundle\LocalePrefixListener
      tags:
          - { name: kernel.event_subscriber }

It’s also possible, even though still inadvisable, to set the locale based on the browser’s preferred language settings. This is accessible through the $request->getPreferredLanguage()property.

If you want to set it up based on the TLD, you can do a similar thing, though since these don’t map directly to a locale code, you will have to use some more complex logic. Hopefully you get the idea from here.

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s