-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Translation] Added intl message formatter. #27399
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
Changes from 10 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
940d440
Make it a warning
Nyholm 19e8e69
use error
Nyholm c2b3dc0
[Translation] Added intl message formatter.
aitboudad b43fe21
Add support for multiple formatters
Nyholm a325a44
Allow config for different domain specific formatters
Nyholm 2aa7181
Fixes according to feedback
Nyholm 597a15d
Use FallbackFormatter instead of support for multiple formatters
Nyholm f88153f
Updates according to feedback
Nyholm b1aa004
Only use the default translator if intl extension is loaded
Nyholm fb30c77
Be more specific with what exception we catch
Nyholm 2a90931
cs
Nyholm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
src/Symfony/Component/Translation/Formatter/FallbackFormatter.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?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\Exception\LogicException; | ||
use Symfony\Component\Translation\Exception\InvalidArgumentException; | ||
|
||
class FallbackFormatter implements MessageFormatterInterface, ChoiceMessageFormatterInterface | ||
{ | ||
/** | ||
* @var MessageFormatterInterface|ChoiceMessageFormatterInterface | ||
*/ | ||
private $firstFormatter; | ||
|
||
/** | ||
* @var MessageFormatterInterface|ChoiceMessageFormatterInterface | ||
*/ | ||
private $secondFormatter; | ||
|
||
public function __construct(MessageFormatterInterface $firstFormatter, MessageFormatterInterface $secondFormatter) | ||
{ | ||
$this->firstFormatter = $firstFormatter; | ||
$this->secondFormatter = $secondFormatter; | ||
} | ||
|
||
public function format($message, $locale, array $parameters = array()) | ||
{ | ||
try { | ||
$result = $this->firstFormatter->format($message, $locale, $parameters); | ||
} catch (InvalidArgumentException $e) { | ||
return $this->secondFormatter->format($message, $locale, $parameters); | ||
} | ||
|
||
if ($result === $message) { | ||
$result = $this->secondFormatter->format($message, $locale, $parameters); | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
public function choiceFormat($message, $number, $locale, array $parameters = array()) | ||
{ | ||
// If both support ChoiceMessageFormatterInterface | ||
if ($this->firstFormatter instanceof ChoiceMessageFormatterInterface && $this->secondFormatter instanceof ChoiceMessageFormatterInterface) { | ||
try { | ||
$result = $this->firstFormatter->choiceFormat($message, $number, $locale, $parameters); | ||
} catch (InvalidArgumentException $e) { | ||
return $this->secondFormatter->choiceFormat($message, $number, $locale, $parameters); | ||
} | ||
|
||
if ($result === $message) { | ||
$result = $this->secondFormatter->choiceFormat($message, $number, $locale, $parameters); | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
if ($this->firstFormatter instanceof ChoiceMessageFormatterInterface) { | ||
return $this->firstFormatter->choiceFormat($message, $number, $locale, $parameters); | ||
} | ||
|
||
if ($this->secondFormatter instanceof ChoiceMessageFormatterInterface) { | ||
return $this->secondFormatter->choiceFormat($message, $number, $locale, $parameters); | ||
} | ||
|
||
throw new LogicException(sprintf('No formatters support plural translations.')); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
src/Symfony/Component/Translation/Formatter/IntlMessageFormatter.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?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\Exception\InvalidArgumentException; | ||
|
||
/** | ||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> | ||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com> | ||
*/ | ||
class IntlMessageFormatter implements MessageFormatterInterface | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function format($message, $locale, array $parameters = array()) | ||
{ | ||
try { | ||
$formatter = new \MessageFormatter($locale, $message); | ||
} catch (\Throwable $e) { | ||
throw new InvalidArgumentException(sprintf('Invalid message format (%s, error #%d).', intl_get_error_message(), intl_get_error_code()), 0, $e); | ||
} | ||
|
||
$message = $formatter->format($parameters); | ||
if (U_ZERO_ERROR !== $formatter->getErrorCode()) { | ||
throw new InvalidArgumentException(sprintf('Unable to format message ( %s, error #%s).', $formatter->getErrorMessage(), $formatter->getErrorCode())); | ||
} | ||
|
||
return $message; | ||
} | ||
} |
214 changes: 214 additions & 0 deletions
214
src/Symfony/Component/Translation/Tests/Formatter/FallbackFormatterTest.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
<?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\Tests\Formatter; | ||
|
||
use Symfony\Component\Translation\Exception\InvalidArgumentException; | ||
use Symfony\Component\Translation\Exception\LogicException; | ||
use Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface; | ||
use Symfony\Component\Translation\Formatter\FallbackFormatter; | ||
use Symfony\Component\Translation\Formatter\MessageFormatterInterface; | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be reverted |
||
class FallbackFormatterTest extends \PHPUnit\Framework\TestCase | ||
{ | ||
public function testFormatSame() | ||
{ | ||
$first = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$first | ||
->expects($this->once()) | ||
->method('format') | ||
->with('foo', 'en', array(2)) | ||
->willReturn('foo'); | ||
|
||
$second = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$second | ||
->expects($this->once()) | ||
->method('format') | ||
->with('foo', 'en', array(2)) | ||
->willReturn('bar'); | ||
|
||
$this->assertEquals('bar', (new FallbackFormatter($first, $second))->format('foo', 'en', array(2))); | ||
} | ||
|
||
public function testFormatDifferent() | ||
{ | ||
$first = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$first | ||
->expects($this->once()) | ||
->method('format') | ||
->with('foo', 'en', array(2)) | ||
->willReturn('new value'); | ||
|
||
$second = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$second | ||
->expects($this->exactly(0)) | ||
->method('format') | ||
->withAnyParameters(); | ||
|
||
$this->assertEquals('new value', (new FallbackFormatter($first, $second))->format('foo', 'en', array(2))); | ||
} | ||
|
||
public function testFormatException() | ||
{ | ||
$first = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$first | ||
->expects($this->once()) | ||
->method('format') | ||
->willThrowException(new InvalidArgumentException()); | ||
|
||
$second = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$second | ||
->expects($this->once()) | ||
->method('format') | ||
->with('foo', 'en', array(2)) | ||
->willReturn('bar'); | ||
|
||
$this->assertEquals('bar', (new FallbackFormatter($first, $second))->format('foo', 'en', array(2))); | ||
} | ||
|
||
public function testFormatExceptionUnknown() | ||
{ | ||
$first = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$first | ||
->expects($this->once()) | ||
->method('format') | ||
->willThrowException(new \RuntimeException()); | ||
|
||
$second = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$second | ||
->expects($this->exactly(0)) | ||
->method('format'); | ||
|
||
$this->expectException(\RuntimeException::class); | ||
$this->assertEquals('bar', (new FallbackFormatter($first, $second))->format('foo', 'en', array(2))); | ||
} | ||
|
||
public function testChoiceFormatSame() | ||
{ | ||
$first = $this->getMockBuilder(SuperFormatterInterface::class)->setMethods(array('format', 'choiceFormat'))->getMock(); | ||
$first | ||
->expects($this->once()) | ||
->method('choiceFormat') | ||
->with('foo', 1, 'en', array(2)) | ||
->willReturn('foo'); | ||
|
||
$second = $this->getMockBuilder(SuperFormatterInterface::class)->setMethods(array('format', 'choiceFormat'))->getMock(); | ||
$second | ||
->expects($this->once()) | ||
->method('choiceFormat') | ||
->with('foo', 1, 'en', array(2)) | ||
->willReturn('bar'); | ||
|
||
$this->assertEquals('bar', (new FallbackFormatter($first, $second))->choiceFormat('foo', 1, 'en', array(2))); | ||
} | ||
|
||
public function testChoiceFormatDifferent() | ||
{ | ||
$first = $this->getMockBuilder(SuperFormatterInterface::class)->setMethods(array('format', 'choiceFormat'))->getMock(); | ||
$first | ||
->expects($this->once()) | ||
->method('choiceFormat') | ||
->with('foo', 1, 'en', array(2)) | ||
->willReturn('new value'); | ||
|
||
$second = $this->getMockBuilder(SuperFormatterInterface::class)->setMethods(array('format', 'choiceFormat'))->getMock(); | ||
$second | ||
->expects($this->exactly(0)) | ||
->method('choiceFormat') | ||
->withAnyParameters() | ||
->willReturn('bar'); | ||
|
||
$this->assertEquals('new value', (new FallbackFormatter($first, $second))->choiceFormat('foo', 1, 'en', array(2))); | ||
} | ||
|
||
public function testChoiceFormatException() | ||
{ | ||
$first = $this->getMockBuilder(SuperFormatterInterface::class)->setMethods(array('format', 'choiceFormat'))->getMock(); | ||
$first | ||
->expects($this->once()) | ||
->method('choiceFormat') | ||
->willThrowException(new InvalidArgumentException()); | ||
|
||
$second = $this->getMockBuilder(SuperFormatterInterface::class)->setMethods(array('format', 'choiceFormat'))->getMock(); | ||
$second | ||
->expects($this->once()) | ||
->method('choiceFormat') | ||
->with('foo', 1, 'en', array(2)) | ||
->willReturn('bar'); | ||
|
||
$this->assertEquals('bar', (new FallbackFormatter($first, $second))->choiceFormat('foo', 1, 'en', array(2))); | ||
} | ||
|
||
public function testChoiceFormatOnlyFirst() | ||
{ | ||
// Implements both interfaces | ||
$first = $this->getMockBuilder(SuperFormatterInterface::class)->setMethods(array('format', 'choiceFormat'))->getMock(); | ||
$first | ||
->expects($this->once()) | ||
->method('choiceFormat') | ||
->with('foo', 1, 'en', array(2)) | ||
->willReturn('bar'); | ||
|
||
// Implements only one interface | ||
$second = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$second | ||
->expects($this->exactly(0)) | ||
->method('format') | ||
->withAnyParameters() | ||
->willReturn('error'); | ||
|
||
$this->assertEquals('bar', (new FallbackFormatter($first, $second))->choiceFormat('foo', 1, 'en', array(2))); | ||
} | ||
|
||
public function testChoiceFormatOnlySecond() | ||
{ | ||
// Implements only one interface | ||
$first = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$first | ||
->expects($this->exactly(0)) | ||
->method('format') | ||
->withAnyParameters() | ||
->willReturn('error'); | ||
|
||
// Implements both interfaces | ||
$second = $this->getMockBuilder(SuperFormatterInterface::class)->setMethods(array('format', 'choiceFormat'))->getMock(); | ||
$second | ||
->expects($this->once()) | ||
->method('choiceFormat') | ||
->with('foo', 1, 'en', array(2)) | ||
->willReturn('bar'); | ||
|
||
$this->assertEquals('bar', (new FallbackFormatter($first, $second))->choiceFormat('foo', 1, 'en', array(2))); | ||
} | ||
|
||
public function testChoiceFormatNoChoiceFormat() | ||
{ | ||
// Implements only one interface | ||
$first = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$first | ||
->expects($this->exactly(0)) | ||
->method('format'); | ||
|
||
// Implements both interfaces | ||
$second = $this->getMockBuilder(MessageFormatterInterface::class)->setMethods(array('format'))->getMock(); | ||
$second | ||
->expects($this->exactly(0)) | ||
->method('format'); | ||
|
||
$this->expectException(LogicException::class); | ||
$this->assertEquals('bar', (new FallbackFormatter($first, $second))->choiceFormat('foo', 1, 'en', array(2))); | ||
} | ||
} | ||
|
||
interface SuperFormatterInterface extends MessageFormatterInterface, ChoiceMessageFormatterInterface | ||
{ | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be removed