///|
priv struct NumberSymbols {
decimal : String
group : String
secondary_group : Int32
min_group_digits : Int32
}
///|
/// Formats an integer with locale-aware digit grouping, such as `1,234,567`
/// for `en`, `1.234.567` for `de`, and `12,34,56,789` for `en-IN`.
pub fn Locale::format_int(self : Locale, value : Int64) -> String {
let symbols = self.number_symbols()
let raw = value.to_string()
if raw.has_prefix("-") {
"-" + group_digits(raw[1:].to_owned(), symbols)
} else {
group_digits(raw, symbols)
}
}
///|
/// Formats a number with locale-aware grouping and decimal separators, such
/// as `1,234.5` for `en`, `1.234,5` for `de`, and `1 234,5` (narrow no-break
/// space) for `fr`. `fraction_digits` defaults to 2 and is clamped to 0..=15;
/// rounding is half up on the absolute value. The scaled magnitude must fit
/// in `Int64`. NaN and infinities render as `NaN`, `∞`, and `-∞`.
pub fn Locale::format_number(
self : Locale,
value : Double,
fraction_digits? : Int32,
) -> String {
if value.is_nan() {
return "NaN"
}
if value.is_inf() {
return if value < 0.0 { "-∞" } else { "∞" }
}
let requested = fraction_digits.unwrap_or(2)
let digits = if requested < 0 {
0
} else if requested > 15 {
15
} else {
requested
}
let mut scale = 1L
for _ in 0.. 0 {
let fraction = (scaled % scale).to_string()
text = text +
symbols.decimal +
"0".repeat(digits - fraction.char_length()) +
fraction
}
if value < 0.0 {
"-" + text
} else {
text
}
}
///|
fn Locale::number_symbols(self : Locale) -> NumberSymbols {
let language = self.language()
if language == "pt" && self.region() == Some("PT") {
return {
decimal: ",",
group: "\u{00A0}",
secondary_group: 0,
min_group_digits: 2,
}
}
if language == "hi" || (language == "en" && self.region() == Some("IN")) {
// CLDR en-IN/hi use lakh/crore grouping: #,##,##0.
return {
decimal: ".",
group: ",",
secondary_group: 2,
min_group_digits: 1,
}
}
match language {
"de" | "es" | "it" | "pt" | "nl" | "da" | "tr" | "el" | "vi" | "id" =>
dot_group_comma_decimal()
"fr" =>
{
decimal: ",",
group: "\u{202F}",
secondary_group: 0,
min_group_digits: 1,
}
"ru" | "uk" | "pl" | "cs" | "hu" | "sv" | "fi" | "no" | "nb" =>
{
decimal: ",",
group: "\u{00A0}",
secondary_group: 0,
min_group_digits: 1,
}
_ => { decimal: ".", group: ",", secondary_group: 0, min_group_digits: 1, }
}
}
///|
fn dot_group_comma_decimal() -> NumberSymbols {
{ decimal: ",", group: ".", secondary_group: 0, min_group_digits: 1, }
}
///|
fn group_digits(digits : String, symbols : NumberSymbols) -> String {
let length = digits.char_length()
if length < 3 + symbols.min_group_digits {
return digits
}
let result = StringBuilder()
for index, char in digits {
let from_right = length - index
if index > 0 &&
(
(symbols.secondary_group == 0 && from_right % 3 == 0) ||
(
symbols.secondary_group > 0 &&
(
from_right == 3 ||
(from_right > 3 && (from_right - 3) % symbols.secondary_group == 0)
)
)
) {
result.write_string(symbols.group)
}
result.write_char(char)
}
result.to_string()
}