cloudiful_bevy_localization/
locale.rs1use crate::definition_registry::active_definition;
2use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeError};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct Locale(&'static str);
6
7impl Default for Locale {
8 fn default() -> Self {
9 active_definition().fallback_locale()
10 }
11}
12
13impl Locale {
14 pub const fn new(id: &'static str) -> Self {
15 Self(id)
16 }
17
18 pub const fn id(self) -> &'static str {
19 self.0
20 }
21
22 pub fn available() -> Vec<Self> {
23 active_definition().locales()
24 }
25
26 pub fn from_serialized(raw: &str) -> Option<Self> {
27 let normalized = canonical_locale_id(raw);
28
29 Self::available()
30 .into_iter()
31 .find(|locale| canonical_locale_id(locale.id()) == normalized)
32 }
33}
34
35impl Serialize for Locale {
36 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
37 where
38 S: Serializer,
39 {
40 serializer.serialize_str(self.id())
41 }
42}
43
44impl<'de> Deserialize<'de> for Locale {
45 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
46 where
47 D: Deserializer<'de>,
48 {
49 let raw = String::deserialize(deserializer)?;
50 Locale::from_serialized(&raw)
51 .ok_or_else(|| D::Error::custom(format!("unknown locale: {raw}")))
52 }
53}
54
55fn canonical_locale_id(raw: &str) -> String {
56 raw.chars()
57 .filter(|ch| ch.is_ascii_alphanumeric())
58 .map(|ch| ch.to_ascii_lowercase())
59 .collect()
60}