8000 [FrameworkBundle] Make Translator works with any PSR-11 container · symfony/symfony@94073df · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 94073df

Browse files
committed
[FrameworkBundle] Make Translator works with any PSR-11 container
1 parent 2a99e16 commit 94073df

File tree

8 files changed

+213
-21
lines changed

8 files changed

+213
-21
lines changed

UPGRADE-3.3.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ FrameworkBundle
172172
class has been deprecated and will be removed in 4.0. Use the
173173
`Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` class instead.
174174

175+
* The `Symfony\Bundle\FrameworkBundle\Translation\Translator` constructor now takes the
176+
default locale as 3rd argument. Not passing it will trigger an error in 4.0.
177+
175178
HttpKernel
176179
-----------
177180

UPGRADE-4.0.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ FrameworkBundle
271271
class has been removed. Use the
272272
`Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` class instead.
273273

274+
* The `Symfony\Bundle\FrameworkBundle\Translation\Translator` constructor now takes the
275+
default locale as 3rd argument.
276+
274277
HttpFoundation
275278
---------------
276279

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ CHANGELOG
3030
* Deprecated `ControllerArgumentValueResolverPass`. Use
3131
`Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass` instead
3232
* Deprecated `RoutingResolverPass`, use `Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` instead
33+
* Added `$defaultLocale` as 3rd argument of `Translator::__construct()` and
34+
made `Translator` works with any PSR-11 container
3335

3436
3.2.0
3537
-----

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1415
use Symfony\Component\DependencyInjection\Reference;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
1617
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
@@ -24,7 +25,9 @@ public function process(ContainerBuilder $container)
2425
}
2526

2627
$loaders = array();
28+
$loaderRefs = array();
2729
foreach ($container->findTaggedServiceIds('translation.loader') as $id => $attributes) {
30+
$loaderRefs[$id] = new Reference($id);
2831
$loaders[$id][] = $attributes[0]['alias'];
2932
if (isset($attributes[0]['legacy-alias'])) {
3033
$loaders[$id][] = $attributes[0]['legacy-alias'];
@@ -35,11 +38,15 @@ public function process(ContainerBuilder $container)
3538
$definition = $container->getDefinition('translation.loader');
3639
foreach ($loaders as $id => $formats) {
3740
foreach ($formats as $format) {
38-
$definition->addMethodCall('addLoader', array($format, new Reference($id)));
41+
$definition->addMethodCall('addLoader', array($format, $loaderRefs[$id]));
3942
}
4043
}
4144
}
4245

43-
$container->findDefinition('translator.default')->replaceArgument(2, $loaders);
46+
$container
47+
->findDefinition('translator.default')
48+
->replaceArgument(0, new ServiceLocatorArgument($loaderRefs))
49+
->replaceArgument(3, $loaders)
50+
;
4451
}
4552
}

src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
<services>
88
<service id="translator.default" class="Symfony\Bundle\FrameworkBundle\Translation\Translator">
9-
<argument type="service" id="service_container" />
9+
<argument type="service-locator" /> <!-- translation loaders -->
1010
<argument type="service" id="translator.selector" />
11-
<argument type="collection" /> <!-- translation loaders -->
11+
<argument>%kernel.default_locale%</argument>
12+
<argument type="collection" /> <!-- translation loaders ids -->
1213
<argument type="collection">
1314
<argument key="cache_dir">%kernel.cache_dir%/translations</argument>
1415
<argument key="debug">%kernel.debug%</argument>
1516
</argument>
16-
<argument type="collection" /> <!-- translation resources -->
1717
<call method="setConfigCacheFactory">
1818
<argument type="service" id="config_cache_factory" />
1919
</call>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1516
use Symfony\Component\DependencyInjection\Reference;
1617
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass;
1718

@@ -39,7 +40,15 @@ public function testValidCollector()
3940
->will($this->returnValue(array('xliff' => array(array('alias' => 'xliff', 'legacy-alias' => 'xlf')))));
4041
$container->expects($this->once())
4142
->method('findDefinition')
42-
->will($this->returnValue($this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock()));
43+
->will($this->returnValue($translator = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock()));
44+
$translator->expects($this->at(0))
45+
->method('replaceArgument')
46+
->with(0, $this->equalTo(new ServiceLocatorArgument(array('xliff' => new Reference('xliff')))))
47+
->willReturn($translator);
48+
$translator->expects($this->at(1))
49+
->method('replaceArgument')
50+
->with(3, array('xliff' => array('xliff', 'xlf')))
51+
->willReturn($translator);
4352
$pass = new TranslatorPass();
4453
$pass->process($container);
4554
}

src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php

Lines changed: 167 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,158 @@ protected function deleteTmpDir()
4242
$fs->remove($dir);
4343
}
4444

45+
/**
46+
* @group legacy
47+
* @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since version 3.3. Not passing it is deprecated and will trigger an error in 4.0.
48+
*/
49+
public function testTransWithoutCachingOmittingLocale()
50+
{
51+
$translator = $this->getTranslator($this->getLoader(), array(), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null);
52+
$translator->setLocale('fr');
53+
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
54+
55+
$this->assertEquals('foo (FR)', $translator->trans('foo'));
56+
$this->assertEquals('bar (EN)', $translator->trans('bar'));
57+
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
58+
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
59+
$this->assertEquals('no translation', $translator->trans('no translation'));
60+
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
61+
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
62+
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
63+
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
64+
}
65+
66+
/**
67+
* @group legacy
68+
* @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since version 3.3. Not passing it is deprecated and will trigger an error in 4.0.
69+
*/
70+
public function testTransWithCachingOmittingLocale()
71+
{
72+
// prime the cache
73+
$translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null);
74+
$translator->setLocale('fr');
75+
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
76+
77+
$this->assertEquals('foo (FR)', $translator->trans('foo'));
78+
$this->assertEquals('bar (EN)', $translator->trans('bar'));
79+
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
80+
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
81+
$this->assertEquals('no translation', $translator->trans('no translation'));
82+
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
83+
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
84+
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
85+
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
86+
87+
// do it another time as the cache is primed now
88+
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
89+
$loader->expects($this->never())->method('load');
90+
91+
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null);
92+
$translator->setLocale('fr');
93+
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
94+
95+
$this->assertEquals('foo (FR)', $translator->trans('foo'));
96+
$this->assertEquals('bar (EN)', $translator->trans('bar'));
97+
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
98+
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
99+
$this->assertEquals('no translation', $translator->trans('no translation'));
100+
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
101+
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
102+
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
103+
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
104+
}
105+
106+
/**
107+
* @group legacy
108+
* @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since version 3.3. Not passing it is deprecated and will trigger an error in 4.0.
109+
* @expectedException \InvalidArgumentException
110+
*/
111+
public function testTransWithCachingWithInvalidLocaleOmittingLocale()
112+
{
113+
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
114+
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Tests\Translation\TranslatorWithInvalidLocale', null);
115+
116+
$translator->trans('foo');
117+
}
118+
119+
/**
120+
* @group legacy
121+
* @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since version 3.3. Not passing it is deprecated and will trigger an error in 4.0.
122+
*/
123+
public function testLoadResourcesWithoutCachingOmittingLocale()
124+
{
125+
$loader = new \Symfony\Component\Translation\Loader\YamlFileLoader();
126+
$resourceFiles = array(
127+
'fr' => array(
128+
__DIR__.'/../Fixtures/Resources/translations/messages.fr.yml',
129+
),
130+
);
131+
132+
$translator = $this->getTranslator($loader, array('resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null);
133+
$translator->setLocale('fr');
134+
135+
$this->assertEquals('répertoire', $translator->trans('folder'));
136+
}
137+
138+
/**
139+
* @group legacy
140+
* @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since version 3.3. Not passing it is deprecated and will trigger an error in 4.0.
141+
*/
142+
public function testGetDefaultLocaleOmittingLocale()
143+
{
144+
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();
145+
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();
146+
$container
147+
->expects($this->once())
148+
->method('getParameter')
149+
->with('kernel.default_locale')
150+
->will($this->returnValue('en'))
151+
;
152+
$translator = new Translator($container, new MessageSelector());
153+
154+
$this->assertSame('en', $translator->getLocale());
155+
}
156+
157+
/**
158+
* @group legacy
159+
* @expectedException \InvalidArgumentException
160+
* @expectedExceptionMessage Missing third $defaultLocale argument.
161+
*/
162+
public function testGetDefaultLocaleOmittingLocaleWithPsrContainer()
163+
{
164+
$container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock();
165+
$translator = new Translator($container, new MessageSelector());
166+
}
167+
168+
/**
169+
* @group legacy
170+
* @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since version 3.3. Not passing it is deprecated and will trigger an error in 4.0.
171+
*/
172+
public function testWarmupOmittingLocale()
173+
{
174+
$loader = new \Symfony\Component\Translation\Loader\YamlFileLoader();
175+
$resourceFiles = array(
176+
'fr' => array(
177+
__DIR__.'/../Fixtures/Resources/translations/messages.fr.yml',
178+
),
179+
);
180+
181+
// prime the cache
182+
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null);
183+
$translator->setFallbackLocales(array('fr'));
184+
$translator->warmup($this->tmpDir);
185+
186+
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
187+
$loader
188+
->expects($this->never())
189+
->method('load');
190+
191+
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null);
192+
$translator->setLocale('fr');
193+
$translator->setFallbackLocales(array('fr'));
194+
$this->assertEquals('répertoire', $translator->trans('folder'));
195+
}
196+
45197
public function testTransWithoutCaching()
46198
{
47199
$translator = $this->getTranslator($this->getLoader());
@@ -123,15 +275,8 @@ public function testLoadResourcesWithoutCaching()
123275

124276
public function testGetDefaultLocale()
125277
{
126-
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();
127-
$container
128-
->expects($this->once())
129-
->method('getParameter')
130-
->with('kernel.default_locale')
131-
->will($this->returnValue('en'))
132-
;
133-
134-
$translator = new Translator($container, new MessageSelector());
278+
$container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock();
279+
$translator = new Translator($container, new MessageSelector(), 'en');
135280

136281
$this->assertSame('en', $translator->getLocale());
137282
}
@@ -219,9 +364,9 @@ protected function getContainer($loader)
219364
return $container;
220365
}
221366

222-
public function getTranslator($loader, $options = array(), $loaderFomat = 'loader', $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator')
367+
public function getTranslator($loader, $options = array(), $loaderFomat = 'loader', $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator', $defaultLocale = 'en')
223368
{
224-
$translator = $this->createTranslator($loader, $options, $translatorClass, $loaderFomat);
369+
$translator = $this->createTranslator($loader, $options, $translatorClass, $loaderFomat, $defaultLocale);
225370

226371
if ('loader' === $loaderFomat) {
227372
$translator->addResource('loader', 'foo', 'fr');
@@ -261,11 +406,21 @@ public function testWarmup()
261406
$this->assertEquals('répertoire', $translator->trans('folder'));
262407
}
263408

264-
private function createTranslator($loader, $options, $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator', $loaderFomat = 'loader')
409+
private function createTranslator($loader, $options, $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator', $loaderFomat = 'loader', $defaultLocale = 'en')
265410
{
411+
if (null === $defaultLocale) {
412+
return new $translatorClass(
413+
$this->getContainer($loader),
414+
new MessageSelector(),
415+
array($loaderFomat => array($loaderFomat)),
416+
$options
417+
);
418+
}
419+
266420
return new $translatorClass(
267421
$this->getContainer($loader),
268422
new MessageSelector(),
423+
$defaultLocale,
269424
array($loaderFomat => array($loaderFomat)),
270425
$options
271426
);

src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Translation;
1313

14+
use Psr\Container\ContainerInterface;
15+
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
1416
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
1517
use Symfony\Component\Translation\Translator as BaseTranslator;
1618
use Symfony\Component\Translation\MessageSelector;
17-
use Symfony\Component\DependencyInjection\ContainerInterface;
1819
use Symfony\Component\Translation\Exception\InvalidArgumentException;
1920

2021
/**
@@ -54,8 +55,20 @@ class Translator extends BaseTranslator implements WarmableInterface
5455
*
5556
* @throws InvalidArgumentException
5657
*/
57-
public function __construct(ContainerInterface $container, MessageSelector $selector, $loaderIds = array(), array $options = array())
58+
public function __construct(ContainerInterface $container, MessageSelector $selector, $defaultLocale = null, array $loaderIds = array(), array $options = array())
5859
{
60+
// BC 3.x, to be removed in 4.0 along with the $defaultLocale default value
61+
if (is_array($defaultLocale) || 3 > func_num_args()) {
62+
if (!$container instanceof SymfonyContainerInterface) {
63+
throw new \InvalidArgumentException('Missing third $defaultLocale argument.');
64+
}
65+
66+
$options = $loaderIds;
67+
$loaderIds = $defaultLocale;
68+
$defaultLocale = $container->getParameter('kernel.default_locale');
69+
@trigger_error(sprintf('Method %s() takes the default locale as 3rd argument since version 3.3. Not passing it is deprecated and will trigger an error in 4.0.', __METHOD__), E_USER_DEPRECATED);
70+
}
71+
5972
$this->container = $container;
6073
$this->loaderIds = $loaderIds;
6174

@@ -70,7 +83,7 @@ public function __construct(ContainerInterface $container, MessageSelector $sele
7083
$this->loadResources();
7184
}
7285

73-
parent::__construct($container->getParameter('kernel.default_locale'), $selector, $this->options['cache_dir'], $this->options['debug']);
86+
parent::__construct($defaultLocale, $selector, $this->options['cache_dir'], $this->options['debug']);
7487
}
7588

7689
/**

0 commit comments

Comments
 (0)
0