Skip to content

Commit

Permalink
feature #18314 [Translation] added support for adding custom message …
Browse files Browse the repository at this point in the history
…formatter (aitboudad)

This PR was merged into the 3.4 branch.

Discussion
----------

[Translation] added support for adding custom message formatter

| Q | A |
| --- | --- |
| Branch? | master |
| Bug fix? | no |
| New feature? | yes |
| BC breaks? | no |
| Deprecations? | yes |
| Tests pass? | yes |
| Fixed tickets | #6009, #10152, one item in #11742, #11948 |
| License | MIT |
| Doc PR | ~ |

Commits
-------

42183b0 [Translation] Support adding custom message formatter
  • Loading branch information
xabbuh committed Sep 12, 2017
2 parents eefded7 + 42183b0 commit ade060e
Show file tree
Hide file tree
Showing 17 changed files with 282 additions and 49 deletions.
21 changes: 21 additions & 0 deletions UPGRADE-3.4.md
Expand Up @@ -239,6 +239,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
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
Expand Up @@ -648,6 +648,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
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
Expand Up @@ -669,6 +669,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
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
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
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
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
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
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
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
@@ -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
@@ -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);
}
}
@@ -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());
}

0 comments on commit ade060e

Please sign in to comment.