Skip to content

Commit

Permalink
Support changing translation after instantiation (matomo-org#14155)
Browse files Browse the repository at this point in the history
  • Loading branch information
c960657 authored and diosmosis committed Mar 3, 2019
1 parent 17f95c2 commit dca17aa
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 97 deletions.
170 changes: 73 additions & 97 deletions core/NumberFormatter.php
Expand Up @@ -17,38 +17,14 @@
*/
class NumberFormatter
{
/** @var string language specific patterns for numbers */
protected $patternNumber;
/** @var Translator */
protected $translator;

/** @var string language specific pattern for percent numbers */
protected $patternPercent;
/** @var array cached patterns per language */
protected $patterns;

/** @var string language specific pattern for currency numbers */
protected $patternCurrency;

/** @var string language specific plus sign */
protected $symbolPlus;

/** @var string language specific minus sign */
protected $symbolMinus;

/** @var string language specific percent sign */
protected $symbolPercent;

/** @var string language specific symbol used as decimal separator */
protected $symbolDecimal;

/** @var string language specific symbol used as group separator */
protected $symbolGroup;

/** @var bool indicates if language uses grouping for numbers */
protected $usesGrouping;

/** @var int language specific size for primary group numbers */
protected $primaryGroupSize;

/** @var int language specific size for secondary group numbers */
protected $secondaryGroupSize;
/** @var array cached symbols per language */
protected $symbols;

/**
* Loads all required data from Intl plugin
Expand All @@ -62,30 +38,7 @@ class NumberFormatter
*/
public function __construct(Translator $translator)
{
$this->patternNumber = $translator->translate('Intl_NumberFormatNumber');
$this->patternCurrency = $translator->translate('Intl_NumberFormatCurrency');
$this->patternPercent = $translator->translate('Intl_NumberFormatPercent');
$this->symbolPlus = $translator->translate('Intl_NumberSymbolPlus');
$this->symbolMinus = $translator->translate('Intl_NumberSymbolMinus');
$this->symbolPercent = $translator->translate('Intl_NumberSymbolPercent');
$this->symbolGroup = $translator->translate('Intl_NumberSymbolGroup');
$this->symbolDecimal = $translator->translate('Intl_NumberSymbolDecimal');
}

/**
* Parses the given pattern and returns patterns for positive and negative numbers
*
* @param string $pattern
* @return array
*/
protected function parsePattern($pattern)
{
$patterns = explode(';', $pattern);
if (!isset($patterns[1])) {
// No explicit negative pattern was provided, construct it.
$patterns[1] = '-' . $patterns[0];
}
return $patterns;
$this->translator = $translator;
}

/**
Expand Down Expand Up @@ -119,14 +72,7 @@ public function format($value, $maximumFractionDigits=0, $minimumFractionDigits=
*/
public function formatNumber($value, $maximumFractionDigits=0, $minimumFractionDigits=0)
{

static $positivePattern, $negativePattern;

if (empty($positivePatter) || empty($negativePattern)) {
list($positivePattern, $negativePattern) = $this->parsePattern($this->patternNumber);
}
$negative = $this->isNegative($value);
$pattern = $negative ? $negativePattern : $positivePattern;
$pattern = $this->getPattern($value, 'Intl_NumberFormatNumber');

return $this->formatNumberWithPattern($pattern, $value, $maximumFractionDigits, $minimumFractionDigits);
}
Expand All @@ -140,19 +86,12 @@ public function formatNumber($value, $maximumFractionDigits=0, $minimumFractionD
*/
public function formatPercent($value, $maximumFractionDigits=0, $minimumFractionDigits=0)
{
static $positivePattern, $negativePattern;

if (empty($positivePatter) || empty($negativePattern)) {
list($positivePattern, $negativePattern) = $this->parsePattern($this->patternPercent);
}

$newValue = trim($value, " \0\x0B%");
$newValue = trim($value, " \0\x0B%");
if (!is_numeric($newValue)) {
return $value;
}

$negative = $this->isNegative($value);
$pattern = $negative ? $negativePattern : $positivePattern;
$pattern = $this->getPattern($value, 'Intl_NumberFormatPercent');

return $this->formatNumberWithPattern($pattern, $newValue, $maximumFractionDigits, $minimumFractionDigits);
}
Expand All @@ -170,8 +109,10 @@ public function formatPercentEvolution($value)

$formatted = self::formatPercent($value);

if($isPositiveEvolution) {
return $this->symbolPlus . $formatted;
if ($isPositiveEvolution) {
// $this->symbols has already been initialized from formatPercent().
$language = $this->translator->getCurrentLanguage();
return $this->symbols[$language]['+'] . $formatted;
}
return $formatted;
}
Expand All @@ -185,19 +126,12 @@ public function formatPercentEvolution($value)
*/
public function formatCurrency($value, $currency, $precision=2)
{
static $positivePattern, $negativePattern;

if (empty($positivePatter) || empty($negativePattern)) {
list($positivePattern, $negativePattern) = $this->parsePattern($this->patternCurrency);
}

$newValue = trim($value, " \0\x0B$currency");
$newValue = trim($value, " \0\x0B$currency");
if (!is_numeric($newValue)) {
return $value;
}

$negative = $this->isNegative($value);
$pattern = $negative ? $negativePattern : $positivePattern;
$pattern = $this->getPattern($value, 'Intl_NumberFormatCurrency');

if ($newValue == round($newValue)) {
// if no fraction digits available, don't show any
Expand All @@ -210,6 +144,43 @@ public function formatCurrency($value, $currency, $precision=2)
return str_replace('¤', $currency, $value);
}

/**
* Returns the relevant pattern for the given number.
*
* @param string $value
* @param string $translationId
* @return string
*/
protected function getPattern($value, $translationId)
{
$language = $this->translator->getCurrentLanguage();

if (!isset($this->patterns[$language][$translationId])) {
$this->patterns[$language][$translationId] = $this->parsePattern($this->translator->translate($translationId));
}

list($positivePattern, $negativePattern) = $this->patterns[$language][$translationId];
$negative = $this->isNegative($value);

return $negative ? $negativePattern : $positivePattern;
}

/**
* Parses the given pattern and returns patterns for positive and negative numbers
*
* @param string $pattern
* @return array
*/
protected function parsePattern($pattern)
{
$patterns = explode(';', $pattern);
if (!isset($patterns[1])) {
// No explicit negative pattern was provided, construct it.
$patterns[1] = '-' . $patterns[0];
}
return $patterns;
}

/**
* Formats the given number with the given pattern
*
Expand All @@ -225,15 +196,15 @@ protected function formatNumberWithPattern($pattern, $value, $maximumFractionDig
return $value;
}

$this->usesGrouping = (strpos($pattern, ',') !== false);
$usesGrouping = (strpos($pattern, ',') !== false);
// if pattern has number groups, parse them.
if ($this->usesGrouping) {
if ($usesGrouping) {
preg_match('/#+0/', $pattern, $primaryGroupMatches);
$this->primaryGroupSize = $this->secondaryGroupSize = strlen($primaryGroupMatches[0]);
$primaryGroupSize = $secondaryGroupSize = strlen($primaryGroupMatches[0]);
$numberGroups = explode(',', $pattern);
// check for distinct secondary group size.
if (count($numberGroups) > 2) {
$this->secondaryGroupSize = strlen($numberGroups[1]);
$secondaryGroupSize = strlen($numberGroups[1]);
}
}

Expand All @@ -248,14 +219,14 @@ protected function formatNumberWithPattern($pattern, $value, $maximumFractionDig
// Account for maximumFractionDigits = 0, where the number won't
// have a decimal point, and $valueParts[1] won't be set.
$minorDigits = isset($valueParts[1]) ? $valueParts[1] : '';
if ($this->usesGrouping) {
if ($usesGrouping) {
// Reverse the major digits, since they are grouped from the right.
$majorDigits = array_reverse(str_split($majorDigits));
// Group the major digits.
$groups = array();
$groups[] = array_splice($majorDigits, 0, $this->primaryGroupSize);
$groups[] = array_splice($majorDigits, 0, $primaryGroupSize);
while (!empty($majorDigits)) {
$groups[] = array_splice($majorDigits, 0, $this->secondaryGroupSize);
$groups[] = array_splice($majorDigits, 0, $secondaryGroupSize);
}
// Reverse the groups and the digits inside of them.
$groups = array_reverse($groups);
Expand Down Expand Up @@ -295,14 +266,19 @@ protected function formatNumberWithPattern($pattern, $value, $maximumFractionDig
*/
protected function replaceSymbols($value)
{
$replacements = array(
'.' => $this->symbolDecimal,
',' => $this->symbolGroup,
'+' => $this->symbolPlus,
'-' => $this->symbolMinus,
'%' => $this->symbolPercent,
);
return strtr($value, $replacements);
$language = $this->translator->getCurrentLanguage();

if (!isset($this->symbols[$language])) {
$this->symbols[$language] = array(
'.' => $this->translator->translate('Intl_NumberSymbolDecimal'),
',' => $this->translator->translate('Intl_NumberSymbolGroup'),
'+' => $this->translator->translate('Intl_NumberSymbolPlus'),
'-' => $this->translator->translate('Intl_NumberSymbolMinus'),
'%' => $this->translator->translate('Intl_NumberSymbolPercent'),
);
}

return strtr($value, $this->symbols[$language]);
}

/**
Expand Down
23 changes: 23 additions & 0 deletions tests/PHPUnit/Integration/NumberFormatterTest.php
Expand Up @@ -154,4 +154,27 @@ public function getPercentNumberEvolutionFormattingTestData()
array('en', -5000000, '-5,000,000%'),
);
}

public function testChangeLanguage()
{
$this->translator->setCurrentLanguage('en');
$numberFormatter = new NumberFormatter($this->translator);

$this->assertEquals('5,000.1', $numberFormatter->formatNumber(5000.1, 1));
$this->assertEquals('50.1%', $numberFormatter->formatPercent(50.1, 1));
$this->assertEquals('+50%', $numberFormatter->formatPercentEvolution(50));
$this->assertEquals('$5,000.1', $numberFormatter->formatCurrency(5000.1, '$'));

$this->translator->setCurrentLanguage('de');
$this->assertEquals('5.000,1', $numberFormatter->formatNumber(5000.1, 1));
$this->assertEquals('50,1 %', $numberFormatter->formatPercent(50.1, 1));
$this->assertEquals('+50 %', $numberFormatter->formatPercentEvolution(50));
$this->assertEquals('5.000,1 €', $numberFormatter->formatCurrency(5000.1, '€'));

$this->translator->setCurrentLanguage('ar');
$this->assertEquals('5٬000٫1٪؜', $numberFormatter->formatPercent(5000.1, 1));

$this->translator->setCurrentLanguage('bn');
$this->assertEquals('50,00,000', $numberFormatter->formatNumber(5000000));
}
}

0 comments on commit dca17aa

Please sign in to comment.