pub mod decimal;
pub mod layout;
pub mod list;
pub mod path;
pub mod string;
pub mod style;
pub mod time;
mod macros;
use std::sync::OnceLock;
use icu_decimal::options::DecimalFormatterOptions;
use icu_decimal::{DecimalFormatter, DecimalFormatterPreferences};
use icu_experimental::relativetime::{
RelativeTimeFormatter, RelativeTimeFormatterOptions, RelativeTimeFormatterPreferences,
};
use icu_list::options::ListFormatterOptions;
use icu_list::{ListFormatter, ListFormatterPreferences};
use icu_locale::{Locale, locale};
use icu_plurals::{PluralRuleType, PluralRules, PluralRulesOptions, PluralRulesPreferences};
pub mod macro_prelude {
pub use icu_locale;
pub use icu_plurals;
}
pub trait Localize {
fn canonical_locale(&self) -> Locale {
locale!("en-US")
}
fn available_locales(&self) -> Vec<Locale> {
vec![self.canonical_locale()]
}
fn localize(&self, context: &Context, buffer: &mut String);
}
pub struct Context {
pub locale: Locale,
pub use_colors: bool,
decimal_formatter: OnceLock<DecimalFormatter>,
list_formatters: [OnceLock<ListFormatter>; 3],
plural_rules: [OnceLock<PluralRules>; 2],
relative_time_formatters: [OnceLock<RelativeTimeFormatter>; 7],
}
impl Context {
#[must_use]
pub const fn new(locale: Locale, use_color: bool) -> Self {
Self {
locale,
use_colors: use_color,
decimal_formatter: OnceLock::new(),
list_formatters: [const { OnceLock::new() }; 3],
plural_rules: [const { OnceLock::new() }; 2],
relative_time_formatters: [const { OnceLock::new() }; 7],
}
}
pub fn decimal_formatter(&self) -> &DecimalFormatter {
self.decimal_formatter.get_or_init(|| {
DecimalFormatter::try_new(
DecimalFormatterPreferences::from(&self.locale),
DecimalFormatterOptions::default(),
)
.unwrap()
})
}
pub fn list_formatter(&self, list_type: list::ListType) -> &ListFormatter {
let index = match list_type {
list::ListType::And => 0,
list::ListType::Or => 1,
list::ListType::Unit => 2,
};
self.list_formatters[index].get_or_init(|| {
let preferences = ListFormatterPreferences::from(&self.locale);
let options = ListFormatterOptions::default();
match list_type {
list::ListType::And => ListFormatter::try_new_and(preferences, options),
list::ListType::Or => ListFormatter::try_new_or(preferences, options),
list::ListType::Unit => ListFormatter::try_new_unit(preferences, options),
}
.unwrap()
})
}
pub fn plural_rule(&self, rule_type: PluralRuleType) -> Option<&PluralRules> {
let index = match rule_type {
PluralRuleType::Cardinal => 0,
PluralRuleType::Ordinal => 1,
_ => return None,
};
let plural_rules = self.plural_rules[index].get_or_init(|| {
PluralRules::try_new(
PluralRulesPreferences::from(&self.locale),
PluralRulesOptions::default().with_type(rule_type),
)
.unwrap()
});
Some(plural_rules)
}
pub fn relative_time_formatter(&self, unit: jiff::Unit) -> Option<&RelativeTimeFormatter> {
let index = match unit {
jiff::Unit::Year => 0,
jiff::Unit::Month => 1,
jiff::Unit::Week => 2,
jiff::Unit::Day => 3,
jiff::Unit::Hour => 4,
jiff::Unit::Minute => 5,
jiff::Unit::Second => 6,
_ => return None,
};
let formatter = self.relative_time_formatters[index].get_or_init(|| {
const OPTIONS: RelativeTimeFormatterOptions = RelativeTimeFormatterOptions {
numeric: icu_experimental::relativetime::options::Numeric::Auto,
};
let preferences = RelativeTimeFormatterPreferences::from(&self.locale);
match unit {
jiff::Unit::Year => RelativeTimeFormatter::try_new_long_year(preferences, OPTIONS),
jiff::Unit::Month => {
RelativeTimeFormatter::try_new_long_month(preferences, OPTIONS)
}
jiff::Unit::Week => RelativeTimeFormatter::try_new_long_week(preferences, OPTIONS),
jiff::Unit::Day => RelativeTimeFormatter::try_new_long_day(preferences, OPTIONS),
jiff::Unit::Hour => RelativeTimeFormatter::try_new_long_hour(preferences, OPTIONS),
jiff::Unit::Minute => {
RelativeTimeFormatter::try_new_long_minute(preferences, OPTIONS)
}
jiff::Unit::Second => {
RelativeTimeFormatter::try_new_long_second(preferences, OPTIONS)
}
_ => unreachable!(),
}
.unwrap()
});
Some(formatter)
}
}