use bevy::prelude::*;
#[derive(Resource, Debug, Clone, PartialEq, Eq)]
pub struct MaterialLocale {
pub tag: String,
}
impl Default for MaterialLocale {
fn default() -> Self {
Self {
tag: system_locale_tag().unwrap_or_else(|| "en-US".to_string()),
}
}
}
impl MaterialLocale {
pub fn new(tag: impl Into<String>) -> Self {
Self { tag: tag.into() }
}
}
#[derive(Component, Debug, Clone, PartialEq, Eq)]
pub struct MaterialLocaleOverride {
pub tag: String,
}
impl MaterialLocaleOverride {
pub fn new(tag: impl Into<String>) -> Self {
Self { tag: tag.into() }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DateFieldOrder {
Mdy,
Dmy,
Ymd,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DateInputPattern {
pub order: DateFieldOrder,
pub separator: char,
}
impl DateInputPattern {
pub const fn new(order: DateFieldOrder, separator: char) -> Self {
Self { order, separator }
}
pub const fn formatted_len(self) -> usize {
10
}
pub fn hint(self) -> String {
match self.order {
DateFieldOrder::Mdy => format!("MM{}DD{}YYYY", self.separator, self.separator),
DateFieldOrder::Dmy => format!("DD{}MM{}YYYY", self.separator, self.separator),
DateFieldOrder::Ymd => format!("YYYY{}MM{}DD", self.separator, self.separator),
}
}
pub fn normalize_digits(self, input: &str) -> String {
let digits: String = input
.chars()
.filter(|c| c.is_ascii_digit())
.take(8)
.collect();
let len = digits.len();
match self.order {
DateFieldOrder::Mdy | DateFieldOrder::Dmy => {
if len <= 2 {
return digits;
}
if len <= 4 {
return format!("{}{}{}", &digits[0..2], self.separator, &digits[2..len]);
}
format!(
"{}{}{}{}{}",
&digits[0..2],
self.separator,
&digits[2..4],
self.separator,
&digits[4..len]
)
}
DateFieldOrder::Ymd => {
if len <= 4 {
return digits;
}
if len <= 6 {
return format!("{}{}{}", &digits[0..4], self.separator, &digits[4..len]);
}
format!(
"{}{}{}{}{}",
&digits[0..4],
self.separator,
&digits[4..6],
self.separator,
&digits[6..len]
)
}
}
}
pub fn try_parse_complete(self, input: &str) -> Option<(i32, u8, u8)> {
if input.len() != self.formatted_len() {
return None;
}
let parts: Vec<&str> = input.split(self.separator).collect();
if parts.len() != 3 {
return None;
}
let (year_str, month_str, day_str) = match self.order {
DateFieldOrder::Mdy => (parts[2], parts[0], parts[1]),
DateFieldOrder::Dmy => (parts[2], parts[1], parts[0]),
DateFieldOrder::Ymd => (parts[0], parts[1], parts[2]),
};
let month: u8 = month_str.parse().ok()?;
let day: u8 = day_str.parse().ok()?;
let year: i32 = year_str.parse().ok()?;
Some((year, month, day))
}
}
pub fn date_input_pattern_for_locale(tag: &str) -> DateInputPattern {
let normalized = tag.trim().replace('_', "-");
let mut parts = normalized.split('-');
let language = parts.next().unwrap_or("").to_ascii_lowercase();
let region = parts.next().unwrap_or("").to_ascii_uppercase();
if matches!(region.as_str(), "US" | "PH" | "BZ") {
return DateInputPattern::new(DateFieldOrder::Mdy, '/');
}
if matches!(language.as_str(), "zh" | "ja" | "ko") {
return DateInputPattern::new(DateFieldOrder::Ymd, '/');
}
DateInputPattern::new(DateFieldOrder::Dmy, '/')
}
fn system_locale_tag() -> Option<String> {
for key in ["LC_ALL", "LANG", "LANGUAGE"] {
if let Ok(value) = std::env::var(key) {
let value = value.trim();
if value.is_empty() {
continue;
}
let without_encoding = value.split('.').next().unwrap_or(value);
let tag = without_encoding.replace('_', "-");
if !tag.is_empty() {
return Some(tag);
}
}
}
None
}