Skip to content

Commit 77e2c9c

Browse files
GregoireHebertchalasr
authored andcommitted
[HttpKernel] Add basic support for language negotiation
1 parent 21b79fc commit 77e2c9c

File tree

17 files changed

+117
-10
lines changed

17 files changed

+117
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ CHANGELOG
44
5.4
55
---
66

7+
* Add `set_locale_from_accept_language` config option to automatically set the request locale based on the `Accept-Language`
8+
HTTP request header and the `framework.enabled_locales` config option
9+
* Add `set_content_language_from_locale` config option to automatically set the `Content-Language` HTTP response header based on the Request locale
10+
* Deprecate the `framework.translator.enabled_locales`, use `framework.enabled_locales` instead
711
* Add autowiring alias for `HttpCache\StoreInterface`
812
* Deprecate the `AdapterInterface` autowiring alias, use `CacheItemPoolInterface` instead
913
* Deprecate the public `profiler` service to private

DependencyInjection/Configuration.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public function getConfigTreeBuilder()
7676
return $v;
7777
})
7878
->end()
79+
->fixXmlConfig('enabled_locale')
7980
->children()
8081
->scalarNode('secret')->end()
8182
->scalarNode('http_method_override')
@@ -85,6 +86,18 @@ public function getConfigTreeBuilder()
8586
->scalarNode('ide')->defaultNull()->end()
8687
->booleanNode('test')->end()
8788
->scalarNode('default_locale')->defaultValue('en')->end()
89+
->booleanNode('set_locale_from_accept_language')
90+
->info('Whether to use the Accept-Language HTTP header to set the Request locale (only when the "_locale" request attribute is not passed).')
91+
->defaultFalse()
92+
->end()
93+
->booleanNode('set_content_language_from_locale')
94+
->info('Whether to set the Content-Language HTTP header on the Response using the Request locale.')
95+
->defaultFalse()
96+
->end()
97+
->arrayNode('enabled_locales')
98+
->info('Defines the possible locales for the application. This list is used for generating translations files, but also to restrict which locales are allowed when it is set from Accept-Language header (using "set_locale_from_accept_language").')
99+
->prototype('scalar')->end()
100+
->end()
88101
->arrayNode('trusted_hosts')
89102
->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
90103
->prototype('scalar')->end()
@@ -812,6 +825,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e
812825
->prototype('scalar')->end()
813826
->end()
814827
->arrayNode('enabled_locales')
828+
->setDeprecated('symfony/framework-bundle', '5.3', 'Option "%node%" at "%path%" is deprecated, set the "framework.enabled_locales" option instead.')
815829
->prototype('scalar')->end()
816830
->defaultValue([])
817831
->end()
@@ -846,7 +860,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e
846860
->arrayNode('locales')
847861
->prototype('scalar')->end()
848862
->defaultValue([])
849-
->info('If not set, all locales listed under framework.translator.enabled_locales are used.')
863+
->info('If not set, all locales listed under framework.enabled_locales are used.')
850864
->end()
851865
->end()
852866
->end()

DependencyInjection/FrameworkExtension.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ public function load(array $configs, ContainerBuilder $container)
275275
}
276276
}
277277

278+
$container->getDefinition('locale_listener')->replaceArgument(3, $config['set_locale_from_accept_language']);
279+
$container->getDefinition('response_listener')->replaceArgument(1, $config['set_content_language_from_locale']);
280+
278281
// If the slugger is used but the String component is not available, we should throw an error
279282
if (!ContainerBuilder::willBeAvailable('symfony/string', SluggerInterface::class, ['symfony/framework-bundle'])) {
280283
$container->register('slugger', 'stdClass')
@@ -297,6 +300,7 @@ public function load(array $configs, ContainerBuilder $container)
297300
$container->setParameter('kernel.http_method_override', $config['http_method_override']);
298301
$container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']);
299302
$container->setParameter('kernel.default_locale', $config['default_locale']);
303+
$container->setParameter('kernel.enabled_locales', $config['enabled_locales']);
300304
$container->setParameter('kernel.error_controller', $config['error_controller']);
301305

302306
if (($config['trusted_proxies'] ?? false) && ($config['trusted_headers'] ?? false)) {
@@ -418,11 +422,13 @@ public function load(array $configs, ContainerBuilder $container)
418422
$this->registerEsiConfiguration($config['esi'], $container, $loader);
419423
$this->registerSsiConfiguration($config['ssi'], $container, $loader);
420424
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
421-
$this->registerTranslatorConfiguration($config['translator'], $container, $loader, $config['default_locale']);
425+
$this->registerTranslatorConfiguration($config['translator'], $container, $loader, $config['default_locale'], $config['enabled_locales']);
422426
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
423427
$this->registerWorkflowConfiguration($config['workflows'], $container, $loader);
424428
$this->registerDebugConfiguration($config['php_errors'], $container, $loader);
425-
$this->registerRouterConfiguration($config['router'], $container, $loader, $config['translator']['enabled_locales'] ?? []);
429+
// @deprecated since Symfony 5.4, in 6.0 change to:
430+
// $this->registerRouterConfiguration($config['router'], $container, $loader, $config['enabled_locales']);
431+
$this->registerRouterConfiguration($config['router'], $container, $loader, $config['translator']['enabled_locales'] ?: $config['enabled_locales']);
426432
$this->registerAnnotationsConfiguration($config['annotations'], $container, $loader);
427433
$this->registerPropertyAccessConfiguration($config['property_access'], $container, $loader);
428434
$this->registerSecretsConfiguration($config['secrets'], $container, $loader);
@@ -1221,7 +1227,7 @@ private function createVersion(ContainerBuilder $container, ?string $version, ?s
12211227
return new Reference('assets.empty_version_strategy');
12221228
}
12231229

1224-
private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader, string $defaultLocale)
1230+
private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader, string $defaultLocale, array $enabledLocales)
12251231
{
12261232
if (!$this->isConfigEnabled($container, $config)) {
12271233
$container->removeDefinition('console.command.translation_debug');
@@ -1245,7 +1251,9 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
12451251
$defaultOptions['cache_dir'] = $config['cache_dir'];
12461252
$translator->setArgument(4, $defaultOptions);
12471253

1248-
$translator->setArgument(5, $config['enabled_locales']);
1254+
// @deprecated since Symfony 5.4, in 6.0 change to:
1255+
// $translator->setArgument(5, $enabledLocales);
1256+
$translator->setArgument(5, $config['enabled_locales'] ?: $enabledLocales);
12491257

12501258
$container->setParameter('translator.logging', $config['logging']);
12511259
$container->setParameter('translator.default_path', $config['default_path']);
@@ -1378,7 +1386,9 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
13781386
return;
13791387
}
13801388

1381-
$locales = $config['enabled_locales'] ?? [];
1389+
// @deprecated since Symfony 5.4, in 6.0 change to:
1390+
// $locales = $enabledLocales;
1391+
$locales = $config['enabled_locales'] ?: $enabledLocales;
13821392

13831393
foreach ($config['providers'] as $provider) {
13841394
if ($provider['locales']) {

Resources/config/schema/symfony-1.0.xsd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@
3737
<xsd:element name="rate-limiter" type="rate_limiter" minOccurs="0" maxOccurs="1" />
3838
<xsd:element name="uid" type="uid" minOccurs="0" maxOccurs="1" />
3939
<xsd:element name="notifier" type="notifier" minOccurs="0" maxOccurs="1" />
40+
<xsd:element name="enabled-locale" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
4041
</xsd:choice>
4142

4243
<xsd:attribute name="http-method-override" type="xsd:boolean" />
4344
<xsd:attribute name="ide" type="xsd:string" />
4445
<xsd:attribute name="secret" type="xsd:string" />
4546
<xsd:attribute name="default-locale" type="xsd:string" />
47+
<xsd:attribute name="set_locale_from_accept_language" type="xsd:boolean" />
48+
<xsd:attribute name="set_content_language_from_locale" type="xsd:boolean" />
4649
<xsd:attribute name="test" type="xsd:boolean" />
4750
<xsd:attribute name="error-controller" type="xsd:string" />
4851
<xsd:attribute name="trusted-hosts" type="xsd:string" />

Resources/config/web.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@
6969
->set('response_listener', ResponseListener::class)
7070
->args([
7171
param('kernel.charset'),
72+
abstract_arg('The "set_content_language_from_locale" config value'),
73+
param('kernel.enabled_locales'),
7274
])
7375
->tag('kernel.event_subscriber')
7476

@@ -80,6 +82,8 @@
8082
service('request_stack'),
8183
param('kernel.default_locale'),
8284
service('router')->ignoreOnInvalid(),
85+
abstract_arg('The "set_locale_from_accept_language" config value'),
86+
param('kernel.enabled_locales'),
8387
])
8488
->tag('kernel.event_subscriber')
8589

Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ protected static function getBundleDefaultConfig()
370370
'http_method_override' => true,
371371
'ide' => null,
372372
'default_locale' => 'en',
373+
'enabled_locales' => [],
374+
'set_locale_from_accept_language' => false,
375+
'set_content_language_from_locale' => false,
373376
'secret' => 's3cr3t',
374377
'trusted_hosts' => [],
375378
'trusted_headers' => [

Tests/DependencyInjection/Fixtures/php/full.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
$container->loadFromExtension('framework', [
44
'secret' => 's3cr3t',
55
'default_locale' => 'fr',
6+
'enabled_locales' => ['fr', 'en'],
67
'csrf_protection' => true,
78
'form' => [
89
'csrf_protection' => [
@@ -51,7 +52,6 @@
5152
'fallback' => 'fr',
5253
'paths' => ['%kernel.project_dir%/Fixtures/translations'],
5354
'cache_dir' => '%kernel.cache_dir%/translations',
54-
'enabled_locales' => ['fr', 'en'],
5555
],
5656
'validation' => [
5757
'enabled' => true,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
$container->loadFromExtension('framework', [
4+
'secret' => 's3cr3t',
5+
'default_locale' => 'fr',
6+
'router' => [
7+
'resource' => '%kernel.project_dir%/config/routing.xml',
8+
'type' => 'xml',
9+
'utf8' => true,
10+
],
11+
'translator' => [
12+
'enabled' => true,
13+
'fallback' => 'fr',
14+
'paths' => ['%kernel.project_dir%/Fixtures/translations'],
15+
'cache_dir' => '%kernel.cache_dir%/translations',
16+
'enabled_locales' => ['fr', 'en'],
17+
],
18+
]);

Tests/DependencyInjection/Fixtures/xml/full.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
88

99
<framework:config secret="s3cr3t" ide="file%%link%%format" default-locale="fr" http-method-override="false">
10+
<framework:enabled-locale>fr</framework:enabled-locale>
11+
<framework:enabled-locale>en</framework:enabled-locale>
1012
<framework:csrf-protection />
1113
<framework:form legacy-error-messages="false">
1214
<framework:csrf-protection field-name="_csrf"/>
@@ -28,8 +30,6 @@
2830
<framework:assets version="v1" />
2931
<framework:translator enabled="true" fallback="fr" logging="true" cache-dir="%kernel.cache_dir%/translations">
3032
<framework:path>%kernel.project_dir%/Fixtures/translations</framework:path>
31-
<framework:enabled-locale>fr</framework:enabled-locale>
32-
<framework:enabled-locale>en</framework:enabled-locale>
3333
</framework:translator>
3434
<framework:validation enabled="true" />
3535
<framework:annotations cache="file" debug="true" file-cache-dir="%kernel.cache_dir%/annotations" />
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:framework="http://symfony.com/schema/dic/symfony"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
8+
9+
<framework:config secret="s3cr3t" ide="file%%link%%format" default-locale="fr" http-method-override="false">
10+
<framework:router resource="%kernel.project_dir%/config/routing.xml" type="xml" utf8="true" />
11+
<framework:translator enabled="true" fallback="fr" logging="true" cache-dir="%kernel.cache_dir%/translations">
12+
<framework:path>%kernel.project_dir%/Fixtures/translations</framework:path>
13+
<framework:enabled-locale>fr</framework:enabled-locale>
14+
<framework:enabled-locale>en</framework:enabled-locale>
15+
</framework:translator>
16+
</framework:config>
17+
</container>

0 commit comments

Comments
 (0)