From c5489a7c55b65b3ec87df0e1a3964c7dd2f87e70 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 27 Jul 2020 07:05:41 +0200 Subject: [PATCH] Restore locale info from localeconv #3578 --- app/Http/Controllers/JavascriptController.php | 7 +- app/Support/Amount.php | 165 ++++++++++++++---- 2 files changed, 137 insertions(+), 35 deletions(-) diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index 2e9f9f8076..1384e541b1 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -144,8 +144,9 @@ class JavascriptController extends Controller $currency = app('amount')->getDefaultCurrency(); } - $accountingLocaleInfo = app('amount')->getAccountingLocaleInfo(); - $accountingLocaleInfo['frac_digits'] = $currency->decimal_places; + $localeconv = app('amount')->getLocaleInfo(); + $accounting = app('amount')->getJsConfig($localeconv); + $accounting['frac_digits'] = $currency->decimal_places; $pref = app('preferences')->get('language', config('firefly.default_language', 'en_US')); /** @noinspection NullPointerExceptionInspection */ $lang = $pref->data; @@ -155,7 +156,7 @@ class JavascriptController extends Controller $data = [ 'currencyCode' => $currency->code, 'currencySymbol' => $currency->symbol, - 'accountingLocaleInfo' => $accountingLocaleInfo, + 'accountingLocaleInfo' => $accounting, 'language' => $lang, 'dateRangeTitle' => $dateRange['title'], 'dateRangeConfig' => $dateRange['configuration'], diff --git a/app/Support/Amount.php b/app/Support/Amount.php index bf51878cf1..30b59fb203 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -37,6 +37,129 @@ use NumberFormatter; */ class Amount { + /** + * bool $sepBySpace is $localeconv['n_sep_by_space'] + * int $signPosn = $localeconv['n_sign_posn'] + * string $sign = $localeconv['negative_sign'] + * bool $csPrecedes = $localeconv['n_cs_precedes']. + * + * @param bool $sepBySpace + * @param int $signPosn + * @param string $sign + * @param bool $csPrecedes + * + * @return string + * + */ + public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string + { + // negative first: + $space = ' '; + + // require space between symbol and amount? + if (false === $sepBySpace) { + $space = ''; // no + } + + // there are five possible positions for the "+" or "-" sign (if it is even used) + // pos_a and pos_e could be the ( and ) symbol. + $posA = ''; // before everything + $posB = ''; // before currency symbol + $posC = ''; // after currency symbol + $posD = ''; // before amount + $posE = ''; // after everything + + // format would be (currency before amount) + // AB%sC_D%vE + // or: + // AD%v_B%sCE (amount before currency) + // the _ is the optional space + + // switch on how to display amount: + switch ($signPosn) { + default: + case 0: + // ( and ) around the whole thing + $posA = '('; + $posE = ')'; + break; + case 1: + // The sign string precedes the quantity and currency_symbol + $posA = $sign; + break; + case 2: + // The sign string succeeds the quantity and currency_symbol + $posE = $sign; + break; + case 3: + // The sign string immediately precedes the currency_symbol + $posB = $sign; + break; + case 4: + // The sign string immediately succeeds the currency_symbol + $posC = $sign; + } + + // default is amount before currency + $format = $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE; + + if ($csPrecedes) { + // alternative is currency before amount + $format = $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE; + } + + return $format; + } + + /** + * This method returns the correct format rules required by accounting.js, + * the library used to format amounts in charts. + * + * @param array $config + * + * @return array + */ + public function getJsConfig(array $config): array + { + $negative = self::getAmountJsConfig($config['n_sep_by_space'], $config['n_sign_posn'], $config['negative_sign'], $config['n_cs_precedes']); + $positive = self::getAmountJsConfig($config['p_sep_by_space'], $config['p_sign_posn'], $config['positive_sign'], $config['p_cs_precedes']); + + return [ + 'mon_decimal_point' => $config['mon_decimal_point'], + 'mon_thousands_sep' => $config['mon_thousands_sep'], + 'format' => [ + 'pos' => $positive, + 'neg' => $negative, + 'zero' => $positive, + ], + ]; + } + + /** + * @return array + */ + public function getLocaleInfo(): array + { + // get config from preference, not from translation: + $locale = app('steam')->getLocale(); + $array = app('steam')->getLocaleArray($locale); + + setlocale(LC_MONETARY, $array); + $info = localeconv(); + // correct variables + $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); + $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); + + $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); + $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); + + $fmt = new NumberFormatter( $locale, NumberFormatter::CURRENCY ); + + $info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL); + $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); + + return $info; + } /** * This method will properly format the given number, in color or "black and white", @@ -144,7 +267,7 @@ class Amount } /** - * @return \FireflyIII\Models\TransactionCurrency + * @return TransactionCurrency */ public function getDefaultCurrency(): TransactionCurrency { @@ -158,7 +281,7 @@ class Amount } /** - * @return \FireflyIII\Models\TransactionCurrency + * @return TransactionCurrency */ public function getSystemCurrency(): TransactionCurrency { @@ -172,7 +295,7 @@ class Amount /** * @param User $user * - * @return \FireflyIII\Models\TransactionCurrency + * @return TransactionCurrency */ public function getDefaultCurrencyByUser(User $user): TransactionCurrency { @@ -208,37 +331,15 @@ class Amount } /** - * @return array + * @param array $info + * @param string $field + * + * @return bool */ - public function getAccountingLocaleInfo(): array + private function getLocaleField(array $info, string $field): bool { - $locale = app('steam')->getLocale(); - - $fmt = new NumberFormatter( $locale, NumberFormatter::CURRENCY ); - - $positivePrefixed = '' !== $fmt->getAttribute(NumberFormatter::POSITIVE_PREFIX); - $negativePrefixed = '' !== $fmt->getAttribute(NumberFormatter::NEGATIVE_PREFIX); - - $formatAccounting = (int) $fmt->getAttribute(NumberFormatter::CURRENCY_ACCOUNTING); - - $positive = ($positivePrefixed) ? '%s %v' : '%v %s'; - $negative = ($negativePrefixed) ? '%s -%v' : '-%v %s'; - - if(0 !== $formatAccounting) { - $negative = '(%v %s)'; - } - - - - return [ - 'mon_decimal_point' => $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL), - 'mon_thousands_sep' => $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL), - 'format' => [ - 'pos' => $positive, - 'neg' => $negative, - 'zero' => $positive, - ] - ]; + return (is_bool($info[$field]) && true === $info[$field]) + || (is_int($info[$field]) && 1 === $info[$field]); } /**