Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f274351

Browse files
committed
Translation parameters
1 parent cfb1016 commit f274351

13 files changed

+316
-16
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
"doctrine/orm": "^2.7.3",
134134
"guzzlehttp/promises": "^1.4",
135135
"masterminds/html5": "^2.6",
136+
"moneyphp/money": "^3.0|^4.0",
136137
"monolog/monolog": "^1.25.1|^2",
137138
"nyholm/psr7": "^1.0",
138139
"paragonie/sodium_compat": "^1.8",

src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Bridge\Twig\Extension\TranslationExtension;
1616
use Symfony\Component\Translation\Loader\ArrayLoader;
17+
use Symfony\Component\Translation\Parameter\MoneyParameter;
1718
use Symfony\Component\Translation\Translator;
1819
use Symfony\Contracts\Translation\TranslatorInterface;
1920
use Twig\Environment;
@@ -134,6 +135,9 @@ public function getTransTests()
134135

135136
// trans object with count
136137
['{{ t("{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples", {\'%count%\': count})|trans }}', 'There is 5 apples', ['count' => 5]],
138+
139+
// trans with parameter
140+
['{% trans into "fr"%}%price%{% endtrans %}', '100,00\u{a0}€', ['price' => new MoneyParameter(100, 'EUR')]],
137141
];
138142
}
139143

src/Symfony/Component/Translation/CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
CHANGELOG
22
=========
33

4-
5.3
5-
---
4+
5.4.0
5+
-----
6+
7+
* Add `ParamaterInterface` to format parameters separately as recommended per the ICU with implementation for both DateTime and Money.
8+
9+
5.3.0
10+
-----
611

712
* Add `translation:pull` and `translation:push` commands to manage translations with third-party providers
813
* Add `TranslatorBagInterface::getCatalogues` method
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Translation\Parameter;
13+
14+
/**
15+
* Wrapper around PHP IntlDateFormatter for date and time
16+
* The timezone from the DateTime instance is used instead of the server's timezone.
17+
*
18+
* @author Sylvain Fabre <[email protected]>
19+
*/
20+
class DateTimeParameter implements ParameterInterface
21+
{
22+
private $dateTime;
23+
private $dateType;
24+
private $timeType;
25+
26+
private $formatters = [];
27+
28+
public function __construct(
29+
\DateTimeInterface $dateTime,
30+
int $dateType = \IntlDateFormatter::SHORT,
31+
int $timeType = \IntlDateFormatter::SHORT
32+
) {
33+
$this->dateTime = $dateTime;
34+
$this->dateType = $dateType;
35+
$this->timeType = $timeType;
36+
}
37+
38+
public function __toString(): string
39+
{
40+
return $this->format('en_US');
41+
}
42+
43+
public function format(string $locale = null): string
44+
{
45+
$timezone = $this->dateTime->getTimezone();
46+
$key = implode('.', [$locale, $this->dateType, $this->timeType, $timezone->getName()]);
47+
if (!isset($this->formatters[$key])) {
48+
$this->formatters[$key] = new \IntlDateFormatter(
49+
$locale,
50+
$this->dateType,
51+
$this->timeType,
52+
$timezone
53+
);
54+
}
55+
56+
return $this->formatters[$key]->format($this->dateTime);
57+
}
58+
59+
/**
60+
* Short-hand to only format a date.
61+
*/
62+
public static function date(\DateTimeInterface $dateTime, int $type = \IntlDateFormatter::SHORT): self
63+
{
64+
return new self($dateTime, $type, \IntlDateFormatter::NONE);
65+
}
66+
67+
/**
68+
* Short-hand to only format a time.
69+
*/
70+
public static function time(\DateTimeInterface $dateTime, int $type = \IntlDateFormatter::SHORT): self
71+
{
72+
return new self($dateTime, \IntlDateFormatter::NONE, $type);
73+
}
74+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Translation\Parameter;
13+
14+
use Money\Currencies\ISOCurrencies;
15+
use Money\Formatter\DecimalMoneyFormatter;
16+
use Money\Money;
17+
18+
/**
19+
* Wrapper around PHP NumberFormatter for money
20+
* The provided currency is used instead of the locale's currency.
21+
*
22+
* @author Sylvain Fabre <[email protected]>
23+
*/
24+
class MoneyParameter implements ParameterInterface
25+
{
26+
private $value;
27+
private $currency;
28+
29+
private $formatters = [];
30+
31+
public function __construct($value, string $currency)
32+
{
33+
$this->value = $value;
34+
$this->currency = $currency;
35+
}
36+
37+
public function __toString(): string
38+
{
39+
return $this->format('en_US');
40+
}
41+
42+
public function format(string $locale = null): string
43+
{
44+
if (!isset($this->formatters[$locale])) {
45+
$this->formatters[$locale] = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
46+
}
47+
48+
return $this->formatters[$locale]->formatCurrency($this->value, $this->currency);
49+
}
50+
51+
/**
52+
* Short-hand to instantiate from a Money instance.
53+
*/
54+
public static function fromMoney(Money $money): self
55+
{
56+
$currencies = new ISOCurrencies();
57+
$moneyFormatter = new DecimalMoneyFormatter($currencies);
58+
59+
return new self($moneyFormatter->format($money), $money->getCurrency()->getCode());
60+
}
61+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Translation\Parameter;
13+
14+
/**
15+
* Implementation of the ICU recommendation to first format advanced parameters before translation.
16+
*
17+
* @see https://unicode-org.github.io/icu/userguide/format_parse/messages/#format-the-parameters-separately-recommended
18+
*
19+
* @author Sylvain Fabre <[email protected]>
20+
*/
21+
interface ParameterInterface
22+
{
23+
public function format(string $locale = null): string;
24+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Translation\Tests\Parameter;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Translation\Parameter\DateTimeParameter;
16+
17+
class DateTimeParameterTest extends TestCase
18+
{
19+
/**
20+
* @dataProvider getValues()
21+
*/
22+
public function testFormat(string $expected, DateTimeParameter $parameter, string $locale)
23+
{
24+
$this->assertSame($expected, $parameter->format($locale));
25+
}
26+
27+
public function getValues(): iterable
28+
{
29+
$dateTime = new \DateTime('2021-01-01 23:55:00', new \DateTimeZone('UTC'));
30+
31+
$parameterDateTime = new DateTimeParameter($dateTime);
32+
yield 'DateTime in French' => ['01/01/2021 23:55', $parameterDateTime, 'fr_FR'];
33+
yield 'DateTime in GB English' => ['01/01/2021, 23:55', $parameterDateTime, 'en_GB'];
34+
yield 'DateTime in US English' => ['1/1/21, 11:55 PM', $parameterDateTime, 'en_US'];
35+
36+
$dateTimeParis = new \DateTime('2021-01-01 23:55:00', new \DateTimeZone('UTC'));
37+
$dateTimeParis->setTimezone(new \DateTimeZone('Europe/Paris'));
38+
39+
$parameterDateTimeParis = new DateTimeParameter($dateTimeParis);
40+
yield 'DateTime in Paris in French' => ['02/01/2021 00:55', $parameterDateTimeParis, 'fr_FR'];
41+
yield 'DateTime in Paris in GB English' => ['02/01/2021, 00:55', $parameterDateTimeParis, 'en_GB'];
42+
yield 'DateTime in Paris in US English' => ['1/2/21, 12:55 AM', $parameterDateTimeParis, 'en_US'];
43+
44+
$parameterDateParis = DateTimeParameter::date($dateTimeParis);
45+
yield 'Date in Paris in French' => ['02/01/2021', $parameterDateParis, 'fr_FR'];
46+
yield 'Date in Paris in GB English' => ['02/01/2021', $parameterDateParis, 'en_GB'];
47+
yield 'Date in Paris in US English' => ['1/2/21', $parameterDateParis, 'en_US'];
48+
49+
$parameterTimeParis = DateTimeParameter::time($dateTimeParis);
50+
yield 'Time in Paris in French' => ['00:55', $parameterTimeParis, 'fr_FR'];
51+
yield 'Time in Paris in GB English' => ['00:55', $parameterTimeParis, 'en_GB'];
52+
yield 'Time in Paris in US English' => ['12:55 AM', $parameterTimeParis, 'en_US'];
53+
}
54+
55+
public function testToString()
56+
{
57+
$dateTime = new \DateTime('2021-01-01 00:00:00+00');
58+
$this->assertSame('1/1/21', (string) DateTimeParameter::date($dateTime));
59+
}
60+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Translation\Tests\Parameter;
13+
14+
use Money\Money;
15+
use PHPUnit\Framework\TestCase;
16+
use Symfony\Component\Translation\Parameter\MoneyParameter;
17+
18+
class MoneyParameterTest extends TestCase
19+
{
20+
/**
21+
* @dataProvider getValues()
22+
*/
23+
public function testFormat(string $expected, MoneyParameter $parameter, string $locale)
24+
{
25+
$this->assertSame($expected, $parameter->format($locale));
26+
}
27+
28+
public function getValues(): iterable
29+
{
30+
$parameterEuros = new MoneyParameter(1000, 'EUR');
31+
$parameterDollars = new MoneyParameter(1000, 'USD');
32+
33+
yield 'Euros in French' => ['1\u{202f}000,00\u{a0}€', $parameterEuros, 'fr_FR'];
34+
yield 'Euros in US English' => ['€1,000.00', $parameterEuros, 'en_US'];
35+
yield 'US Dollars in French' => ['1\u{202f}000,00\u{a0}$US', $parameterDollars, 'fr_FR'];
36+
yield 'US Dollars in US English' => ['$1,000.00', $parameterDollars, 'en_US'];
37+
38+
$parameterEuros = MoneyParameter::fromMoney(Money::EUR(100000));
39+
yield 'Euros in French from Money' => ['1\u{202f}000,00\u{a0}€', $parameterEuros, 'fr_FR'];
40+
yield 'Euros in US English from Money' => ['€1,000.00', $parameterEuros, 'en_US'];
41+
42+
$parameterDollars = MoneyParameter::fromMoney(Money::USD(100000));
43+
yield 'US Dollars in French from Money' => ['1\u{202f}000,00\u{a0}$US', $parameterDollars, 'fr_FR'];
44+
yield 'US Dollars in US English from Money' => ['$1,000.00', $parameterDollars, 'en_US'];
45+
}
46+
47+
public function testToString()
48+
{
49+
$this->assertSame('€100.00', (string) new MoneyParameter(100, 'EUR'));
50+
}
51+
}

src/Symfony/Component/Translation/Tests/TranslatableTest.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,8 @@ public function testToString()
4949

5050
public function getTransTests()
5151
{
52-
return [
53-
['Symfony est super !', new TranslatableMessage('Symfony is great!', [], ''), 'Symfony est super !', 'fr'],
54-
['Symfony est awesome !', new TranslatableMessage('Symfony is %what%!', ['%what%' => 'awesome'], ''), 'Symfony est %what% !', 'fr'],
55-
];
52+
yield ['Symfony est super !', new TranslatableMessage('Symfony is great!', [], ''), 'Symfony est super !', 'fr'];
53+
yield ['Symfony est awesome !', new TranslatableMessage('Symfony is %what%!', ['%what%' => 'awesome'], ''), 'Symfony est %what% !', 'fr'];
5654
}
5755

5856
public function getFlattenedTransTests()

src/Symfony/Component/Translation/Tests/TranslatorTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Translation\Exception\RuntimeException;
1818
use Symfony\Component\Translation\Loader\ArrayLoader;
1919
use Symfony\Component\Translation\MessageCatalogue;
20+
use Symfony\Component\Translation\Parameter\MoneyParameter;
2021
use Symfony\Component\Translation\Translator;
2122

2223
class TranslatorTest extends TestCase
@@ -463,12 +464,11 @@ public function getTransFileTests()
463464

464465
public function getTransTests()
465466
{
466-
return [
467-
['Symfony est super !', 'Symfony is great!', 'Symfony est super !', [], 'fr', ''],
468-
['Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', ['%what%' => 'awesome'], 'fr', ''],
469-
['Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', [], 'fr', ''],
470-
['', null, '', [], 'fr', ''],
471-
];
467+
yield ['Symfony est super !', 'Symfony is great!', 'Symfony est super !', [], 'fr', ''];
468+
yield ['Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', ['%what%' => 'awesome'], 'fr', ''];
469+
yield ['Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', [], 'fr', ''];
470+
yield ['Symfony coûte 0,00\u{a0}€ !', 'Symfony cost %price%!', 'Symfony coûte %price% !', ['%price%' => new MoneyParameter(0, 'EUR')], 'fr', ''];
471+
yield ['', null, '', [], 'fr', ''];
472472
}
473473

474474
public function getTransICUTests()

src/Symfony/Component/Translation/Translator.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\Translation\Formatter\MessageFormatter;
2222
use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
2323
use Symfony\Component\Translation\Loader\LoaderInterface;
24+
use Symfony\Component\Translation\Parameter\ParameterInterface;
2425
use Symfony\Contracts\Translation\LocaleAwareInterface;
2526
use Symfony\Contracts\Translation\TranslatorInterface;
2627

@@ -214,15 +215,25 @@ public function trans(?string $id, array $parameters = [], string $domain = null
214215
}
215216
}
216217

218+
$message = $catalogue->get($id, $domain);
219+
220+
$parameters = array_map(function ($parameter) use ($locale) {
221+
if ($parameter instanceof ParameterInterface) {
222+
return $parameter->format($locale);
223+
}
224+
225+
return $parameter;
226+
}, $parameters);
227+
217228
$len = \strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX);
218229
if ($this->hasIntlFormatter
219230
&& ($catalogue->defines($id, $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX)
220231
|| (\strlen($domain) > $len && 0 === substr_compare($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX, -$len, $len)))
221232
) {
222-
return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, $parameters);
233+
return $this->formatter->formatIntl($message, $locale, $parameters);
223234
}
224235

225-
return $this->formatter->format($catalogue->get($id, $domain), $locale, $parameters);
236+
return $this->formatter->format($message, $locale, $parameters);
226237
}
227238

228239
/**

0 commit comments

Comments
 (0)