01/01/2015
Reading the current locale in Symfony
The locale lives on the Request. In a controller, ask for the request as
an argument and read it:
use Symfony\Component\HttpFoundation\Request;
public function index(Request $request): Response
{
$locale = $request->getLocale(); // 'en', 'fr_FR', ...
}
In a Twig template it is on the app global, with no plumbing at all:
{{ app.request.locale }}
In a service, which has no request argument to take, inject
RequestStack:
use Symfony\Component\HttpFoundation\RequestStack;
public function __construct(private RequestStack $requestStack)
{
}
public function currentLocale(): string
{
return $this->requestStack->getCurrentRequest()?->getLocale() ?? 'en';
}
The null-safe call matters: a service can also run from a console command
or a message handler, where there is no current request at all, and
getCurrentRequest() returns null rather than inventing one.
Why the old two-liner does not run
$request = $this->get('request');
$locale = $request->getLocale();
$this->get('request') fetched the request as a service. That was
deprecated in Symfony 2.4 and removed in 3.0, and the reason is worth
knowing rather than just the fact: one shared request object is wrong the
moment a sub-request exists. Render a controller from inside a template
and there are two requests in flight; a single service can only be one of
them. RequestStack replaced it precisely because a stack is what the
situation actually is.
$this->get() itself is gone too, removed in Symfony 6.0. A
controller does not reach into the container any more; it declares what it
needs as an argument or in its constructor.
getLocale() was never the problem and has not changed.
Where the locale gets set
Reading it is the easy half. It is set, in this order, by:
framework.default_localeinconfig/packages/framework.yaml, the fallback when nothing else says otherwise;a
_localeroute parameter, which Symfony recognizes specially:# config/routes.yaml article_show: path: /{_locale}/articles/{slug} requirements: _locale: en|fr|de$request->setLocale(), usually from a listener that reads a stored preference off the user or a cookie.
A common mistake is calling setLocale() inside a controller and
wondering why the translator ignored it — by then the translator has
already been handed a locale. Set it in a listener on kernel.request,
before the controller runs.
Questions this keeps raising
What replaced $this->get('request')?
Take Request as a controller argument, or inject RequestStack in a service and call getCurrentRequest(). The request service was deprecated in Symfony 2.4 and removed in 3.0 because a single shared request cannot represent a sub-request, which is a normal thing to have when one controller renders another.
How do I read the locale in a service?
Inject RequestStack and call getCurrentRequest()?->getLocale(). Keep the null-safe operator and a fallback: the same service may run in a console command or a message handler where there is no request, and getCurrentRequest() returns null there.
Why does setLocale in my controller not change translations?
Because it runs too late. The translator has already been given a locale by the time the controller executes. Set it in a listener on the kernel.request event instead, which is where the _locale route parameter is applied too.
How do I get the locale in a Twig template?
app.request.locale. The app variable is available in every template with no configuration, and app.request is the current Request object, so anything the controller can read off it a template can too.