1
0
Fork 0

Fix #12123: Allow not emitting zero digits in certain cases in number formats

pull/12152/head
Rubidium 2024-02-21 19:35:12 +01:00
parent 5d2e6e4efa
commit 02a8263730
4 changed files with 41 additions and 13 deletions

View File

@ -3,8 +3,8 @@
##isocode ko_KR ##isocode ko_KR
##plural 11 ##plural 11
##textdir ltr ##textdir ltr
##numberformat 0000경0000조0000억0000만0000 ##numberformat 0000경{NBSP}1000조{NBSP}1000억{NBSP}1000만1000
##numberabbreviations 4=0000경0000조0000억0000만|8=0000경0000조0000억|12=0000경0000조|16=0000경 ##numberabbreviations 4=0000경{NBSP}1000조{NBSP}1000억{NBSP}1000만|8=0000경{NBSP}1000조{NBSP}1000억|12=0000경{NBSP}1000조|16=0000경
##decimalsep . ##decimalsep .
##winlangid 0x0412 ##winlangid 0x0412
##grflangid 0x3a ##grflangid 0x3a

View File

@ -109,13 +109,23 @@ extern std::unique_ptr<icu::Collator> _current_collator;
/** The number digits available in a uint64_t. */ /** The number digits available in a uint64_t. */
constexpr int DIGITS_IN_UINT64_T = 20; 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 * 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 * 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. * 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 * @see ParseNumberFormatSeparators
*/ */
using NumberFormatSeparators = std::array<std::string, DIGITS_IN_UINT64_T>; using NumberFormatSeparators = std::array<DigitFormat, DIGITS_IN_UINT64_T>;
/** Container for the power to abbreviation mapping for formatting short numbers. */ /** Container for the power to abbreviation mapping for formatting short numbers. */
struct NumberAbbreviation { struct NumberAbbreviation {
NumberAbbreviation(int64_t threshold, NumberFormatSeparators &format) : threshold(threshold), format(format) {} NumberAbbreviation(int64_t threshold, NumberFormatSeparators &format) : threshold(threshold), format(format) {}

View File

@ -994,6 +994,15 @@ static std::string ReplaceNBSP(std::string string)
return 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. * 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 * 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. * 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 * locations so the digit grouping is correct. When formatting numbers, it will start at the appropriate digit and
* continue from there with separators. * continue from there with separators.
* *
@ -1021,22 +1030,26 @@ static std::string ReplaceNBSP(std::string string)
*/ */
std::optional<std::string> ParseNumberFormatSeparators(NumberFormatSeparators &separators, std::string_view format, size_t length) std::optional<std::string> ParseNumberFormatSeparators(NumberFormatSeparators &separators, std::string_view format, size_t length)
{ {
static const std::string EXPECTED_DIGIT = "01";
separators.fill({}); separators.fill({});
size_t seen_zeros = 0; size_t seen_digits = 0;
auto it_separator = separators.rbegin(); 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()) { 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; ++it_separator;
format = format.substr(0, iter); 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; return std::nullopt;
} }

View File

@ -423,15 +423,20 @@ static void FormatNumber(StringBuilder &builder, int64_t number, const NumberFor
uint64_t divisor = 10000000000000000000ULL; uint64_t divisor = 10000000000000000000ULL;
uint64_t num = number; uint64_t num = number;
uint64_t tot = 0; 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; uint64_t quot = 0;
if (num >= divisor) { if (num >= divisor) {
quot = num / divisor; quot = num / divisor;
num = 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 += '0' + quot; // quot is a single digit
builder += separators[i].data(); builder += format.separator.data();
emitted = true;
} }
divisor /= 10; divisor /= 10;