public function buildPriceSpecification( $localeCode, CldrLocaleInterface $cldrLocale, Currency $currency, $numberGroupingUsed, $currencyDisplayType, ?int $maxFractionDigits = null ) { $currencyPattern = $currency->getPattern($localeCode) ?: $cldrLocale->getCurrencyPattern(); $numbersSymbols = $cldrLocale->getAllNumberSymbols(); // Use positive pattern to retrieve information $positivePattern = $this->getPositivePattern($currencyPattern); return new PriceSpecification( $positivePattern, $this->getNegativePattern($currencyPattern), $this->computeNumberSymbolLists($numbersSymbols), $maxFractionDigits ?? $this->getMaxFractionDigits($positivePattern), $currency->getDecimalPrecision() ?: $this->getMinFractionDigits($positivePattern), $numberGroupingUsed && $this->getPrimaryGroupSize($positivePattern) > 1, $this->getPrimaryGroupSize($positivePattern), $this->getSecondaryGroupSize($positivePattern), $currencyDisplayType, $currency->getSymbol($localeCode), $currency->getIsoCode() ); } /** * Extract the positive pattern from a CLDR formatting pattern * Works with any formatting pattern (number, price, percentage). * * @param string $pattern * The CLDR pattern * * @return string * The extracted positive pattern */ protected function getPositivePattern($pattern) { $patterns = explode(';', $pattern); return $patterns[0] ?? ''; } /** * Extract the negative pattern from a CLDR formatting pattern * Works with any formatting pattern (number, price, percentage). * * @param string $pattern * The CLDR pattern * * @return string * The extracted negative pattern */ protected function getNegativePattern($pattern) { $patterns = explode(';', $pattern); return $patterns[1] ?? '-' . $patterns[0]; } /** * Convert a list of CLDR number symbols data into a list of NumberSymbolList objects. * * @param NumberSymbolsData[] $allNumberSymbolsData * All the CLDR number symbols data indexed by numbering system * * @return NumberSymbolList[] * * @throws LocalizationException * If passed data is invalid */ protected function computeNumberSymbolLists($allNumberSymbolsData) { $symbolsLists = []; foreach ($allNumberSymbolsData as $numberingSystem => $numberSymbolsData) { $symbolsLists[$numberingSystem] = $this->getNumberSymbolList($numberSymbolsData); } return $symbolsLists; } /** * Get a NumberSymbolList object from a CLDR NumberSymbolsData object. * * @param NumberSymbolsData $symbolsData Data that will be used to build the NumberSymbolList object * * @return NumberSymbolList An immutable NumberSymbolList object * * @throws LocalizationException If passed data is invalid */ protected function getNumberSymbolList(NumberSymbolsData $symbolsData) { return new NumberSymbolList( $symbolsData->getDecimal(), $symbolsData->getGroup(), $symbolsData->getList(), $symbolsData->getPercentSign(), $symbolsData->getMinusSign(), $symbolsData->getPlusSign(), $symbolsData->getExponential(), $symbolsData->getSuperscriptingExponent(), $symbolsData->getPerMille(), $symbolsData->getInfinity(), $symbolsData->getNan() ); } /** * Extract the min number of fraction digits from a number pattern (decimal, currency, percentage). * * @param string $pattern * The formatting pattern to use for extraction * * @return int * The min number of fraction digits to display in the final number */ protected function getMinFractionDigits($pattern) { $dotPos = (int) strpos($pattern, '.'); return substr_count($pattern, '0', $dotPos); } /** * Extract the max number of fraction digits from a number pattern (decimal, currency, percentage). * * @param string $pattern The formatting pattern to use for extraction (eg 0.00##) * * @return int The max number of fraction digits to display in the final number */ protected function getMaxFractionDigits($pattern) { $dotPos = (int) strpos($pattern, '.'); return strlen(substr($pattern, $dotPos + 1)); } /** * Get the primary digits group size from a number formatting pattern. * * @param string $pattern * The CLDR number formatting pattern (e.g.: #,##0.###) * * @return int * The primary group size of the passed pattern */ protected function getPrimaryGroupSize($pattern) { $groups = $this->getPatternGroups($pattern); $nbGroups = count($groups); return strlen($groups[$nbGroups - 1]); } /** * Get the secondary digits group size from a number formatting pattern * e.g.: with #,##0.### => No secondary group. Will return primary group size. * e.g.: with #,##,##0.### => Secondary group size is 2, primary group size is 3. * * @param string $pattern The CLDR number formatting pattern * * @return int The secondary group size of the passed pattern */ protected function getSecondaryGroupSize($pattern) { $groups = $this->getPatternGroups($pattern); $nbGroups = count($groups); if ($nbGroups > 2) { return strlen($groups[$nbGroups - 2]); } return strlen($groups[$nbGroups - 1]); } protected function getPatternGroups($pattern) { $parts = explode('.', $pattern); $integerPart = $parts[0]; return explode(',', $integerPart); } }