|
@@ -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<icu::Collator> _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<int64_t>(), _number_format_separators);
|
|
|
FormatCommaNumber(builder, args.GetNextParameter<int64_t>());
|
|
|
break;
|
|
|
|
|
|
case SCC_DECIMAL: { // {DECIMAL}
|
|
|
int64_t number = args.GetNextParameter<int64_t>();
|
|
|
int digits = args.GetNextParameter<int>();
|
|
|
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. */
|