12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063 |
- <?php
- namespace yii\i18n;
- use Closure;
- use DateInterval;
- use DateTime;
- use DateTimeInterface;
- use DateTimeZone;
- use IntlDateFormatter;
- use NumberFormatter;
- use Yii;
- use yii\base\Component;
- use yii\base\InvalidArgumentException;
- use yii\base\InvalidConfigException;
- use yii\helpers\FormatConverter;
- use yii\helpers\Html;
- use yii\helpers\HtmlPurifier;
- 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 = 'mass';
-
- public $nullDisplay;
-
- public $booleanFormat;
-
- public $locale;
-
- public $timeZone;
-
- public $defaultTimeZone = 'UTC';
-
- public $dateFormat = 'medium';
-
- public $timeFormat = 'medium';
-
- public $datetimeFormat = 'medium';
-
- public $calendar;
-
- public $decimalSeparator;
-
- public $thousandSeparator;
-
- public $numberFormatterOptions = [];
-
- public $numberFormatterTextOptions = [];
-
- public $numberFormatterSymbols = [];
-
- public $currencyCode;
-
- public $sizeFormatBase = 1024;
-
- public $systemOfUnits = self::UNIT_SYSTEM_METRIC;
-
- 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' => 10,
- '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,
- ],
- ],
- ];
-
- public $baseUnits = [
- self::UNIT_LENGTH => [
- self::UNIT_SYSTEM_IMPERIAL => 12,
- self::UNIT_SYSTEM_METRIC => 1000,
- ],
- self::UNIT_WEIGHT => [
- self::UNIT_SYSTEM_IMPERIAL => 7000,
- self::UNIT_SYSTEM_METRIC => 1000,
- ],
- ];
-
- private $_intlLoaded = false;
-
- private $_resourceBundle;
-
- private $_unitMessages = [];
-
- public function init()
- {
- if ($this->timeZone === null) {
- $this->timeZone = Yii::$app->timeZone;
- }
- if ($this->locale === null) {
- $this->locale = Yii::$app->language;
- }
- if ($this->booleanFormat === null) {
- $this->booleanFormat = [Yii::t('yii', 'No', [], $this->locale), Yii::t('yii', 'Yes', [], $this->locale)];
- }
- if ($this->nullDisplay === null) {
- $this->nullDisplay = '<span class="not-set">' . Yii::t('yii', '(not set)', [], $this->locale) . '</span>';
- }
- $this->_intlLoaded = extension_loaded('intl');
- if (!$this->_intlLoaded) {
- if ($this->decimalSeparator === null) {
- $this->decimalSeparator = '.';
- }
- if ($this->thousandSeparator === null) {
- $this->thousandSeparator = ',';
- }
- }
- }
-
- public function format($value, $format)
- {
- if ($format instanceof Closure) {
- return call_user_func($format, $value, $this);
- } elseif (is_array($format)) {
- if (!isset($format[0])) {
- throw new InvalidArgumentException('The $format array must contain at least one element.');
- }
- $f = $format[0];
- $format[0] = $value;
- $params = $format;
- $format = $f;
- } else {
- $params = [$value];
- }
- $method = 'as' . $format;
- if ($this->hasMethod($method)) {
- return call_user_func_array([$this, $method], $params);
- }
- throw new InvalidArgumentException("Unknown format type: $format");
- }
-
-
- public function asRaw($value)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- return $value;
- }
-
- public function asText($value)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- return Html::encode($value);
- }
-
- public function asNtext($value)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- return nl2br(Html::encode($value));
- }
-
- public function asParagraphs($value)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- return str_replace('<p></p>', '', '<p>' . preg_replace('/\R{2,}/u', "</p>\n<p>", Html::encode($value)) . '</p>');
- }
-
- public function asHtml($value, $config = null)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- return HtmlPurifier::process($value, $config);
- }
-
- public function asEmail($value, $options = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- return Html::mailto(Html::encode($value), $value, $options);
- }
-
- public function asImage($value, $options = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- return Html::img($value, $options);
- }
-
- public function asUrl($value, $options = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $url = $value;
- if (strpos($url, '://') === false) {
- $url = 'http://' . $url;
- }
- return Html::a(Html::encode($value), $url, $options);
- }
-
- public function asBoolean($value)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- return $value ? $this->booleanFormat[1] : $this->booleanFormat[0];
- }
-
-
- public function asDate($value, $format = null)
- {
- if ($format === null) {
- $format = $this->dateFormat;
- }
- return $this->formatDateTimeValue($value, $format, 'date');
- }
-
- public function asTime($value, $format = null)
- {
- if ($format === null) {
- $format = $this->timeFormat;
- }
- return $this->formatDateTimeValue($value, $format, 'time');
- }
-
- public function asDatetime($value, $format = null)
- {
- if ($format === null) {
- $format = $this->datetimeFormat;
- }
- return $this->formatDateTimeValue($value, $format, 'datetime');
- }
-
- private $_dateFormats = [
- 'short' => 3,
- 'medium' => 2,
- 'long' => 1,
- 'full' => 0,
- ];
-
- private function formatDateTimeValue($value, $format, $type)
- {
- $timeZone = $this->timeZone;
-
- if ($type === 'date' || $type === 'time') {
- list($timestamp, $hasTimeInfo, $hasDateInfo) = $this->normalizeDatetimeValue($value, true);
- if ($type === 'date' && !$hasTimeInfo || $type === 'time' && !$hasDateInfo) {
- $timeZone = $this->defaultTimeZone;
- }
- } else {
- $timestamp = $this->normalizeDatetimeValue($value);
- }
- if ($timestamp === null) {
- return $this->nullDisplay;
- }
-
- $year = $timestamp->format('Y');
- if ($this->_intlLoaded && !(PHP_INT_SIZE === 4 && ($year <= 1901 || $year >= 2038))) {
- if (strncmp($format, 'php:', 4) === 0) {
- $format = FormatConverter::convertDatePhpToIcu(substr($format, 4));
- }
- if (isset($this->_dateFormats[$format])) {
- if ($type === 'date') {
- $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE, $timeZone, $this->calendar);
- } elseif ($type === 'time') {
- $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, $this->_dateFormats[$format], $timeZone, $this->calendar);
- } else {
- $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], $this->_dateFormats[$format], $timeZone, $this->calendar);
- }
- } else {
- $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $timeZone, $this->calendar, $format);
- }
- if ($formatter === null) {
- throw new InvalidConfigException(intl_get_error_message());
- }
-
- if ($timestamp instanceof \DateTimeImmutable) {
- $timestamp = new DateTime($timestamp->format(DateTime::ISO8601), $timestamp->getTimezone());
- }
- return $formatter->format($timestamp);
- }
- if (strncmp($format, 'php:', 4) === 0) {
- $format = substr($format, 4);
- } else {
- $format = FormatConverter::convertDateIcuToPhp($format, $type, $this->locale);
- }
- if ($timeZone != null) {
- if ($timestamp instanceof \DateTimeImmutable) {
- $timestamp = $timestamp->setTimezone(new DateTimeZone($timeZone));
- } else {
- $timestamp->setTimezone(new DateTimeZone($timeZone));
- }
- }
- return $timestamp->format($format);
- }
-
- protected function normalizeDatetimeValue($value, $checkDateTimeInfo = false)
- {
-
- if ($value === null || $value instanceof DateTime || $value instanceof DateTimeInterface) {
-
- return $checkDateTimeInfo ? [$value, true, true] : $value;
- }
- if (empty($value)) {
- $value = 0;
- }
- try {
- if (is_numeric($value)) {
- $timestamp = new DateTime('@' . (int) $value, new DateTimeZone('UTC'));
- return $checkDateTimeInfo ? [$timestamp, true, true] : $timestamp;
- } elseif (($timestamp = DateTime::createFromFormat('Y-m-d|', $value, new DateTimeZone($this->defaultTimeZone))) !== false) {
- return $checkDateTimeInfo ? [$timestamp, false, true] : $timestamp;
- } elseif (($timestamp = DateTime::createFromFormat('Y-m-d H:i:s', $value, new DateTimeZone($this->defaultTimeZone))) !== false) {
- return $checkDateTimeInfo ? [$timestamp, true, true] : $timestamp;
- }
-
- if ($checkDateTimeInfo) {
- $timestamp = new DateTime($value, new DateTimeZone($this->defaultTimeZone));
- $info = date_parse($value);
- return [
- $timestamp,
- !($info['hour'] === false && $info['minute'] === false && $info['second'] === false),
- !($info['year'] === false && $info['month'] === false && $info['day'] === false && empty($info['zone'])),
- ];
- }
- return new DateTime($value, new DateTimeZone($this->defaultTimeZone));
- } catch (\Exception $e) {
- throw new InvalidArgumentException("'$value' is not a valid date time value: " . $e->getMessage()
- . "\n" . print_r(DateTime::getLastErrors(), true), $e->getCode(), $e);
- }
- }
-
- public function asTimestamp($value)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $timestamp = $this->normalizeDatetimeValue($value);
- return number_format($timestamp->format('U'), 0, '.', '');
- }
-
- public function asRelativeTime($value, $referenceTime = null)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- if ($value instanceof DateInterval) {
- $interval = $value;
- } else {
- $timestamp = $this->normalizeDatetimeValue($value);
- if ($timestamp === false) {
-
-
- try {
- $interval = new DateInterval($value);
- } catch (\Exception $e) {
-
- return $this->nullDisplay;
- }
- } else {
- $timeZone = new DateTimeZone($this->timeZone);
- if ($referenceTime === null) {
- $dateNow = new DateTime('now', $timeZone);
- } else {
- $dateNow = $this->normalizeDatetimeValue($referenceTime);
- $dateNow->setTimezone($timeZone);
- }
- $dateThen = $timestamp->setTimezone($timeZone);
- $interval = $dateThen->diff($dateNow);
- }
- }
- if ($interval->invert) {
- if ($interval->y >= 1) {
- 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], $this->locale);
- }
- if ($interval->d >= 1) {
- 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], $this->locale);
- }
- if ($interval->i >= 1) {
- return Yii::t('yii', 'in {delta, plural, =1{a minute} other{# minutes}}', ['delta' => $interval->i], $this->locale);
- }
- if ($interval->s == 0) {
- return Yii::t('yii', 'just now', [], $this->locale);
- }
- return Yii::t('yii', 'in {delta, plural, =1{a second} other{# seconds}}', ['delta' => $interval->s], $this->locale);
- }
- if ($interval->y >= 1) {
- 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], $this->locale);
- }
- if ($interval->d >= 1) {
- 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], $this->locale);
- }
- if ($interval->i >= 1) {
- return Yii::t('yii', '{delta, plural, =1{a minute} other{# minutes}} ago', ['delta' => $interval->i], $this->locale);
- }
- if ($interval->s == 0) {
- return Yii::t('yii', 'just now', [], $this->locale);
- }
- return Yii::t('yii', '{delta, plural, =1{a second} other{# seconds}} ago', ['delta' => $interval->s], $this->locale);
- }
-
- public function asDuration($value, $implodeString = ', ', $negativeSign = '-')
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- if ($value instanceof DateInterval) {
- $isNegative = $value->invert;
- $interval = $value;
- } elseif (is_numeric($value)) {
- $isNegative = $value < 0;
- $zeroDateTime = (new DateTime())->setTimestamp(0);
- $valueDateTime = (new DateTime())->setTimestamp(abs($value));
- $interval = $valueDateTime->diff($zeroDateTime);
- } elseif (strncmp($value, 'P-', 2) === 0) {
- $interval = new DateInterval('P' . substr($value, 2));
- $isNegative = true;
- } else {
- $interval = new DateInterval($value);
- $isNegative = $interval->invert;
- }
- $parts = [];
- if ($interval->y > 0) {
- $parts[] = Yii::t('yii', '{delta, plural, =1{1 year} other{# years}}', ['delta' => $interval->y], $this->locale);
- }
- if ($interval->m > 0) {
- $parts[] = Yii::t('yii', '{delta, plural, =1{1 month} other{# months}}', ['delta' => $interval->m], $this->locale);
- }
- if ($interval->d > 0) {
- $parts[] = Yii::t('yii', '{delta, plural, =1{1 day} other{# days}}', ['delta' => $interval->d], $this->locale);
- }
- if ($interval->h > 0) {
- $parts[] = Yii::t('yii', '{delta, plural, =1{1 hour} other{# hours}}', ['delta' => $interval->h], $this->locale);
- }
- if ($interval->i > 0) {
- $parts[] = Yii::t('yii', '{delta, plural, =1{1 minute} other{# minutes}}', ['delta' => $interval->i], $this->locale);
- }
- if ($interval->s > 0) {
- $parts[] = Yii::t('yii', '{delta, plural, =1{1 second} other{# seconds}}', ['delta' => $interval->s], $this->locale);
- }
- if ($interval->s === 0 && empty($parts)) {
- $parts[] = Yii::t('yii', '{delta, plural, =1{1 second} other{# seconds}}', ['delta' => $interval->s], $this->locale);
- $isNegative = false;
- }
- return empty($parts) ? $this->nullDisplay : (($isNegative ? $negativeSign : '') . implode($implodeString, $parts));
- }
-
-
- public function asInteger($value, $options = [], $textOptions = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $normalizedValue = $this->normalizeNumericValue($value);
- if ($this->isNormalizedValueMispresented($value, $normalizedValue)) {
- return $this->asIntegerStringFallback((string) $value);
- }
- if ($this->_intlLoaded) {
- $f = $this->createNumberFormatter(NumberFormatter::DECIMAL, null, $options, $textOptions);
- $f->setAttribute(NumberFormatter::FRACTION_DIGITS, 0);
- if (($result = $f->format($normalizedValue, NumberFormatter::TYPE_INT64)) === false) {
- throw new InvalidArgumentException('Formatting integer value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
- }
- return $result;
- }
- return number_format((int) $normalizedValue, 0, $this->decimalSeparator, $this->thousandSeparator);
- }
-
- public function asDecimal($value, $decimals = null, $options = [], $textOptions = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $normalizedValue = $this->normalizeNumericValue($value);
- if ($this->isNormalizedValueMispresented($value, $normalizedValue)) {
- return $this->asDecimalStringFallback((string) $value, $decimals);
- }
- if ($this->_intlLoaded) {
- $f = $this->createNumberFormatter(NumberFormatter::DECIMAL, $decimals, $options, $textOptions);
- if (($result = $f->format($normalizedValue)) === false) {
- throw new InvalidArgumentException('Formatting decimal value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
- }
- return $result;
- }
- if ($decimals === null) {
- $decimals = 2;
- }
- return number_format($normalizedValue, $decimals, $this->decimalSeparator, $this->thousandSeparator);
- }
-
- public function asPercent($value, $decimals = null, $options = [], $textOptions = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $normalizedValue = $this->normalizeNumericValue($value);
- if ($this->isNormalizedValueMispresented($value, $normalizedValue)) {
- return $this->asPercentStringFallback((string) $value, $decimals);
- }
- if ($this->_intlLoaded) {
- $f = $this->createNumberFormatter(NumberFormatter::PERCENT, $decimals, $options, $textOptions);
- if (($result = $f->format($normalizedValue)) === false) {
- throw new InvalidArgumentException('Formatting percent value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
- }
- return $result;
- }
- if ($decimals === null) {
- $decimals = 0;
- }
- $normalizedValue *= 100;
- return number_format($normalizedValue, $decimals, $this->decimalSeparator, $this->thousandSeparator) . '%';
- }
-
- public function asScientific($value, $decimals = null, $options = [], $textOptions = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $value = $this->normalizeNumericValue($value);
- if ($this->_intlLoaded) {
- $f = $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $decimals, $options, $textOptions);
- if (($result = $f->format($value)) === false) {
- throw new InvalidArgumentException('Formatting scientific number value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
- }
- return $result;
- }
- if ($decimals !== null) {
- return sprintf("%.{$decimals}E", $value);
- }
- return sprintf('%.E', $value);
- }
-
- public function asCurrency($value, $currency = null, $options = [], $textOptions = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $normalizedValue = $this->normalizeNumericValue($value);
- if ($this->isNormalizedValueMispresented($value, $normalizedValue)) {
- return $this->asCurrencyStringFallback((string) $value, $currency);
- }
- if ($this->_intlLoaded) {
- $currency = $currency ?: $this->currencyCode;
-
-
- if ($currency && !isset($textOptions[NumberFormatter::CURRENCY_CODE])) {
- $textOptions[NumberFormatter::CURRENCY_CODE] = $currency;
- }
- $formatter = $this->createNumberFormatter(NumberFormatter::CURRENCY, null, $options, $textOptions);
- if ($currency === null) {
- $result = $formatter->format($normalizedValue);
- } else {
- $result = $formatter->formatCurrency($normalizedValue, $currency);
- }
- if ($result === false) {
- throw new InvalidArgumentException('Formatting currency value failed: ' . $formatter->getErrorCode() . ' ' . $formatter->getErrorMessage());
- }
- return $result;
- }
- if ($currency === null) {
- if ($this->currencyCode === null) {
- throw new InvalidConfigException('The default currency code for the formatter is not defined and the php intl extension is not installed which could take the default currency from the locale.');
- }
- $currency = $this->currencyCode;
- }
- return $currency . ' ' . $this->asDecimal($normalizedValue, 2, $options, $textOptions);
- }
-
- public function asSpellout($value)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $value = $this->normalizeNumericValue($value);
- if ($this->_intlLoaded) {
- $f = $this->createNumberFormatter(NumberFormatter::SPELLOUT);
- if (($result = $f->format($value)) === false) {
- throw new InvalidArgumentException('Formatting number as spellout failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
- }
- return $result;
- }
- throw new InvalidConfigException('Format as Spellout is only supported when PHP intl extension is installed.');
- }
-
- public function asOrdinal($value)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- $value = $this->normalizeNumericValue($value);
- if ($this->_intlLoaded) {
- $f = $this->createNumberFormatter(NumberFormatter::ORDINAL);
- if (($result = $f->format($value)) === false) {
- throw new InvalidArgumentException('Formatting number as ordinal failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
- }
- return $result;
- }
- throw new InvalidConfigException('Format as Ordinal is only supported when PHP intl extension is installed.');
- }
-
- public function asShortSize($value, $decimals = null, $options = [], $textOptions = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- list($params, $position) = $this->formatNumber($value, $decimals, 4, $this->sizeFormatBase, $options, $textOptions);
- if ($this->sizeFormatBase == 1024) {
- switch ($position) {
- case 0:
- return Yii::t('yii', '{nFormatted} B', $params, $this->locale);
- case 1:
- return Yii::t('yii', '{nFormatted} KiB', $params, $this->locale);
- case 2:
- return Yii::t('yii', '{nFormatted} MiB', $params, $this->locale);
- case 3:
- return Yii::t('yii', '{nFormatted} GiB', $params, $this->locale);
- case 4:
- return Yii::t('yii', '{nFormatted} TiB', $params, $this->locale);
- default:
- return Yii::t('yii', '{nFormatted} PiB', $params, $this->locale);
- }
- } else {
- switch ($position) {
- case 0:
- return Yii::t('yii', '{nFormatted} B', $params, $this->locale);
- case 1:
- return Yii::t('yii', '{nFormatted} kB', $params, $this->locale);
- case 2:
- return Yii::t('yii', '{nFormatted} MB', $params, $this->locale);
- case 3:
- return Yii::t('yii', '{nFormatted} GB', $params, $this->locale);
- case 4:
- return Yii::t('yii', '{nFormatted} TB', $params, $this->locale);
- default:
- return Yii::t('yii', '{nFormatted} PB', $params, $this->locale);
- }
- }
- }
-
- public function asSize($value, $decimals = null, $options = [], $textOptions = [])
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- list($params, $position) = $this->formatNumber($value, $decimals, 4, $this->sizeFormatBase, $options, $textOptions);
- if ($this->sizeFormatBase == 1024) {
- switch ($position) {
- case 0:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{byte} other{bytes}}', $params, $this->locale);
- case 1:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}', $params, $this->locale);
- case 2:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}', $params, $this->locale);
- case 3:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}', $params, $this->locale);
- case 4:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}', $params, $this->locale);
- default:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}', $params, $this->locale);
- }
- } else {
- switch ($position) {
- case 0:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{byte} other{bytes}}', $params, $this->locale);
- case 1:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}', $params, $this->locale);
- case 2:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}', $params, $this->locale);
- case 3:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}', $params, $this->locale);
- case 4:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}', $params, $this->locale);
- default:
- return Yii::t('yii', '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}', $params, $this->locale);
- }
- }
- }
-
- public function asLength($value, $decimals = null, $numberOptions = [], $textOptions = [])
- {
- return $this->formatUnit(self::UNIT_LENGTH, self::FORMAT_WIDTH_LONG, $value, null, null, $decimals, $numberOptions, $textOptions);
- }
-
- public function asShortLength($value, $decimals = null, $options = [], $textOptions = [])
- {
- return $this->formatUnit(self::UNIT_LENGTH, self::FORMAT_WIDTH_SHORT, $value, null, null, $decimals, $options, $textOptions);
- }
-
- public function asWeight($value, $decimals = null, $options = [], $textOptions = [])
- {
- return $this->formatUnit(self::UNIT_WEIGHT, self::FORMAT_WIDTH_LONG, $value, null, null, $decimals, $options, $textOptions);
- }
-
- public function asShortWeight($value, $decimals = null, $options = [], $textOptions = [])
- {
- return $this->formatUnit(self::UNIT_WEIGHT, self::FORMAT_WIDTH_SHORT, $value, null, null, $decimals, $options, $textOptions);
- }
-
- private function formatUnit($unitType, $unitFormat, $value, $baseUnit, $unitSystem, $decimals, $options, $textOptions)
- {
- if ($value === null) {
- return $this->nullDisplay;
- }
- if ($unitSystem === null) {
- $unitSystem = $this->systemOfUnits;
- }
- if ($baseUnit === null) {
- $baseUnit = $this->baseUnits[$unitType][$unitSystem];
- }
- $multipliers = array_values($this->measureUnits[$unitType][$unitSystem]);
- list($params, $position) = $this->formatNumber(
- $this->normalizeNumericValue($value) * $baseUnit,
- $decimals,
- null,
- $multipliers,
- $options,
- $textOptions
- );
- $message = $this->getUnitMessage($unitType, $unitFormat, $unitSystem, $position);
- return (new \MessageFormatter($this->locale, $message))->format([
- '0' => $params['nFormatted'],
- 'n' => $params['n'],
- ]);
- }
-
- private function getUnitMessage($unitType, $unitFormat, $system, $position)
- {
- if (isset($this->_unitMessages[$unitType][$system][$position])) {
- return $this->_unitMessages[$unitType][$system][$position];
- }
- if (!$this->_intlLoaded) {
- throw new InvalidConfigException('Format of ' . $unitType . ' is only supported when PHP intl extension is installed.');
- }
- if ($this->_resourceBundle === null) {
- try {
- $this->_resourceBundle = new \ResourceBundle($this->locale, 'ICUDATA-unit');
- } catch (\IntlException $e) {
- throw new InvalidConfigException('Current ICU data does not contain information about measure units. Check system requirements.');
- }
- }
- $unitNames = array_keys($this->measureUnits[$unitType][$system]);
- $bundleKey = 'units' . ($unitFormat === self::FORMAT_WIDTH_SHORT ? 'Short' : '');
- $unitBundle = $this->_resourceBundle[$bundleKey][$unitType][$unitNames[$position]];
- if ($unitBundle === null) {
- throw new InvalidConfigException('Current ICU data version does not contain information about unit type "' . $unitType . '" and unit measure "' . $unitNames[$position] . '". Check system requirements.');
- }
- $message = [];
- foreach ($unitBundle as $key => $value) {
- if ($key === 'dnam') {
- continue;
- }
- $message[] = "$key{{$value}}";
- }
- return $this->_unitMessages[$unitType][$system][$position] = '{n, plural, ' . implode(' ', $message) . '}';
- }
-
- private function formatNumber($value, $decimals, $maxPosition, $formatBase, $options, $textOptions)
- {
- $value = $this->normalizeNumericValue($value);
- $position = 0;
- if (is_array($formatBase)) {
- $maxPosition = count($formatBase) - 1;
- }
- do {
- if (is_array($formatBase)) {
- if (!isset($formatBase[$position + 1])) {
- break;
- }
- if (abs($value) < $formatBase[$position + 1]) {
- break;
- }
- } else {
- if (abs($value) < $formatBase) {
- break;
- }
- $value /= $formatBase;
- }
- $position++;
- } while ($position < $maxPosition + 1);
- if (is_array($formatBase) && $position !== 0) {
- $value /= $formatBase[$position];
- }
-
- if ($position === 0) {
- $decimals = 0;
- } elseif ($decimals !== null) {
- $value = round($value, $decimals);
- }
-
- $oldThousandSeparator = $this->thousandSeparator;
- $this->thousandSeparator = '';
- if ($this->_intlLoaded && !isset($options[NumberFormatter::GROUPING_USED])) {
- $options[NumberFormatter::GROUPING_USED] = false;
- }
-
- $params = [
-
-
-
- 'n' => abs($value),
-
- 'nFormatted' => $this->asDecimal($value, $decimals, $options, $textOptions),
- ];
- $this->thousandSeparator = $oldThousandSeparator;
- return [$params, $position];
- }
-
- protected function normalizeNumericValue($value)
- {
- if (empty($value)) {
- return 0;
- }
- if (is_string($value) && is_numeric($value)) {
- $value = (float) $value;
- }
- if (!is_numeric($value)) {
- throw new InvalidArgumentException("'$value' is not a numeric value.");
- }
- return $value;
- }
-
- protected function createNumberFormatter($style, $decimals = null, $options = [], $textOptions = [])
- {
- $formatter = new NumberFormatter($this->locale, $style);
-
- foreach ($this->numberFormatterTextOptions as $name => $attribute) {
- $formatter->setTextAttribute($name, $attribute);
- }
- foreach ($textOptions as $name => $attribute) {
- $formatter->setTextAttribute($name, $attribute);
- }
-
- foreach ($this->numberFormatterOptions as $name => $value) {
- $formatter->setAttribute($name, $value);
- }
- foreach ($options as $name => $value) {
- $formatter->setAttribute($name, $value);
- }
- if ($decimals !== null) {
- $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimals);
- $formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimals);
- }
-
- if ($this->decimalSeparator !== null) {
- $formatter->setSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, $this->decimalSeparator);
- }
- if ($this->thousandSeparator !== null) {
- $formatter->setSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, $this->thousandSeparator);
- $formatter->setSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, $this->thousandSeparator);
- }
- foreach ($this->numberFormatterSymbols as $name => $symbol) {
- $formatter->setSymbol($name, $symbol);
- }
- return $formatter;
- }
-
- protected function isNormalizedValueMispresented($value, $normalizedValue)
- {
- if (empty($value)) {
- $value = 0;
- }
- return (string) $normalizedValue !== $this->normalizeNumericStringValue((string) $value);
- }
-
- protected function normalizeNumericStringValue($value)
- {
- $powerPosition = strrpos($value, 'E');
- if ($powerPosition !== false) {
- $valuePart = substr($value, 0, $powerPosition);
- $powerPart = substr($value, $powerPosition + 1);
- } else {
- $powerPart = null;
- $valuePart = $value;
- }
- $separatorPosition = strrpos($valuePart, '.');
- if ($separatorPosition !== false) {
- $integerPart = substr($valuePart, 0, $separatorPosition);
- $fractionalPart = substr($valuePart, $separatorPosition + 1);
- } else {
- $integerPart = $valuePart;
- $fractionalPart = null;
- }
-
- $integerPart = preg_replace('/^\+?(-?)0*(\d+)$/', '$1$2', $integerPart);
-
- $integerPart = preg_replace('/^\+?(-?)0*$/', '${1}0', $integerPart);
- if ($fractionalPart !== null) {
-
- $fractionalPart = rtrim($fractionalPart, '0');
- if (empty($fractionalPart)) {
- $fractionalPart = $powerPart !== null ? '0' : null;
- }
- }
- $normalizedValue = $integerPart;
- if ($fractionalPart !== null) {
- $normalizedValue .= '.' . $fractionalPart;
- } elseif ($normalizedValue === '-0') {
- $normalizedValue = '0';
- }
- if ($powerPart !== null) {
- $normalizedValue .= 'E' . $powerPart;
- }
- return $normalizedValue;
- }
-
- protected function asDecimalStringFallback($value, $decimals = 2)
- {
- if (empty($value)) {
- $value = 0;
- }
- $value = $this->normalizeNumericStringValue((string) $value);
- $separatorPosition = strrpos($value, '.');
- if ($separatorPosition !== false) {
- $integerPart = substr($value, 0, $separatorPosition);
- $fractionalPart = substr($value, $separatorPosition + 1);
- } else {
- $integerPart = $value;
- $fractionalPart = null;
- }
- $decimalOutput = '';
- if ($decimals === null) {
- $decimals = 2;
- }
- $carry = 0;
- if ($decimals > 0) {
- $decimalSeparator = $this->decimalSeparator;
- if ($this->decimalSeparator === null) {
- $decimalSeparator = '.';
- }
- if ($fractionalPart === null) {
- $fractionalPart = str_repeat('0', $decimals);
- } elseif (strlen($fractionalPart) > $decimals) {
- $cursor = $decimals;
-
- if ((int) substr($fractionalPart, $cursor, 1) >= 5) {
- while (--$cursor >= 0) {
- $carry = 0;
- $oneUp = (int) substr($fractionalPart, $cursor, 1) + 1;
- if ($oneUp === 10) {
- $oneUp = 0;
- $carry = 1;
- }
- $fractionalPart = substr($fractionalPart, 0, $cursor) . $oneUp . substr($fractionalPart, $cursor + 1);
- if ($carry === 0) {
- break;
- }
- }
- }
- $fractionalPart = substr($fractionalPart, 0, $decimals);
- } elseif (strlen($fractionalPart) < $decimals) {
- $fractionalPart = str_pad($fractionalPart, $decimals, '0');
- }
- $decimalOutput .= $decimalSeparator . $fractionalPart;
- }
-
- if ($carry || ($decimals === 0 && $fractionalPart !== null && (int) substr($fractionalPart, 0, 1) >= 5)) {
- $integerPartLength = strlen($integerPart);
- $cursor = 0;
- while (++$cursor <= $integerPartLength) {
- $carry = 0;
- $oneUp = (int) substr($integerPart, -$cursor, 1) + 1;
- if ($oneUp === 10) {
- $oneUp = 0;
- $carry = 1;
- }
- $integerPart = substr($integerPart, 0, -$cursor) . $oneUp . substr($integerPart, $integerPartLength - $cursor + 1);
- if ($carry === 0) {
- break;
- }
- }
- if ($carry === 1) {
- $integerPart = '1' . $integerPart;
- }
- }
- if (strlen($integerPart) > 3) {
- $thousandSeparator = $this->thousandSeparator;
- if ($thousandSeparator === null) {
- $thousandSeparator = ',';
- }
- $integerPart = strrev(implode(',', str_split(strrev($integerPart), 3)));
- if ($thousandSeparator !== ',') {
- $integerPart = str_replace(',', $thousandSeparator, $integerPart);
- }
- }
- return $integerPart . $decimalOutput;
- }
-
- protected function asIntegerStringFallback($value)
- {
- if (empty($value)) {
- $value = 0;
- }
- $value = $this->normalizeNumericStringValue((string) $value);
- $separatorPosition = strrpos($value, '.');
- if ($separatorPosition !== false) {
- $integerPart = substr($value, 0, $separatorPosition);
- } else {
- $integerPart = $value;
- }
- return $this->asDecimalStringFallback($integerPart, 0);
- }
-
- protected function asPercentStringFallback($value, $decimals = null)
- {
- if (empty($value)) {
- $value = 0;
- }
- if ($decimals === null) {
- $decimals = 0;
- }
- $value = $this->normalizeNumericStringValue((string) $value);
- $separatorPosition = strrpos($value, '.');
- if ($separatorPosition !== false) {
- $integerPart = substr($value, 0, $separatorPosition);
- $fractionalPart = str_pad(substr($value, $separatorPosition + 1), 2, '0');
- $integerPart .= substr($fractionalPart, 0, 2);
- $fractionalPart = substr($fractionalPart, 2);
- if ($fractionalPart === '') {
- $multipliedValue = $integerPart;
- } else {
- $multipliedValue = $integerPart . '.' . $fractionalPart;
- }
- } else {
- $multipliedValue = $value . '00';
- }
- return $this->asDecimalStringFallback($multipliedValue, $decimals) . '%';
- }
-
- protected function asCurrencyStringFallback($value, $currency = null)
- {
- if ($currency === null) {
- if ($this->currencyCode === null) {
- throw new InvalidConfigException('The default currency code for the formatter is not defined.');
- }
- $currency = $this->currencyCode;
- }
- return $currency . ' ' . $this->asDecimalStringFallback($value, 2);
- }
- }
|