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(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy