Skip to content
Commits on Source (2)
  • Reiter, Christoph's avatar
    Add a locale service for working with the the active locale · 00cef2a7
    Reiter, Christoph authored
    We currently configure symfony to apply the "Accept-Language"
    header to all requests, so there remain only two cases where
    we need to manually adjust things:
    
    * In case an endpoint renders HTML that is displayed in an iframe
    or popup we still need to provide an option to pass a language via
    a query parameter. This then takes precedence over the header.
    
    * In case we need a language identifier for another service, where
    we need to request an external resource for a specific language.
    In this case we either need to take the request language or in case
    we are in a CLI context the default locale configured in symfony.
    00cef2a7
  • Reiter, Christoph's avatar
    release · cc22deb2
    Reiter, Christoph authored
    cc22deb2
......@@ -18,6 +18,7 @@ $config->setRules([
'strict_param' => true,
'declare_strict_types' => true,
'method_argument_space' => ['on_multiline' => 'ignore'],
'phpdoc_to_comment' => false,
])
->setRiskyAllowed(true)
->setFinder($finder);
......
# v0.1.52
* new Locale service for setting a locale from a requests and forwarding
to other services
# v0.1.45
* dbp:relay:core:migrate: Work around issues in DoctrineMigrationsBundle which
......
......@@ -29,7 +29,8 @@
"symfony/twig-bundle": "^5.4",
"symfony/uid": "^5.4",
"symfony/validator": "^5.4",
"symfony/yaml": "^5.4"
"symfony/yaml": "^5.4",
"ext-intl": "*"
},
"require-dev": {
"brainmaestro/composer-git-hooks": "^2.8.5",
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9c42ac8c7816cdf96cd4c2f000cabaf3",
"content-hash": "6ddd4889d724c8beabfe6b3b58bf35d7",
"packages": [
{
"name": "api-platform/core",
......@@ -9843,7 +9843,8 @@
"platform": {
"php": ">=7.3",
"ext-fileinfo": "*",
"ext-json": "*"
"ext-json": "*",
"ext-intl": "*"
},
"platform-dev": [],
"platform-overrides": {
......
<?php
declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\Locale;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* A service which can be injected, which provides the current active language and allows setting the active
* language based on a query parameters.
*
* This assumes that Symfony is configured to apply the 'Accept-Language' header by default to all requests.
*/
class Locale
{
/** @var RequestStack */
private $requestStack;
/**
* @var ParameterBagInterface
*/
private $parameters;
public function __construct(RequestStack $requestStack, ParameterBagInterface $parameters)
{
$this->requestStack = $requestStack;
$this->parameters = $parameters;
}
/**
* Returns the primary language (in ISO 639‑1 format) for the current context.
* In case there is a request then the request language, otherwise the default language.
*/
public function getCurrentPrimaryLanguage(): string
{
$locale = $this->getCurrentLocale();
$lang = \Locale::getPrimaryLanguage($locale);
/** @psalm-suppress RedundantCondition */
assert($lang !== null);
return $lang;
}
/**
* Sets the locale for the active request via a query parameter.
* The query parameter format is the same as the 'Accept-Language' HTTP header format.
* In case the query parameter isn't part of the request then nothing changes.
*/
public function setCurrentRequestLocaleFromQuery(string $queryParam = 'lang'): void
{
$request = $this->requestStack->getCurrentRequest();
if ($request === null) {
throw new \RuntimeException('No active request');
}
self::setRequestLocaleFromQuery($request, $queryParam);
}
/**
* Returns the current locale, either from the active request, or the default one.
*/
private function getCurrentLocale(): string
{
$request = $this->requestStack->getCurrentRequest();
if ($request !== null) {
$locale = $request->getLocale();
} else {
$locale = $this->parameters->get('kernel.default_locale');
assert(is_string($locale));
}
return $locale;
}
private static function setRequestLocaleFromQuery(Request $request, string $queryParam): void
{
if ($request->query->has($queryParam)) {
$lang = $request->query->get($queryParam);
assert(is_string($lang));
$locale = \Locale::acceptFromHttp($lang);
if ($locale === false) {
throw new \RuntimeException('Failed to parse Accept-Language');
}
$request->setLocale($locale);
}
}
}
......@@ -100,3 +100,7 @@ services:
Dbp\Relay\CoreBundle\Authorization\DebugCommand:
autowire: true
autoconfigure: true
Dbp\Relay\CoreBundle\Locale\Locale:
autowire: true
autoconfigure: true
<?php
declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\Tests;
use Dbp\Relay\CoreBundle\Locale\Locale;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
class LocaleTest extends TestCase
{
public function testWithRequest()
{
$stack = new RequestStack();
$request = new Request(['lang' => 'de']);
$request->setLocale(\Locale::acceptFromHttp('en'));
$stack->push($request);
$params = new ParameterBag([]);
$service = new Locale($stack, $params);
$lang = $service->getCurrentPrimaryLanguage();
$this->assertSame('en', $lang);
$service->setCurrentRequestLocaleFromQuery('lang');
$lang = $service->getCurrentPrimaryLanguage();
$this->assertSame('de', $lang);
}
public function testWithoutRequest()
{
$stack = new RequestStack();
$params = new ParameterBag(['kernel.default_locale' => \Locale::acceptFromHttp('de')]);
$service = new Locale($stack, $params);
$lang = $service->getCurrentPrimaryLanguage();
$this->assertSame('de', $lang);
}
}