Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for more missing MessageFormat types in our fallback code #8106

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 94 additions & 8 deletions Sources/Localization/MessageFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ public static function formatMessage(string $message, array $args = []): string
$rest = trim($rest);

switch ($fmt_type) {
// @todo Implement 'spellout' properly.
case 'spellout':
case 'number':
if (str_starts_with($rest, '::')) {
$final .= self::applyNumberSkeleton($args[$arg_name] ?? 0, ltrim(substr($rest, 2)));
Expand All @@ -168,9 +170,60 @@ public static function formatMessage(string $message, array $args = []): string
$final .= self::applyNumberSkeleton(round(($args[$arg_name] ?? 0), 2), 'percent scale/100');
break;

// @todo
// case 'currency':
// break;
// For this one, we need to figure out a default currency to use...
case 'currency':
// If paid subscriptions are set up, use that currency.
if (isset(Config::$modSettings['paid_currency'])) {
$currency = Config::$modSettings['paid_currency'];
}
// Try to guess the currency based on country.
else {
require_once Config::$sourcedir . '/Unicode/Currencies.php';
$country_currencies = country_currencies();

// If the admin wants to prioritize a certain country, use that.
if (!empty(Config::$modSettings['timezone_priority_countries'])) {
$cc = explode(',', Config::$modSettings['timezone_priority_countries'])[0];
}
// Guess based on the locale.
else {
[$lang, $cc] = explode('_', Lang::$txt['lang_locale']);

if (!isset($country_currencies[$cc])) {
switch ($lang) {
case 'cs':
$cc = 'CZ';
break;

case 'de':
$cc = 'DE';
break;

case 'en':
$cc = 'US';
break;

case 'sr':
$cc = 'SR';
break;

case 'zh':
$cc = 'CN';
break;

default:
$cc = '';
break;
}
}
}

$currency = $country_currencies[$cc] ?? 'XXX';
}

$final .= self::applyNumberSkeleton($args[$arg_name] ?? 0, 'currency/' . $currency);

break;

default:
$skeleton = is_int($args[$arg_name] + 0) ? '' : '.000';
Expand Down Expand Up @@ -262,9 +315,21 @@ public static function formatMessage(string $message, array $args = []): string
$final .= $args[$arg_name]->format($fmt);
break;

// @todo
// case 'duration':
// break;
case 'duration':
// Input is a number of seconds.
$args[$arg_name] = (int) $args[$arg_name];

$seconds = sprintf('%02d', $args[$arg_name] % 60);
$minutes = sprintf('%02d', $args[$arg_name] >= 60 ? (int) ($args[$arg_name] / 60) % 60 : 0);
$hours = sprintf('%02d', $args[$arg_name] >= 3600 ? (int) ($args[$arg_name] / 3600) : 0);

if ($hours === '00' && $minutes === '00') {
$final .= Lang::getTxt('number_of_seconds', [$seconds]);
} else {
$final .= ltrim(implode(':', [$hours, $minutes, $seconds]), '0:');
}

break;

case 'plural':
if (str_starts_with($rest, 'offset:')) {
Expand Down Expand Up @@ -341,7 +406,11 @@ public static function formatMessage(string $message, array $args = []): string
break;

default:
$final .= $rest;
if (is_scalar($args[$arg_name]) || $args[$arg_name] instanceof \Stringable) {
$final .= (string) $args[$arg_name];
} else {
$final .= $part;
}
break;
}
}
Expand Down Expand Up @@ -718,7 +787,24 @@ protected static function applyNumberSkeleton(int|float|string $number, string $
break;

case 'currency':
$post_processing[] = fn ($number) => (in_array(substr($number, 0, 1), ['-', '+']) ? substr($number, 0, 1) : '') . strtr(Lang::$txt['currency_format'], ['{0}' => in_array(substr($number, 0, 1), ['-', '+']) ? substr($number, 1) : $number, '¤' => self::CURRENCY_SYMBOLS[$options[0]] ?? $options[0] . "\u{A0}"]);
require_once Config::$sourcedir . '/Unicode/Currencies.php';

$currencies = currencies();

if (!isset($currencies[$options[0]])) {
$options[0] = 'DEFAULT';
}

$currency = $currencies[$options[0]];

if ($currency['digits'] === 0) {
$number = strval(intval($number));
} else {
$number = sprintf('%0.' . $currency['digits'] . 'F', $number);
}

$post_processing[] = fn ($number) => (in_array(substr($number, 0, 1), ['-', '+']) ? substr($number, 0, 1) : '') . strtr(Lang::$txt['currency_format'], ['{0}' => in_array(substr($number, 0, 1), ['-', '+']) ? substr($number, 1) : $number, '¤' => self::CURRENCY_SYMBOLS[$options[0]] ?? ($options[0] === 'DEFAULT' ? '¤' : $options[0] . "\u{A0}")]);

break;

case 'group-auto':
Expand Down
89 changes: 89 additions & 0 deletions Sources/Tasks/UpdateUnicode.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,38 @@ class UpdateUnicode extends BackgroundTask
],
'data' => [],
],
'currencies' => [
'file' => 'Currencies.php',
'key_type' => 'string',
'val_type' => 'array',
'desc' => [
'Helper function for SMF\Localization\MessageFormatter::formatMessage.',
'',
'Rules compiled from:',
'https://github.com/unicode-org/cldr-json/blob/main/cldr-json/cldr-core/supplemental/currencyData.json',
],
'return' => [
'type' => 'array',
'desc' => 'Information about different currencies',
],
'data' => [],
],
'country_currencies' => [
'file' => 'Currencies.php',
'key_type' => 'string',
'val_type' => 'array',
'desc' => [
'Helper function for SMF\Localization\MessageFormatter::formatMessage.',
'',
'Rules compiled from:',
'https://github.com/unicode-org/cldr-json/blob/main/cldr-json/cldr-core/supplemental/currencyData.json',
],
'return' => [
'type' => 'array',
'desc' => 'Information about currencies used in different countries',
],
'data' => [],
],
];

/**
Expand Down Expand Up @@ -430,6 +462,7 @@ class UpdateUnicode extends BackgroundTask
self::DATA_URL_CLDR => [
'cldr-core/supplemental/plurals.json',
'cldr-core/supplemental/ordinals.json',
'cldr-core/supplemental/currencyData.json',
],
];

Expand Down Expand Up @@ -577,6 +610,7 @@ public function execute(): bool
* CLDR data *
*************/
$success = $this->build_plurals() & $success;
$success = $this->build_currencies() & $success;

$this->export_funcs_to_file();

Expand Down Expand Up @@ -2044,6 +2078,61 @@ private function build_plurals(): bool

return true;
}

/**
* Builds information about different currencies.
*/
private function build_currencies(): bool
{
$sourcefile = 'cldr-core/supplemental/currencyData.json';

$local_file = $this->fetch_unicode_file($sourcefile, self::DATA_URL_CLDR);

if (empty($local_file)) {
return false;
}

$data = json_decode(file_get_contents($local_file), true);

foreach ($data['supplemental']['currencyData']['region'] as $cc => $region_currency_info) {
foreach ($region_currency_info as $region_currencies) {
foreach ($region_currencies as $currency_code => $currency_info) {
if (!empty($currency_info['_to'])) {
continue;
}

if (isset($currency_info['_tender']) && $currency_info['_tender'] === 'false') {
continue;
}

$this->funcs['country_currencies']['data'][$cc][] = var_export($currency_code, true);

// Set these to the default for now.
$this->funcs['currencies']['data'][$currency_code]['digits'] = 2;
$this->funcs['currencies']['data'][$currency_code]['rounding'] = 0;
}
}
}

foreach ($data['supplemental']['currencyData']['fractions'] as $currency_code => $fractions) {
if (!isset($this->funcs['currencies']['data'][$currency_code]) && $currency_code !== 'DEFAULT') {
continue;
}

foreach (['_digits', '_rounding', '_cashDigits', '_cashRounding'] as $key) {
if (!isset($fractions[$key])) {
continue;
}

$this->funcs['currencies']['data'][$currency_code][substr($key, 1)] = $fractions[$key];
}
}

ksort($this->funcs['currencies']['data']);
ksort($this->funcs['country_currencies']['data']);

return true;
}
}

?>
Loading
Loading