Frank de Jonge
Contributed by Frank de Jonge in #26143

We recently improved the performance of the Routing component to make it the fastest PHP router available. Now, just in time for including it in Symfony 4.1, we added another amazing feature to it: support for internationalized routes.

This feature allows to define different paths for each route depending on the user locale. This feature is essential for internationalized apps and that's why third-party bundles like JMSI18nRoutingBundle have millions on downloads. In Symfony 4.1 we decided to integrate this into the routing component itself so you can use it out-of-the-box in Symfony apps.

In practice, when defining a route you can now provide an array in the path option to define a different path per locale:

1
2
3
4
5
contact:
    controller: App\Controller\ContactController::send
    path:
        en: /send-us-an-email
        nl: /stuur-ons-een-email

This format is also supported for routes defined in XML and PHP annotations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\Routing\Annotation\Route;

class ContactController
{
    /**
     * @Route({
     *     "en": "/send-us-an-email",
     *     "nl": "/stuur-ons-een-email"
     * }, name="contact")
     */
    public function send()
    {
        // ...
    }
}

Internally Symfony creates one route per locale (called contact.en and contact.nl in this example) but you can still use the original route name to generate URLs. When generating a URL, the request locale is used by default, but you can pass the locale explicitly to generate any of the alternative URLs:

1
2
3
4
5
6
7
8
/** @var UrlGeneratorInterface $urlGenerator */
// uses the current request locale
$url = $urlGenerator->generate('contact');

// ignores the current request locale and generates '/stuur-ons-een-email'
$url = $urlGenerator->generate('contact', ['_locale' => 'nl']);
// this would also work, but it's not recommended:
// $url = $urlGenerator->generate('contact.nl');

The route prefix can also be translated (both in YAML/XML files and @Route annotations defined at controller class level) and you can mix translated and not translated paths too:

1
2
3
4
5
6
7
# config/routes/annotations.yaml
site:
    resource: '../src/Controller/'
    type: annotation
    prefix:
        en: '/site'
        es: '/sitio'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class DefaultController extends Controller
{
    /**
     * @Route({"en": "/contact", "es": "/contacto"}, name="contact")
     */
    public function contact()
    {
        // ...
    }

    /**
     * @Route("/page/{slug}", name="page")
     */
    public function page($slug)
    {
        // ...
    }
}

The four resulting routes generated by Symfony will be:

Route name Route path
contact.en /site/contact
contact.es /sitio/contacto
page.en /site/page/{slug}
page.es /sitio/page/{slug}
Published in #Living on the edge