Skip to content

[HttpFoundation] add a default Content-Language to the Response #36507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
5.1.0
-----

* Added `available_locales` for `Content-language` header handling
* Added link to source for controllers registered as named services
* Added link to source on controller on `router:match`/`debug:router` (when `framework.ide` is configured)
* Added `Routing\Loader` and `Routing\Loader\Configurator` namespaces to ease defining routes with default controllers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ public function getConfigTreeBuilder()
return $v;
})
->end()
->beforeNormalization()
->ifTrue(function ($v) { return !isset($v['available_locales']) && (isset($v['translator']['enabled_locales']) || isset($v['translator']['enabled-locale'])); })
->then(function ($v) {
$v['available_locales'] = $v['translator']['enabled_locales'] ?? $v['translator']['enabled-locale'];

return $v;
})
->end()
->children()
->scalarNode('secret')->end()
->scalarNode('http_method_override')
Expand All @@ -81,6 +89,11 @@ public function getConfigTreeBuilder()
->scalarNode('ide')->defaultNull()->end()
->booleanNode('test')->end()
->scalarNode('default_locale')->defaultValue('en')->end()
->arrayNode('available_locales')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is enabled_locales already, can't we reuse it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's in the translator section.
what if the translator is not available?
Is it still relevant to reuse it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My fear is that it will be confusing for users to have to configure this twice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to make the translator option defaulting to the value of this new parameter if not set explicitly (and to not set it explicitly in the recipe). It’s already what we do for the default local IIRC.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or we could move the setting out of the translation subtree.
again, having twice the same setting is calling for troubles to me.

->info('An array of available locales for your application. It will help determine the locale according to the Accept-Language header.')
->beforeNormalization()->ifEmpty()->thenUnset()->end()
->prototype('scalar')->end()
->end()
->arrayNode('trusted_hosts')
->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
->prototype('scalar')->end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('kernel.http_method_override', $config['http_method_override']);
$container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']);
$container->setParameter('kernel.default_locale', $config['default_locale']);
$container->setParameter('kernel.available_locales', $config['available_locales']);
$container->setParameter('kernel.error_controller', $config['error_controller']);

if (!$container->hasParameter('debug.file_link_format')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<xsd:element name="messenger" type="messenger" minOccurs="0" maxOccurs="1" />
<xsd:element name="http-client" type="http_client" minOccurs="0" maxOccurs="1" />
<xsd:element name="mailer" type="mailer" minOccurs="0" maxOccurs="1" />
<xsd:element name="available-locales" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>

<xsd:attribute name="http-method-override" type="xsd:boolean" />
Expand All @@ -41,6 +42,7 @@
<xsd:attribute name="default-locale" type="xsd:string" />
<xsd:attribute name="test" type="xsd:boolean" />
<xsd:attribute name="error-controller" type="xsd:string" />

</xsd:complexType>

<xsd:complexType name="form">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<argument type="service" id="request_stack" />
<argument>%kernel.default_locale%</argument>
<argument type="service" id="router" on-invalid="ignore" />
<argument>%kernel.available_locales%</argument>
</service>

<service id="validate_request_listener" class="Symfony\Component\HttpKernel\EventListener\ValidateRequestListener">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ protected static function getBundleDefaultConfig()
'http_method_override' => true,
'ide' => null,
'default_locale' => 'en',
'available_locales' => [],
'csrf_protection' => [
'enabled' => false,
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

$container->loadFromExtension('framework', [
'secret' => 's3cr3t',
'default_locale' => 'fr',
'available_locales' => ['mi', 'fr'],
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

$container->loadFromExtension('framework', [
'secret' => 's3cr3t',
'default_locale' => 'fr',
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

$container->loadFromExtension('framework', [
'secret' => 's3cr3t',
'default_locale' => 'fr',
'translator' => [
'enabled' => true,
'fallback' => 'fr',
'paths' => ['%kernel.project_dir%/Fixtures/translations'],
'cache_dir' => '%kernel.cache_dir%/translations',
'enabled_locales' => ['mi', 'fr'],
],
]);
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
$container->loadFromExtension('framework', [
'secret' => 's3cr3t',
'default_locale' => 'fr',
'available_locales' => ['mi', 'fr'],
'csrf_protection' => true,
'form' => [
'csrf_protection' => [
Expand Down Expand Up @@ -50,7 +51,7 @@
'fallback' => 'fr',
'paths' => ['%kernel.project_dir%/Fixtures/translations'],
'cache_dir' => '%kernel.cache_dir%/translations',
'enabled_locales' => ['fr', 'en']
'enabled_locales' => ['fr', 'en'],
],
'validation' => [
'enabled' => true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

<framework:config secret="s3cr3t" default-locale="fr">
<framework:available-locales>mi</framework:available-locales>
<framework:available-locales>fr</framework:available-locales>
</framework:config>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

<framework:config secret="s3cr3t" default-locale="fr">
<framework:translator enabled="true" fallback="fr" logging="true" cache-dir="%kernel.cache_dir%/translations">
<framework:path>%kernel.project_dir%/Fixtures/translations</framework:path>
</framework:translator>
</framework:config>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

<framework:config secret="s3cr3t" default-locale="fr">
<framework:translator enabled="true" fallback="fr" logging="true" cache-dir="%kernel.cache_dir%/translations">
<framework:path>%kernel.project_dir%/Fixtures/translations</framework:path>
<framework:enabled-locale>mi</framework:enabled-locale>
<framework:enabled-locale>fr</framework:enabled-locale>
</framework:translator>
</framework:config>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

<framework:config secret="s3cr3t" ide="file%%link%%format" default-locale="fr" http-method-override="false">
<framework:available-locales>mi</framework:available-locales>
<framework:available-locales>fr</framework:available-locales>
<framework:csrf-protection />
<framework:form>
<framework:csrf-protection field-name="_csrf"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
framework:
secret: s3cr3t
default_locale: fr
available_locales: [mi, fr]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
framework:
secret: s3cr3t
default_locale: fr
translator:
enabled: true
fallback: fr
cache_dir: '%kernel.cache_dir%/translations'
paths: ['%kernel.project_dir%/Fixtures/translations']
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
framework:
secret: s3cr3t
default_locale: fr
translator:
enabled: true
fallback: fr
cache_dir: '%kernel.cache_dir%/translations'
paths: ['%kernel.project_dir%/Fixtures/translations']
enabled_locales: [mi, fr]
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
framework:
secret: s3cr3t
default_locale: fr
available_locales: ['mi', 'fr']
csrf_protection: true
form:
csrf_protection:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ public function testSession()

$this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml');
$this->assertEquals('fr', $container->getParameter('kernel.default_locale'));
$this->assertEquals(['mi', 'fr'], $container->getParameter('kernel.available_locales'));
$this->assertEquals('session.storage.native', (string) $container->getAlias('session.storage'));
$this->assertEquals('session.handler.native_file', (string) $container->getAlias('session.handler'));

Expand Down Expand Up @@ -726,6 +727,24 @@ public function testMessengerInvalidTransportRouting()
$this->createContainerFromFile('messenger_routing_invalid_transport');
}

public function testAvailableLocales()
{
$container = $this->createContainerFromFile('available_locales');
$this->assertEquals(['mi', 'fr'], $container->getParameter('kernel.available_locales'));
}

public function testAvailableLocalesFallbackOnEnabledLocales()
{
$container = $this->createContainerFromFile('available_locales_fallback_enabled_locales');
$this->assertEquals(['mi', 'fr'], $container->getParameter('kernel.available_locales'));
}

public function testAvailableLocalesCannotFallbackOnEnabledLocales()
{
$container = $this->createContainerFromFile('available_locales_cannot_fallback_enabled_locales');
$this->assertEquals([], $container->getParameter('kernel.available_locales'));
}

public function testTranslator()
{
$container = $this->createContainerFromFile('full');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ imports:
framework:
secret: '%secret%'
default_locale: '%env(LOCALE)%'
available_locales: ['%env(LOCALE)%']
session:
cookie_httponly: '%env(bool:COOKIE_HTTPONLY)%'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ imports:
framework:
secret: '%secret%'
default_locale: '%env(LOCALE)%'
available_locales: ['%env(LOCALE)%']
translator:
fallbacks:
- '%env(LOCALE)%'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ imports:
framework:
secret: '%secret%'
default_locale: '%env(LOCALE)%'
available_locales: ['%env(LOCALE)%']
translator:
fallbacks:
- '%env(LOCALE)%'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ framework:
form: true
test: true
default_locale: en
available_locales: ['en', 'fr']
session:
storage_id: session.storage.mock_file

Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/HttpKernel/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
5.1.0
-----

* Allow request locale and Response `Content-Language` header to be set from `preferredLocale` thanks to `available_locales`
* made `WarmableInterface::warmUp()` return a list of classes or files to preload on PHP 7.4+;
not returning an array is deprecated
* deprecated support for `service:action` syntax to reference controllers, use `serviceOrFqcn::method` instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ class LocaleListener implements EventSubscriberInterface
{
private $router;
private $defaultLocale;
private $availableLocales;
private $requestStack;

public function __construct(RequestStack $requestStack, string $defaultLocale = 'en', RequestContextAwareInterface $router = null)
public function __construct(RequestStack $requestStack, string $defaultLocale = 'en', RequestContextAwareInterface $router = null, array $availableLocales = [])
{
$this->availableLocales = $availableLocales;
$this->defaultLocale = $defaultLocale;
$this->requestStack = $requestStack;
$this->router = $router;
Expand Down Expand Up @@ -65,6 +67,11 @@ private function setLocale(Request $request)
if ($locale = $request->attributes->get('_locale')) {
$request->setLocale($locale);
}

if (null === $locale && !empty($this->availableLocales) &&
$preferredLanguage = $request->getPreferredLanguage($this->availableLocales)) {
$request->setLocale($preferredLanguage);
}
}

private function setRouterContext(Request $request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
class ResponseListener implements EventSubscriberInterface
{
private $charset;
private $availableLocales;

public function __construct(string $charset)
public function __construct(string $charset, array $availableLocales = [])
{
$this->charset = $charset;
$this->availableLocales = $availableLocales;
}

/**
Expand All @@ -46,6 +48,11 @@ public function onKernelResponse(ResponseEvent $event)
$response->setCharset($this->charset);
}

if (!empty($this->availableLocales) && !$response->isInformational() && !$response->isEmpty() && !$response->headers->has('Content-Language')) {
$response->headers->set('Content-Language', $event->getRequest()->getLocale());
$response->setVary('Accept-Language', false);
}

$response->prepare($event->getRequest());
}

Expand Down
Loading