diff --git a/UPGRADE-4.2.md b/UPGRADE-4.2.md index 18b88f2fa2318..6b3c4423d19fd 100644 --- a/UPGRADE-4.2.md +++ b/UPGRADE-4.2.md @@ -114,6 +114,7 @@ FrameworkBundle set the "APP_ENV" environment variable instead. * The `--no-debug` console option has been deprecated, set the "APP_DEBUG" environment variable to "0" instead. + * The `Templating\Helper\TranslatorHelper::transChoice()` method has been deprecated, use the `trans()` one instead with a `%count%` parameter. Messenger --------- @@ -219,9 +220,15 @@ Translation ----------- * The `TranslatorInterface` has been deprecated in favor of `Symfony\Contracts\Translation\TranslatorInterface` + * The `Translator::transChoice()` method has been deprecated in favor of using `Translator::trans()` with "%count%" as the parameter driving plurals * The `MessageSelector`, `Interval` and `PluralizationRules` classes have been deprecated, use `IdentityTranslator` instead * The `Translator::getFallbackLocales()` and `TranslationDataCollector::getFallbackLocales()` method have been marked as internal +TwigBundle +---------- + + * The `transchoice` tag and filter have been deprecated, use the `trans` ones instead with a `%count%` parameter. + Validator --------- diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index e7470a2467db7..a876f151b9b6a 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -120,6 +120,7 @@ FrameworkBundle set the "APP_ENV" environment variable instead. * The `--no-debug` console option has been removed, set the "APP_DEBUG" environment variable to "0" instead. + * The `Templating\Helper\TranslatorHelper::transChoice()` method has been removed, use the `trans()` one instead with a `%count%` parameter. HttpFoundation -------------- @@ -196,11 +197,13 @@ Translation * The `TranslatorInterface` has been removed in favor of `Symfony\Contracts\Translation\TranslatorInterface` * The `MessageSelector`, `Interval` and `PluralizationRules` classes have been removed, use `IdentityTranslator` instead * The `Translator::getFallbackLocales()` and `TranslationDataCollector::getFallbackLocales()` method are now internal + * The `Translator::transChoice()` method has been removed in favor of using `Translator::trans()` with "%count%" as the parameter driving plurals TwigBundle ---------- * The default value (`false`) of the `twig.strict_variables` configuration option has been changed to `%kernel.debug%`. + * The `transchoice` tag and filter have been removed, use the `trans` ones instead with a `%count%` parameter. Validator -------- diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index a2e55f6db2364..341bcfa538fdb 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -4,8 +4,9 @@ CHANGELOG 4.2.0 ----- -* add bundle name suggestion on wrongly overridden templates paths -* added `name` argument in `debug:twig` command and changed `filter` argument as `--filter` option + * add bundle name suggestion on wrongly overridden templates paths + * added `name` argument in `debug:twig` command and changed `filter` argument as `--filter` option + * deprecated the `transchoice` tag and filter, use the `trans` ones instead with a `%count%` parameter 4.1.0 ----- diff --git a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php index 607302d9917c6..ec5d452cef460 100644 --- a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php @@ -16,6 +16,7 @@ use Symfony\Bridge\Twig\TokenParser\TransChoiceTokenParser; use Symfony\Bridge\Twig\TokenParser\TransDefaultDomainTokenParser; use Symfony\Bridge\Twig\TokenParser\TransTokenParser; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorTrait; use Twig\Extension\AbstractExtension; @@ -27,6 +28,8 @@ * Provides integration of the Translation component with Twig. * * @author Fabien Potencier + * + * @final since Symfony 4.2 */ class TranslationExtension extends AbstractExtension { @@ -34,20 +37,30 @@ class TranslationExtension extends AbstractExtension getLocale as private; setLocale as private; trans as private doTrans; - transChoice as private doTransChoice; } private $translator; private $translationNodeVisitor; - public function __construct(TranslatorInterface $translator = null, NodeVisitorInterface $translationNodeVisitor = null) + /** + * @param TranslatorInterface|null $translator + */ + public function __construct($translator = null, NodeVisitorInterface $translationNodeVisitor = null) { + if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->translator = $translator; $this->translationNodeVisitor = $translationNodeVisitor; } + /** + * @deprecated since Symfony 4.2 + */ public function getTranslator() { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED); + return $this->translator; } @@ -58,7 +71,7 @@ public function getFilters() { return array( new TwigFilter('trans', array($this, 'trans')), - new TwigFilter('transchoice', array($this, 'transchoice')), + new TwigFilter('transchoice', array($this, 'transchoice'), array('deprecated' => '4.2', 'alternative' => 'trans" with parameter "%count%')), ); } @@ -96,8 +109,11 @@ public function getTranslationNodeVisitor() return $this->translationNodeVisitor ?: $this->translationNodeVisitor = new TranslationNodeVisitor(); } - public function trans($message, array $arguments = array(), $domain = null, $locale = null) + public function trans($message, array $arguments = array(), $domain = null, $locale = null, $count = null) { + if (null !== $count) { + $arguments['%count%'] = $count; + } if (null === $this->translator) { return $this->doTrans($message, $arguments, $domain, $locale); } @@ -105,10 +121,16 @@ public function trans($message, array $arguments = array(), $domain = null, $loc return $this->translator->trans($message, $arguments, $domain, $locale); } + /** + * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter + */ public function transchoice($message, $count, array $arguments = array(), $domain = null, $locale = null) { if (null === $this->translator) { - return $this->doTransChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + return $this->doTrans($message, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + } + if ($this->translator instanceof TranslatorInterface) { + return $this->translator->trans($message, array_merge(array('%count%' => $count), $arguments), $domain, $locale); } return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); diff --git a/src/Symfony/Bridge/Twig/Node/TransNode.php b/src/Symfony/Bridge/Twig/Node/TransNode.php index 7eb8d743e9b3e..578e37dd2c3ad 100644 --- a/src/Symfony/Bridge/Twig/Node/TransNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransNode.php @@ -60,19 +60,12 @@ public function compile(Compiler $compiler) $method = !$this->hasNode('count') ? 'trans' : 'transChoice'; $compiler - ->write('echo $this->env->getExtension(\'Symfony\Bridge\Twig\Extension\TranslationExtension\')->getTranslator()->'.$method.'(') + ->write('echo $this->env->getExtension(\'Symfony\Bridge\Twig\Extension\TranslationExtension\')->trans(') ->subcompile($msg) ; $compiler->raw(', '); - if ($this->hasNode('count')) { - $compiler - ->subcompile($this->getNode('count')) - ->raw(', ') - ; - } - if (null !== $vars) { $compiler ->raw('array_merge(') @@ -98,7 +91,17 @@ public function compile(Compiler $compiler) ->raw(', ') ->subcompile($this->getNode('locale')) ; + } elseif ($this->hasNode('count')) { + $compiler->raw(', null'); } + + if ($this->hasNode('count')) { + $compiler + ->raw(', ') + ->subcompile($this->getNode('count')) + ; + } + $compiler->raw(");\n"); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php index feba2b2b6b0d0..bb36cda8eaf56 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -45,6 +45,15 @@ public function testTrans($template, $expected, array $variables = array()) $this->assertEquals($expected, $this->getTemplate($template)->render($variables)); } + /** + * @group legacy + * @dataProvider getTransChoiceTests + */ + public function testTransChoice($template, $expected, array $variables = array()) + { + $this->testTrans($template, $expected, $variables); + } + /** * @expectedException \Twig\Error\SyntaxError * @expectedExceptionMessage Unexpected token. Twig was looking for the "with", "from", or "into" keyword in "index" at line 3. @@ -64,6 +73,7 @@ public function testTransComplexBody() } /** + * @group legacy * @expectedException \Twig\Error\SyntaxError * @expectedExceptionMessage A message inside a transchoice tag must be a simple text in "index" at line 2. */ @@ -87,6 +97,69 @@ public function getTransTests() array('{% trans into "fr"%}Hello{% endtrans %}', 'Hello'), + // trans with count + array( + '{% trans from "messages" %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtrans %}', + 'There is no apples', + array('count' => 0), + ), + array( + '{% trans %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtrans %}', + 'There is 5 apples', + array('count' => 5), + ), + array( + '{% trans %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%){% endtrans %}', + 'There is 5 apples (Symfony)', + array('count' => 5, 'name' => 'Symfony'), + ), + array( + '{% trans with { \'%name%\': \'Symfony\' } %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%){% endtrans %}', + 'There is 5 apples (Symfony)', + array('count' => 5), + ), + array( + '{% trans into "fr"%}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtrans %}', + 'There is no apples', + array('count' => 0), + ), + array( + '{% trans count 5 into "fr"%}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtrans %}', + 'There is 5 apples', + ), + + // trans filter + array('{{ "Hello"|trans }}', 'Hello'), + array('{{ name|trans }}', 'Symfony', array('name' => 'Symfony')), + array('{{ hello|trans({ \'%name%\': \'Symfony\' }) }}', 'Hello Symfony', array('hello' => 'Hello %name%')), + array('{% set vars = { \'%name%\': \'Symfony\' } %}{{ hello|trans(vars) }}', 'Hello Symfony', array('hello' => 'Hello %name%')), + array('{{ "Hello"|trans({}, "messages", "fr") }}', 'Hello'), + + // trans filter with count + array('{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|trans(count=count) }}', 'There is 5 apples', array('count' => 5)), + array('{{ text|trans(count=5, arguments={\'%name%\': \'Symfony\'}) }}', 'There is 5 apples (Symfony)', array('text' => '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%)')), + array('{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|trans({}, "messages", "fr", count) }}', 'There is 5 apples', array('count' => 5)), + ); + } + + /** + * @group legacy + */ + public function getTransChoiceTests() + { + return array( + // trans tag + array('{% trans %}Hello{% endtrans %}', 'Hello'), + array('{% trans %}%name%{% endtrans %}', 'Symfony', array('name' => 'Symfony')), + + array('{% trans from elsewhere %}Hello{% endtrans %}', 'Hello'), + + array('{% trans %}Hello %name%{% endtrans %}', 'Hello Symfony', array('name' => 'Symfony')), + array('{% trans with { \'%name%\': \'Symfony\' } %}Hello %name%{% endtrans %}', 'Hello Symfony'), + array('{% set vars = { \'%name%\': \'Symfony\' } %}{% trans with vars %}Hello %name%{% endtrans %}', 'Hello Symfony'), + + array('{% trans into "fr"%}Hello{% endtrans %}', 'Hello'), + // transchoice array( '{% transchoice count from "messages" %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}', @@ -145,8 +218,8 @@ public function testDefaultTranslationDomain() {%- trans from "custom" %}foo{% endtrans %} {{- "foo"|trans }} {{- "foo"|trans({}, "custom") }} - {{- "foo"|transchoice(1) }} - {{- "foo"|transchoice(1, {}, "custom") }} + {{- "foo"|trans(count=1) }} + {{- "foo"|trans({"%count%":1}, "custom") }} {% endblock %} ', @@ -174,12 +247,12 @@ public function testDefaultTranslationDomainWithNamedArguments() {%- block content %} {{- "foo"|trans(arguments = {}, domain = "custom") }} - {{- "foo"|transchoice(count = 1) }} - {{- "foo"|transchoice(count = 1, arguments = {}, domain = "custom") }} + {{- "foo"|trans(count = 1) }} + {{- "foo"|trans(count = 1, arguments = {}, domain = "custom") }} {{- "foo"|trans({}, domain = "custom") }} {{- "foo"|trans({}, "custom", locale = "fr") }} - {{- "foo"|transchoice(1, arguments = {}, domain = "custom") }} - {{- "foo"|transchoice(1, {}, "custom", locale = "fr") }} + {{- "foo"|trans(arguments = {"%count%":1}, domain = "custom") }} + {{- "foo"|trans({"%count%":1}, "custom", locale = "fr") }} {% endblock %} ', diff --git a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php index d45f60c09d1c6..e4a20d72fc0a7 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php @@ -34,7 +34,7 @@ public function testCompileStrict() $this->assertEquals( sprintf( - 'echo $this->env->getExtension(\'Symfony\Bridge\Twig\Extension\TranslationExtension\')->getTranslator()->trans("trans %%var%%", array_merge(array("%%var%%" => %s), %s), "messages");', + 'echo $this->env->getExtension(\'Symfony\Bridge\Twig\Extension\TranslationExtension\')->trans("trans %%var%%", array_merge(array("%%var%%" => %s), %s), "messages");', $this->getVariableGetterWithoutStrictCheck('var'), $this->getVariableGetterWithStrictCheck('foo') ), diff --git a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php index e46ba205e5e17..fca5f4d4a9d3a 100644 --- a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php @@ -50,15 +50,21 @@ public function testExtract($template, $messages) } } + /** + * @group legacy + * @dataProvider getLegacyExtractData + */ + public function testLegacyExtract($template, $messages) + { + $this->testExtract($template, $messages); + } + public function getExtractData() { return array( array('{{ "new key" | trans() }}', array('new key' => 'messages')), array('{{ "new key" | trans() | upper }}', array('new key' => 'messages')), array('{{ "new key" | trans({}, "domain") }}', array('new key' => 'domain')), - array('{{ "new key" | transchoice(1) }}', array('new key' => 'messages')), - array('{{ "new key" | transchoice(1) | upper }}', array('new key' => 'messages')), - array('{{ "new key" | transchoice(1, {}, "domain") }}', array('new key' => 'domain')), array('{% trans %}new key{% endtrans %}', array('new key' => 'messages')), array('{% trans %} new key {% endtrans %}', array('new key' => 'messages')), array('{% trans from "domain" %}new key{% endtrans %}', array('new key' => 'domain')), @@ -67,11 +73,27 @@ public function getExtractData() // make sure 'trans_default_domain' tag is supported array('{% trans_default_domain "domain" %}{{ "new key"|trans }}', array('new key' => 'domain')), - array('{% trans_default_domain "domain" %}{{ "new key"|transchoice }}', array('new key' => 'domain')), array('{% trans_default_domain "domain" %}{% trans %}new key{% endtrans %}', array('new key' => 'domain')), // make sure this works with twig's named arguments array('{{ "new key" | trans(domain="domain") }}', array('new key' => 'domain')), + ); + } + + /** + * @group legacy + */ + public function getLegacyExtractData() + { + return array( + array('{{ "new key" | transchoice(1) }}', array('new key' => 'messages')), + array('{{ "new key" | transchoice(1) | upper }}', array('new key' => 'messages')), + array('{{ "new key" | transchoice(1, {}, "domain") }}', array('new key' => 'domain')), + + // make sure 'trans_default_domain' tag is supported + array('{% trans_default_domain "domain" %}{{ "new key"|transchoice }}', array('new key' => 'domain')), + + // make sure this works with twig's named arguments array('{{ "new key" | transchoice(domain="domain", count=1) }}', array('new key' => 'domain')), ); } diff --git a/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php index 5261d7ad52cee..490fb14ff45e5 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php @@ -23,6 +23,8 @@ * Token Parser for the 'transchoice' tag. * * @author Fabien Potencier + * + * @deprecated since Symfony 4.2, use the "trans" tag with a "%count%" parameter instead */ class TransChoiceTokenParser extends TransTokenParser { @@ -38,6 +40,8 @@ public function parse(Token $token) $lineno = $token->getLine(); $stream = $this->parser->getStream(); + @trigger_error(sprintf('The "transchoice" tag is deprecated since Symfony 4.2, use the "trans" one instead with a "%count%" parameter in %s line %d.', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED); + $vars = new ArrayExpression(array(), $lineno); $count = $this->parser->getExpressionParser()->parseExpression(); diff --git a/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php index 76c8dc06100c1..f5bf12b57ee4b 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php @@ -39,10 +39,17 @@ public function parse(Token $token) $lineno = $token->getLine(); $stream = $this->parser->getStream(); + $count = null; $vars = new ArrayExpression(array(), $lineno); $domain = null; $locale = null; if (!$stream->test(Token::BLOCK_END_TYPE)) { + if ($stream->test('count')) { + // {% trans count 5 %} + $stream->next(); + $count = $this->parser->getExpressionParser()->parseExpression(); + } + if ($stream->test('with')) { // {% trans with vars %} $stream->next(); @@ -74,7 +81,7 @@ public function parse(Token $token) $stream->expect(Token::BLOCK_END_TYPE); - return new TransNode($body, $domain, null, $vars, $locale, $lineno, $this->getTag()); + return new TransNode($body, $domain, $count, $vars, $locale, $lineno, $this->getTag()); } public function decideTransFork($token) diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index e2e56664891c5..ed75ed1f90dcb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -18,6 +18,7 @@ CHANGELOG the "APP_ENV" environment variable instead. * Deprecated the `--no-debug` console option, set the "APP_DEBUG" environment variable to "0" instead. + * Deprecated the `Templating\Helper\TranslatorHelper::transChoice()` method, use the `trans()` one instead with a `%count%` parameter 4.1.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index dc14dea0ef644..6e2c1324ba884 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -26,6 +26,7 @@ use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Reader\TranslationReaderInterface; use Symfony\Component\Translation\Translator; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -50,8 +51,14 @@ class TranslationDebugCommand extends Command private $defaultTransPath; private $defaultViewsPath; - public function __construct(TranslatorInterface $translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultTransPath = null, string $defaultViewsPath = null) + /** + * @param TranslatorInterface $translator + */ + public function __construct($translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultTransPath = null, string $defaultViewsPath = null) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } parent::__construct(); $this->translator = $translator; diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/TranslatorHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/TranslatorHelper.php index b3dd76d42adc2..8120a0cd8efb3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/TranslatorHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/TranslatorHelper.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorTrait; @@ -24,13 +25,18 @@ class TranslatorHelper extends Helper getLocale as private; setLocale as private; trans as private doTrans; - transChoice as private doTransChoice; } protected $translator; - public function __construct(TranslatorInterface $translator = null) + /** + * @param TranslatorInterface|null $translator + */ + public function __construct($translator = null) { + if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->translator = $translator; } @@ -48,11 +54,17 @@ public function trans($id, array $parameters = array(), $domain = 'messages', $l /** * @see TranslatorInterface::transChoice() + * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ public function transChoice($id, $number, array $parameters = array(), $domain = 'messages', $locale = null) { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%count%" parameter.', __METHOD__), E_USER_DEPRECATED); + if (null === $this->translator) { - return $this->doTransChoice($id, $number, $parameters, $domain, $locale); + return $this->doTrans($id, array('%count%' => $number) + $parameters, $domain, $locale); + } + if ($this->translator instanceof TranslatorInterface) { + return $this->translator->trans($id, array('%count%' => $number) + $parameters, $domain, $locale); } return $this->translator->transChoice($id, $number, $parameters, $domain, $locale); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index eeb4e0daa48de..f5a4818553b2b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -52,14 +52,27 @@ public function testTransWithoutCaching() $this->assertEquals('foo (FR)', $translator->trans('foo')); $this->assertEquals('bar (EN)', $translator->trans('bar')); $this->assertEquals('foobar (ES)', $translator->trans('foobar')); - $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('choice 0 (EN)', $translator->trans('choice', array('%count%' => 0))); $this->assertEquals('no translation', $translator->trans('no translation')); $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); - $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('other choice 1 (PT-BR)', $translator->trans('other choice', array('%count%' => 1))); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); } + /** + * @group legacy + */ + public function testTransChoiceWithoutCaching() + { + $translator = $this->getTranslator($this->getLoader()); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + } + public function testTransWithCaching() { // prime the cache @@ -70,10 +83,10 @@ public function testTransWithCaching() $this->assertEquals('foo (FR)', $translator->trans('foo')); $this->assertEquals('bar (EN)', $translator->trans('bar')); $this->assertEquals('foobar (ES)', $translator->trans('foobar')); - $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('choice 0 (EN)', $translator->trans('choice', array('%count%' => 0))); $this->assertEquals('no translation', $translator->trans('no translation')); $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); - $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('other choice 1 (PT-BR)', $translator->trans('other choice', array('%count%' => 1))); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); @@ -88,14 +101,37 @@ public function testTransWithCaching() $this->assertEquals('foo (FR)', $translator->trans('foo')); $this->assertEquals('bar (EN)', $translator->trans('bar')); $this->assertEquals('foobar (ES)', $translator->trans('foobar')); - $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); $this->assertEquals('no translation', $translator->trans('no translation')); $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); - $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); } + /** + * @group legacy + */ + public function testTransChoiceWithCaching() + { + // prime the cache + $translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir)); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + + // do it another time as the cache is primed now + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $loader->expects($this->never())->method('load'); + + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir)); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + } + /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage Invalid "invalid locale" locale. diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php index 8c519a81ec03e..d84d09b38600c 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php @@ -13,6 +13,7 @@ use Symfony\Component\Form\AbstractExtension; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -28,11 +29,14 @@ class CsrfExtension extends AbstractExtension /** * @param CsrfTokenManagerInterface $tokenManager The CSRF token manager - * @param TranslatorInterface $translator The translator for translating error messages + * @param TranslatorInterface|null $translator The translator for translating error messages * @param string|null $translationDomain The translation domain for translating */ - public function __construct(CsrfTokenManagerInterface $tokenManager, TranslatorInterface $translator = null, string $translationDomain = null) + public function __construct(CsrfTokenManagerInterface $tokenManager, $translator = null, string $translationDomain = null) { + if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 2 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->tokenManager = $tokenManager; $this->translator = $translator; $this->translationDomain = $translationDomain; diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php index f8987378438f3..4a49a85fcdd31 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php +++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php @@ -18,6 +18,7 @@ use Symfony\Component\Form\Util\ServerParams; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -40,8 +41,14 @@ public static function getSubscribedEvents() ); } - public function __construct(string $fieldName, CsrfTokenManagerInterface $tokenManager, string $tokenId, string $errorMessage, TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null) + /** + * @param TranslatorInterface|null $translator + */ + public function __construct(string $fieldName, CsrfTokenManagerInterface $tokenManager, string $tokenId, string $errorMessage, $translator = null, string $translationDomain = null, ServerParams $serverParams = null) { + if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 5 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->fieldName = $fieldName; $this->tokenManager = $tokenManager; $this->tokenId = $tokenId; diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index 35621c585bf32..7e16f6dc375f2 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -19,6 +19,7 @@ use Symfony\Component\Form\Util\ServerParams; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -33,8 +34,14 @@ class FormTypeCsrfExtension extends AbstractTypeExtension private $translationDomain; private $serverParams; - public function __construct(CsrfTokenManagerInterface $defaultTokenManager, bool $defaultEnabled = true, string $defaultFieldName = '_token', TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null) + /** + * @param TranslatorInterface|null $translator + */ + public function __construct(CsrfTokenManagerInterface $defaultTokenManager, bool $defaultEnabled = true, string $defaultFieldName = '_token', $translator = null, string $translationDomain = null, ServerParams $serverParams = null) { + if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 4 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->defaultTokenManager = $defaultTokenManager; $this->defaultEnabled = $defaultEnabled; $this->defaultFieldName = $defaultFieldName; diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php index fa09cbc21f55b..9e5eec80014f2 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php @@ -14,6 +14,7 @@ use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -25,8 +26,14 @@ class UploadValidatorExtension extends AbstractTypeExtension private $translator; private $translationDomain; - public function __construct(TranslatorInterface $translator, string $translationDomain = null) + /** + * @param TranslatorInterface $translator + */ + public function __construct($translator, string $translationDomain = null) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->translator = $translator; $this->translationDomain = $translationDomain; } diff --git a/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php b/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php index 792e347816af0..7cf059886df3b 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php @@ -17,6 +17,7 @@ use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -29,8 +30,14 @@ class TranslatorListener implements EventSubscriberInterface private $translator; private $requestStack; - public function __construct(TranslatorInterface $translator, RequestStack $requestStack) + /** + * @param TranslatorInterface $translator + */ + public function __construct($translator, RequestStack $requestStack) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->translator = $translator; $this->requestStack = $requestStack; } diff --git a/src/Symfony/Component/Translation/CHANGELOG.md b/src/Symfony/Component/Translation/CHANGELOG.md index 6bbecdd099a30..e3a4725c3d5d6 100644 --- a/src/Symfony/Component/Translation/CHANGELOG.md +++ b/src/Symfony/Component/Translation/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG ----- * Started using ICU parent locales as fallback locales. + * deprecated `Translator::transChoice()` in favor of using `Translator::trans()` with a `%count%` parameter * deprecated `TranslatorInterface` in favor of `Symfony\Contracts\Translation\TranslatorInterface` * deprecated `MessageSelector`, `Interval` and `PluralizationRules`; use `IdentityTranslator` instead * Added `IntlMessageFormatter` and `FallbackMessageFormatter` diff --git a/src/Symfony/Component/Translation/DataCollectorTranslator.php b/src/Symfony/Component/Translation/DataCollectorTranslator.php index 10ac93516b315..3e2087220d657 100644 --- a/src/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/src/Symfony/Component/Translation/DataCollectorTranslator.php @@ -18,7 +18,7 @@ /** * @author Abdellatif Ait boudad */ -class DataCollectorTranslator implements LegacyTranslatorInterface, TranslatorBagInterface +class DataCollectorTranslator implements LegacyTranslatorInterface, TranslatorInterface, TranslatorBagInterface { const MESSAGE_DEFINED = 0; const MESSAGE_MISSING = 1; @@ -34,8 +34,11 @@ class DataCollectorTranslator implements LegacyTranslatorInterface, TranslatorBa /** * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface */ - public function __construct(TranslatorInterface $translator) + public function __construct($translator) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } if (!$translator instanceof TranslatorBagInterface) { throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator))); } @@ -56,11 +59,18 @@ public function trans($id, array $parameters = array(), $domain = null, $locale /** * {@inheritdoc} + * + * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { + if ($this->translator instanceof TranslatorInterface) { + $trans = $this->translator->trans($id, array('%count%' => $number) + $parameters, $domain, $locale); + } + $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); - $this->collectMessage($locale, $domain, $id, $trans, $parameters, $number); + + $this->collectMessage($locale, $domain, $id, $trans, array('%count%' => $number) + $parameters); return $trans; } @@ -125,9 +135,8 @@ public function getCollectedMessages() * @param string $id * @param string $translation * @param array|null $parameters - * @param int|null $number */ - private function collectMessage($locale, $domain, $id, $translation, $parameters = array(), $number = null) + private function collectMessage($locale, $domain, $id, $translation, $parameters = array()) { if (null === $domain) { $domain = 'messages'; @@ -160,8 +169,8 @@ private function collectMessage($locale, $domain, $id, $translation, $parameters 'id' => $id, 'translation' => $translation, 'parameters' => $parameters, - 'transChoiceNumber' => $number, 'state' => $state, + 'transChoiceNumber' => isset($parameters['%count%']) && is_numeric($parameters['%count%']) ? $parameters['%count%'] : null, ); } } diff --git a/src/Symfony/Component/Translation/Formatter/ChoiceMessageFormatterInterface.php b/src/Symfony/Component/Translation/Formatter/ChoiceMessageFormatterInterface.php index 92acbcafe2032..6bc68384af920 100644 --- a/src/Symfony/Component/Translation/Formatter/ChoiceMessageFormatterInterface.php +++ b/src/Symfony/Component/Translation/Formatter/ChoiceMessageFormatterInterface.php @@ -13,6 +13,8 @@ /** * @author Abdellatif Ait boudad + * + * @deprecated since Symfony 4.2, use MessageFormatterInterface::format() with a %count% parameter instead */ interface ChoiceMessageFormatterInterface { diff --git a/src/Symfony/Component/Translation/Formatter/MessageFormatter.php b/src/Symfony/Component/Translation/Formatter/MessageFormatter.php index 862e03aab17d7..1119c356f5afe 100644 --- a/src/Symfony/Component/Translation/Formatter/MessageFormatter.php +++ b/src/Symfony/Component/Translation/Formatter/MessageFormatter.php @@ -13,6 +13,7 @@ use Symfony\Component\Translation\IdentityTranslator; use Symfony\Component\Translation\MessageSelector; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -29,7 +30,7 @@ public function __construct($translator = null) { if ($translator instanceof MessageSelector) { $translator = new IdentityTranslator($translator); - } elseif (null !== $translator && !$translator instanceof TranslatorInterface) { + } elseif (null !== $translator && !$translator instanceof TranslatorInterface && !$translator instanceof LegacyTranslatorInterface) { throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); } @@ -41,15 +42,27 @@ public function __construct($translator = null) */ public function format($message, $locale, array $parameters = array()) { + if ($this->translator instanceof TranslatorInterface) { + return $this->translator->trans($message, $parameters, null, $locale); + } + return strtr($message, $parameters); } /** * {@inheritdoc} + * + * @deprecated since Symfony 4.2, use format() with a %count% parameter instead */ public function choiceFormat($message, $number, $locale, array $parameters = array()) { - $parameters = array_merge(array('%count%' => $number), $parameters); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the format() one instead with a %count% parameter.', __METHOD__), E_USER_DEPRECATED); + + $parameters = array('%count%' => $number) + $parameters; + + if ($this->translator instanceof TranslatorInterface) { + return $this->format($message, $locale, $parameters); + } return $this->format($this->translator->transChoice($message, $number, array(), null, $locale), $locale, $parameters); } diff --git a/src/Symfony/Component/Translation/IdentityTranslator.php b/src/Symfony/Component/Translation/IdentityTranslator.php index b69312ec79de3..31abdaab12513 100644 --- a/src/Symfony/Component/Translation/IdentityTranslator.php +++ b/src/Symfony/Component/Translation/IdentityTranslator.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Translation; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorTrait; /** @@ -18,11 +20,9 @@ * * @author Fabien Potencier */ -class IdentityTranslator implements TranslatorInterface +class IdentityTranslator implements LegacyTranslatorInterface, TranslatorInterface { - use TranslatorTrait { - transChoice as private doTransChoice; - } + use TranslatorTrait; private $selector; @@ -40,14 +40,18 @@ public function __construct(MessageSelector $selector = null) /** * {@inheritdoc} + * + * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%count%" parameter.', __METHOD__), E_USER_DEPRECATED); + if ($this->selector) { - return strtr($this->selector->choose((string) $id, (int) $number, $locale ?: $this->getLocale()), $parameters); + return strtr($this->selector->choose((string) $id, $number, $locale ?: $this->getLocale()), $parameters); } - return $this->doTransChoice($id, $number, $parameters, $domain, $locale); + return $this->trans($id, array('%count%' => $number) + $parameters, $domain, $locale); } private function getPluralizationRule(int $number, string $locale): int diff --git a/src/Symfony/Component/Translation/LoggingTranslator.php b/src/Symfony/Component/Translation/LoggingTranslator.php index cfb60ab0b0fcd..ee711568ab15d 100644 --- a/src/Symfony/Component/Translation/LoggingTranslator.php +++ b/src/Symfony/Component/Translation/LoggingTranslator.php @@ -32,8 +32,11 @@ class LoggingTranslator implements LegacyTranslatorInterface, TranslatorBagInter * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface * @param LoggerInterface $logger */ - public function __construct(TranslatorInterface $translator, LoggerInterface $logger) + public function __construct($translator, LoggerInterface $logger) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } if (!$translator instanceof TranslatorBagInterface) { throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator))); } @@ -55,10 +58,19 @@ public function trans($id, array $parameters = array(), $domain = null, $locale /** * {@inheritdoc} + * + * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { - $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%count%" parameter.', __METHOD__), E_USER_DEPRECATED); + + if ($this->translator instanceof TranslatorInterface) { + $trans = $this->translator->trans($id, array('%count%' => $number) + $parameters, $domain, $locale); + } else { + $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); + } + $this->log($id, $domain, $locale); return $trans; diff --git a/src/Symfony/Component/Translation/MessageSelector.php b/src/Symfony/Component/Translation/MessageSelector.php index 867a345cedeb9..06639417b623a 100644 --- a/src/Symfony/Component/Translation/MessageSelector.php +++ b/src/Symfony/Component/Translation/MessageSelector.php @@ -43,9 +43,9 @@ class MessageSelector * The two methods can also be mixed: * {0} There are no apples|one: There is one apple|more: There are %count% apples * - * @param string $message The message being translated - * @param int $number The number of items represented for the message - * @param string $locale The locale to use for choosing + * @param string $message The message being translated + * @param int|float $number The number of items represented for the message + * @param string $locale The locale to use for choosing * * @return string * diff --git a/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php b/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php index 1cdd33b395f0d..19e056c12e9d4 100644 --- a/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php @@ -25,7 +25,7 @@ public function testCollectMessages() $collector->trans('foo'); $collector->trans('bar'); - $collector->transChoice('choice', 0); + $collector->trans('choice', array('%count%' => 0)); $collector->trans('bar_ru'); $collector->trans('bar_ru', array('foo' => 'bar')); @@ -54,7 +54,7 @@ public function testCollectMessages() 'locale' => 'en', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_MISSING, - 'parameters' => array(), + 'parameters' => array('%count%' => 0), 'transChoiceNumber' => 0, ); $expectedMessages[] = array( @@ -79,6 +79,30 @@ public function testCollectMessages() $this->assertEquals($expectedMessages, $collector->getCollectedMessages()); } + /** + * @group legacy + */ + public function testCollectMessagesTransChoice() + { + $collector = $this->createCollector(); + $collector->setFallbackLocales(array('fr', 'ru')); + $collector->transChoice('choice', 0); + + $expectedMessages = array(); + + $expectedMessages[] = array( + 'id' => 'choice', + 'translation' => 'choice', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array('%count%' => 0), + 'transChoiceNumber' => 0, + ); + + $this->assertEquals($expectedMessages, $collector->getCollectedMessages()); + } + private function createCollector() { $translator = new Translator('en'); diff --git a/src/Symfony/Component/Translation/Tests/Formatter/MessageFormatterTest.php b/src/Symfony/Component/Translation/Tests/Formatter/MessageFormatterTest.php index 1fa736e7e3df4..f4c96aa12f4fc 100644 --- a/src/Symfony/Component/Translation/Tests/Formatter/MessageFormatterTest.php +++ b/src/Symfony/Component/Translation/Tests/Formatter/MessageFormatterTest.php @@ -26,6 +26,7 @@ public function testFormat($expected, $message, $parameters = array()) /** * @dataProvider getTransChoiceMessages + * @group legacy */ public function testFormatPlural($expected, $message, $number, $parameters) { diff --git a/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php b/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php index 022379e934834..0e43cbecf4197 100644 --- a/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php @@ -21,17 +21,19 @@ class LoggingTranslatorTest extends TestCase public function testTransWithNoTranslationIsLogged() { $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $logger->expects($this->exactly(2)) + $logger->expects($this->exactly(1)) ->method('warning') ->with('Translation not found.') ; $translator = new Translator('ar'); $loggableTranslator = new LoggingTranslator($translator, $logger); - $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10)); $loggableTranslator->trans('bar'); } + /** + * @group legacy + */ public function testTransChoiceFallbackIsLogged() { $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); @@ -47,4 +49,20 @@ public function testTransChoiceFallbackIsLogged() $loggableTranslator = new LoggingTranslator($translator, $logger); $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10)); } + + /** + * @group legacy + */ + public function testTransChoiceWithNoTranslationIsLogged() + { + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $logger->expects($this->exactly(1)) + ->method('warning') + ->with('Translation not found.') + ; + + $translator = new Translator('ar'); + $loggableTranslator = new LoggingTranslator($translator, $logger); + $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10)); + } } diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php index b0efefb7d089e..d630a7491d4bc 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php @@ -393,6 +393,7 @@ public function testFlattenedTrans($expected, $messages, $id) /** * @dataProvider getTransChoiceTests + * @group legacy */ public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain) { @@ -406,6 +407,7 @@ public function testTransChoice($expected, $id, $translation, $number, $paramete /** * @dataProvider getInvalidLocalesTests * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + * @group legacy */ public function testTransChoiceInvalidLocale($locale) { @@ -418,6 +420,7 @@ public function testTransChoiceInvalidLocale($locale) /** * @dataProvider getValidLocalesTests + * @group legacy */ public function testTransChoiceValidLocale($locale) { @@ -499,7 +502,7 @@ public function getTransChoiceTests() 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(), 'fr', ''), // Override %count% with a custom value - array('Il y a quelques pommes', 'one: There is one apple|more: There are %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 2, array('%count%' => 'quelques'), 'fr', ''), + array('Il y a quelques pommes', 'one: There is one apple|more: There are %count% apples', 'one: Il y a %count% pomme|more: Il y a quelques pommes', 2, array('%count%' => 'quelques'), 'fr', ''), ); } @@ -537,6 +540,9 @@ public function getValidLocalesTests() ); } + /** + * @group legacy + */ public function testTransChoiceFallback() { $translator = new Translator('ru'); @@ -547,6 +553,9 @@ public function testTransChoiceFallback() $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10))); } + /** + * @group legacy + */ public function testTransChoiceFallbackBis() { $translator = new Translator('ru'); @@ -557,6 +566,9 @@ public function testTransChoiceFallbackBis() $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10))); } + /** + * @group legacy + */ public function testTransChoiceFallbackWithNoTranslation() { $translator = new Translator('ru'); diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index b9a876497dbe8..d8d27301f04df 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -22,11 +22,13 @@ use Symfony\Component\Translation\Formatter\MessageFormatter; use Symfony\Component\Translation\Formatter\MessageFormatterInterface; use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Fabien Potencier */ -class Translator implements TranslatorInterface, TranslatorBagInterface +class Translator implements LegacyTranslatorInterface, TranslatorInterface, TranslatorBagInterface { /** * @var MessageCatalogueInterface[] @@ -199,9 +201,13 @@ public function trans($id, array $parameters = array(), $domain = null, $locale /** * {@inheritdoc} + * + * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%count%" parameter.', __METHOD__), E_USER_DEPRECATED); + if (!$this->formatter instanceof ChoiceMessageFormatterInterface) { throw new LogicException(sprintf('The formatter "%s" does not support plural translations.', \get_class($this->formatter))); } diff --git a/src/Symfony/Component/Translation/TranslatorInterface.php b/src/Symfony/Component/Translation/TranslatorInterface.php index 829c6f3d144fa..02cb5027b862b 100644 --- a/src/Symfony/Component/Translation/TranslatorInterface.php +++ b/src/Symfony/Component/Translation/TranslatorInterface.php @@ -11,13 +11,59 @@ namespace Symfony\Component\Translation; -use Symfony\Contracts\Translation\TranslatorInterface as BaseTranslatorInterface; +use Symfony\Component\Translation\Exception\InvalidArgumentException; /** + * TranslatorInterface. + * * @author Fabien Potencier * - * @deprecated since Symfony 4.2, use the same interface from the Symfony\Contracts\Translation namespace + * @deprecated since Symfony 4.2, use Symfony\Contracts\Translation\TranslatorInterface instead */ -interface TranslatorInterface extends BaseTranslatorInterface +interface TranslatorInterface { + /** + * Translates the given message. + * + * @param string $id The message id (may also be an object that can be cast to string) + * @param array $parameters An array of parameters for the message + * @param string|null $domain The domain for the message or null to use the default + * @param string|null $locale The locale or null to use the default + * + * @return string The translated string + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function trans($id, array $parameters = array(), $domain = null, $locale = null); + + /** + * Translates the given choice message by choosing a translation according to a number. + * + * @param string $id The message id (may also be an object that can be cast to string) + * @param int $number The number to use to find the indice of the message + * @param array $parameters An array of parameters for the message + * @param string|null $domain The domain for the message or null to use the default + * @param string|null $locale The locale or null to use the default + * + * @return string The translated string + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null); + + /** + * Sets the current locale. + * + * @param string $locale The locale + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function setLocale($locale); + + /** + * Returns the current locale. + * + * @return string The locale + */ + public function getLocale(); } diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index 03f56e9175088..fd9d9605e01f7 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Context; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; @@ -140,8 +141,11 @@ class ExecutionContext implements ExecutionContextInterface * @internal Called by {@link ExecutionContextFactory}. Should not be used * in user code. */ - public function __construct(ValidatorInterface $validator, $root, TranslatorInterface $translator, string $translationDomain = null) + public function __construct(ValidatorInterface $validator, $root, $translator, string $translationDomain = null) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 3 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->validator = $validator; $this->root = $root; $this->translator = $translator; diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php b/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php index 0bb1be767e6ee..b8d56eee9a968 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Context; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -34,8 +35,12 @@ class ExecutionContextFactory implements ExecutionContextFactoryInterface * use for translating * violation messages */ - public function __construct(TranslatorInterface $translator, string $translationDomain = null) + public function __construct($translator, string $translationDomain = null) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } + $this->translator = $translator; $this->translationDomain = $translationDomain; } diff --git a/src/Symfony/Component/Validator/Util/LegacyTranslatorProxy.php b/src/Symfony/Component/Validator/Util/LegacyTranslatorProxy.php index 077d37931f98e..12f487bf2f896 100644 --- a/src/Symfony/Component/Validator/Util/LegacyTranslatorProxy.php +++ b/src/Symfony/Component/Validator/Util/LegacyTranslatorProxy.php @@ -17,7 +17,7 @@ /** * @internal to be removed in Symfony 5.0. */ -class LegacyTranslatorProxy implements LegacyTranslatorInterface +class LegacyTranslatorProxy implements LegacyTranslatorInterface, TranslatorInterface { private $translator; @@ -60,6 +60,6 @@ public function trans($id, array $parameters = array(), $domain = null, $locale */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { - return $this->translator->transChoice($id, $number, $parameters, $domain, $locale); + return $this->translator->trans($id, array('%count%' => $number) + $parameters, $domain, $locale); } } diff --git a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php index 621c38d9afc68..abca36a536622 100644 --- a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php +++ b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Violation; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; @@ -43,8 +44,14 @@ class ConstraintViolationBuilder implements ConstraintViolationBuilderInterface */ private $cause; - public function __construct(ConstraintViolationList $violations, Constraint $constraint, $message, array $parameters, $root, $propertyPath, $invalidValue, TranslatorInterface $translator, $translationDomain = null) + /** + * @param TranslatorInterface $translator + */ + public function __construct(ConstraintViolationList $violations, Constraint $constraint, $message, array $parameters, $root, $propertyPath, $invalidValue, $translator, $translationDomain = null) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 8 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } $this->violations = $violations; $this->message = $message; $this->parameters = $parameters; @@ -147,6 +154,12 @@ public function addViolation() $this->parameters, $this->translationDomain ); + } elseif ($this->translator instanceof TranslatorInterface) { + $translatedMessage = $this->translator->trans( + $this->message, + array('%count%' => $this->plural) + $this->parameters, + $this->translationDomain + ); } else { try { $translatedMessage = $this->translator->transChoice( diff --git a/src/Symfony/Contracts/Tests/Translation/TranslatorTest.php b/src/Symfony/Contracts/Tests/Translation/TranslatorTest.php index e7af8e4da3820..a3b67dfe5eea9 100644 --- a/src/Symfony/Contracts/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Contracts/Tests/Translation/TranslatorTest.php @@ -50,24 +50,24 @@ public function testTrans($expected, $id, $parameters) /** * @dataProvider getTransChoiceTests */ - public function testTransChoiceWithExplicitLocale($expected, $id, $number, $parameters) + public function testTransChoiceWithExplicitLocale($expected, $id, $number) { $translator = $this->getTranslator(); $translator->setLocale('en'); - $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters)); + $this->assertEquals($expected, $translator->trans($id, array('%count%' => $number))); } /** * @dataProvider getTransChoiceTests */ - public function testTransChoiceWithDefaultLocale($expected, $id, $number, $parameters) + public function testTransChoiceWithDefaultLocale($expected, $id, $number) { \Locale::setDefault('en'); $translator = $this->getTranslator(); - $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters)); + $this->assertEquals($expected, $translator->trans($id, array('%count%' => $number))); } public function testGetSetLocale() @@ -103,14 +103,14 @@ public function getTransTests() public function getTransChoiceTests() { return array( - array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0, array('%count%' => 0)), - array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1, array('%count%' => 1)), - array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10, array('%count%' => 10)), - array('There are 0 apples', 'There is 1 apple|There are %count% apples', 0, array('%count%' => 0)), - array('There is 1 apple', 'There is 1 apple|There are %count% apples', 1, array('%count%' => 1)), - array('There are 10 apples', 'There is 1 apple|There are %count% apples', 10, array('%count%' => 10)), + array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0), + array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1), + array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10), + array('There are 0 apples', 'There is 1 apple|There are %count% apples', 0), + array('There is 1 apple', 'There is 1 apple|There are %count% apples', 1), + array('There are 10 apples', 'There is 1 apple|There are %count% apples', 10), // custom validation messages may be coded with a fixed value - array('There are 2 apples', 'There are 2 apples', 2, array('%count%' => 2)), + array('There are 2 apples', 'There are 2 apples', 2), ); } @@ -121,7 +121,7 @@ public function testInterval($expected, $number, $interval) { $translator = $this->getTranslator(); - $this->assertEquals($expected, $translator->transChoice($interval.' foo|[1,Inf[ bar', $number)); + $this->assertEquals($expected, $translator->trans($interval.' foo|[1,Inf[ bar', array('%count%' => $number))); } public function getInternal() @@ -146,14 +146,14 @@ public function testChoose($expected, $id, $number) { $translator = $this->getTranslator(); - $this->assertEquals($expected, $translator->transChoice($id, $number)); + $this->assertEquals($expected, $translator->trans($id, array('%count%' => $number))); } public function testReturnMessageIfExactlyOneStandardRuleIsGiven() { $translator = $this->getTranslator(); - $this->assertEquals('There are two apples', $translator->transChoice('There are two apples', 2)); + $this->assertEquals('There are two apples', $translator->trans('There are two apples', array('%count%' => 2))); } /** @@ -164,7 +164,7 @@ public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number) { $translator = $this->getTranslator(); - $translator->transChoice($id, $number); + $translator->trans($id, array('%count%' => $number)); } public function getNonMatchingMessages() @@ -186,29 +186,29 @@ public function getChooseTests() array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1), - array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10), - array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10), - array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10), + array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10), + array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10), + array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10), - array('There are %count% apples', 'There is one apple|There are %count% apples', 0), + array('There are 0 apples', 'There is one apple|There are %count% apples', 0), array('There is one apple', 'There is one apple|There are %count% apples', 1), - array('There are %count% apples', 'There is one apple|There are %count% apples', 10), + array('There are 10 apples', 'There is one apple|There are %count% apples', 10), - array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 0), + array('There are 0 apples', 'one: There is one apple|more: There are %count% apples', 0), array('There is one apple', 'one: There is one apple|more: There are %count% apples', 1), - array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 10), + array('There are 10 apples', 'one: There is one apple|more: There are %count% apples', 10), array('There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0), array('There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1), - array('There are %count% apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10), + array('There are 10 apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10), array('', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0), array('', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1), // Indexed only tests which are Gettext PoFile* compatible strings. - array('There are %count% apples', 'There is one apple|There are %count% apples', 0), + array('There are 0 apples', 'There is one apple|There are %count% apples', 0), array('There is one apple', 'There is one apple|There are %count% apples', 1), - array('There are %count% apples', 'There is one apple|There are %count% apples', 2), + array('There are 2 apples', 'There is one apple|There are %count% apples', 2), // Tests for float numbers array('There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7), diff --git a/src/Symfony/Contracts/Translation/TranslatorInterface.php b/src/Symfony/Contracts/Translation/TranslatorInterface.php index a54718733b06e..2130c1b2cf0a6 100644 --- a/src/Symfony/Contracts/Translation/TranslatorInterface.php +++ b/src/Symfony/Contracts/Translation/TranslatorInterface.php @@ -19,19 +19,8 @@ interface TranslatorInterface /** * Translates the given message. * - * @param string $id The message id (may also be an object that can be cast to string) - * @param array $parameters An array of parameters for the message - * @param string|null $domain The domain for the message or null to use the default - * @param string|null $locale The locale or null to use the default - * - * @return string The translated string - * - * @throws \InvalidArgumentException If the locale contains invalid characters - */ - public function trans($id, array $parameters = array(), $domain = null, $locale = null); - - /** - * Translates the given choice message by choosing a translation according to a number. + * When a number is provided as a parameter named "%count%", the message is parsed for plural + * forms and a translation is chosen according to this number using the following rules: * * Given a message with different plural translations separated by a * pipe (|), this method returns the correct portion of the message based @@ -64,7 +53,6 @@ public function trans($id, array $parameters = array(), $domain = null, $locale * @see https://en.wikipedia.org/wiki/ISO_31-11 * * @param string $id The message id (may also be an object that can be cast to string) - * @param int $number The number to use to find the indice of the message * @param array $parameters An array of parameters for the message * @param string|null $domain The domain for the message or null to use the default * @param string|null $locale The locale or null to use the default @@ -73,7 +61,7 @@ public function trans($id, array $parameters = array(), $domain = null, $locale * * @throws \InvalidArgumentException If the locale contains invalid characters */ - public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null); + public function trans($id, array $parameters = array(), $domain = null, $locale = null); /** * Sets the current locale. diff --git a/src/Symfony/Contracts/Translation/TranslatorTrait.php b/src/Symfony/Contracts/Translation/TranslatorTrait.php index 7c392b2bd418a..e19e37cfe5208 100644 --- a/src/Symfony/Contracts/Translation/TranslatorTrait.php +++ b/src/Symfony/Contracts/Translation/TranslatorTrait.php @@ -42,17 +42,14 @@ public function getLocale() * {@inheritdoc} */ public function trans($id, array $parameters = array(), $domain = null, $locale = null) - { - return strtr((string) $id, $parameters); - } - - /** - * {@inheritdoc} - */ - public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { $id = (string) $id; - $number = (float) $number; + + if (!isset($parameters['%count%']) || !is_numeric($parameters['%count%'])) { + return strtr($id, $parameters); + } + + $number = (float) $parameters['%count%']; $locale = (string) $locale ?: $this->getLocale(); $parts = array();