From 60ad3a235a88a0627a8f410bb659435855e28567 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Mon, 22 Jun 2015 17:57:50 +0000 Subject: [PATCH 1/2] [Translator] Adding support for intl message formatter and decoupling default formatter. --- .../Extension/TranslationExtensionTest.php | 9 +- .../DependencyInjection/Configuration.php | 1 + .../FrameworkExtension.php | 1 + .../Resources/config/schema/symfony-1.0.xsd | 1 + .../Resources/config/translation.xml | 9 +- .../DependencyInjection/ConfigurationTest.php | 1 + .../Tests/Translation/TranslatorTest.php | 6 +- .../Translation/Translator.php | 12 +-- .../Formatter/DefaultMessageFormatter.php | 26 ++++++ .../Formatter/IntlMessageFormatter.php | 38 ++++++++ .../Formatter/MessageFormatterInterface.php | 33 +++++++ .../Translation/IdentityTranslator.php | 35 ++++++-- .../Formatter/DefaultMessageFormatterTest.php | 54 ++++++++++++ .../Formatter/IntlMessageFormatterTest.php | 88 +++++++++++++++++++ .../Translation/Tests/TranslatorTest.php | 29 +++--- .../Component/Translation/Translator.php | 36 ++++++-- 16 files changed, 334 insertions(+), 45 deletions(-) create mode 100644 src/Symfony/Component/Translation/Formatter/DefaultMessageFormatter.php create mode 100644 src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php create mode 100644 src/Symfony/Component/Translation/Formatter/MessageFormatterInterface.php create mode 100644 src/Symfony/Component/Translation/Tests/Formatter/DefaultMessageFormatterTest.php create mode 100644 src/Symfony/Component/Translation/Tests/Formatter/IntlMessageFormatterTest.php diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php index dc06c73dba03e..d3c777d08dbd2 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -13,7 +13,6 @@ use Symfony\Bridge\Twig\Extension\TranslationExtension; use Symfony\Component\Translation\Translator; -use Symfony\Component\Translation\MessageSelector; use Symfony\Component\Translation\Loader\ArrayLoader; class TranslationExtensionTest extends \PHPUnit_Framework_TestCase @@ -34,7 +33,7 @@ public function testTrans($template, $expected, array $variables = array()) print $template."\n"; $loader = new \Twig_Loader_Array(array('index' => $template)); $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false)); - $twig->addExtension(new TranslationExtension(new Translator('en', new MessageSelector()))); + $twig->addExtension(new TranslationExtension(new Translator('en'))); echo $twig->compile($twig->parse($twig->tokenize($twig->getLoader()->getSource('index'), 'index')))."\n\n"; $this->assertEquals($expected, $this->getTemplate($template)->render($variables)); @@ -136,7 +135,7 @@ public function testDefaultTranslationDomain() ', ); - $translator = new Translator('en', new MessageSelector()); + $translator = new Translator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foo (messages)'), 'en'); $translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom'); @@ -169,7 +168,7 @@ public function testDefaultTranslationDomainWithNamedArguments() ', ); - $translator = new Translator('en', new MessageSelector()); + $translator = new Translator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foo (messages)'), 'en'); $translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom'); @@ -184,7 +183,7 @@ public function testDefaultTranslationDomainWithNamedArguments() protected function getTemplate($template, $translator = null) { if (null === $translator) { - $translator = new Translator('en', new MessageSelector()); + $translator = new Translator('en'); } if (is_array($template)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 023276e476b9e..8b259217aa166 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -584,6 +584,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode) ->defaultValue(array('en')) ->end() ->booleanNode('logging')->defaultValue($this->debug)->end() + ->scalarNode('formatter')->defaultValue('translator.formatter.default')->end() ->arrayNode('paths') ->prototype('scalar')->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 1ccb2188d757a..06b0a7ac71e35 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -663,6 +663,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder // Use the "real" translator instead of the identity default $container->setAlias('translator', 'translator.default'); + $container->setAlias('translator.formatter', $config['formatter']); $translator = $container->findDefinition('translator.default'); $translator->addMethodCall('setFallbackLocales', array($config['fallbacks'])); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index 4190dad7cb787..aafabf67137ef 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -189,6 +189,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml index 0007a360c6e46..1d3361122beab 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml @@ -38,7 +38,7 @@ - + %kernel.cache_dir%/translations @@ -54,6 +54,13 @@ + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 04b6c95b46dae..f9bf8aa5b00d0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -155,6 +155,7 @@ protected static function getBundleDefaultConfig() ), 'translator' => array( 'enabled' => false, + 'formatter' => 'translator.formatter.default', 'fallbacks' => array('en'), 'logging' => true, 'paths' => array(), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index c7b61cb88170e..458749a259b56 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -14,7 +14,7 @@ use Symfony\Bundle\FrameworkBundle\Translation\Translator; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Translation\MessageSelector; +use Symfony\Component\Translation\Formatter\DefaultMessageFormatter; class TranslatorTest extends \PHPUnit_Framework_TestCase { @@ -157,7 +157,7 @@ public function testGetDefaultLocale() ->will($this->returnValue('en')) ; - $translator = new Translator($container, new MessageSelector()); + $translator = new Translator($container, new DefaultMessageFormatter()); $this->assertSame('en', $translator->getLocale()); } @@ -290,7 +290,7 @@ private function createTranslator($loader, $options, $translatorClass = '\Symfon { return new $translatorClass( $this->getContainer($loader), - new MessageSelector(), + new DefaultMessageFormatter(), array($loaderFomat => array($loaderFomat)), $options ); diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php index 9cc92f9c2d616..bb9aac854e8ba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -46,14 +46,14 @@ class Translator extends BaseTranslator implements WarmableInterface * * debug: Whether to enable debugging or not (false by default) * * resource_files: List of translation resources available grouped by locale. * - * @param ContainerInterface $container A ContainerInterface instance - * @param MessageSelector $selector The message selector for pluralization - * @param array $loaderIds An array of loader Ids - * @param array $options An array of options + * @param ContainerInterface $container A ContainerInterface instance + * @param MessageFormatterInterface|MessageSelector $formatter The message formatter + * @param array $loaderIds An array of loader Ids + * @param array $options An array of options * * @throws \InvalidArgumentException */ - public function __construct(ContainerInterface $container, MessageSelector $selector, $loaderIds = array(), array $options = array()) + public function __construct(ContainerInterface $container, $formatter, $loaderIds = array(), array $options = array()) { $this->container = $container; $this->loaderIds = $loaderIds; @@ -69,7 +69,7 @@ public function __construct(ContainerInterface $container, MessageSelector $sele $this->loadResources(); } - parent::__construct($container->getParameter('kernel.default_locale'), $selector, $this->options['cache_dir'], $this->options['debug']); + parent::__construct($container->getParameter('kernel.default_locale'), $formatter, $this->options['cache_dir'], $this->options['debug']); } /** diff --git a/src/Symfony/Component/Translation/Formatter/DefaultMessageFormatter.php b/src/Symfony/Component/Translation/Formatter/DefaultMessageFormatter.php new file mode 100644 index 0000000000000..76ec92754a14c --- /dev/null +++ b/src/Symfony/Component/Translation/Formatter/DefaultMessageFormatter.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +/** + * @author Guilherme Blanco + */ +class DefaultMessageFormatter implements MessageFormatterInterface +{ + /** + * {@inheritdoc} + */ + public function format($locale, $id, array $arguments = array()) + { + return strtr($id, $arguments); + } +} diff --git a/src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php b/src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php new file mode 100644 index 0000000000000..b56da59d82ad8 --- /dev/null +++ b/src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +/** + * @author Guilherme Blanco + */ +class IntlMessageFormatter implements MessageFormatterInterface +{ + /** + * {@inheritdoc} + */ + public function format($locale, $id, array $arguments = array()) + { + $formatter = new \MessageFormatter($locale, $id); + + if (null === $formatter) { + throw new \InvalidArgumentException(sprintf('Invalid message format. Reason: %s (error #%d)', intl_get_error_message(), intl_get_error_code())); + } + + $message = $formatter->format($arguments); + + if ($formatter->getErrorCode() !== U_ZERO_ERROR) { + throw new \InvalidArgumentException(sprintf('Unable to format message. Reason: %s (error #%s)', $formatter->getErrorMessage(), $formatter->getErrorCode())); + } + + return $message; + } +} diff --git a/src/Symfony/Component/Translation/Formatter/MessageFormatterInterface.php b/src/Symfony/Component/Translation/Formatter/MessageFormatterInterface.php new file mode 100644 index 0000000000000..9a2f0aec47f99 --- /dev/null +++ b/src/Symfony/Component/Translation/Formatter/MessageFormatterInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +/** + * MessageFormatterInterface. + * + * @author Guilherme Blanco + */ +interface MessageFormatterInterface +{ + /** + * Formats a lozalized message pattern with given arguments. + * + * @param string $locale The message locale + * @param string $id The message id (may also be an object that can be cast to string) + * @param array $arguments An array of parameters for the message + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function format($locale, $id, array $arguments = array()); +} diff --git a/src/Symfony/Component/Translation/IdentityTranslator.php b/src/Symfony/Component/Translation/IdentityTranslator.php index 9c9212e803050..f37f110a7ef1d 100644 --- a/src/Symfony/Component/Translation/IdentityTranslator.php +++ b/src/Symfony/Component/Translation/IdentityTranslator.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Translation; +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; +use Symfony\Component\Translation\Formatter\DefaultMessageFormatter; + /** * IdentityTranslator does not translate anything. * @@ -21,18 +24,28 @@ class IdentityTranslator implements TranslatorInterface { private $selector; + private $formatter; private $locale; /** - * Constructor. - * - * @param MessageSelector|null $selector The message selector for pluralization + * @param MessageFormatterInterface|MessageSelector $formatter The message formatter * * @api */ - public function __construct(MessageSelector $selector = null) + public function __construct($formatter = null) { - $this->selector = $selector ?: new MessageSelector(); + if ($formatter instanceof MessageSelector) { + @trigger_error('Passing a MessageSelector instance into the '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0. Inject a MessageFormatterInterface instance instead.', E_USER_DEPRECATED); + $this->selector = $formatter; + $formatter = new DefaultMessageFormatter(); + } else { + $this->selector = new MessageSelector(); + } + + $this->formatter = $formatter ?: new DefaultMessageFormatter(); + if (!$this->formatter instanceof MessageFormatterInterface) { + throw new \InvalidArgumentException(sprintf('The message formatter "%s" must implement MessageFormatterInterface.', get_class($this->formatter))); + } } /** @@ -62,7 +75,11 @@ public function getLocale() */ public function trans($id, array $parameters = array(), $domain = null, $locale = null) { - return strtr((string) $id, $parameters); + if (!$locale) { + $locale = $this->getLocale(); + } + + return $this->formatter->format($locale, (string) $id, $parameters); } /** @@ -72,6 +89,10 @@ public function trans($id, array $parameters = array(), $domain = null, $locale */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { - return strtr($this->selector->choose((string) $id, (int) $number, $locale ?: $this->getLocale()), $parameters); + if (!$locale) { + $locale = $this->getLocale(); + } + + return $this->formatter->format($locale, $this->selector->choose((string) $id, (int) $number, $locale ?: $this->getLocale()), $parameters); } } diff --git a/src/Symfony/Component/Translation/Tests/Formatter/DefaultMessageFormatterTest.php b/src/Symfony/Component/Translation/Tests/Formatter/DefaultMessageFormatterTest.php new file mode 100644 index 0000000000000..e0319aa22acdd --- /dev/null +++ b/src/Symfony/Component/Translation/Tests/Formatter/DefaultMessageFormatterTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Formatter; + +use Symfony\Component\Translation\Formatter\DefaultMessageFormatter; + +class DefaultMessageFormatterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideDataForFormat + */ + public function testFormat($expected, $message, $arguments) + { + $formatter = new DefaultMessageFormatter(); + + $this->assertEquals($expected, $formatter->format('en', $message, $arguments)); + } + + public function provideDataForFormat() + { + return array( + array( + 'There is one apple', + 'There is one apple', + array(), + ), + array( + 'There are 5 apples', + 'There are %count% apples', + array('%count%' => 5), + ), + ); + } + + private function mockMessageSelector($willCallChoose) + { + $mock = $this->getMock('Symfony\Component\Translation\MessageSelector'); + + $mock->expects($willCallChoose ? $this->once() : $this->never()) + ->method('choose') + ->will($this->returnValue('Message')); + + return $mock; + } +} diff --git a/src/Symfony/Component/Translation/Tests/Formatter/IntlMessageFormatterTest.php b/src/Symfony/Component/Translation/Tests/Formatter/IntlMessageFormatterTest.php new file mode 100644 index 0000000000000..869597efaeda8 --- /dev/null +++ b/src/Symfony/Component/Translation/Tests/Formatter/IntlMessageFormatterTest.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Formatter; + +use Symfony\Component\Translation\Formatter\IntlMessageFormatter; + +class IntlMessageFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped( + 'The Intl extension is not available.' + ); + } + } + + /** + * @dataProvider provideDataForFormat + */ + public function testFormat($expected, $message, $arguments) + { + $formatter = new IntlMessageFormatter(); + + $this->assertEquals($expected, trim($formatter->format('en', $message, $arguments))); + } + + public function testFormatWithNamedArguments() + { + if (PHP_VERSION_ID < 50500 || version_compare(INTL_ICU_VERSION, '4.8', '<')) { + $this->markTestSkipped('Format with named arguments can only be run with ICU 4.8 or higher and PHP >= 5.5'); + } + + $chooseMessage = <<<_MSG_ +{gender_of_host, select, + female {{num_guests, plural, offset:1 + =0 {{host} does not give a party.} + =1 {{host} invites {guest} to her party.} + =2 {{host} invites {guest} and one other person to her party.} + other {{host} invites {guest} as one of the # people invited to her party.}}} + male {{num_guests, plural, offset:1 + =0 {{host} does not give a party.} + =1 {{host} invites {guest} to his party.} + =2 {{host} invites {guest} and one other person to his party.} + other {{host} invites {guest} as one of the # people invited to his party.}}} + other {{num_guests, plural, offset:1 + =0 {{host} does not give a party.} + =1 {{host} invites {guest} to their party.} + =2 {{host} invites {guest} and one other person to their party.} + other {{host} invites {guest} as one of the # people invited to their party.}}}} +_MSG_; + + $formatter = new IntlMessageFormatter(); + $message = $formatter->format('en', $chooseMessage, array( + 'gender_of_host' => 'male', + 'num_guests' => 10, + 'host' => 'Fabien', + 'guest' => 'Guilherme', + )); + + $this->assertEquals('Fabien invites Guilherme as one of the 9 people invited to his party.', $message); + } + + public function provideDataForFormat() + { + return array( + array( + 'There is one apple', + 'There is one apple', + array(), + ), + array( + '4,560 monkeys on 123 trees make 37.073 monkeys per tree', + '{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree', + array(4560, 123, 4560 / 123), + ), + ); + } +} diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php index e8e967139dac2..74d111b58f594 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Translation\Tests; use Symfony\Component\Translation\Translator; -use Symfony\Component\Translation\MessageSelector; use Symfony\Component\Translation\Loader\ArrayLoader; use Symfony\Component\Translation\MessageCatalogue; @@ -24,7 +23,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase */ public function testConstructorInvalidLocale($locale) { - $translator = new Translator($locale, new MessageSelector()); + $translator = new Translator($locale); } /** @@ -32,14 +31,14 @@ public function testConstructorInvalidLocale($locale) */ public function testConstructorValidLocale($locale) { - $translator = new Translator($locale, new MessageSelector()); + $translator = new Translator($locale); $this->assertEquals($locale, $translator->getLocale()); } public function testConstructorWithoutLocale() { - $translator = new Translator(null, new MessageSelector()); + $translator = new Translator(null); $this->assertNull($translator->getLocale()); } @@ -60,7 +59,7 @@ public function testSetGetLocale() */ public function testSetInvalidLocale($locale) { - $translator = new Translator('fr', new MessageSelector()); + $translator = new Translator('fr'); $translator->setLocale($locale); } @@ -69,7 +68,7 @@ public function testSetInvalidLocale($locale) */ public function testSetValidLocale($locale) { - $translator = new Translator($locale, new MessageSelector()); + $translator = new Translator($locale); $translator->setLocale($locale); $this->assertEquals($locale, $translator->getLocale()); @@ -143,7 +142,7 @@ public function testSetFallbackLocalesMultiple() */ public function testSetFallbackInvalidLocales($locale) { - $translator = new Translator('fr', new MessageSelector()); + $translator = new Translator('fr'); $translator->setFallbackLocales(array('fr', $locale)); } @@ -152,7 +151,7 @@ public function testSetFallbackInvalidLocales($locale) */ public function testSetFallbackValidLocales($locale) { - $translator = new Translator($locale, new MessageSelector()); + $translator = new Translator($locale); $translator->setFallbackLocales(array('fr', $locale)); // no assertion. this method just asserts that no exception is thrown } @@ -174,7 +173,7 @@ public function testTransWithFallbackLocale() */ public function testAddResourceInvalidLocales($locale) { - $translator = new Translator('fr', new MessageSelector()); + $translator = new Translator('fr'); $translator->addResource('array', array('foo' => 'foofoo'), $locale); } @@ -183,7 +182,7 @@ public function testAddResourceInvalidLocales($locale) */ public function testAddResourceValidLocales($locale) { - $translator = new Translator('fr', new MessageSelector()); + $translator = new Translator('fr'); $translator->addResource('array', array('foo' => 'foofoo'), $locale); // no assertion. this method just asserts that no exception is thrown } @@ -291,7 +290,7 @@ public function testTrans($expected, $id, $translation, $parameters, $locale, $d */ public function testTransInvalidLocale($locale) { - $translator = new Translator('en', new MessageSelector()); + $translator = new Translator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foofoo'), 'en'); @@ -303,7 +302,7 @@ public function testTransInvalidLocale($locale) */ public function testTransValidLocale($locale) { - $translator = new Translator($locale, new MessageSelector()); + $translator = new Translator($locale); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('test' => 'OK'), $locale); @@ -341,7 +340,7 @@ public function testTransChoice($expected, $id, $translation, $number, $paramete */ public function testTransChoiceInvalidLocale($locale) { - $translator = new Translator('en', new MessageSelector()); + $translator = new Translator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foofoo'), 'en'); @@ -353,7 +352,7 @@ public function testTransChoiceInvalidLocale($locale) */ public function testTransChoiceValidLocale($locale) { - $translator = new Translator('en', new MessageSelector()); + $translator = new Translator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foofoo'), 'en'); @@ -506,7 +505,7 @@ public function testLegacyGetMessages($resources, $locale, $expected) $_locale = null !== $locale ? $locale : reset($locales); $locales = array_slice($locales, 0, array_search($_locale, $locales)); - $translator = new Translator($_locale, new MessageSelector()); + $translator = new Translator($_locale); $translator->setFallbackLocales(array_reverse($locales)); $translator->addLoader('array', new ArrayLoader()); foreach ($resources as $_locale => $domainMessages) { diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index 58dbc8a1a811a..4e652fa84f93f 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -16,11 +16,14 @@ use Symfony\Component\Config\ConfigCacheInterface; use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\Config\ConfigCacheFactory; +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; +use Symfony\Component\Translation\Formatter\DefaultMessageFormatter; /** * Translator. * * @author Fabien Potencier + * @author Guilherme Blanco * * @api */ @@ -51,6 +54,11 @@ class Translator implements TranslatorInterface, TranslatorBagInterface */ private $resources = array(); + /** + * @var MessageFormatterInterface + */ + private $formatter; + /** * @var MessageSelector */ @@ -74,19 +82,31 @@ class Translator implements TranslatorInterface, TranslatorBagInterface /** * Constructor. * - * @param string $locale The locale - * @param MessageSelector|null $selector The message selector for pluralization - * @param string|null $cacheDir The directory to use for the cache - * @param bool $debug Use cache in debug mode ? + * @param string $locale The locale + * @param MessageFormatterInterface|MessageSelector $formatter The message formatter + * @param string|null $cacheDir The directory to use for the cache + * @param bool $debug Use cache in debug mode ? * * @throws \InvalidArgumentException If a locale contains invalid characters * * @api */ - public function __construct($locale, MessageSelector $selector = null, $cacheDir = null, $debug = false) + public function __construct($locale, $formatter = null, $cacheDir = null, $debug = false) { $this->setLocale($locale); - $this->selector = $selector ?: new MessageSelector(); + if ($formatter instanceof MessageSelector) { + @trigger_error('Passing a MessageSelector instance into the '.__METHOD__.' as a second argument is deprecated since version 2.8 and will be removed in 3.0. Inject a MessageFormatterInterface instance instead.', E_USER_DEPRECATED); + $this->selector = $formatter; + $formatter = new DefaultMessageFormatter(); + } else { + $this->selector = new MessageSelector(); + } + + $this->formatter = $formatter ?: new DefaultMessageFormatter(); + if (!$this->formatter instanceof MessageFormatterInterface) { + throw new \InvalidArgumentException(sprintf('The message formatter "%s" must implement MessageFormatterInterface.', get_class($this->formatter))); + } + $this->cacheDir = $cacheDir; $this->debug = $debug; } @@ -226,7 +246,7 @@ public function trans($id, array $parameters = array(), $domain = null, $locale $domain = 'messages'; } - return strtr($this->getCatalogue($locale)->get((string) $id, $domain), $parameters); + return $this->formatter->format($locale, $this->getCatalogue($locale)->get((string) $id, $domain), $parameters); } /** @@ -252,7 +272,7 @@ public function transChoice($id, $number, array $parameters = array(), $domain = } } - return strtr($this->selector->choose($catalogue->get($id, $domain), (int) $number, $locale), $parameters); + return $this->formatter->format($locale, $this->selector->choose($catalogue->get($id, $domain), (int) $number, $locale), $parameters); } /** From 49dbc6a934f11fbdf211615fd00973b84cbd4c58 Mon Sep 17 00:00:00 2001 From: Abdellatif Ait boudad Date: Thu, 27 Aug 2015 10:39:15 +0000 Subject: [PATCH 2/2] deprecate transChoice method + [IntlFormater] fallback the lagacy massages --- .../Twig/Extension/TranslationExtension.php | 5 + .../Extension/TranslationExtensionTest.php | 103 ++++++++++++++---- .../DependencyInjection/Configuration.php | 2 +- .../Resources/config/translation.xml | 4 +- .../DependencyInjection/ConfigurationTest.php | 2 +- .../Tests/Translation/TranslatorTest.php | 22 ++-- .../Translation/DataCollectorTranslator.php | 1 + .../Formatter/DefaultMessageFormatter.php | 26 ----- .../Formatter/IntlMessageFormatter.php | 16 ++- .../Formatter/LegacyIntlMessageFormatter.php | 49 +++++++++ .../Formatter/MessageFormatterInterface.php | 8 +- .../Translation/IdentityTranslator.php | 7 +- .../Translation/LoggingTranslator.php | 1 + .../Tests/DataCollectorTranslatorTest.php | 64 ++++++----- .../Formatter/IntlMessageFormatterTest.php | 9 +- ...php => LegacyIntlMessageFormatterTest.php} | 27 +++-- .../Tests/IdentityTranslatorTest.php | 2 + .../Tests/LoggingTranslatorTest.php | 6 +- .../Translation/Tests/TranslatorTest.php | 98 +++++++++++------ .../Component/Translation/Translator.php | 7 +- .../Translation/TranslatorInterface.php | 3 + .../Tests/Validator/Abstract2Dot5ApiTest.php | 3 +- 22 files changed, 309 insertions(+), 156 deletions(-) delete mode 100644 src/Symfony/Component/Translation/Formatter/DefaultMessageFormatter.php create mode 100644 src/Symfony/Component/Translation/Formatter/LegacyIntlMessageFormatter.php rename src/Symfony/Component/Translation/Tests/Formatter/{DefaultMessageFormatterTest.php => LegacyIntlMessageFormatterTest.php} (53%) diff --git a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php index f1f2fbd20b82e..d2fc5d746f857 100644 --- a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php @@ -93,8 +93,13 @@ public function trans($message, array $arguments = array(), $domain = null, $loc return $this->translator->trans($message, $arguments, $domain, $locale); } + /** + * @deprecated since version 2.8, to be removed in 3.0. Use the {@link trans} method instead. + */ public function transchoice($message, $count, array $arguments = array(), $domain = null, $locale = null) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use trans() method instead.', E_USER_DEPRECATED); + return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php index d3c777d08dbd2..09400dfbe4b7f 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -42,6 +42,15 @@ public function testTrans($template, $expected, array $variables = array()) $this->assertEquals($expected, $this->getTemplate($template)->render($variables)); } + /** + * @dataProvider getTranschoiceTests + * @group legacy + */ + public function testTranschoice($template, $expected, array $variables = array()) + { + $this->testTrans($template, $expected, $variables); + } + /** * @expectedException \Twig_Error_Syntax * @expectedExceptionMessage Unexpected token. Twig was looking for the "with", "from", or "into" keyword in "index" at line 3. @@ -84,6 +93,18 @@ public function getTransTests() array('{% trans into "fr"%}Hello{% endtrans %}', 'Hello'), + // 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'), + ); + } + + public function getTranschoiceTests() + { + return array( // transchoice array('{% transchoice count from "messages" %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}', 'There is no apples', array('count' => 0),), @@ -98,13 +119,6 @@ public function getTransTests() array('{% transchoice 5 into "fr"%}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}', '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'), - // transchoice filter array('{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|transchoice(count) }}', 'There is 5 apples', array('count' => 5)), array('{{ text|transchoice(5, {\'%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%)')), @@ -112,7 +126,7 @@ public function getTransTests() ); } - public function testDefaultTranslationDomain() + public function testTransDefaultTranslationDomain() { $templates = array( 'index' => ' @@ -125,6 +139,31 @@ public function testDefaultTranslationDomain() {%- trans from "custom" %}foo{% endtrans %} {{- "foo"|trans }} {{- "foo"|trans({}, "custom") }} + {% endblock %} + ', + + 'base' => ' + {%- block content "" %} + ', + ); + + $template = $this->getTemplate($templates, $this->getTranslator()); + + $this->assertEquals('foo (foo)foo (custom)foo (foo)foo (custom)', trim($template->render(array()))); + } + + /** + * @group legacy + */ + public function testTransChoiceDefaultTranslationDomain() + { + $templates = array( + 'index' => ' + {%- extends "base" %} + + {%- trans_default_domain "foo" %} + + {%- block content %} {{- "foo"|transchoice(1) }} {{- "foo"|transchoice(1, {}, "custom") }} {% endblock %} @@ -135,15 +174,9 @@ public function testDefaultTranslationDomain() ', ); - $translator = new Translator('en'); - $translator->addLoader('array', new ArrayLoader()); - $translator->addResource('array', array('foo' => 'foo (messages)'), 'en'); - $translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom'); - $translator->addResource('array', array('foo' => 'foo (foo)'), 'en', 'foo'); - - $template = $this->getTemplate($templates, $translator); + $template = $this->getTemplate($templates, $this->getTranslator()); - $this->assertEquals('foo (foo)foo (custom)foo (foo)foo (custom)foo (foo)foo (custom)', trim($template->render(array()))); + $this->assertEquals('foo (foo)foo (custom)', trim($template->render(array()))); } public function testDefaultTranslationDomainWithNamedArguments() @@ -154,10 +187,33 @@ public function testDefaultTranslationDomainWithNamedArguments() {%- block content %} {{- "foo"|trans(arguments = {}, domain = "custom") }} - {{- "foo"|transchoice(count = 1) }} - {{- "foo"|transchoice(count = 1, arguments = {}, domain = "custom") }} {{- "foo"|trans({}, domain = "custom") }} {{- "foo"|trans({}, "custom", locale = "fr") }} + {% endblock %} + ', + + 'base' => ' + {%- block content "" %} + ', + ); + + $template = $this->getTemplate($templates, $this->getTranslator()); + + $this->assertEquals('foo (custom)foo (custom)foo (fr)', trim($template->render(array()))); + } + + /** + * @group legacy + */ + public function testTransChoiceDefaultTranslationDomainWithNamedArguments() + { + $templates = array( + 'index' => ' + {%- trans_default_domain "foo" %} + + {%- block content %} + {{- "foo"|transchoice(count = 1) }} + {{- "foo"|transchoice(count = 1, arguments = {}, domain = "custom") }} {{- "foo"|transchoice(1, arguments = {}, domain = "custom") }} {{- "foo"|transchoice(1, {}, "custom", locale = "fr") }} {% endblock %} @@ -168,6 +224,13 @@ public function testDefaultTranslationDomainWithNamedArguments() ', ); + $template = $this->getTemplate($templates, $this->getTranslator()); + + $this->assertEquals('foo (foo)foo (custom)foo (custom)foo (fr)', trim($template->render(array()))); + } + + protected function getTranslator() + { $translator = new Translator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foo (messages)'), 'en'); @@ -175,9 +238,7 @@ public function testDefaultTranslationDomainWithNamedArguments() $translator->addResource('array', array('foo' => 'foo (foo)'), 'en', 'foo'); $translator->addResource('array', array('foo' => 'foo (fr)'), 'fr', 'custom'); - $template = $this->getTemplate($templates, $translator); - - $this->assertEquals('foo (custom)foo (foo)foo (custom)foo (custom)foo (fr)foo (custom)foo (fr)', trim($template->render(array()))); + return $translator; } protected function getTemplate($template, $translator = null) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 8b259217aa166..0c467eb2848c1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -584,7 +584,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode) ->defaultValue(array('en')) ->end() ->booleanNode('logging')->defaultValue($this->debug)->end() - ->scalarNode('formatter')->defaultValue('translator.formatter.default')->end() + ->scalarNode('formatter')->defaultValue('translator.formatter.legacy_intl')->end() ->arrayNode('paths') ->prototype('scalar')->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml index 1d3361122beab..cbf7d213bfa93 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml @@ -57,10 +57,10 @@ - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index f9bf8aa5b00d0..6a029d139b724 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -155,7 +155,7 @@ protected static function getBundleDefaultConfig() ), 'translator' => array( 'enabled' => false, - 'formatter' => 'translator.formatter.default', + 'formatter' => 'translator.formatter.legacy_intl', 'fallbacks' => array('en'), 'logging' => true, 'paths' => array(), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index 458749a259b56..0f7735dca244e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -14,7 +14,7 @@ use Symfony\Bundle\FrameworkBundle\Translation\Translator; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Translation\Formatter\DefaultMessageFormatter; +use Symfony\Component\Translation\Formatter\IntlMessageFormatter; class TranslatorTest extends \PHPUnit_Framework_TestCase { @@ -50,10 +50,10 @@ 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(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(1))); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); } @@ -68,10 +68,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(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(1))); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); @@ -86,10 +86,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(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(1))); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); } @@ -157,7 +157,7 @@ public function testGetDefaultLocale() ->will($this->returnValue('en')) ; - $translator = new Translator($container, new DefaultMessageFormatter()); + $translator = new Translator($container, new IntlMessageFormatter()); $this->assertSame('en', $translator->getLocale()); } @@ -191,7 +191,7 @@ protected function getLoader() ->will($this->returnValue($this->getCatalogue('en', array( 'foo' => 'foo (EN)', 'bar' => 'bar (EN)', - 'choice' => '{0} choice 0 (EN)|{1} choice 1 (EN)|]1,Inf] choice inf (EN)', + 'choice' => '{0, plural, =0 {choice 0 (EN)} =1 {choice 1 (EN)} other {# choice inf (EN)}}', )))) ; $loader @@ -212,7 +212,7 @@ protected function getLoader() ->expects($this->at(4)) ->method('load') ->will($this->returnValue($this->getCatalogue('pt_BR', array( - 'other choice' => '{0} other choice 0 (PT-BR)|{1} other choice 1 (PT-BR)|]1,Inf] other choice inf (PT-BR)', + 'other choice' => '{0, plural, =0 {other choice 0 (PT-BR)} =1 {other choice 1 (PT-BR)} other {# other choice inf (PT-BR)}}', )))) ; $loader @@ -290,7 +290,7 @@ private function createTranslator($loader, $options, $translatorClass = '\Symfon { return new $translatorClass( $this->getContainer($loader), - new DefaultMessageFormatter(), + new IntlMessageFormatter(), array($loaderFomat => array($loaderFomat)), $options ); diff --git a/src/Symfony/Component/Translation/DataCollectorTranslator.php b/src/Symfony/Component/Translation/DataCollectorTranslator.php index a7478da3740e9..39ca68384279a 100644 --- a/src/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/src/Symfony/Component/Translation/DataCollectorTranslator.php @@ -58,6 +58,7 @@ public function trans($id, array $parameters = array(), $domain = null, $locale */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Rely on the MessageFormatterInterface and TranslatorInterface::trans() method instead.', E_USER_DEPRECATED); $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); $this->collectMessage($locale, $domain, $id, $trans, $parameters, $number); diff --git a/src/Symfony/Component/Translation/Formatter/DefaultMessageFormatter.php b/src/Symfony/Component/Translation/Formatter/DefaultMessageFormatter.php deleted file mode 100644 index 76ec92754a14c..0000000000000 --- a/src/Symfony/Component/Translation/Formatter/DefaultMessageFormatter.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Translation\Formatter; - -/** - * @author Guilherme Blanco - */ -class DefaultMessageFormatter implements MessageFormatterInterface -{ - /** - * {@inheritdoc} - */ - public function format($locale, $id, array $arguments = array()) - { - return strtr($id, $arguments); - } -} diff --git a/src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php b/src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php index b56da59d82ad8..7c32300140a6c 100644 --- a/src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php +++ b/src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php @@ -19,20 +19,28 @@ class IntlMessageFormatter implements MessageFormatterInterface /** * {@inheritdoc} */ - public function format($locale, $id, array $arguments = array()) + public function format($locale, $id, array $parameters = array()) { - $formatter = new \MessageFormatter($locale, $id); + if (!$parameters) { + return $id; + } + $formatter = new \MessageFormatter($locale, $id); if (null === $formatter) { throw new \InvalidArgumentException(sprintf('Invalid message format. Reason: %s (error #%d)', intl_get_error_message(), intl_get_error_code())); } - $message = $formatter->format($arguments); - + $message = $formatter->format($parameters); if ($formatter->getErrorCode() !== U_ZERO_ERROR) { throw new \InvalidArgumentException(sprintf('Unable to format message. Reason: %s (error #%s)', $formatter->getErrorMessage(), $formatter->getErrorCode())); } + if (!$formatter->parse($message) && $formatter->getErrorCode() === U_ZERO_ERROR) { + @trigger_error('Passing a MessageSelector instance into the '.__METHOD__.' as a second argument is deprecated since version 2.8 and will be removed in 3.0. Inject a MessageFormatterInterface instance instead.', E_USER_DEPRECATED); + + return strtr($message, $parameters); + } + return $message; } } diff --git a/src/Symfony/Component/Translation/Formatter/LegacyIntlMessageFormatter.php b/src/Symfony/Component/Translation/Formatter/LegacyIntlMessageFormatter.php new file mode 100644 index 0000000000000..74f5f5f2949cd --- /dev/null +++ b/src/Symfony/Component/Translation/Formatter/LegacyIntlMessageFormatter.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +/** + * @author Guilherme Blanco + */ +class LegacyIntlMessageFormatter implements MessageFormatterInterface +{ + /** + * {@inheritdoc} + */ + public function format($locale, $id, array $parameters = array()) + { + if (!$parameters) { + return $id; + } + + $formatter = new \MessageFormatter($locale, $id); + if (null === $formatter) { + return $this->fallbackToLegacyFormatter($id, $parameters); + } + + $message = $formatter->format($parameters); + if ($formatter->getErrorCode() !== U_ZERO_ERROR) { + return $this->fallbackToLegacyFormatter($message, $parameters); + } + + if (!$formatter->parse($message) && $formatter->getErrorCode() === U_ZERO_ERROR) { + return $this->fallbackToLegacyFormatter($message, $parameters); + } + + return $message; + } + + private function fallbackToLegacyFormatter($message, $parameters) + { + return strtr($message, $parameters); + } +} diff --git a/src/Symfony/Component/Translation/Formatter/MessageFormatterInterface.php b/src/Symfony/Component/Translation/Formatter/MessageFormatterInterface.php index 9a2f0aec47f99..91449ce1295ac 100644 --- a/src/Symfony/Component/Translation/Formatter/MessageFormatterInterface.php +++ b/src/Symfony/Component/Translation/Formatter/MessageFormatterInterface.php @@ -21,13 +21,13 @@ interface MessageFormatterInterface /** * Formats a lozalized message pattern with given arguments. * - * @param string $locale The message locale - * @param string $id The message id (may also be an object that can be cast to string) - * @param array $arguments An array of parameters for the message + * @param string $locale The message locale + * @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 * * @return string * * @throws \InvalidArgumentException */ - public function format($locale, $id, array $arguments = array()); + public function format($locale, $id, array $parameters = array()); } diff --git a/src/Symfony/Component/Translation/IdentityTranslator.php b/src/Symfony/Component/Translation/IdentityTranslator.php index f37f110a7ef1d..dc29c1ce86cbd 100644 --- a/src/Symfony/Component/Translation/IdentityTranslator.php +++ b/src/Symfony/Component/Translation/IdentityTranslator.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Translation; use Symfony\Component\Translation\Formatter\MessageFormatterInterface; -use Symfony\Component\Translation\Formatter\DefaultMessageFormatter; +use Symfony\Component\Translation\Formatter\LegacyIntlMessageFormatter; /** * IdentityTranslator does not translate anything. @@ -37,12 +37,12 @@ public function __construct($formatter = null) if ($formatter instanceof MessageSelector) { @trigger_error('Passing a MessageSelector instance into the '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0. Inject a MessageFormatterInterface instance instead.', E_USER_DEPRECATED); $this->selector = $formatter; - $formatter = new DefaultMessageFormatter(); + $formatter = new LegacyIntlMessageFormatter(); } else { $this->selector = new MessageSelector(); } - $this->formatter = $formatter ?: new DefaultMessageFormatter(); + $this->formatter = $formatter ?: new LegacyIntlMessageFormatter(); if (!$this->formatter instanceof MessageFormatterInterface) { throw new \InvalidArgumentException(sprintf('The message formatter "%s" must implement MessageFormatterInterface.', get_class($this->formatter))); } @@ -89,6 +89,7 @@ public function trans($id, array $parameters = array(), $domain = null, $locale */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Rely on the MessageFormatterInterface and TranslatorInterface::trans() method instead.', E_USER_DEPRECATED); if (!$locale) { $locale = $this->getLocale(); } diff --git a/src/Symfony/Component/Translation/LoggingTranslator.php b/src/Symfony/Component/Translation/LoggingTranslator.php index 4ff3531228071..80a1d94e3cec2 100644 --- a/src/Symfony/Component/Translation/LoggingTranslator.php +++ b/src/Symfony/Component/Translation/LoggingTranslator.php @@ -58,6 +58,7 @@ public function trans($id, array $parameters = array(), $domain = null, $locale */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Rely on the MessageFormatterInterface and TranslatorInterface::trans() method instead.', E_USER_DEPRECATED); $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); $this->log($id, $domain, $locale); diff --git a/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php b/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php index 31be3f2f15ebe..5735348f5ad62 100644 --- a/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\DataCollectorTranslator; use Symfony\Component\Translation\Loader\ArrayLoader; +use Symfony\Component\Translation\Formatter\IntlMessageFormatter; class DataCollectorTranslatorTest extends \PHPUnit_Framework_TestCase { @@ -23,15 +24,44 @@ protected function setUp() $this->markTestSkipped('The "DataCollector" is not available'); } } - public function testCollectMessages() + + /** + * @group legacy + */ + public function testCollectLegacyMessages() { $collector = $this->createCollector(); $collector->setFallbackLocales(array('fr', 'ru')); + $collector->transChoice('choice', 0); + + $expectedMessages = array( + array( + 'id' => 'choice', + 'translation' => 'choice', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array(), + 'transChoiceNumber' => 0, + ), + ); + + $this->assertEquals($expectedMessages, $collector->getCollectedMessages()); + } + + public function testCollectMessages() + { + $resources = array( + array('array', array('foo' => 'foo (en)'), 'en'), + array('array', array('bar' => 'bar (fr)'), 'fr'), + array('array', array('bar_ru' => '{foo} (ru)'), 'ru'), + ); + + $collector = $this->createCollector($resources); + $collector->setFallbackLocales(array('fr', 'ru')); $collector->trans('foo'); $collector->trans('bar'); - $collector->transChoice('choice', 0); - $collector->trans('bar_ru'); $collector->trans('bar_ru', array('foo' => 'bar')); $expectedMessages = array(); @@ -53,24 +83,6 @@ public function testCollectMessages() 'parameters' => array(), 'transChoiceNumber' => null, ); - $expectedMessages[] = array( - 'id' => 'choice', - 'translation' => 'choice', - 'locale' => 'en', - 'domain' => 'messages', - 'state' => DataCollectorTranslator::MESSAGE_MISSING, - 'parameters' => array(), - 'transChoiceNumber' => 0, - ); - $expectedMessages[] = array( - 'id' => 'bar_ru', - 'translation' => 'bar (ru)', - 'locale' => 'ru', - 'domain' => 'messages', - 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, - 'parameters' => array(), - 'transChoiceNumber' => null, - ); $expectedMessages[] = array( 'id' => 'bar_ru', 'translation' => 'bar (ru)', @@ -84,13 +96,13 @@ public function testCollectMessages() $this->assertEquals($expectedMessages, $collector->getCollectedMessages()); } - private function createCollector() + private function createCollector($resources = array()) { - $translator = new Translator('en'); + $translator = new Translator('en', new IntlMessageFormatter()); $translator->addLoader('array', new ArrayLoader()); - $translator->addResource('array', array('foo' => 'foo (en)'), 'en'); - $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr'); - $translator->addResource('array', array('bar_ru' => 'bar (ru)'), 'ru'); + foreach ($resources as $resource) { + $translator->addResource($resource[0], $resource[1], $resource[2], isset($resource[3]) ? $resource[3] : null); + } $collector = new DataCollectorTranslator($translator); diff --git a/src/Symfony/Component/Translation/Tests/Formatter/IntlMessageFormatterTest.php b/src/Symfony/Component/Translation/Tests/Formatter/IntlMessageFormatterTest.php index 869597efaeda8..0bc7d5f4fbe8c 100644 --- a/src/Symfony/Component/Translation/Tests/Formatter/IntlMessageFormatterTest.php +++ b/src/Symfony/Component/Translation/Tests/Formatter/IntlMessageFormatterTest.php @@ -29,7 +29,7 @@ public function setUp() */ public function testFormat($expected, $message, $arguments) { - $formatter = new IntlMessageFormatter(); + $formatter = $this->getMessageFormatter(); $this->assertEquals($expected, trim($formatter->format('en', $message, $arguments))); } @@ -59,7 +59,7 @@ public function testFormatWithNamedArguments() other {{host} invites {guest} as one of the # people invited to their party.}}}} _MSG_; - $formatter = new IntlMessageFormatter(); + $formatter = $this->getMessageFormatter(); $message = $formatter->format('en', $chooseMessage, array( 'gender_of_host' => 'male', 'num_guests' => 10, @@ -85,4 +85,9 @@ public function provideDataForFormat() ), ); } + + protected function getMessageFormatter() + { + return new IntlMessageFormatter(); + } } diff --git a/src/Symfony/Component/Translation/Tests/Formatter/DefaultMessageFormatterTest.php b/src/Symfony/Component/Translation/Tests/Formatter/LegacyIntlMessageFormatterTest.php similarity index 53% rename from src/Symfony/Component/Translation/Tests/Formatter/DefaultMessageFormatterTest.php rename to src/Symfony/Component/Translation/Tests/Formatter/LegacyIntlMessageFormatterTest.php index e0319aa22acdd..76c3a61253400 100644 --- a/src/Symfony/Component/Translation/Tests/Formatter/DefaultMessageFormatterTest.php +++ b/src/Symfony/Component/Translation/Tests/Formatter/LegacyIntlMessageFormatterTest.php @@ -11,21 +11,21 @@ namespace Symfony\Component\Translation\Tests\Formatter; -use Symfony\Component\Translation\Formatter\DefaultMessageFormatter; +use Symfony\Component\Translation\Formatter\LegacyIntlMessageFormatter; -class DefaultMessageFormatterTest extends \PHPUnit_Framework_TestCase +class LegacyIntlMessageFormatterTest extends IntlMessageFormatterTest { /** - * @dataProvider provideDataForFormat + * @dataProvider legacyMessages */ - public function testFormat($expected, $message, $arguments) + public function testFormatLegacyMessages($expected, $message, $arguments) { - $formatter = new DefaultMessageFormatter(); + $formatter = $this->getMessageFormatter(); $this->assertEquals($expected, $formatter->format('en', $message, $arguments)); } - public function provideDataForFormat() + public function legacyMessages() { return array( array( @@ -38,17 +38,16 @@ public function provideDataForFormat() 'There are %count% apples', array('%count%' => 5), ), + array( + 'There are 5 apples', + 'There are {{count}} apples', + array('{{count}}' => 5), + ), ); } - private function mockMessageSelector($willCallChoose) + protected function getMessageFormatter() { - $mock = $this->getMock('Symfony\Component\Translation\MessageSelector'); - - $mock->expects($willCallChoose ? $this->once() : $this->never()) - ->method('choose') - ->will($this->returnValue('Message')); - - return $mock; + return new LegacyIntlMessageFormatter(); } } diff --git a/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php b/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php index 352dd318dc73c..188d53dd44b3f 100644 --- a/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php @@ -28,6 +28,7 @@ public function testTrans($expected, $id, $parameters) /** * @dataProvider getTransChoiceTests + * @group legacy */ public function testTransChoiceWithExplicitLocale($expected, $id, $number, $parameters) { @@ -39,6 +40,7 @@ public function testTransChoiceWithExplicitLocale($expected, $id, $number, $para /** * @dataProvider getTransChoiceTests + * @group legacy */ public function testTransChoiceWithDefaultLocale($expected, $id, $number, $parameters) { diff --git a/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php b/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php index ab98d72e7425c..d1de0f69cc9c5 100644 --- a/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php @@ -27,17 +27,19 @@ protected function setUp() public function testTransWithNoTranslationIsLogged() { $logger = $this->getMock('Psr\Log\LoggerInterface'); - $logger->expects($this->exactly(2)) + $logger->expects($this->any()) ->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->getMock('Psr\Log\LoggerInterface'); diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php index 74d111b58f594..e777e5a7cf3ec 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Loader\ArrayLoader; use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Formatter\IntlMessageFormatter; class TranslatorTest extends \PHPUnit_Framework_TestCase { @@ -23,7 +24,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase */ public function testConstructorInvalidLocale($locale) { - $translator = new Translator($locale); + $this->getTranslator($locale); } /** @@ -31,21 +32,21 @@ public function testConstructorInvalidLocale($locale) */ public function testConstructorValidLocale($locale) { - $translator = new Translator($locale); + $translator = $this->getTranslator($locale); $this->assertEquals($locale, $translator->getLocale()); } public function testConstructorWithoutLocale() { - $translator = new Translator(null); + $translator = $this->getTranslator(null); $this->assertNull($translator->getLocale()); } public function testSetGetLocale() { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $this->assertEquals('en', $translator->getLocale()); @@ -59,7 +60,7 @@ public function testSetGetLocale() */ public function testSetInvalidLocale($locale) { - $translator = new Translator('fr'); + $translator = $this->getTranslator('fr'); $translator->setLocale($locale); } @@ -68,7 +69,7 @@ public function testSetInvalidLocale($locale) */ public function testSetValidLocale($locale) { - $translator = new Translator($locale); + $translator = $this->getTranslator($locale); $translator->setLocale($locale); $this->assertEquals($locale, $translator->getLocale()); @@ -76,7 +77,7 @@ public function testSetValidLocale($locale) public function testGetCatalogue() { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $this->assertEquals(new MessageCatalogue('en'), $translator->getCatalogue()); @@ -93,7 +94,7 @@ public function testGetCatalogueReturnsConsolidatedCatalogue() */ $locale = 'whatever'; - $translator = new Translator($locale); + $translator = $this->getTranslator($locale); $translator->addLoader('loader-a', new ArrayLoader()); $translator->addLoader('loader-b', new ArrayLoader()); $translator->addResource('loader-a', array('foo' => 'foofoo'), $locale, 'domain-a'); @@ -110,7 +111,7 @@ public function testGetCatalogueReturnsConsolidatedCatalogue() public function testSetFallbackLocales() { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foofoo'), 'en'); $translator->addResource('array', array('bar' => 'foobar'), 'fr'); @@ -124,7 +125,7 @@ public function testSetFallbackLocales() public function testSetFallbackLocalesMultiple() { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foo (en)'), 'en'); $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr'); @@ -142,7 +143,7 @@ public function testSetFallbackLocalesMultiple() */ public function testSetFallbackInvalidLocales($locale) { - $translator = new Translator('fr'); + $translator = $this->getTranslator('fr'); $translator->setFallbackLocales(array('fr', $locale)); } @@ -151,14 +152,14 @@ public function testSetFallbackInvalidLocales($locale) */ public function testSetFallbackValidLocales($locale) { - $translator = new Translator($locale); + $translator = $this->getTranslator($locale); $translator->setFallbackLocales(array('fr', $locale)); // no assertion. this method just asserts that no exception is thrown } public function testTransWithFallbackLocale() { - $translator = new Translator('fr_FR'); + $translator = $this->getTranslator('fr_FR'); $translator->setFallbackLocales(array('en')); $translator->addLoader('array', new ArrayLoader()); @@ -173,7 +174,7 @@ public function testTransWithFallbackLocale() */ public function testAddResourceInvalidLocales($locale) { - $translator = new Translator('fr'); + $translator = $this->getTranslator('fr'); $translator->addResource('array', array('foo' => 'foofoo'), $locale); } @@ -182,14 +183,14 @@ public function testAddResourceInvalidLocales($locale) */ public function testAddResourceValidLocales($locale) { - $translator = new Translator('fr'); + $translator = $this->getTranslator('fr'); $translator->addResource('array', array('foo' => 'foofoo'), $locale); // no assertion. this method just asserts that no exception is thrown } public function testAddResourceAfterTrans() { - $translator = new Translator('fr'); + $translator = $this->getTranslator('fr'); $translator->addLoader('array', new ArrayLoader()); $translator->setFallbackLocales(array('en')); @@ -208,7 +209,7 @@ public function testAddResourceAfterTrans() public function testTransWithoutFallbackLocaleFile($format, $loader) { $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader; - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader($format, new $loaderClass()); $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en'); $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en'); @@ -223,7 +224,7 @@ public function testTransWithoutFallbackLocaleFile($format, $loader) public function testTransWithFallbackLocaleFile($format, $loader) { $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader; - $translator = new Translator('en_GB'); + $translator = $this->getTranslator('en_GB'); $translator->addLoader($format, new $loaderClass()); $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en_GB'); $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources'); @@ -233,7 +234,7 @@ public function testTransWithFallbackLocaleFile($format, $loader) public function testTransWithFallbackLocaleBis() { - $translator = new Translator('en_US'); + $translator = $this->getTranslator('en_US'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foofoo'), 'en_US'); $translator->addResource('array', array('bar' => 'foobar'), 'en'); @@ -242,7 +243,7 @@ public function testTransWithFallbackLocaleBis() public function testTransWithFallbackLocaleTer() { - $translator = new Translator('fr_FR'); + $translator = $this->getTranslator('fr_FR'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US'); $translator->addResource('array', array('bar' => 'bar (en)'), 'en'); @@ -255,7 +256,7 @@ public function testTransWithFallbackLocaleTer() public function testTransNonExistentWithFallback() { - $translator = new Translator('fr'); + $translator = $this->getTranslator('fr'); $translator->setFallbackLocales(array('en')); $translator->addLoader('array', new ArrayLoader()); $this->assertEquals('non-existent', $translator->trans('non-existent')); @@ -266,7 +267,7 @@ public function testTransNonExistentWithFallback() */ public function testWhenAResourceHasNoRegisteredLoader() { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addResource('array', array('foo' => 'foofoo'), 'en'); $translator->trans('foo'); @@ -277,20 +278,32 @@ public function testWhenAResourceHasNoRegisteredLoader() */ public function testTrans($expected, $id, $translation, $parameters, $locale, $domain) { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array((string) $id => $translation), $locale, $domain); $this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale)); } + /** + * @group legacy + */ + public function testTransLagacyMessages() + { + $translator = $this->getTranslator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('Symfony is {what}!' => 'Symfony is %what%!'), 'en', 'messages'); + + $this->assertEquals('Symfony is awesome!', $translator->trans('Symfony is {what}!', array('%what%' => 'awesome'))); + } + /** * @dataProvider getInvalidLocalesTests * @expectedException \InvalidArgumentException */ public function testTransInvalidLocale($locale) { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foofoo'), 'en'); @@ -302,7 +315,7 @@ public function testTransInvalidLocale($locale) */ public function testTransValidLocale($locale) { - $translator = new Translator($locale); + $translator = $this->getTranslator($locale); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('test' => 'OK'), $locale); @@ -315,7 +328,7 @@ public function testTransValidLocale($locale) */ public function testFlattenedTrans($expected, $messages, $id) { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', $messages, 'fr', ''); @@ -324,10 +337,11 @@ public function testFlattenedTrans($expected, $messages, $id) /** * @dataProvider getTransChoiceTests + * @group legacy */ public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain) { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array((string) $id => $translation), $locale, $domain); @@ -337,10 +351,11 @@ public function testTransChoice($expected, $id, $translation, $number, $paramete /** * @dataProvider getInvalidLocalesTests * @expectedException \InvalidArgumentException + * @group legacy */ public function testTransChoiceInvalidLocale($locale) { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foofoo'), 'en'); @@ -349,10 +364,11 @@ public function testTransChoiceInvalidLocale($locale) /** * @dataProvider getValidLocalesTests + * @group legacy */ public function testTransChoiceValidLocale($locale) { - $translator = new Translator('en'); + $translator = $this->getTranslator('en'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('foo' => 'foofoo'), 'en'); @@ -379,7 +395,7 @@ public function getTransTests() { return array( array('Symfony est super !', 'Symfony is great!', 'Symfony est super !', array(), 'fr', ''), - array('Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', array('%what%' => 'awesome'), 'fr', ''), + array('Symfony est awesome !', 'Symfony is {0}!', 'Symfony est {0} !', array('awesome'), 'fr', ''), array('Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', array(), 'fr', ''), ); } @@ -464,9 +480,12 @@ public function getValidLocalesTests() ); } + /** + * @group legacy + */ public function testTransChoiceFallback() { - $translator = new Translator('ru'); + $translator = $this->getTranslator('ru'); $translator->setFallbackLocales(array('en')); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en'); @@ -474,9 +493,12 @@ public function testTransChoiceFallback() $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10))); } + /** + * @group legacy + */ public function testTransChoiceFallbackBis() { - $translator = new Translator('ru'); + $translator = $this->getTranslator('ru'); $translator->setFallbackLocales(array('en_US', 'en')); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US'); @@ -484,9 +506,12 @@ public function testTransChoiceFallbackBis() $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10))); } + /** + * @group legacy + */ public function testTransChoiceFallbackWithNoTranslation() { - $translator = new Translator('ru'); + $translator = $this->getTranslator('ru'); $translator->setFallbackLocales(array('en')); $translator->addLoader('array', new ArrayLoader()); @@ -505,7 +530,7 @@ public function testLegacyGetMessages($resources, $locale, $expected) $_locale = null !== $locale ? $locale : reset($locales); $locales = array_slice($locales, 0, array_search($_locale, $locales)); - $translator = new Translator($_locale); + $translator = $this->getTranslator($_locale); $translator->setFallbackLocales(array_reverse($locales)); $translator->addLoader('array', new ArrayLoader()); foreach ($resources as $_locale => $domainMessages) { @@ -609,6 +634,11 @@ public function dataProviderGetMessages() ), ); } + + private function getTranslator($locale) + { + return new Translator($locale, new IntlMessageFormatter()); + } } class StringClass diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index 4e652fa84f93f..697abfe9073a3 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -17,7 +17,7 @@ use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\Config\ConfigCacheFactory; use Symfony\Component\Translation\Formatter\MessageFormatterInterface; -use Symfony\Component\Translation\Formatter\DefaultMessageFormatter; +use Symfony\Component\Translation\Formatter\LegacyIntlMessageFormatter; /** * Translator. @@ -97,12 +97,12 @@ public function __construct($locale, $formatter = null, $cacheDir = null, $debug if ($formatter instanceof MessageSelector) { @trigger_error('Passing a MessageSelector instance into the '.__METHOD__.' as a second argument is deprecated since version 2.8 and will be removed in 3.0. Inject a MessageFormatterInterface instance instead.', E_USER_DEPRECATED); $this->selector = $formatter; - $formatter = new DefaultMessageFormatter(); + $formatter = new LegacyIntlMessageFormatter(); } else { $this->selector = new MessageSelector(); } - $this->formatter = $formatter ?: new DefaultMessageFormatter(); + $this->formatter = $formatter ?: new LegacyIntlMessageFormatter(); if (!$this->formatter instanceof MessageFormatterInterface) { throw new \InvalidArgumentException(sprintf('The message formatter "%s" must implement MessageFormatterInterface.', get_class($this->formatter))); } @@ -256,6 +256,7 @@ public function trans($id, array $parameters = array(), $domain = null, $locale */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Rely on the MessageFormatterInterface and TranslatorInterface::trans() method instead.', E_USER_DEPRECATED); if (null === $domain) { $domain = 'messages'; } diff --git a/src/Symfony/Component/Translation/TranslatorInterface.php b/src/Symfony/Component/Translation/TranslatorInterface.php index fe1a8659e2c93..f76dad6de1f46 100644 --- a/src/Symfony/Component/Translation/TranslatorInterface.php +++ b/src/Symfony/Component/Translation/TranslatorInterface.php @@ -39,6 +39,9 @@ public function trans($id, array $parameters = array(), $domain = null, $locale /** * Translates the given choice message by choosing a translation according to a number. * + * @deprecated since version 2.8, to be removed in 3.0. + * Use the {@link trans} method instead. + * * @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 diff --git a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php index 6995d25817988..d281f47d46a63 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php @@ -521,7 +521,6 @@ public function testAddCustomizedViolation() $context->buildViolation('Message %param%') ->setParameter('%param%', 'value') ->setInvalidValue('Invalid value') - ->setPlural(2) ->setCode(42) ->addViolation(); }; @@ -538,7 +537,7 @@ public function testAddCustomizedViolation() $this->assertSame('', $violations[0]->getPropertyPath()); $this->assertSame($entity, $violations[0]->getRoot()); $this->assertSame('Invalid value', $violations[0]->getInvalidValue()); - $this->assertSame(2, $violations[0]->getPlural()); + $this->assertNull($violations[0]->getPlural()); $this->assertSame(42, $violations[0]->getCode()); }