diff --git a/src/strings.cpp b/src/strings.cpp --- a/src/strings.cpp +++ b/src/strings.cpp @@ -55,9 +55,6 @@ const LanguageMetadata *_current_languag TextDirection _current_text_dir; ///< Text direction of the currently selected language. -static NumberFormatSeparators _number_format_separators; -static NumberAbbreviations _number_abbreviations; - #ifdef WITH_ICU_I18N std::unique_ptr _current_collator; ///< Collator for the language currently in use. #endif /* WITH_ICU_I18N */ @@ -382,62 +379,53 @@ void SetDParamStr(size_t n, std::string static const char *GetDecimalSeparator() { - const char *decimal_separator = _settings_client.gui.digit_decimal_separator.c_str(); + const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str(); if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator; return decimal_separator; } -void InitializeNumberFormats() -{ - bool loaded_number_format = false; - if (!_settings_client.gui.number_format.empty()) { - auto res = ParseNumberFormatSeparators(_number_format_separators, _settings_client.gui.number_format); - if (res.has_value()) UserError("The setting 'number_format' under 'gui' is invalid: {}", *res); - loaded_number_format = !res.has_value(); - } - if (!loaded_number_format) ParseNumberFormatSeparators(_number_format_separators, _current_language->number_format); - - bool loaded_number_abbreviations = false; - if (!_settings_client.gui.number_abbreviations.empty()) { - auto res = ParseNumberAbbreviations(_number_abbreviations, _settings_client.gui.number_abbreviations); - if (res.has_value()) UserError("The setting 'number_abbreviations' under 'gui' is invalid: {}", *res); - loaded_number_abbreviations = !res.has_value(); - } - if (!loaded_number_abbreviations) ParseNumberAbbreviations(_number_abbreviations, _current_language->number_abbreviations); - _number_abbreviations.emplace_back(1, _number_format_separators); -} - /** * Format a number into a string. - * @param builder The string builder to write to. - * @param number The number to write down. - * @param separators The separator to use between each of the digits. + * @param builder the string builder to write to + * @param number the number to write down + * @param last the last element in the buffer + * @param separator the thousands-separator to use */ -static void FormatNumber(StringBuilder &builder, int64_t number, const NumberFormatSeparators &separators) +static void FormatNumber(StringBuilder &builder, int64_t number, const char *separator) { + static const int max_digits = 20; + uint64_t divisor = 10000000000000000000ULL; + int thousands_offset = (max_digits - 1) % 3; + if (number < 0) { builder += '-'; number = -number; } - uint64_t divisor = 10000000000000000000ULL; uint64_t num = number; uint64_t tot = 0; - for (size_t i = 0; i < separators.size(); i++) { + for (int i = 0; i < max_digits; i++) { uint64_t quot = 0; if (num >= divisor) { quot = num / divisor; num = num % divisor; } - if ((tot |= quot) != 0 || i == separators.size() - 1) { + if ((tot |= quot) || i == max_digits - 1) { builder += '0' + quot; // quot is a single digit - builder += separators[i].data(); + if ((i % 3) == thousands_offset && i < max_digits - 1) builder += separator; } divisor /= 10; } } +static void FormatCommaNumber(StringBuilder &builder, int64_t number) +{ + const char *separator = _settings_game.locale.digit_group_separator.c_str(); + if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator; + FormatNumber(builder, number, separator); +} + static void FormatNoCommaNumber(StringBuilder &builder, int64_t number) { fmt::format_to(builder, "{}", number); @@ -532,29 +520,35 @@ static void FormatGenericCurrency(String * The only remaining value is 1 (suffix), so everything that is not 1 */ if (spec->symbol_pos != 1) builder += spec->prefix; - NumberFormatSeparators *format = &_number_format_separators; + StringID number_str = STR_NULL; /* For huge numbers, compact the number. */ if (compact) { - auto it = _number_abbreviations.begin(); - for (;;) { - int64_t threshold = it->threshold; - ++it; - if (it == _number_abbreviations.end()) break; - - int64_t divisor = it->threshold; - threshold -= divisor / 2; - - if ((int64_t)number > threshold) { - format = &it->format; - number += divisor / 2; - number /= divisor; - break; - } + /* Take care of the thousand rounding. Having 1 000 000 k + * and 1 000 M is inconsistent, so always use 1 000 M. */ + if (number >= Money(1'000'000'000'000'000) - 500'000'000) { + number = (number + Money(500'000'000'000)) / Money(1'000'000'000'000); + number_str = STR_CURRENCY_SHORT_TERA; + } else if (number >= Money(1'000'000'000'000) - 500'000) { + number = (number + 500'000'000) / 1'000'000'000; + number_str = STR_CURRENCY_SHORT_GIGA; + } else if (number >= 1'000'000'000 - 500) { + number = (number + 500'000) / 1'000'000; + number_str = STR_CURRENCY_SHORT_MEGA; + } else if (number >= 1'000'000) { + number = (number + 500) / 1'000; + number_str = STR_CURRENCY_SHORT_KILO; } } - FormatNumber(builder, number, *format); + const char *separator = _settings_game.locale.digit_group_separator_currency.c_str(); + if (StrEmpty(separator)) separator = _currency->separator.c_str(); + if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency; + FormatNumber(builder, number, separator); + if (number_str != STR_NULL) { + auto tmp_params = ArrayStringParameters<0>(); + FormatString(builder, GetStringPtr(number_str), tmp_params); + } /* Add suffix part, following symbol_pos specification. * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix). @@ -1195,21 +1189,21 @@ static void FormatString(StringBuilder & } case SCC_COMMA: // {COMMA} - FormatNumber(builder, args.GetNextParameter(), _number_format_separators); + FormatCommaNumber(builder, args.GetNextParameter()); break; case SCC_DECIMAL: { // {DECIMAL} int64_t number = args.GetNextParameter(); int digits = args.GetNextParameter(); if (digits == 0) { - FormatNumber(builder, number, _number_format_separators); + FormatCommaNumber(builder, number); break; } int64_t divisor = PowerOfTen(digits); int64_t fractional = number % divisor; number /= divisor; - FormatNumber(builder, number, _number_format_separators); + FormatCommaNumber(builder, number); fmt::format_to(builder, "{}{:0{}d}", GetDecimalSeparator(), fractional, digits); break; } @@ -1256,7 +1250,7 @@ static void FormatString(StringBuilder & } } - FormatNumber(builder, amount, _number_format_separators); + FormatCommaNumber(builder, amount); break; } @@ -1894,8 +1888,8 @@ bool LanguagePackHeader::IsValid() const StrValid(this->name, lastof(this->name)) && StrValid(this->own_name, lastof(this->own_name)) && StrValid(this->isocode, lastof(this->isocode)) && - StrValid(this->number_format, lastof(this->number_format)) && - StrValid(this->number_abbreviations, lastof(this->number_abbreviations)) && + StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) && + StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) && StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator)); } @@ -1991,8 +1985,6 @@ bool ReadLanguagePack(const LanguageMeta } #endif /* WITH_ICU_I18N */ - InitializeNumberFormats(); - Layouter::Initialize(); /* Some lists need to be sorted again after a language change. */