Andrei Herford
Andrei Herford

Reputation: 18729

Symfony Routing: When / where is _locale attribute set

I am working on a WebApp project using Symfony 2.8. I would like to add different languages to the page. Depending on the users locale all routes/URLs should be changed from /some/url to /locale/some/url, e.g./en/some/url`.

Before adding the languages the main routing file looked like this:

<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <!-- Routes for the public pages -->
    <import
        resource="@AppBundle/Resources/config/routing/public_routes.xml" />

    ...
</routes>

And the public_routes.xml has the following content:

<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="home" path="" methods="GET">
        <default key="_controller">AppBundle:Default:home</default>
    </route>

    <route id="public_price" path="/price" methods="GET">
        <default key="_controller">AppBundle:Default:price</default>
    </route>

    <route id="public_about" path="/about" methods="GET">
        <default key="_controller">AppBundle:Default:about</default>
    </route>

    ...
</routes>

So far, so simple. Now I have added the following to the routing to add the localization:

<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <!-- Home route to redirect to the right route -->
    <route id="home_redirect" path="" methods="GET">
        <default key="_controller">AppBundle:Default:home</default>
    </route>

    <!-- Routes for the localized public pages -->
    <import
        resource="@AppBundle/Resources/config/routing/public_routes.xml"
        prefix="/{_locale}" />

    ...
</routes>

So the import of the public pages was extended with prefix="/{_locale}" which automatically adds the current locale to all routes from public_routes.xml.

This works fine. I can now navigate to /en, /en/price, /en/about, etc.

However the still has to be an "empty" route. Otherwise the domain itself would have not valid route anymore. (BTW: Is there any difference between path="" and path="/"?)

This is handeled by the new home_redirect route with its controller:

public function homeAction(Request $request) {
    $locale = $request->attributes->get('_locale');
    if (!$locale) {
        // Try to get the preferred language from the request
        $locale = MySettings::getLanguage($request);               
        return $this->redirectToRoute('home', array('_locale' => $locale));
    } elseif (!MySettings::checkLanguage($locale)) {
        // Invalid Locale...
        throw $this->createNotFoundException();
    }

    return $this->render('AppBundle:Default:homepage.html.twig');
}

So if _locale is set for the request, the homeAction checks if the language is supported or throws an error. If _locale is not set, homeAction tries to get the current _locale from the request (e.g. from the browser accepted languages).

THE PROBLEM: This solution above workes fine for all calles to /. In this case _locale is not set and thus the call is re-directed to /someLocale/, e.g. `/en/.

BUT: All calls to /somepage are also handled by homeAction. This is because somepage is interpreted as locale! Thus homeAction gets called with $request->attributes->get('_locale') == 'somepage'. This cannot work obviously.

So if someone calls the old URL /about instead of the new /en/about, the routing systems matches this call with route home with _locale about and the page is not found.

Where exactly does Symfony extracts _locale from the URL? Is it possible to intercept this extraction and make it a little bit smarter? E.g. to ignore all "locales" that are more than 2 letters long?

Thanks!

Upvotes: 1

Views: 1323

Answers (1)

john Smith
john Smith

Reputation: 17906

simply make use of requirements and default properties of routedefinition

so you can set {_locale} for all routes

  requirements:
    _locale: fr|en
  defaults: { _locale: fr}

in this yml example it defaults to "fr" if no _locale was given e.g / and due to the requirements about would not be matched as _locale

Upvotes: 3

Related Questions