diff --git a/framework/i18n/Formatter.php b/framework/i18n/Formatter.php index 28c84024f9..6229974161 100644 --- a/framework/i18n/Formatter.php +++ b/framework/i18n/Formatter.php @@ -176,10 +176,10 @@ class Formatter extends Component $this->locale = Yii::$app->language; } if ($this->booleanFormat === null) { - $this->booleanFormat = [Yii::t('yii', 'No'), Yii::t('yii', 'Yes')]; + $this->booleanFormat = [Yii::t('yii', 'No', [], $this->locale), Yii::t('yii', 'Yes', [], $this->locale)]; } if ($this->nullDisplay === null) { - $this->nullDisplay = '' . Yii::t('yii', '(not set)') . ''; + $this->nullDisplay = '' . Yii::t('yii', '(not set)', [], $this->locale) . ''; } $this->_intlLoaded = extension_loaded('intl'); if (!$this->_intlLoaded) { @@ -470,14 +470,14 @@ class Formatter extends Component */ private $_phpNameToPattern = [ 'short' => [ - 'date' => 'd.m.Y', + 'date' => 'n/j/y', 'time' => 'H:i', - 'datetime' => 'd.m.Y H:i', + 'datetime' => 'n/j/y H:i', ], 'medium' => [ 'date' => 'M j, Y', - 'time' => 'H:i:s', - 'datetime' => 'M j, Y H:i:s', + 'time' => 'g:i:s A', + 'datetime' => 'M j, Y g:i:s A', ], 'long' => [ 'date' => 'F j, Y', @@ -612,9 +612,6 @@ class Formatter extends Component */ private function convertPatternIcuToPhp($pattern) { - if (isset($this->_dateFormats[$pattern])) { - return $pattern; - } return strtr($pattern, [ 'dd' => 'd', // day with leading zeros 'd' => 'j', // day without leading zeros @@ -688,9 +685,6 @@ class Formatter extends Component */ private function convertPatternPhpToIcu($pattern) { - if (isset($this->_dateFormats[$pattern])) { - return $pattern; - } return strtr($pattern, [ 'd' => 'dd', // day with leading zeros 'j' => 'd', // day without leading zeros @@ -828,40 +822,40 @@ class Formatter extends Component if ($interval->invert) { if ($interval->y >= 1) { - return Yii::t('yii', 'in {delta, plural, =1{a year} other{# years}}', ['delta' => $interval->y]); + return Yii::t('yii', 'in {delta, plural, =1{a year} other{# years}}', ['delta' => $interval->y], $this->locale); } if ($interval->m >= 1) { - return Yii::t('yii', 'in {delta, plural, =1{a month} other{# months}}', ['delta' => $interval->m]); + return Yii::t('yii', 'in {delta, plural, =1{a month} other{# months}}', ['delta' => $interval->m], $this->locale); } if ($interval->d >= 1) { - return Yii::t('yii', 'in {delta, plural, =1{a day} other{# days}}', ['delta' => $interval->d]); + return Yii::t('yii', 'in {delta, plural, =1{a day} other{# days}}', ['delta' => $interval->d], $this->locale); } if ($interval->h >= 1) { - return Yii::t('yii', 'in {delta, plural, =1{an hour} other{# hours}}', ['delta' => $interval->h]); + return Yii::t('yii', 'in {delta, plural, =1{an hour} other{# hours}}', ['delta' => $interval->h], $this->locale); } if ($interval->i >= 1) { - return Yii::t('yii', 'in {delta, plural, =1{a minute} other{# minutes}}', ['delta' => $interval->i]); + return Yii::t('yii', 'in {delta, plural, =1{a minute} other{# minutes}}', ['delta' => $interval->i], $this->locale); } - return Yii::t('yii', 'in {delta, plural, =1{a second} other{# seconds}}', ['delta' => $interval->s]); + return Yii::t('yii', 'in {delta, plural, =1{a second} other{# seconds}}', ['delta' => $interval->s], $this->locale); } else { if ($interval->y >= 1) { - return Yii::t('yii', '{delta, plural, =1{a year} other{# years}} ago', ['delta' => $interval->y]); + return Yii::t('yii', '{delta, plural, =1{a year} other{# years}} ago', ['delta' => $interval->y], $this->locale); } if ($interval->m >= 1) { - return Yii::t('yii', '{delta, plural, =1{a month} other{# months}} ago', ['delta' => $interval->m]); + return Yii::t('yii', '{delta, plural, =1{a month} other{# months}} ago', ['delta' => $interval->m], $this->locale); } if ($interval->d >= 1) { - return Yii::t('yii', '{delta, plural, =1{a day} other{# days}} ago', ['delta' => $interval->d]); + return Yii::t('yii', '{delta, plural, =1{a day} other{# days}} ago', ['delta' => $interval->d], $this->locale); } if ($interval->h >= 1) { - return Yii::t('yii', '{delta, plural, =1{an hour} other{# hours}} ago', ['delta' => $interval->h]); + return Yii::t('yii', '{delta, plural, =1{an hour} other{# hours}} ago', ['delta' => $interval->h], $this->locale); } if ($interval->i >= 1) { - return Yii::t('yii', '{delta, plural, =1{a minute} other{# minutes}} ago', ['delta' => $interval->i]); + return Yii::t('yii', '{delta, plural, =1{a minute} other{# minutes}} ago', ['delta' => $interval->i], $this->locale); } - return Yii::t('yii', '{delta, plural, =1{a second} other{# seconds}} ago', ['delta' => $interval->s]); + return Yii::t('yii', '{delta, plural, =1{a second} other{# seconds}} ago', ['delta' => $interval->s], $this->locale); } } @@ -899,7 +893,8 @@ class Formatter extends Component * value is rounded automatically to the defined decimal digits. * * @param mixed $value the value to be formatted. - * @param integer $decimals the number of digits after the decimal point. + * @param integer $decimals the number of digits after the decimal point. If not given the number of digits is determined from the + * [[locale]] and if the [PHP intl extension](http://php.net/manual/en/book.intl.php) is not available defaults to `2`. * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]]. * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]]. * @return string the formatted result. @@ -1100,33 +1095,33 @@ class Formatter extends Component if ($binaryPrefix && $this->sizeFormat['base'] === 1024) { switch ($position) { case 0: - return $verbose ? Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params) : Yii::t('yii', '{n} B', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params, $this->locale) : Yii::t('yii', '{n} B', $params, $this->locale); case 1: - return $verbose ? Yii::t('yii', '{n, plural, =1{# kibibyte} other{# kibibytes}}', $params) : Yii::t('yii', '{n} KiB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# kibibyte} other{# kibibytes}}', $params, $this->locale) : Yii::t('yii', '{n} KiB', $params, $this->locale); case 2: - return $verbose ? Yii::t('yii', '{n, plural, =1{# mebibyte} other{# mebibytes}}', $params) : Yii::t('yii', '{n} MiB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# mebibyte} other{# mebibytes}}', $params, $this->locale) : Yii::t('yii', '{n} MiB', $params, $this->locale); case 3: - return $verbose ? Yii::t('yii', '{n, plural, =1{# gibibyte} other{# gibibytes}}', $params) : Yii::t('yii', '{n} GiB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# gibibyte} other{# gibibytes}}', $params, $this->locale) : Yii::t('yii', '{n} GiB', $params, $this->locale); case 4: - return $verbose ? Yii::t('yii', '{n, plural, =1{# tebibyte} other{# tebibytes}}', $params) : Yii::t('yii', '{n} TiB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# tebibyte} other{# tebibytes}}', $params, $this->locale) : Yii::t('yii', '{n} TiB', $params, $this->locale); default: - return $verbose ? Yii::t('yii', '{n, plural, =1{# pebibyte} other{# pebibytes}}', $params) : Yii::t('yii', '{n} PiB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# pebibyte} other{# pebibytes}}', $params, $this->locale) : Yii::t('yii', '{n} PiB', $params, $this->locale); } } switch ($position) { case 0: - return $verbose ? Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params) : Yii::t('yii', '{n} B', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params, $this->locale) : Yii::t('yii', '{n} B', $params, $this->locale); case 1: - return $verbose ? Yii::t('yii', '{n, plural, =1{# kilobyte} other{# kilobytes}}', $params) : Yii::t('yii', '{n} KB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# kilobyte} other{# kilobytes}}', $params, $this->locale) : Yii::t('yii', '{n} KB', $params, $this->locale); case 2: - return $verbose ? Yii::t('yii', '{n, plural, =1{# megabyte} other{# megabytes}}', $params) : Yii::t('yii', '{n} MB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# megabyte} other{# megabytes}}', $params, $this->locale) : Yii::t('yii', '{n} MB', $params, $this->locale); case 3: - return $verbose ? Yii::t('yii', '{n, plural, =1{# gigabyte} other{# gigabytes}}', $params) : Yii::t('yii', '{n} GB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# gigabyte} other{# gigabytes}}', $params, $this->locale) : Yii::t('yii', '{n} GB', $params, $this->locale); case 4: - return $verbose ? Yii::t('yii', '{n, plural, =1{# terabyte} other{# terabytes}}', $params) : Yii::t('yii', '{n} TB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# terabyte} other{# terabytes}}', $params, $this->locale) : Yii::t('yii', '{n} TB', $params, $this->locale); default: - return $verbose ? Yii::t('yii', '{n, plural, =1{# petabyte} other{# petabytes}}', $params) : Yii::t('yii', '{n} PB', $params); + return $verbose ? Yii::t('yii', '{n, plural, =1{# petabyte} other{# petabytes}}', $params, $this->locale) : Yii::t('yii', '{n} PB', $params, $this->locale); } } diff --git a/tests/unit/framework/i18n/FormatterTest.php b/tests/unit/framework/i18n/FormatterTest.php index 607e633ca6..8aaedb20d8 100644 --- a/tests/unit/framework/i18n/FormatterTest.php +++ b/tests/unit/framework/i18n/FormatterTest.php @@ -20,6 +20,7 @@ function extension_loaded($name) namespace yiiunit\framework\i18n; +use yii\base\InvalidParamException; use yii\i18n\Formatter; use yiiunit\TestCase; use DateTime; @@ -54,7 +55,8 @@ class FormatterTest extends TestCase } $this->mockApplication([ - 'timeZone' => 'UTC' + 'timeZone' => 'UTC', + 'language' => 'ru-RU', ]); $this->formatter = new Formatter(['locale' => 'en-US']); } @@ -76,6 +78,17 @@ class FormatterTest extends TestCase $this->assertSame(date('Y-m-d', $value), $this->formatter->format($value, 'data')); } + public function testLocale() + { + // locale is configured explicitly + $f = new Formatter(['locale' => 'en-US']); + $this->assertEquals('en-US', $f->locale); + + // if not, take from application + $f = new Formatter(); + $this->assertEquals('ru-RU', $f->locale); + } + public function testAsRaw() { @@ -211,38 +224,39 @@ class FormatterTest extends TestCase $this->testAsDate(); } - public function testIntlAsTime() - { - $this->testAsTime(); - } - - public function testIntlAsDatetime() - { - $this->testAsDatetime(); - } - public function testAsDate() { $value = time(); $this->assertSame(date('M j, Y', $value), $this->formatter->asDate($value)); $this->assertSame(date('Y/m/d', $value), $this->formatter->asDate($value, 'php:Y/m/d')); - $this->assertSame(date('d.m.Y', $value), $this->formatter->asDate($value, 'short')); + $this->assertSame(date('m/d/Y', $value), $this->formatter->asDate($value, 'MM/dd/yyyy')); + $this->assertSame(date('n/j/y', $value), $this->formatter->asDate($value, 'short')); $this->assertSame(date('F j, Y', $value), $this->formatter->asDate($value, 'long')); // null display $this->assertSame($this->formatter->nullDisplay, $this->formatter->asDate(null)); } + public function testIntlAsTime() + { + $this->testAsTime(); + } + public function testAsTime() { $value = time(); $this->assertSame(date('g:i:s A', $value), $this->formatter->asTime($value)); - $this->assertSame(date('n:i:s A', $value), $this->formatter->asTime($value, 'php:n:i:s A')); + $this->assertSame(date('h:i:s A', $value), $this->formatter->asTime($value, 'php:h:i:s A')); // null display $this->assertSame($this->formatter->nullDisplay, $this->formatter->asTime(null)); } + public function testIntlAsDatetime() + { + $this->testAsDatetime(); + } + public function testAsDatetime() { $value = time(); @@ -410,10 +424,8 @@ class FormatterTest extends TestCase $this->assertSame("123", $this->formatter->asInteger(123.23)); $this->assertSame("123", $this->formatter->asInteger(123.53)); $this->assertSame("0", $this->formatter->asInteger(0)); - $this->assertSame("0", $this->formatter->asInteger('a')); $this->assertSame("-123", $this->formatter->asInteger(-123.23)); $this->assertSame("-123", $this->formatter->asInteger(-123.53)); - $this->assertSame("-123", $this->formatter->asInteger("-123abc")); $this->assertSame("123,456", $this->formatter->asInteger(123456)); $this->assertSame("123,456", $this->formatter->asInteger(123456.789)); @@ -422,9 +434,59 @@ class FormatterTest extends TestCase $this->assertSame($this->formatter->nullDisplay, $this->formatter->asInteger(null)); } + /** + * @expectedException \yii\base\InvalidParamException + */ + public function testAsIntegerException() + { + $this->assertSame("0", $this->formatter->asInteger('a')); + } + + /** + * @expectedException \yii\base\InvalidParamException + */ + public function testAsIntegerException2() + { + $this->assertSame("0", $this->formatter->asInteger('-123abc')); + } + public function testIntlAsDecimal() { - $this->testAsDecimal(); + $value = 123.12; + $this->assertSame("123.12", $this->formatter->asDecimal($value, 2)); + $this->assertSame("123.1", $this->formatter->asDecimal($value, 1)); + $this->assertSame("123", $this->formatter->asDecimal($value, 0)); + + $value = 123; + $this->assertSame("123", $this->formatter->asDecimal($value)); + $this->assertSame("123.00", $this->formatter->asDecimal($value, 2)); + $this->formatter->decimalSeparator = ','; + $this->formatter->thousandSeparator = '.'; + $value = 123.12; + $this->assertSame("123,12", $this->formatter->asDecimal($value)); + $this->assertSame("123,1", $this->formatter->asDecimal($value, 1)); + $this->assertSame("123", $this->formatter->asDecimal($value, 0)); + $value = 123123.123; + $this->assertSame("123.123", $this->formatter->asDecimal($value, 0)); + $this->assertSame("123.123,12", $this->formatter->asDecimal($value, 2)); + $this->formatter->thousandSeparator = ''; + $this->assertSame("123123,1", $this->formatter->asDecimal($value, 1)); + $this->formatter->thousandSeparator = ' '; + $this->assertSame("12 31 23,1", $this->formatter->asDecimal($value, 1, [\NumberFormatter::GROUPING_SIZE => 2])); + + $value = 123123.123; + $this->formatter->decimalSeparator = ','; + $this->formatter->thousandSeparator = ' '; + $this->assertSame("123 123", $this->formatter->asDecimal($value, 0)); + $this->assertSame("123 123,12", $this->formatter->asDecimal($value, 2)); + + $this->formatter->decimalSeparator = null; + $this->formatter->thousandSeparator = null; + $value = '-123456.123'; + $this->assertSame("-123,456.123", $this->formatter->asDecimal($value)); + + // null display + $this->assertSame($this->formatter->nullDisplay, $this->formatter->asDecimal(null)); } public function testAsDecimal() @@ -443,21 +505,22 @@ class FormatterTest extends TestCase $this->assertSame("123", $this->formatter->asDecimal($value, 0)); $value = 123123.123; $this->assertSame("123.123,12", $this->formatter->asDecimal($value)); - $this->assertSame("123123,12", $this->formatter->asDecimal($value, 2, null, false)); $value = 123123.123; - $this->assertSame("123,123", $this->formatter->asDecimal($value)); - $this->assertSame("123,123.12", $this->formatter->asDecimal($value, 2)); + $this->assertSame("123.123,12", $this->formatter->asDecimal($value)); + $this->assertSame("123.123,12", $this->formatter->asDecimal($value, 2)); $this->formatter->decimalSeparator = ','; $this->formatter->thousandSeparator = ' '; - $this->assertSame("123 123", $this->formatter->asDecimal($value)); + $this->assertSame("123 123,12", $this->formatter->asDecimal($value)); $this->assertSame("123 123,12", $this->formatter->asDecimal($value, 2)); $this->formatter->thousandSeparator = ''; - $this->assertSame("123123", $this->formatter->asDecimal($value)); + $this->assertSame("123123,12", $this->formatter->asDecimal($value)); $this->assertSame("123123,12", $this->formatter->asDecimal($value, 2)); + $this->formatter->decimalSeparator = null; + $this->formatter->thousandSeparator = null; $value = '-123456.123'; - $this->assertSame("-123,456.123", $this->formatter->asDecimal($value)); + $this->assertSame("-123,456.123", $this->formatter->asDecimal($value, 3)); // null display $this->assertSame($this->formatter->nullDisplay, $this->formatter->asDecimal(null)); @@ -470,12 +533,12 @@ class FormatterTest extends TestCase public function testAsPercent() { - $value = '123'; - $this->assertSame('12,300%', $this->formatter->asPercent($value)); - $value = '0.1234'; - $this->assertSame("12%", $this->formatter->asPercent($value)); - $value = '-0.009343'; - $this->assertSame("-1%", $this->formatter->asPercent($value)); + $this->assertSame('12,300%', $this->formatter->asPercent(123)); + $this->assertSame('12,300%', $this->formatter->asPercent('123')); + $this->assertSame("12%", $this->formatter->asPercent(0.1234)); + $this->assertSame("12%", $this->formatter->asPercent('0.1234')); + $this->assertSame("-1%", $this->formatter->asPercent(-0.009343)); + $this->assertSame("-1%", $this->formatter->asPercent('-0.009343')); // null display $this->assertSame($this->formatter->nullDisplay, $this->formatter->asPercent(null)); @@ -508,13 +571,13 @@ class FormatterTest extends TestCase $this->formatter->currencyCode = 'USD'; $this->assertSame('USD 123.00', $this->formatter->asCurrency('123')); $this->assertSame('USD 0.00', $this->formatter->asCurrency('0')); - $this->assertSame('USD -123.45', $this->formatter->asCurrency('-123,45')); + $this->assertSame('USD -123.45', $this->formatter->asCurrency('-123.45')); $this->assertSame('USD -123.45', $this->formatter->asCurrency(-123.45)); $this->formatter->currencyCode = 'EUR'; $this->assertSame('EUR 123.00', $this->formatter->asCurrency('123')); $this->assertSame('EUR 0.00', $this->formatter->asCurrency('0')); - $this->assertSame('EUR -123.45', $this->formatter->asCurrency('-123,45')); + $this->assertSame('EUR -123.45', $this->formatter->asCurrency('-123.45')); $this->assertSame('EUR -123.45', $this->formatter->asCurrency(-123.45)); // null display @@ -522,11 +585,6 @@ class FormatterTest extends TestCase } public function testIntlAsScientific() - { - $this->testAsScientific(); - } - - public function testAsScientific() { $value = '123'; $this->assertSame('1.23E2', $this->formatter->asScientific($value)); @@ -539,6 +597,19 @@ class FormatterTest extends TestCase $this->assertSame($this->formatter->nullDisplay, $this->formatter->asScientific(null)); } + public function testAsScientific() + { + $value = '123'; + $this->assertSame('1.23E+2', $this->formatter->asScientific($value, 2)); + $value = '123456'; + $this->assertSame("1.234560E+5", $this->formatter->asScientific($value)); + $value = '-123456.123'; + $this->assertSame("-1.234561E+5", $this->formatter->asScientific($value)); + + // null display + $this->assertSame($this->formatter->nullDisplay, $this->formatter->asScientific(null)); + } + public function testIntlAsSpellout() { $this->assertSame('one hundred twenty-three', $this->formatter->asSpellout(123));