aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/commerceguys/intl/src/Formatter/FormatterTrait.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/commerceguys/intl/src/Formatter/FormatterTrait.php')
-rw-r--r--vendor/commerceguys/intl/src/Formatter/FormatterTrait.php215
1 files changed, 215 insertions, 0 deletions
diff --git a/vendor/commerceguys/intl/src/Formatter/FormatterTrait.php b/vendor/commerceguys/intl/src/Formatter/FormatterTrait.php
new file mode 100644
index 000000000..aa63c1f5f
--- /dev/null
+++ b/vendor/commerceguys/intl/src/Formatter/FormatterTrait.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace CommerceGuys\Intl\Formatter;
+
+use CommerceGuys\Intl\Calculator;
+use CommerceGuys\Intl\Exception\InvalidArgumentException;
+use CommerceGuys\Intl\NumberFormat\NumberFormat;
+
+trait FormatterTrait
+{
+ /**
+ * The parsed number patterns, keyed by locale and style.
+ *
+ * @var ParsedPattern[]
+ */
+ protected $parsedPatterns = [];
+
+ /**
+ * Localized digits.
+ *
+ * @var array
+ */
+ protected $digits = [
+ NumberFormat::NUMBERING_SYSTEM_ARABIC => [
+ 0 => '٠', 1 => '١', 2 => '٢', 3 => '٣', 4 => '٤',
+ 5 => '٥', 6 => '٦', 7 => '٧', 8 => '٨', 9 => '٩',
+ ],
+ NumberFormat::NUMBERING_SYSTEM_ARABIC_EXTENDED => [
+ 0 => '۰', 1 => '۱', 2 => '۲', 3 => '۳', 4 => '۴',
+ 5 => '۵', 6 => '۶', 7 => '۷', 8 => '۸', 9 => '۹',
+ ],
+ NumberFormat::NUMBERING_SYSTEM_BENGALI => [
+ 0 => '০', 1 => '১', 2 => '২', 3 => '৩', 4 => '৪',
+ 5 => '৫', 6 => '৬', 7 => '৭', 8 => '৮', 9 => '৯',
+ ],
+ NumberFormat::NUMBERING_SYSTEM_DEVANAGARI => [
+ 0 => '०', 1 => '१', 2 => '२', 3 => '३', 4 => '४',
+ 5 => '५', 6 => '६', 7 => '७', 8 => '८', 9 => '९',
+ ],
+ ];
+
+ /**
+ * Formats the number according to the number format.
+ *
+ * @param string $number The number.
+ * @param NumberFormat $numberFormat The number format.
+ *
+ * @return string The formatted number.
+ */
+ protected function formatNumber($number, NumberFormat $numberFormat, array $options = [])
+ {
+ $parsedPattern = $this->getParsedPattern($numberFormat, $options['style']);
+ // Start by rounding the number, if rounding is enabled.
+ if (is_int($options['rounding_mode'])) {
+ $number = Calculator::round($number, $options['maximum_fraction_digits'], $options['rounding_mode']);
+ }
+ $negative = (Calculator::compare('0', $number, 12) == 1);
+ // Ensure that the value is positive and has the right number of digits.
+ $signMultiplier = $negative ? '-1' : '1';
+ $number = bcdiv($number, $signMultiplier, $options['maximum_fraction_digits']);
+ // Split the number into major and minor digits.
+ $numberParts = explode('.', $number);
+ $majorDigits = $numberParts[0];
+ // Account for maximumFractionDigits = 0, where the number won't
+ // have a decimal point, and $numberParts[1] won't be set.
+ $minorDigits = isset($numberParts[1]) ? $numberParts[1] : '';
+
+ if ($options['use_grouping'] && $parsedPattern->isGroupingUsed()) {
+ // Reverse the major digits, since they are grouped from the right.
+ $majorDigits = array_reverse(str_split($majorDigits));
+ // Group the major digits.
+ $groups = [];
+ $groups[] = array_splice($majorDigits, 0, $parsedPattern->getPrimaryGroupSize());
+ while (!empty($majorDigits)) {
+ $groups[] = array_splice($majorDigits, 0, $parsedPattern->getSecondaryGroupSize());
+ }
+ // Reverse the groups and the digits inside of them.
+ $groups = array_reverse($groups);
+ foreach ($groups as &$group) {
+ $group = implode(array_reverse($group));
+ }
+ // Reconstruct the major digits.
+ $majorDigits = implode(',', $groups);
+ }
+
+ if ($options['minimum_fraction_digits'] < $options['maximum_fraction_digits']) {
+ // Strip any trailing zeroes.
+ $minorDigits = rtrim($minorDigits, '0');
+ if (strlen($minorDigits) < $options['minimum_fraction_digits']) {
+ // Now there are too few digits, re-add trailing zeroes
+ // until the desired length is reached.
+ $neededZeroes = $options['minimum_fraction_digits'] - strlen($minorDigits);
+ $minorDigits .= str_repeat('0', $neededZeroes);
+ }
+ }
+
+ // Assemble the final number and insert it into the pattern.
+ $number = strlen($minorDigits) ? $majorDigits . '.' . $minorDigits : $majorDigits;
+ $pattern = $negative ? $parsedPattern->getNegativePattern() : $parsedPattern->getPositivePattern();
+ $number = preg_replace('/#(?:[\.,]#+)*0(?:[,\.][0#]+)*/', $number, $pattern);
+ $number = $this->localizeNumber($number, $numberFormat);
+
+ return $number;
+ }
+
+ /**
+ * Localizes the number according to the number format.
+ *
+ * Both the digits and the symbols are replaced
+ * with their localized equivalents.
+ *
+ * @param string $number The number.
+ * @param NumberFormat $numberFormat The number format.
+ *
+ * @return string The localized number.
+ *
+ * @see http://cldr.unicode.org/translation/number-symbols
+ */
+ protected function localizeNumber($number, NumberFormat $numberFormat)
+ {
+ // Localize digits.
+ $numberingSystem = $numberFormat->getNumberingSystem();
+ if (isset($this->digits[$numberingSystem])) {
+ $number = strtr($number, $this->digits[$numberingSystem]);
+ }
+ // Localize symbols.
+ $replacements = [
+ '.' => $numberFormat->getDecimalSeparator(),
+ ',' => $numberFormat->getGroupingSeparator(),
+ '+' => $numberFormat->getPlusSign(),
+ '-' => $numberFormat->getMinusSign(),
+ '%' => $numberFormat->getPercentSign(),
+ ];
+ $number = strtr($number, $replacements);
+
+ return $number;
+ }
+
+ /**
+ * Parses the number according to the number format.
+ *
+ * Both the digits and the symbols are replaced
+ * with their non-localized equivalents.
+ *
+ * @param string $number The number.
+ * @param NumberFormat $numberFormat The number format.
+ *
+ * @return string The localized number.
+ */
+ protected function parseNumber($number, NumberFormat $numberFormat)
+ {
+ $replacements = [
+ $numberFormat->getGroupingSeparator() => '',
+ // Convert the localized symbols back to their original form.
+ $numberFormat->getDecimalSeparator() => '.',
+ $numberFormat->getPlusSign() => '+',
+ $numberFormat->getMinusSign() => '-',
+ $numberFormat->getPercentSign() => '%',
+
+ // Strip whitespace (spaces and non-breaking spaces).
+ ' ' => '',
+ chr(0xC2) . chr(0xA0) => '',
+ ];
+ $numberingSystem = $numberFormat->getNumberingSystem();
+ if (isset($this->digits[$numberingSystem])) {
+ // Convert the localized digits back to latin.
+ $replacements += array_flip($this->digits[$numberingSystem]);
+ }
+ $number = strtr($number, $replacements);
+
+ // Convert the accounting format for negative numbers.
+ if (substr($number, 0, 1) == '(' && substr($number, -1, 1) == ')') {
+ $number = '-' . str_replace(['(', ')'], '', $number);
+ }
+ // Convert percentages back to their decimal form.
+ if (strpos($number, '%') !== false) {
+ $number = str_replace('%', '', $number);
+ $number = Calculator::divide($number, '100');
+ }
+
+ return is_numeric($number) ? $number : false;
+ }
+
+ /**
+ * Gets the pattern for the provided number format.
+ *
+ * @param NumberFormat $numberFormat The number format.
+ * @param string $style The formatter style.
+ *
+ * @return ParsedPattern
+ */
+ protected function getParsedPattern(NumberFormat $numberFormat, $style)
+ {
+ $locale = $numberFormat->getLocale();
+ if (!isset($this->parsedPatterns[$locale][$style])) {
+ $availablePatterns = $this->getAvailablePatterns($numberFormat);
+ if (!isset($availablePatterns[$style])) {
+ throw new InvalidArgumentException(sprintf('Unrecognized style "%s".', $style));
+ }
+
+ $this->parsedPatterns[$locale][$style] = new ParsedPattern($availablePatterns[$style]);
+ }
+
+ return $this->parsedPatterns[$locale][$style];
+ }
+
+ /**
+ * Gets the available patterns for the provided number format.
+ *
+ * @param NumberFormat $numberFormat The number format.
+ *
+ * @return string[] The patterns, keyed by style.
+ */
+ abstract protected function getAvailablePatterns(NumberFormat $numberFormat);
+}