diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 1984e7aeda..d583c39058 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -3,8 +3,8 @@ ##isocode ko_KR ##plural 11 ##textdir ltr -##numberformat 0000경0000조0000억0000만0000 -##numberabbreviations 4=0000경0000조0000억0000만|8=0000경0000조0000억|12=0000경0000조|16=0000경 +##numberformat 0000경{NBSP}1000조{NBSP}1000억{NBSP}1000만1000 +##numberabbreviations 4=0000경{NBSP}1000조{NBSP}1000억{NBSP}1000만|8=0000경{NBSP}1000조{NBSP}1000억|12=0000경{NBSP}1000조|16=0000경 ##decimalsep . ##winlangid 0x0412 ##grflangid 0x3a diff --git a/src/language.h b/src/language.h index 3664113194..73a951d639 100644 --- a/src/language.h +++ b/src/language.h @@ -109,13 +109,23 @@ extern std::unique_ptr _current_collator; /** The number digits available in a uint64_t. */ constexpr int DIGITS_IN_UINT64_T = 20; +/** Container for the formatting of a single digit. */ +struct DigitFormat { + enum EmitBehaviour { + DEFAULT, ///< Only emit the digit when it's not zero, or there is a higher digit not zero. + EMIT_WHEN_NOTHING_IS_EMITTED_YET, ///< Emit the digit when it's not zero, or there is a higher digit not zero, or no digit has been emitted yet. + RESET_HIGHER_DIGIT, ///< Only emit the digit when it is not zero, and reset the 'there is a higher digit not zero'-flag. + }; + std::string separator; ///< The string to write after the digit, when the digit is emitted. + EmitBehaviour emit_behaviour; ///< The behaviour with respect to emitting the digit. +}; /** * Table with the text to place after each of the digits of a number. The text at index "20 - i" will be * inserted after the digit with value "10**i". So, for "normal" thousand separators, the strings at indices * 3, 6, 9, 12, 15 and 18 will be filled. For CJK the strings at indices 0, 4, 8, 12 and 16 will be filled. * @see ParseNumberFormatSeparators */ -using NumberFormatSeparators = std::array; +using NumberFormatSeparators = std::array; /** Container for the power to abbreviation mapping for formatting short numbers. */ struct NumberAbbreviation { NumberAbbreviation(int64_t threshold, NumberFormatSeparators &format) : threshold(threshold), format(format) {} diff --git a/src/strgen/strgen_base.cpp b/src/strgen/strgen_base.cpp index 4b08d05099..7a2085425a 100644 --- a/src/strgen/strgen_base.cpp +++ b/src/strgen/strgen_base.cpp @@ -994,6 +994,15 @@ static std::string ReplaceNBSP(std::string string) return string; } +static DigitFormat::EmitBehaviour GetEmitBehaviour(char c) +{ + switch (c) { + case '0': return DigitFormat::DEFAULT; + case '1': return DigitFormat::RESET_HIGHER_DIGIT; + default: NOT_REACHED(); + } +} + /** * Parse the \c NumberFormatSeparators out of the given format string, with the expected number of digits. * @@ -1008,7 +1017,7 @@ static std::string ReplaceNBSP(std::string string) * configured. The simplest solution is just defining what character to place between each of the digits, i.e what * characters separate each of the digits. These are the \c NumberFormatSeparators. * - * To define these, you simply write a string of \c length zeros and then add any characters in between at the right + * To define these, you simply write a string of \c length digits and then add any characters in between at the right * locations so the digit grouping is correct. When formatting numbers, it will start at the appropriate digit and * continue from there with separators. * @@ -1021,22 +1030,26 @@ static std::string ReplaceNBSP(std::string string) */ std::optional ParseNumberFormatSeparators(NumberFormatSeparators &separators, std::string_view format, size_t length) { + static const std::string EXPECTED_DIGIT = "01"; separators.fill({}); - size_t seen_zeros = 0; + size_t seen_digits = 0; auto it_separator = separators.rbegin(); - auto iter = format.find_last_of('0'); + auto iter = format.find_last_of(EXPECTED_DIGIT); while (iter != std::string_view::npos && it_separator != separators.rend()) { - seen_zeros++; + seen_digits++; - *it_separator = ReplaceNBSP(std::string(format.substr(iter + 1))); + it_separator->emit_behaviour = GetEmitBehaviour(format[iter]); + it_separator->separator = ReplaceNBSP(std::string(format.substr(iter + 1))); ++it_separator; format = format.substr(0, iter); - iter = format.find_last_of('0'); + iter = format.find_last_of(EXPECTED_DIGIT); } - if (seen_zeros != length) return fmt::format("Unexpected number of digits ({} vs {}) in format string: [{}]", seen_zeros, length, format); + /* Always emit the last digit when nothing is emitted. */ + separators.rbegin()->emit_behaviour = DigitFormat::EMIT_WHEN_NOTHING_IS_EMITTED_YET; + if (seen_digits != length) return fmt::format("Unexpected number of digits ({} vs {}) in format string: [{}]", seen_digits, length, format); return std::nullopt; } diff --git a/src/strings.cpp b/src/strings.cpp index 9f34191a68..64dc8c0694 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -423,15 +423,20 @@ static void FormatNumber(StringBuilder &builder, int64_t number, const NumberFor uint64_t divisor = 10000000000000000000ULL; uint64_t num = number; uint64_t tot = 0; - for (size_t i = 0; i < separators.size(); i++) { + bool emitted = false; + for (const DigitFormat &format : separators) { uint64_t quot = 0; if (num >= divisor) { quot = num / divisor; num = num % divisor; } - if ((tot |= quot) != 0 || i == separators.size() - 1) { + + if (format.emit_behaviour == DigitFormat::RESET_HIGHER_DIGIT) tot = 0; + + if ((tot |= quot) != 0 || (format.emit_behaviour == DigitFormat::EMIT_WHEN_NOTHING_IS_EMITTED_YET && !emitted)) { builder += '0' + quot; // quot is a single digit - builder += separators[i].data(); + builder += format.separator.data(); + emitted = true; } divisor /= 10;