4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Translation\Tests;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Translation\Translator;
16 use Symfony\Component\Translation\MessageSelector;
17 use Symfony\Component\Translation\Loader\ArrayLoader;
18 use Symfony\Component\Translation\MessageCatalogue;
20 class TranslatorTest extends TestCase
23 * @dataProvider getInvalidLocalesTests
24 * @expectedException \InvalidArgumentException
26 public function testConstructorInvalidLocale($locale)
28 $translator = new Translator($locale, new MessageSelector());
32 * @dataProvider getValidLocalesTests
34 public function testConstructorValidLocale($locale)
36 $translator = new Translator($locale, new MessageSelector());
38 $this->assertEquals($locale, $translator->getLocale());
41 public function testConstructorWithoutLocale()
43 $translator = new Translator(null, new MessageSelector());
45 $this->assertNull($translator->getLocale());
48 public function testSetGetLocale()
50 $translator = new Translator('en');
52 $this->assertEquals('en', $translator->getLocale());
54 $translator->setLocale('fr');
55 $this->assertEquals('fr', $translator->getLocale());
59 * @dataProvider getInvalidLocalesTests
60 * @expectedException \InvalidArgumentException
62 public function testSetInvalidLocale($locale)
64 $translator = new Translator('fr', new MessageSelector());
65 $translator->setLocale($locale);
69 * @dataProvider getValidLocalesTests
71 public function testSetValidLocale($locale)
73 $translator = new Translator($locale, new MessageSelector());
74 $translator->setLocale($locale);
76 $this->assertEquals($locale, $translator->getLocale());
79 public function testGetCatalogue()
81 $translator = new Translator('en');
83 $this->assertEquals(new MessageCatalogue('en'), $translator->getCatalogue());
85 $translator->setLocale('fr');
86 $this->assertEquals(new MessageCatalogue('fr'), $translator->getCatalogue('fr'));
89 public function testGetCatalogueReturnsConsolidatedCatalogue()
92 * This will be useful once we refactor so that different domains will be loaded lazily (on-demand).
93 * In that case, getCatalogue() will probably have to load all missing domains in order to return
94 * one complete catalogue.
98 $translator = new Translator($locale);
99 $translator->addLoader('loader-a', new ArrayLoader());
100 $translator->addLoader('loader-b', new ArrayLoader());
101 $translator->addResource('loader-a', array('foo' => 'foofoo'), $locale, 'domain-a');
102 $translator->addResource('loader-b', array('bar' => 'foobar'), $locale, 'domain-b');
105 * Test that we get a single catalogue comprising messages
106 * from different loaders and different domains
108 $catalogue = $translator->getCatalogue($locale);
109 $this->assertTrue($catalogue->defines('foo', 'domain-a'));
110 $this->assertTrue($catalogue->defines('bar', 'domain-b'));
113 public function testSetFallbackLocales()
115 $translator = new Translator('en');
116 $translator->addLoader('array', new ArrayLoader());
117 $translator->addResource('array', array('foo' => 'foofoo'), 'en');
118 $translator->addResource('array', array('bar' => 'foobar'), 'fr');
120 // force catalogue loading
121 $translator->trans('bar');
123 $translator->setFallbackLocales(array('fr'));
124 $this->assertEquals('foobar', $translator->trans('bar'));
127 public function testSetFallbackLocalesMultiple()
129 $translator = new Translator('en');
130 $translator->addLoader('array', new ArrayLoader());
131 $translator->addResource('array', array('foo' => 'foo (en)'), 'en');
132 $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
134 // force catalogue loading
135 $translator->trans('bar');
137 $translator->setFallbackLocales(array('fr_FR', 'fr'));
138 $this->assertEquals('bar (fr)', $translator->trans('bar'));
142 * @dataProvider getInvalidLocalesTests
143 * @expectedException \InvalidArgumentException
145 public function testSetFallbackInvalidLocales($locale)
147 $translator = new Translator('fr', new MessageSelector());
148 $translator->setFallbackLocales(array('fr', $locale));
152 * @dataProvider getValidLocalesTests
154 public function testSetFallbackValidLocales($locale)
156 $translator = new Translator($locale, new MessageSelector());
157 $translator->setFallbackLocales(array('fr', $locale));
158 // no assertion. this method just asserts that no exception is thrown
159 $this->addToAssertionCount(1);
162 public function testTransWithFallbackLocale()
164 $translator = new Translator('fr_FR');
165 $translator->setFallbackLocales(array('en'));
167 $translator->addLoader('array', new ArrayLoader());
168 $translator->addResource('array', array('bar' => 'foobar'), 'en');
170 $this->assertEquals('foobar', $translator->trans('bar'));
174 * @dataProvider getInvalidLocalesTests
175 * @expectedException \InvalidArgumentException
177 public function testAddResourceInvalidLocales($locale)
179 $translator = new Translator('fr', new MessageSelector());
180 $translator->addResource('array', array('foo' => 'foofoo'), $locale);
184 * @dataProvider getValidLocalesTests
186 public function testAddResourceValidLocales($locale)
188 $translator = new Translator('fr', new MessageSelector());
189 $translator->addResource('array', array('foo' => 'foofoo'), $locale);
190 // no assertion. this method just asserts that no exception is thrown
191 $this->addToAssertionCount(1);
194 public function testAddResourceAfterTrans()
196 $translator = new Translator('fr');
197 $translator->addLoader('array', new ArrayLoader());
199 $translator->setFallbackLocales(array('en'));
201 $translator->addResource('array', array('foo' => 'foofoo'), 'en');
202 $this->assertEquals('foofoo', $translator->trans('foo'));
204 $translator->addResource('array', array('bar' => 'foobar'), 'en');
205 $this->assertEquals('foobar', $translator->trans('bar'));
209 * @dataProvider getTransFileTests
210 * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
212 public function testTransWithoutFallbackLocaleFile($format, $loader)
214 $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
215 $translator = new Translator('en');
216 $translator->addLoader($format, new $loaderClass());
217 $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en');
218 $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en');
220 // force catalogue loading
221 $translator->trans('foo');
225 * @dataProvider getTransFileTests
227 public function testTransWithFallbackLocaleFile($format, $loader)
229 $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
230 $translator = new Translator('en_GB');
231 $translator->addLoader($format, new $loaderClass());
232 $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en_GB');
233 $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources');
235 $this->assertEquals('bar', $translator->trans('foo', array(), 'resources'));
238 public function testTransWithFallbackLocaleBis()
240 $translator = new Translator('en_US');
241 $translator->addLoader('array', new ArrayLoader());
242 $translator->addResource('array', array('foo' => 'foofoo'), 'en_US');
243 $translator->addResource('array', array('bar' => 'foobar'), 'en');
244 $this->assertEquals('foobar', $translator->trans('bar'));
247 public function testTransWithFallbackLocaleTer()
249 $translator = new Translator('fr_FR');
250 $translator->addLoader('array', new ArrayLoader());
251 $translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US');
252 $translator->addResource('array', array('bar' => 'bar (en)'), 'en');
254 $translator->setFallbackLocales(array('en_US', 'en'));
256 $this->assertEquals('foo (en_US)', $translator->trans('foo'));
257 $this->assertEquals('bar (en)', $translator->trans('bar'));
260 public function testTransNonExistentWithFallback()
262 $translator = new Translator('fr');
263 $translator->setFallbackLocales(array('en'));
264 $translator->addLoader('array', new ArrayLoader());
265 $this->assertEquals('non-existent', $translator->trans('non-existent'));
269 * @expectedException \RuntimeException
271 public function testWhenAResourceHasNoRegisteredLoader()
273 $translator = new Translator('en');
274 $translator->addResource('array', array('foo' => 'foofoo'), 'en');
276 $translator->trans('foo');
279 public function testNestedFallbackCatalogueWhenUsingMultipleLocales()
281 $translator = new Translator('fr');
282 $translator->setFallbackLocales(array('ru', 'en'));
284 $translator->getCatalogue('fr');
286 $this->assertNotNull($translator->getCatalogue('ru')->getFallbackCatalogue());
289 public function testFallbackCatalogueResources()
291 $translator = new Translator('en_GB', new MessageSelector());
292 $translator->addLoader('yml', new \Symfony\Component\Translation\Loader\YamlFileLoader());
293 $translator->addResource('yml', __DIR__.'/fixtures/empty.yml', 'en_GB');
294 $translator->addResource('yml', __DIR__.'/fixtures/resources.yml', 'en');
296 // force catalogue loading
297 $this->assertEquals('bar', $translator->trans('foo', array()));
299 $resources = $translator->getCatalogue('en')->getResources();
300 $this->assertCount(1, $resources);
301 $this->assertContains(__DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'resources.yml', $resources);
303 $resources = $translator->getCatalogue('en_GB')->getResources();
304 $this->assertCount(2, $resources);
305 $this->assertContains(__DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'empty.yml', $resources);
306 $this->assertContains(__DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'resources.yml', $resources);
310 * @dataProvider getTransTests
312 public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
314 $translator = new Translator('en');
315 $translator->addLoader('array', new ArrayLoader());
316 $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
318 $this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
322 * @dataProvider getInvalidLocalesTests
323 * @expectedException \InvalidArgumentException
325 public function testTransInvalidLocale($locale)
327 $translator = new Translator('en', new MessageSelector());
328 $translator->addLoader('array', new ArrayLoader());
329 $translator->addResource('array', array('foo' => 'foofoo'), 'en');
331 $translator->trans('foo', array(), '', $locale);
335 * @dataProvider getValidLocalesTests
337 public function testTransValidLocale($locale)
339 $translator = new Translator($locale, new MessageSelector());
340 $translator->addLoader('array', new ArrayLoader());
341 $translator->addResource('array', array('test' => 'OK'), $locale);
343 $this->assertEquals('OK', $translator->trans('test'));
344 $this->assertEquals('OK', $translator->trans('test', array(), null, $locale));
348 * @dataProvider getFlattenedTransTests
350 public function testFlattenedTrans($expected, $messages, $id)
352 $translator = new Translator('en');
353 $translator->addLoader('array', new ArrayLoader());
354 $translator->addResource('array', $messages, 'fr', '');
356 $this->assertEquals($expected, $translator->trans($id, array(), '', 'fr'));
360 * @dataProvider getTransChoiceTests
362 public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
364 $translator = new Translator('en');
365 $translator->addLoader('array', new ArrayLoader());
366 $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
368 $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
372 * @dataProvider getInvalidLocalesTests
373 * @expectedException \InvalidArgumentException
375 public function testTransChoiceInvalidLocale($locale)
377 $translator = new Translator('en', new MessageSelector());
378 $translator->addLoader('array', new ArrayLoader());
379 $translator->addResource('array', array('foo' => 'foofoo'), 'en');
381 $translator->transChoice('foo', 1, array(), '', $locale);
385 * @dataProvider getValidLocalesTests
387 public function testTransChoiceValidLocale($locale)
389 $translator = new Translator('en', new MessageSelector());
390 $translator->addLoader('array', new ArrayLoader());
391 $translator->addResource('array', array('foo' => 'foofoo'), 'en');
393 $translator->transChoice('foo', 1, array(), '', $locale);
394 // no assertion. this method just asserts that no exception is thrown
395 $this->addToAssertionCount(1);
398 public function getTransFileTests()
401 array('csv', 'CsvFileLoader'),
402 array('ini', 'IniFileLoader'),
403 array('mo', 'MoFileLoader'),
404 array('po', 'PoFileLoader'),
405 array('php', 'PhpFileLoader'),
406 array('ts', 'QtFileLoader'),
407 array('xlf', 'XliffFileLoader'),
408 array('yml', 'YamlFileLoader'),
409 array('json', 'JsonFileLoader'),
413 public function getTransTests()
416 array('Symfony est super !', 'Symfony is great!', 'Symfony est super !', array(), 'fr', ''),
417 array('Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', array('%what%' => 'awesome'), 'fr', ''),
418 array('Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', array(), 'fr', ''),
422 public function getFlattenedTransTests()
427 'great' => 'Symfony est super!',
432 'baz' => 'Foo Bar Baz',
439 array('Symfony est super!', $messages, 'symfony.is.great'),
440 array('Foo Bar Baz', $messages, 'foo.bar.baz'),
441 array('Foo Baz', $messages, 'foo.baz'),
445 public function getTransChoiceTests()
448 array('Il y a 0 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
449 array('Il y a 1 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
450 array('Il y a 10 pommes', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
452 array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
453 array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
454 array('Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
456 array('Il y a 0 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
457 array('Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
458 array('Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
460 array('Il n\'y a aucune pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
461 array('Il y a 1 pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
462 array('Il y a 10 pommes', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
464 array('Il y a 0 pomme', new StringClass('{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
468 public function getInvalidLocalesTests()
485 public function getValidLocalesTests()
502 public function testTransChoiceFallback()
504 $translator = new Translator('ru');
505 $translator->setFallbackLocales(array('en'));
506 $translator->addLoader('array', new ArrayLoader());
507 $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
509 $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
512 public function testTransChoiceFallbackBis()
514 $translator = new Translator('ru');
515 $translator->setFallbackLocales(array('en_US', 'en'));
516 $translator->addLoader('array', new ArrayLoader());
517 $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US');
519 $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
522 public function testTransChoiceFallbackWithNoTranslation()
524 $translator = new Translator('ru');
525 $translator->setFallbackLocales(array('en'));
526 $translator->addLoader('array', new ArrayLoader());
528 // consistent behavior with Translator::trans(), which returns the string
529 // unchanged if it can't be found
530 $this->assertEquals('some_message2', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
535 * @dataProvider dataProviderGetMessages
537 public function testLegacyGetMessages($resources, $locale, $expected)
539 $locales = array_keys($resources);
540 $_locale = null !== $locale ? $locale : reset($locales);
541 $locales = array_slice($locales, 0, array_search($_locale, $locales));
543 $translator = new Translator($_locale, new MessageSelector());
544 $translator->setFallbackLocales(array_reverse($locales));
545 $translator->addLoader('array', new ArrayLoader());
546 foreach ($resources as $_locale => $domainMessages) {
547 foreach ($domainMessages as $domain => $messages) {
548 $translator->addResource('array', $messages, $_locale, $domain);
551 $result = $translator->getMessages($locale);
553 $this->assertEquals($expected, $result);
556 public function dataProviderGetMessages()
560 'jsmessages' => array(
565 'foo' => 'foo messages (EN)',
567 'validators' => array(
568 'int' => 'integer (EN)',
573 'foo' => 'foo messages (PT)',
575 'validators' => array(
576 'str' => 'integer (PT)',
580 'validators' => array(
581 'int' => 'integer (BR)',
587 array($resources, null,
589 'jsmessages' => array(
594 'foo' => 'foo messages (EN)',
596 'validators' => array(
597 'int' => 'integer (EN)',
601 array($resources, 'en',
603 'jsmessages' => array(
608 'foo' => 'foo messages (EN)',
610 'validators' => array(
611 'int' => 'integer (EN)',
615 array($resources, 'pt-PT',
617 'jsmessages' => array(
622 'foo' => 'foo messages (PT)',
624 'validators' => array(
625 'int' => 'integer (EN)',
626 'str' => 'integer (PT)',
630 array($resources, 'pt_BR',
632 'jsmessages' => array(
637 'foo' => 'foo messages (PT)',
639 'validators' => array(
640 'int' => 'integer (BR)',
641 'str' => 'integer (PT)',
653 public function __construct($str)
658 public function __toString()