From 4d3c211e247e36ecfde6d43cdfda02907b924e90 Mon Sep 17 00:00:00 2001 From: John Was Date: Mon, 21 Sep 2015 17:34:24 +0200 Subject: [PATCH] use unit translations from ResourceBundle --- framework/i18n/Formatter.php | 228 ++++++++------------- tests/framework/i18n/FormatterUnitTest.php | 46 +++++ 2 files changed, 134 insertions(+), 140 deletions(-) create mode 100644 tests/framework/i18n/FormatterUnitTest.php diff --git a/framework/i18n/Formatter.php b/framework/i18n/Formatter.php index 9f02826c88..283e3c0f2c 100644 --- a/framework/i18n/Formatter.php +++ b/framework/i18n/Formatter.php @@ -48,6 +48,10 @@ class Formatter extends Component { const UNIT_SYSTEM_METRIC = 'metric'; const UNIT_SYSTEM_IMPERIAL = 'imperial'; + const FORMAT_WIDTH_LONG = 'long'; + const FORMAT_WIDTH_SHORT = 'short'; + const UNIT_LENGTH = 'length'; + const UNIT_WEIGHT = 'weight'; /** * @var string the text to be displayed when formatting a `null` value. @@ -226,6 +230,57 @@ class Formatter extends Component * @var string default system of measure units. */ public $systemOfUnits = self::UNIT_SYSTEM_METRIC; + /** + * @var array configuration of measure units + */ + public $measureUnits = [ + self::UNIT_LENGTH => [ + self::UNIT_SYSTEM_IMPERIAL => [ + 'inch' => 1, + 'foot' => 12, + 'yard' => 36, + 'chain' => 792, + 'furlong' => 7920, + 'mile' => 63360, + ], + self::UNIT_SYSTEM_METRIC => [ + 'millimeter' => 1, + 'centimeter' => 100, + 'meter' => 1000, + 'kilometer' => 1000000, + ], + ], + self::UNIT_WEIGHT => [ + self::UNIT_SYSTEM_IMPERIAL => [ + 'grain' => 1, + 'drachm' => 27.34375, + 'ounce' => 437.5, + 'pound' => 7000, + 'stone' => 98000, + 'quarter' => 196000, + 'hundredweight' => 784000, + 'ton' => 15680000, + ], + self::UNIT_SYSTEM_METRIC => [ + 'gram' => 1, + 'kilogram' => 1000, + 'ton' => 1000000, + ], + ], + ]; + /** + * @var array base units as multipliers for smallest possible unit from $measureUnits + */ + public $baseUnits = [ + self::UNIT_LENGTH => [ + self::UNIT_SYSTEM_IMPERIAL => 12, // 1 feet = 12 inches + self::UNIT_SYSTEM_METRIC => 1000, // 1 meter = 1000 millimeters + ], + self::UNIT_WEIGHT => [ + self::UNIT_SYSTEM_IMPERIAL => 7000, // 1 pound = 7000 grains + self::UNIT_SYSTEM_METRIC => 1000, // 1 kilogram = 1000 grams + ], + ]; /** * @var boolean whether the [PHP intl extension](http://php.net/manual/en/book.intl.php) is loaded. @@ -1134,7 +1189,7 @@ class Formatter extends Component */ public function asLength($value, $baseUnit = null, $system = null, $decimals = null, $options = [], $textOptions = []) { - return $this->formatUnit('length', 'long', $value, $baseUnit, $system, $decimals, $options, $textOptions); + return $this->formatUnit(self::UNIT_LENGTH, self::FORMAT_WIDTH_LONG, $value, $baseUnit, $system, $decimals, $options, $textOptions); } /** @@ -1154,7 +1209,7 @@ class Formatter extends Component */ public function asShortLength($value, $baseUnit = null, $system = null, $decimals = null, $options = [], $textOptions = []) { - return $this->formatUnit('length', 'short', $value, $baseUnit, $system, $decimals, $options, $textOptions); + return $this->formatUnit(self::UNIT_LENGTH, self::FORMAT_WIDTH_SHORT, $value, $baseUnit, $system, $decimals, $options, $textOptions); } /** @@ -1172,7 +1227,7 @@ class Formatter extends Component */ public function asWeight($value, $baseUnit = null, $system = null, $decimals = null, $options = [], $textOptions = []) { - return $this->formatUnit('weight', 'long', $value, $baseUnit, $system, $decimals, $options, $textOptions); + return $this->formatUnit(self::UNIT_WEIGHT, self::FORMAT_WIDTH_LONG, $value, $baseUnit, $system, $decimals, $options, $textOptions); } /** @@ -1192,12 +1247,12 @@ class Formatter extends Component */ public function asShortWeight($value, $baseUnit = null, $system = null, $decimals = null, $options = [], $textOptions = []) { - return $this->formatUnit('weight', 'short', $value, $baseUnit, $system, $decimals, $options, $textOptions); + return $this->formatUnit(self::UNIT_WEIGHT, self::FORMAT_WIDTH_SHORT, $value, $baseUnit, $system, $decimals, $options, $textOptions); } /** - * @param string $unit one of: weight, length - * @param string $type one of: short, long + * @param string $unitType one of: weight, length + * @param string $unitFormat one of: short, long * @param double $value * @param double $baseUnit * @param string $system @@ -1206,7 +1261,7 @@ class Formatter extends Component * @param $textOptions * @return string */ - private function formatUnit($unit, $type, $value, $baseUnit, $system, $decimals, $options, $textOptions) + private function formatUnit($unitType, $unitFormat, $value, $baseUnit, $system, $decimals, $options, $textOptions) { if ($value === null) { return $this->nullDisplay; @@ -1215,13 +1270,30 @@ class Formatter extends Component $system = $this->systemOfUnits; } if ($baseUnit === null) { - $baseUnit = $this->getBaseMeasureUnit($unit, $system); + $baseUnit = $this->baseUnits[$unitType][$system]; } - $multipliers = $this->getUnitMultipliers($unit, $system); + $multipliers = array_values($this->measureUnits[$unitType][$system]); + $unitNames = array_keys($this->measureUnits[$unitType][$system]); - list($params, $position) = $this->formatNumber($value / $baseUnit, $decimals, null, $multipliers, $options, $textOptions); + list($params, $position) = $this->formatNumber($value * $baseUnit, $decimals, null, $multipliers, $options, $textOptions); - return $this->getMeasureUnit($unit, $system, 'short', $multipliers[$position], $params); + // build the message pattern + $resourceBundle = new \ResourceBundle($this->locale, 'ICUDATA-unit'); + $bundleKey = 'units' . ($unitFormat === self::FORMAT_WIDTH_SHORT ? 'Short' : ''); + $unitBundle = $resourceBundle[$bundleKey][$unitType][$unitNames[$position]]; + $message = []; + foreach ($unitBundle as $key => $value) { + if ($key === 'dnam') { + continue; + } + $message[] = "$key{{$value}}"; + } + $message = '{n, plural, '.implode(' ', $message).'}'; + + return (new \MessageFormatter($this->locale, $message))->format([ + '0' => $params['nFormatted'], + 'n' => $params['n'], + ]); } /** @@ -1251,7 +1323,7 @@ class Formatter extends Component } do { if (is_array($formatBase)) { - if (abs($value) < $formatBase[$position]) { + if (abs($value) < $formatBase[$position + 1]) { break; } } else { @@ -1262,8 +1334,11 @@ class Formatter extends Component } $position++; } while ($position < $maxPosition + 1); + if (is_array($formatBase) && $formatBase[$position - 1] !== 1) { + $value /= $formatBase[$position - 1]; + } - // no decimals for bytes + // no decimals for smallest unit if ($position === 0) { $decimals = 0; } elseif ($decimals !== null) { @@ -1289,133 +1364,6 @@ class Formatter extends Component return [$params, $position]; } - protected function getBaseMeasureUnit($unit, $system) - { - switch ($unit) { - case 'length': - switch ($system) { - case self::UNIT_SYSTEM_IMPERIAL: - return 12; // 1 feet = 12 inches - case self::UNIT_SYSTEM_METRIC: - return 1000; // 1 meter = 1000 millimeters - } - case 'weight': - switch ($system) { - case self::UNIT_SYSTEM_IMPERIAL: - return 7000; // 1 pound = 7000 grains - case self::UNIT_SYSTEM_METRIC: - return 1000; // 1 kilogram = 1000 grams - } - } - } - - protected function getUnitMultipliers($unit, $system) - { - switch ($unit) { - case 'length': - switch ($system) { - case self::UNIT_SYSTEM_IMPERIAL: - return [1, 12, 36, 792, 7920, 63360]; - case self::UNIT_SYSTEM_METRIC: - return [1, 100, 1000, 1000000]; - } - - case 'weight': - switch ($system) { - case self::UNIT_SYSTEM_IMPERIAL: - return [1, 27.34375, 437.5, 7000, 98000, 196000, 784000, 15680000]; - case self::UNIT_SYSTEM_METRIC: - return [1, 1000, 1000000]; - } - } - } - - protected function getMeasureUnit($unit, $system, $type, $baseUnit, $params) - { - switch ($unit) { - case 'length': - switch ($system) { - case self::UNIT_SYSTEM_IMPERIAL: - if ($type === 'short') { - switch ($baseUnit) { - case 1: return Yii::t('yii', '{nFormatted} in', $params, $this->locale); - case 12: return Yii::t('yii', '{nFormatted} ft', $params, $this->locale); - case 36: return Yii::t('yii', '{nFormatted} yd', $params, $this->locale); - case 792: return Yii::t('yii', '{nFormatted} ch', $params, $this->locale); - case 7920: return Yii::t('yii', '{nFormatted} fur', $params, $this->locale); - case 63360: return Yii::t('yii', '{nFormatted} mi', $params, $this->locale); - } - } else { - switch ($baseUnit) { - case 1: return Yii::t('yii', '{nFormatted} {n, plural, =1{inch} other{inches}', $params, $this->locale); - case 12: return Yii::t('yii', '{nFormatted} {n, plural, =1{foot} other{feet}', $params, $this->locale); - case 36: return Yii::t('yii', '{nFormatted} {n, plural, =1{yard} other{yards}', $params, $this->locale); - case 792: return Yii::t('yii', '{nFormatted} {n, plural, =1{chain} other{chains}', $params, $this->locale); - case 7920: return Yii::t('yii', '{nFormatted} {n, plural, =1{furlong} other{furlongs}', $params, $this->locale); - case 63360: return Yii::t('yii', '{nFormatted} {n, plural, =1{mile} other{miles}', $params, $this->locale); - } - } - case self::UNIT_SYSTEM_METRIC: - if ($type === 'short') { - switch ($baseUnit) { - case 1: return Yii::t('yii', '{nFormatted} mm', $params, $this->locale); - case 100: return Yii::t('yii', '{nFormatted} cm', $params, $this->locale); - case 1000: return Yii::t('yii', '{nFormatted} m', $params, $this->locale); - case 1000000: return Yii::t('yii', '{nFormatted} km', $params, $this->locale); - } - } else { - switch ($baseUnit) { - case 1: return Yii::t('yii', '{nFormatted} {n, plural, =1{millimeter} other{millimeters}', $params, $this->locale); - case 100: return Yii::t('yii', '{nFormatted} {n, plural, =1{centimeter} other{centimeters}', $params, $this->locale); - case 1000: return Yii::t('yii', '{nFormatted} {n, plural, =1{meter} other{meters}', $params, $this->locale); - case 1000000: return Yii::t('yii', '{nFormatted} {n, plural, =1{kilometer} other{kilometers}', $params, $this->locale); - } - } - } - case 'weight': - switch ($system) { - case self::UNIT_SYSTEM_IMPERIAL: - if ($type === 'short') { - switch ($baseUnit) { - case 1: return Yii::t('yii', '{nFormatted} gr', $params, $this->locale); - case 27.34375: return Yii::t('yii', '{nFormatted} dr', $params, $this->locale); - case 437.5: return Yii::t('yii', '{nFormatted} oz', $params, $this->locale); - case 7000: return Yii::t('yii', '{nFormatted} lb', $params, $this->locale); - case 98000: return Yii::t('yii', '{nFormatted} st', $params, $this->locale); - case 196000: return Yii::t('yii', '{nFormatted} qr', $params, $this->locale); - case 784000: return Yii::t('yii', '{nFormatted} cwt', $params, $this->locale); - case 15680000: return Yii::t('yii', '{nFormatted} t', $params, $this->locale); - } - } else { - switch ($baseUnit) { - case 1: return Yii::t('yii', '{nFormatted} {n, plural, =1{grain} other{grains}', $params, $this->locale); - case 27.34375: return Yii::t('yii', '{nFormatted} {n, plural, =1{drachm} other{drachms}', $params, $this->locale); - case 437.5: return Yii::t('yii', '{nFormatted} {n, plural, =1{ounce} other{ounces}', $params, $this->locale); - case 7000: return Yii::t('yii', '{nFormatted} {n, plural, =1{pound} other{pounds}', $params, $this->locale); - case 98000: return Yii::t('yii', '{nFormatted} {n, plural, =1{stone} other{stones}', $params, $this->locale); - case 196000: return Yii::t('yii', '{nFormatted} {n, plural, =1{quarter} other{quarters}', $params, $this->locale); - case 784000: return Yii::t('yii', '{nFormatted} {n, plural, =1{hundredweight} other{hundredweights}', $params, $this->locale); - case 15680000: return Yii::t('yii', '{nFormatted} {n, plural, =1{ton} other{tons}', $params, $this->locale); - } - } - case self::UNIT_SYSTEM_METRIC: - if ($type === 'short') { - switch ($baseUnit) { - case 1: return Yii::t('yii', '{nFormatted} g', $params, $this->locale); - case 1000: return Yii::t('yii', '{nFormatted} kg', $params, $this->locale); - case 1000000: return Yii::t('yii', '{nFormatted} t', $params, $this->locale); - } - } else { - switch ($baseUnit) { - case 1: return Yii::t('yii', '{nFormatted} {n, plural, =1{gram} other{grams}', $params, $this->locale); - case 1000: return Yii::t('yii', '{nFormatted} {n, plural, =1{kilogram} other{kilograms}', $params, $this->locale); - case 1000000: return Yii::t('yii', '{nFormatted} {n, plural, =1{ton} other{tons}', $params, $this->locale); - } - } - } - } - } - /** * Normalizes a numeric input value * diff --git a/tests/framework/i18n/FormatterUnitTest.php b/tests/framework/i18n/FormatterUnitTest.php new file mode 100644 index 0000000000..5984f35658 --- /dev/null +++ b/tests/framework/i18n/FormatterUnitTest.php @@ -0,0 +1,46 @@ +mockApplication([ + 'timeZone' => 'UTC', + 'language' => 'en-US', + ]); + $this->formatter = new Formatter(['locale' => 'pl-PL']); + } + + protected function tearDown() + { + parent::tearDown(); + IntlTestHelper::resetIntlStatus(); + $this->formatter = null; + } + + public function testAsLength() + { + $this->assertSame("53 milimetry", $this->formatter->asLength(0.053)); + $this->assertSame("0,99 centrymetra", $this->formatter->asLength(0.099)); + $this->assertSame("0,12 metra", $this->formatter->asLength(0.123)); + } +}