Skip to content

[Translation] added support for adding custom message formatter #18314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions UPGRADE-3.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,27 @@ Translation
and will be removed in 4.0, use `Symfony\Component\Translation\Writer\TranslationWriter::write`
instead.

* Passing a `Symfony\Component\Translation\MessageSelector` to `Translator` has been
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be added to the component's changelog file too. And we also need to update the UPGRADE-4.0.md file accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

deprecated. You should pass a message formatter instead

Before:

```php
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;

$translator = new Translator('fr_FR', new MessageSelector());
```

After:

```php
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Formatter\MessageFormatter;

$translator = new Translator('fr_FR', new MessageFormatter());
```

TwigBridge
----------

Expand Down
3 changes: 3 additions & 0 deletions UPGRADE-4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,9 @@ Translation
* Removed `Symfony\Component\Translation\Writer\TranslationWriter::writeTranslations`,
use `Symfony\Component\Translation\Writer\TranslationWriter::write` instead.

* Removed support for passing `Symfony\Component\Translation\MessageSelector` as a second argument to the
`Translator::__construct()`. You should pass an instance of `Symfony\Component\Translation\Formatter\MessageFormatterInterface` instead.

TwigBundle
----------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Twig\Extension\TranslationExtension;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Twig\Environment;
use Twig\Loader\ArrayLoader as TwigArrayLoader;
Expand All @@ -37,7 +36,7 @@ public function testTrans($template, $expected, array $variables = array())
echo $template."\n";
$loader = new TwigArrayLoader(array('index' => $template));
$twig = new 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()->getSourceContext('index'))))."\n\n";
$this->assertEquals($expected, $this->getTemplate($template)->render($variables));
Expand Down Expand Up @@ -139,7 +138,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');
Expand Down Expand Up @@ -172,7 +171,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');
Expand All @@ -187,7 +186,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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,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', new Alias($config['formatter'], false));
$translator = $container->findDefinition('translator.default');
$translator->addMethodCall('setFallbackLocales', array($config['fallbacks']));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@
<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="fallback" type="xsd:string" />
<xsd:attribute name="logging" type="xsd:boolean" />
<xsd:attribute name="formatter" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="validation">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<service id="translator.default" class="Symfony\Bundle\FrameworkBundle\Translation\Translator" public="true">
<argument /> <!-- translation loaders locator -->
<argument type="service" id="translator.selector" />
<argument type="service" id="translator.formatter" />
<argument>%kernel.default_locale%</argument>
<argument type="collection" /> <!-- translation loaders ids -->
<argument type="collection">
Expand All @@ -28,6 +28,10 @@
<tag name="monolog.logger" channel="translation" />
</service>

<service id="translator.formatter.default" class="Symfony\Component\Translation\Formatter\MessageFormatter">
<argument type="service" id="translator.selector" />
</service>

<service id="translation.loader.php" class="Symfony\Component\Translation\Loader\PhpFileLoader" public="true">
<tag name="translation.loader" alias="php" />
</service>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ protected static function getBundleDefaultConfig()
'enabled' => !class_exists(FullStack::class),
'fallbacks' => array('en'),
'logging' => true,
'formatter' => 'translator.formatter.default',
'paths' => array(),
),
'validation' => array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Translation\Translator;
use Symfony\Component\Translation\Formatter\MessageFormatter;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Translation\MessageSelector;

class TranslatorTest extends TestCase
{
Expand Down Expand Up @@ -149,7 +149,7 @@ public function testGetDefaultLocaleOmittingLocale()
->with('kernel.default_locale')
->will($this->returnValue('en'))
;
$translator = new Translator($container, new MessageSelector());
$translator = new Translator($container, new MessageFormatter());

$this->assertSame('en', $translator->getLocale());
}
Expand All @@ -162,7 +162,7 @@ public function testGetDefaultLocaleOmittingLocale()
public function testGetDefaultLocaleOmittingLocaleWithPsrContainer()
{
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
$translator = new Translator($container, new MessageSelector());
$translator = new Translator($container, new MessageFormatter());
}

/**
Expand Down Expand Up @@ -277,7 +277,7 @@ public function testLoadResourcesWithoutCaching()
public function testGetDefaultLocale()
{
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
$translator = new Translator($container, new MessageSelector(), 'en');
$translator = new Translator($container, new MessageFormatter(), 'en');

$this->assertSame('en', $translator->getLocale());
}
Expand All @@ -290,7 +290,7 @@ public function testInvalidOptions()
{
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();

(new Translator($container, new MessageSelector(), 'en', array(), array('foo' => 'bar')));
(new Translator($container, new MessageFormatter(), 'en', array(), array('foo' => 'bar')));
}

/** @dataProvider getDebugModeAndCacheDirCombinations */
Expand Down Expand Up @@ -468,15 +468,15 @@ private function createTranslator($loader, $options, $translatorClass = '\Symfon
if (null === $defaultLocale) {
return new $translatorClass(
$this->getContainer($loader),
new MessageSelector(),
new MessageFormatter(),
array($loaderFomat => array($loaderFomat)),
$options
);
}

return new $translatorClass(
$this->getContainer($loader),
new MessageSelector(),
new MessageFormatter(),
$defaultLocale,
array($loaderFomat => array($loaderFomat)),
$options
Expand Down
16 changes: 8 additions & 8 deletions src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
use Symfony\Component\Translation\Translator as BaseTranslator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
use Symfony\Component\Translation\Formatter\MessageFormatterInterface;

/**
* Translator.
Expand Down Expand Up @@ -56,15 +56,15 @@ 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 string $defaultLocale
* @param array $loaderIds An array of loader Ids
* @param array $options An array of options
* @param ContainerInterface $container A ContainerInterface instance
* @param MessageFormatterInterface $formatter The message formatter
* @param string $defaultLocale
* @param array $loaderIds An array of loader Ids
* @param array $options An array of options
*
* @throws InvalidArgumentException
*/
public function __construct(ContainerInterface $container, MessageSelector $selector, $defaultLocale = null, array $loaderIds = array(), array $options = array())
public function __construct(ContainerInterface $container, $formatter, $defaultLocale = null, array $loaderIds = array(), array $options = array())
{
// BC 3.x, to be removed in 4.0 along with the $defaultLocale default value
if (is_array($defaultLocale) || 3 > func_num_args()) {
Expand All @@ -90,7 +90,7 @@ public function __construct(ContainerInterface $container, MessageSelector $sele
$this->resourceLocales = array_keys($this->options['resource_files']);
$this->addResourceFiles($this->options['resource_files']);

parent::__construct($defaultLocale, $selector, $this->options['cache_dir'], $this->options['debug']);
parent::__construct($defaultLocale, $formatter, $this->options['cache_dir'], $this->options['debug']);
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Translation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CHANGELOG
* Improved Xliff 2.0 loader to load `<notes>` section.
* Added `TranslationWriterInterface`
* Deprecated `TranslationWriter::writeTranslations` in favor of `TranslationWriter::write`
* added support for adding custom message formatter and decoupling the default one.

3.2.0
-----
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
interface ChoiceMessageFormatterInterface
{
/**
* Formats a localized message pattern with given arguments.
*
* @param string $message The message (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 string $locale The message locale
* @param array $parameters An array of parameters for the message
*
* @return string
*/
public function choiceFormat($message, $number, $locale, array $parameters = array());
}
48 changes: 48 additions & 0 deletions src/Symfony/Component/Translation/Formatter/MessageFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Translation\Formatter;

use Symfony\Component\Translation\MessageSelector;

/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class MessageFormatter implements MessageFormatterInterface, ChoiceMessageFormatterInterface
{
private $selector;

/**
* @param MessageSelector|null $selector The message selector for pluralization
*/
public function __construct(MessageSelector $selector = null)
{
$this->selector = $selector ?: new MessageSelector();
}

/**
* {@inheritdoc}
*/
public function format($message, $locale, array $parameters = array())
{
return strtr($message, $parameters);
}

/**
* {@inheritdoc}
*/
public function choiceFormat($message, $number, $locale, array $parameters = array())
{
$parameters = array_merge(array('%count%' => $number), $parameters);

return $this->format($this->selector->choose($message, (int) $number, $locale), $locale, $parameters);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question for the int.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to keep this one as it's used before and might break some apps if we remove it.

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <guilhermeblanco@hotmail.com>
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
interface MessageFormatterInterface
{
/**
* Formats a localized message pattern with given arguments.
*
* @param string $message The message (may also be an object that can be cast to string)
* @param string $locale The message locale
* @param array $parameters An array of parameters for the message
*
* @return string
*/
public function format($message, $locale, array $parameters = array());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fail to understand why we would need $locale here. We do not use it and I can't come up with any valid example were it is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see the use-case IntlMessageFormatter (#20007)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you

}
Loading