Skip to content

Commit 3cda561

Browse files
authored
ENGCOM-5167: Add a MessageFormatter Phrase renderer #22996
2 parents 2bb30bc + 9a23a20 commit 3cda561

File tree

4 files changed

+163
-0
lines changed

4 files changed

+163
-0
lines changed

app/code/Magento/Translation/etc/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
<arguments>
4444
<argument name="renderers" xsi:type="array">
4545
<item name="translation" xsi:type="object">Magento\Framework\Phrase\Renderer\Translate</item>
46+
<item name="messageFormatter" xsi:type="object">Magento\Framework\Phrase\Renderer\MessageFormatter</item>
4647
<item name="placeholder" xsi:type="object">Magento\Framework\Phrase\Renderer\Placeholder</item>
4748
<item name="inline" xsi:type="object">Magento\Framework\Phrase\Renderer\Inline</item>
4849
</argument>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Framework\Phrase\Renderer;
10+
11+
use Magento\Framework\Phrase\RendererInterface;
12+
use Magento\Framework\TranslateInterface;
13+
14+
/**
15+
* Process texts to resolve ICU MessageFormat
16+
*/
17+
class MessageFormatter implements RendererInterface
18+
{
19+
/** @var TranslateInterface */
20+
private $translate;
21+
22+
/**
23+
* @param TranslateInterface $translate
24+
*/
25+
public function __construct(TranslateInterface $translate)
26+
{
27+
$this->translate = $translate;
28+
}
29+
30+
/**
31+
* @inheritdoc
32+
*/
33+
public function render(array $source, array $arguments)
34+
{
35+
$text = end($source);
36+
37+
if (strpos($text, '{') === false) {
38+
// About 5x faster for non-MessageFormatted strings
39+
// Only slightly slower for MessageFormatted strings (~0.3x)
40+
return $text;
41+
}
42+
43+
$result = \MessageFormatter::formatMessage($this->translate->getLocale(), $text, $arguments);
44+
return $result !== false ? $result : $text;
45+
}
46+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Framework\Phrase\Test\Unit\Renderer;
10+
11+
use Magento\Framework\Phrase\Renderer\MessageFormatter;
12+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
13+
use Magento\Framework\Translate;
14+
use PHPUnit\Framework\TestCase;
15+
16+
/**
17+
* Tests that messages sent through the MessageFormatter phrase renderer result in what would be expected when sent
18+
* through PHP's native MessageFormatter, and that the locale is pulled from the Translate dependency
19+
*/
20+
class MessageFormatterTest extends TestCase
21+
{
22+
/** @var ObjectManager */
23+
private $objectManager;
24+
25+
/**
26+
* @inheritdoc
27+
*/
28+
protected function setUp()
29+
{
30+
$this->objectManager = new ObjectManager($this);
31+
}
32+
33+
/**
34+
* Retrieve test cases
35+
*
36+
* @return array [Raw Phrase, Locale, Arguments, Expected Result]
37+
* @throws \Exception
38+
*/
39+
public function renderMessageFormatterDataProvider(): array
40+
{
41+
$twentynineteenJuneTwentyseven = new \DateTime('2019-06-27');
42+
43+
return [
44+
[
45+
'A table has {legs, plural, =0 {no legs} =1 {one leg} other {# legs}}.',
46+
'en_US',
47+
['legs' => 4],
48+
'A table has 4 legs.'
49+
],
50+
[
51+
'A table has {legs, plural, =0 {no legs} =1 {one leg} other {# legs}}.',
52+
'en_US',
53+
['legs' => 0],
54+
'A table has no legs.'
55+
],
56+
[
57+
'A table has {legs, plural, =0 {no legs} =1 {one leg} other {# legs}}.',
58+
'en_US',
59+
['legs' => 1],
60+
'A table has one leg.'
61+
],
62+
['The table costs {price, number, currency}.', 'en_US', ['price' => 23.4], 'The table costs $23.40.'],
63+
[
64+
'Today is {date, date, long}.',
65+
'en_US',
66+
['date' => $twentynineteenJuneTwentyseven],
67+
'Today is June 27, 2019.'
68+
],
69+
[
70+
'Today is {date, date, long}.',
71+
'ja_JP',
72+
['date' => $twentynineteenJuneTwentyseven],
73+
'Today is 2019年6月27日.'
74+
],
75+
];
76+
}
77+
78+
/**
79+
* Test MessageFormatter
80+
*
81+
* @param string $text The text with MessageFormat markers
82+
* @param string $locale
83+
* @param array $arguments The arguments supplying values for the variables
84+
* @param string $result The expected result of Phrase rendering
85+
*
86+
* @dataProvider renderMessageFormatterDataProvider
87+
*/
88+
public function testRenderMessageFormatter(string $text, string $locale, array $arguments, string $result): void
89+
{
90+
$renderer = $this->getMessageFormatter($locale);
91+
92+
$this->assertEquals($result, $renderer->render([$text], $arguments));
93+
}
94+
95+
/**
96+
* Create a MessageFormatter object provided a locale
97+
*
98+
* Automatically sets up the Translate dependency to return the provided locale and returns a MessageFormatter
99+
* that has been provided that dependency
100+
*
101+
* @param string $locale
102+
* @return MessageFormatter
103+
*/
104+
private function getMessageFormatter(string $locale): MessageFormatter
105+
{
106+
$translateMock = $this->getMockBuilder(Translate::class)
107+
->disableOriginalConstructor()
108+
->setMethods(['getLocale'])
109+
->getMock();
110+
$translateMock->method('getLocale')
111+
->willReturn($locale);
112+
113+
return $this->objectManager->getObject(MessageFormatter::class, ['translate' => $translateMock]);
114+
}
115+
}

lib/internal/Magento/Framework/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"ext-gd": "*",
1717
"ext-hash": "*",
1818
"ext-iconv": "*",
19+
"ext-intl": "*",
1920
"ext-openssl": "*",
2021
"ext-simplexml": "*",
2122
"ext-spl": "*",

0 commit comments

Comments
 (0)