Changeset - r28770:f692bd3cca47
[Not reviewed]
master
0 1 0
Rubidium - 10 months ago 2024-02-08 20:35:33
rubidium@openttd.org
Feature: Fully customisable number format per translation
1 file changed with 21 insertions and 26 deletions:
0 comments (0 inline, 0 general)
src/strings.cpp
Show inline comments
 
@@ -52,12 +52,14 @@
 
std::string _config_language_file;                ///< The file (name) stored in the configuration.
 
LanguageList _languages;                          ///< The actual list of language meta data.
 
const LanguageMetadata *_current_language = nullptr; ///< The currently loaded language.
 

	
 
TextDirection _current_text_dir; ///< Text direction of the currently selected language.
 

	
 
static NumberFormatSeparators _number_format_separators;
 

	
 
#ifdef WITH_ICU_I18N
 
std::unique_ptr<icu::Collator> _current_collator;    ///< Collator for the language currently in use.
 
#endif /* WITH_ICU_I18N */
 

	
 
ArrayStringParameters<20> _global_string_params;
 

	
 
@@ -381,54 +383,48 @@ static const char *GetDecimalSeparator()
 
{
 
	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()
 
{
 
	ParseNumberFormatSeparators(_number_format_separators, _current_language->number_format);
 
}
 

	
 
/**
 
 * Format a number into a string.
 
 * @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
 
 * @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.
 
 */
 
static void FormatNumber(StringBuilder &builder, int64_t number, const char *separator)
 
static void FormatNumber(StringBuilder &builder, int64_t number, const NumberFormatSeparators &separators)
 
{
 
	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 (int i = 0; i < max_digits; i++) {
 
	for (size_t i = 0; i < separators.size(); i++) {
 
		uint64_t quot = 0;
 
		if (num >= divisor) {
 
			quot = num / divisor;
 
			num = num % divisor;
 
		}
 
		if ((tot |= quot) || i == max_digits - 1) {
 
		if ((tot |= quot) != 0 || i == separators.size() - 1) {
 
			builder += '0' + quot; // quot is a single digit
 
			if ((i % 3) == thousands_offset && i < max_digits - 1) builder += separator;
 
			builder += separators[i].data();
 
		}
 

	
 
		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);
 
}
 

	
 
static void FormatZerofillNumber(StringBuilder &builder, int64_t number, int count)
 
@@ -538,16 +534,13 @@ static void FormatGenericCurrency(String
 
		} else if (number >= 1'000'000) {
 
			number = (number + 500) / 1'000;
 
			number_str = STR_CURRENCY_SHORT_KILO;
 
		}
 
	}
 

	
 
	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);
 
	FormatNumber(builder, number, _number_format_separators);
 
	if (number_str != STR_NULL) {
 
		auto tmp_params = ArrayStringParameters<0>();
 
		FormatString(builder, GetStringPtr(number_str), tmp_params);
 
	}
 

	
 
	/* Add suffix part, following symbol_pos specification.
 
@@ -1186,23 +1179,23 @@ static void FormatString(StringBuilder &
 
					}
 
					next_substr_case_index = 0;
 
					break;
 
				}
 

	
 
				case SCC_COMMA: // {COMMA}
 
					FormatCommaNumber(builder, args.GetNextParameter<int64_t>());
 
					FormatNumber(builder, args.GetNextParameter<int64_t>(), _number_format_separators);
 
					break;
 

	
 
				case SCC_DECIMAL: { // {DECIMAL}
 
					int64_t number = args.GetNextParameter<int64_t>();
 
					int digits = args.GetNextParameter<int>();
 

	
 
					int64_t divisor = PowerOfTen(digits);
 
					int64_t fractional = number % divisor;
 
					number /= divisor;
 
					FormatCommaNumber(builder, number);
 
					FormatNumber(builder, number, _number_format_separators);
 
					fmt::format_to(builder, "{}{:0{}d}", GetDecimalSeparator(), fractional, digits);
 
					break;
 
				}
 

	
 
				case SCC_NUM: // {NUM}
 
					FormatNoCommaNumber(builder, args.GetNextParameter<int64_t>());
 
@@ -1243,13 +1236,13 @@ static void FormatString(StringBuilder &
 
						default: {
 
							amount = args.GetNextParameter<int64_t>();
 
							break;
 
						}
 
					}
 

	
 
					FormatCommaNumber(builder, amount);
 
					FormatNumber(builder, amount, _number_format_separators);
 
					break;
 
				}
 

	
 
				case SCC_CARGO_SHORT: { // {CARGO_SHORT}
 
					/* Short description of cargotypes. Layout:
 
					 * param 1: cargo type
 
@@ -1980,12 +1973,14 @@ bool ReadLanguagePack(const LanguageMeta
 
	/* Avoid using the collator if it is not correctly set. */
 
	if (U_FAILURE(status)) {
 
		_current_collator.reset();
 
	}
 
#endif /* WITH_ICU_I18N */
 

	
 
	InitializeNumberFormats();
 

	
 
	Layouter::Initialize();
 

	
 
	/* Some lists need to be sorted again after a language change. */
 
	ReconsiderGameScriptLanguage();
 
	InitializeSortedCargoSpecs();
 
	SortIndustryTypes();
0 comments (0 inline, 0 general)