use icu_calendar::Date;
use icu_calendar::cal::{Buddhist, Ethiopian, Iso, Persian};
use icu_datetime::DateTimeFormatter;
use icu_datetime::fieldsets;
use icu_locale::Locale;
use jiff::civil::Date as JiffDate;
use jiff_icu::ConvertFrom;
use std::sync::OnceLock;
use crate::i18n::get_locale_from_env;
pub fn get_time_locale() -> &'static (Locale, super::UEncoding) {
static TIME_LOCALE: OnceLock<(Locale, super::UEncoding)> = OnceLock::new();
TIME_LOCALE.get_or_init(|| get_locale_from_env("LC_TIME"))
}
pub fn should_use_icu_locale() -> bool {
use icu_locale::locale;
let (locale, _encoding) = get_time_locale();
*locale != locale!("und")
}
pub fn get_locale_calendar_type(locale: &Locale) -> CalendarType {
let locale_str = locale.to_string();
match locale_str.as_str() {
s if s.starts_with("th") => CalendarType::Buddhist,
s if s.starts_with("fa") => CalendarType::Persian,
s if s.starts_with("am") => CalendarType::Ethiopian,
_ => CalendarType::Gregorian,
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CalendarType {
Gregorian,
Buddhist,
Persian,
Ethiopian,
}
pub fn localize_format_string(format: &str, date: JiffDate) -> String {
const PERCENT_PLACEHOLDER: &str = "\x00\x00";
let (locale, _) = get_time_locale();
let iso_date = Date::<Iso>::convert_from(date);
let mut fmt = format.replace("%%", PERCENT_PLACEHOLDER);
let calendar_type = get_locale_calendar_type(locale);
if calendar_type != CalendarType::Gregorian {
let (cal_year, cal_month, cal_day) = match calendar_type {
CalendarType::Buddhist => {
let d = iso_date.to_calendar(Buddhist);
(d.extended_year(), d.month().ordinal, d.day_of_month().0)
}
CalendarType::Persian => {
let d = iso_date.to_calendar(Persian);
(d.extended_year(), d.month().ordinal, d.day_of_month().0)
}
CalendarType::Ethiopian => {
let d = iso_date.to_calendar(Ethiopian::new());
(d.extended_year(), d.month().ordinal, d.day_of_month().0)
}
CalendarType::Gregorian => unreachable!(),
};
fmt = fmt
.replace("%Y", &cal_year.to_string())
.replace("%m", &format!("{cal_month:02}"))
.replace("%d", &format!("{cal_day:02}"))
.replace("%e", &format!("{cal_day:2}"));
}
let locale_prefs = locale.clone().into();
if fmt.contains("%B") {
if let Ok(f) = DateTimeFormatter::try_new(locale_prefs, fieldsets::M::long()) {
fmt = fmt.replace("%B", &f.format(&iso_date).to_string());
}
}
if fmt.contains("%b") || fmt.contains("%h") {
if let Ok(f) = DateTimeFormatter::try_new(locale_prefs, fieldsets::M::medium()) {
let month_abbrev = f.format(&iso_date).to_string();
let month_abbrev = month_abbrev.trim_end_matches('.').to_string();
fmt = fmt
.replace("%b", &month_abbrev)
.replace("%h", &month_abbrev);
}
}
if fmt.contains("%A") {
if let Ok(f) = DateTimeFormatter::try_new(locale_prefs, fieldsets::E::long()) {
fmt = fmt.replace("%A", &f.format(&iso_date).to_string());
}
}
if fmt.contains("%a") {
if let Ok(f) = DateTimeFormatter::try_new(locale_prefs, fieldsets::E::short()) {
fmt = fmt.replace("%a", &f.format(&iso_date).to_string());
}
}
fmt.replace(PERCENT_PLACEHOLDER, "%%")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calendar_type_detection() {
use icu_locale::locale;
assert_eq!(
get_locale_calendar_type(&locale!("th-TH")),
CalendarType::Buddhist
);
assert_eq!(
get_locale_calendar_type(&locale!("fa-IR")),
CalendarType::Persian
);
assert_eq!(
get_locale_calendar_type(&locale!("am-ET")),
CalendarType::Ethiopian
);
assert_eq!(
get_locale_calendar_type(&locale!("en-US")),
CalendarType::Gregorian
);
}
}