trait_kit/i18n/
i18n_impl.rs1use std::cmp::Ordering;
6use std::str::FromStr;
7
8use icu::collator::Collator;
9use icu::collator::options::CollatorOptions;
10use icu::datetime::DateTimeFormatter;
11use icu::datetime::fieldsets::YMD;
12use icu::datetime::input::{Date, DateTime, Time};
13use icu::decimal::DecimalFormatter;
14use icu::decimal::input::Decimal;
15use icu::decimal::options::DecimalFormatterOptions;
16use icu::locale::Locale;
17use icu::plurals::{PluralCategory, PluralRules, PluralRulesOptions};
18use writeable::Writeable;
19
20use super::{I18nError, I18nFormatter};
21
22impl I18nFormatter {
23 pub fn new(locale: &str) -> Result<Self, I18nError> {
29 let parsed = Locale::from_str(locale).map_err(|e| I18nError::InvalidLocale {
30 input: locale.to_string(),
31 reason: e.to_string(),
32 })?;
33
34 let decimal_formatter =
35 DecimalFormatter::try_new(parsed.clone().into(), DecimalFormatterOptions::default())
36 .map_err(|e| I18nError::FormatError(e.to_string()))?;
37
38 let plural_rules =
39 PluralRules::try_new(parsed.clone().into(), PluralRulesOptions::default())
40 .map_err(|e| I18nError::FormatError(e.to_string()))?;
41
42 let collator = Collator::try_new(parsed.clone().into(), CollatorOptions::default())
43 .map_err(|e| I18nError::FormatError(e.to_string()))?;
44
45 Ok(Self {
46 locale: parsed,
47 decimal_formatter,
48 plural_rules,
49 collator,
50 })
51 }
52
53 pub fn format_number(&self, value: f64) -> Result<String, I18nError> {
60 if !value.is_finite() {
61 return Err(I18nError::InvalidNumber {
62 input: value.to_string(),
63 reason: "value is not finite (NaN or Infinity)".into(),
64 });
65 }
66 let repr = format!("{value:.20}");
70 let repr = repr.trim_end_matches('0');
72 let repr = repr.trim_end_matches('.');
73 let decimal = Decimal::from_str(repr).map_err(|e| I18nError::InvalidNumber {
74 input: repr.to_string(),
75 reason: e.to_string(),
76 })?;
77 let formatted = self.decimal_formatter.format(&decimal);
78 Ok(formatted.write_to_string().to_string())
79 }
80
81 pub fn format_date(&self, year: i32, month: u8, day: u8) -> Result<String, I18nError> {
88 let date =
89 Date::try_new_iso(year, month, day).map_err(|e| I18nError::DateError(e.to_string()))?;
90 let time = Time::try_new(0, 0, 0, 0).map_err(|e| I18nError::DateError(e.to_string()))?;
91 let datetime = DateTime { date, time };
92
93 let dtf = DateTimeFormatter::try_new(self.locale.clone().into(), YMD::medium())
94 .map_err(|e| I18nError::FormatError(e.to_string()))?;
95 let formatted = dtf.format(&datetime);
96 Ok(formatted.write_to_string().to_string())
97 }
98
99 pub fn plural_category(&self, count: u64) -> Result<PluralCategory, I18nError> {
105 Ok(self.plural_rules.category_for(count))
106 }
107
108 pub fn compare(&self, a: &str, b: &str) -> Result<Ordering, I18nError> {
114 Ok(self.collator.compare(a, b))
115 }
116}