use super::*;
use std::cmp::Ordering;
use std::str::FromStr;
use icu::collator::options::CollatorOptions;
use icu::collator::{Collator, CollatorBorrowed};
use icu::datetime::DateTimeFormatter;
use icu::datetime::fieldsets::YMD;
use icu::datetime::input::{Date, DateTime, Time};
use icu::decimal::DecimalFormatter;
use icu::decimal::input::Decimal;
use icu::decimal::options::DecimalFormatterOptions;
use icu::locale::Locale;
use icu::plurals::{PluralCategory, PluralRules, PluralRulesOptions};
use writeable::Writeable;
fn plural_category_name(category: PluralCategory) -> &'static str {
match category {
PluralCategory::Zero => "Zero",
PluralCategory::One => "One",
PluralCategory::Two => "Two",
PluralCategory::Few => "Few",
PluralCategory::Many => "Many",
PluralCategory::Other => "Other",
}
}
impl CacheI18nFormatter {
pub fn new(locale: &str) -> Result<Self, I18nError> {
let parsed = Locale::from_str(locale).map_err(|e| I18nError::InvalidLocale {
input: locale.to_string(),
reason: e.to_string(),
})?;
let decimal_formatter = DecimalFormatter::try_new(parsed.clone().into(), DecimalFormatterOptions::default())
.map_err(|e| I18nError::FormatError(e.to_string()))?;
let plural_rules = PluralRules::try_new(parsed.clone().into(), PluralRulesOptions::default())
.map_err(|e| I18nError::FormatError(e.to_string()))?;
let collator = Collator::try_new(parsed.clone().into(), CollatorOptions::default())
.map_err(|e| I18nError::FormatError(e.to_string()))?;
Ok(Self {
locale: parsed,
decimal_formatter,
plural_rules,
collator,
})
}
pub fn format_number(&self, value: f64) -> Result<String, I18nError> {
if !value.is_finite() {
return Err(I18nError::InvalidNumber {
input: value.to_string(),
reason: "value is not finite (NaN or Infinity)".into(),
});
}
let repr = format!("{value}");
let decimal = Decimal::from_str(&repr).map_err(|e| I18nError::InvalidNumber {
input: repr,
reason: e.to_string(),
})?;
let formatted = self.decimal_formatter.format(&decimal);
Ok(formatted.write_to_string().into_owned())
}
pub fn format_cache_key(&self, namespace: &str, count: u64) -> Result<String, I18nError> {
let formatted_count = self.format_number(count as f64)?;
Ok(format!("{namespace}:{formatted_count}"))
}
pub fn format_expiry(&self, year: i32, month: u8, day: u8) -> Result<String, I18nError> {
let date = Date::try_new_iso(year, month, day).map_err(|e| I18nError::DateError(e.to_string()))?;
let time = Time::try_new(0, 0, 0, 0).map_err(|e| I18nError::DateError(e.to_string()))?;
let datetime = DateTime { date, time };
let dtf = DateTimeFormatter::try_new(self.locale.clone().into(), YMD::medium())
.map_err(|e| I18nError::FormatError(e.to_string()))?;
let formatted = dtf.format(&datetime);
Ok(formatted.write_to_string().into_owned())
}
pub fn format_count(&self, count: u64) -> Result<String, I18nError> {
Ok(plural_category_name(self.plural_rules.category_for(count)).to_string())
}
pub fn compare_keys(&self, a: &str, b: &str) -> Result<Ordering, I18nError> {
Ok(self.collator.compare(a, b))
}
}