hex_patch/app/settings/
locale.rs

1use serde::{Deserialize, Serialize};
2
3macro_rules! EnumValues {(
4    $(#[$attr:meta])*
5    $pub:vis enum $enum_name:ident {
6        $(
7            $(#[$field_attr:meta])*
8            $field_name:ident,
9        )*
10    }) => {
11        impl $enum_name {
12            $pub const VALUES: &'static [$enum_name] = &[
13                $(
14                    $enum_name::$field_name,
15                )*
16            ];
17        }
18    }
19}
20macro_rules! LocaleCode {(
21    $(#[$attr:meta])*
22    $pub:vis enum $enum_name:ident {
23        $(
24            $(#[$field_attr:meta])*
25            $field_name:ident,
26        )*
27    }) => {
28        impl $enum_name {
29            $pub const fn code(&self) -> &'static str {
30                match self {
31                    $(
32                        $enum_name::$field_name => const_str::replace!(stringify!($field_name),"_", "-"),
33                    )*
34                }
35            }
36        }
37    }
38}
39
40#[allow(non_camel_case_types)]
41#[non_exhaustive]
42#[derive(EnumValues!)]
43#[derive(LocaleCode!)]
44#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
45pub enum Locale {
46    #[default]
47    auto, // "auto" has to be the first variant to make match_locale work correctly
48    en,
49    #[serde(rename = "it-IT")]
50    it_IT,
51    #[serde(rename = "fr-FR")]
52    fr_FR,
53    #[serde(rename = "es-ES")]
54    es_ES,
55    #[serde(rename = "de-DE")]
56    de_DE,
57    #[serde(rename = "ja-JP")]
58    ja_JP,
59    #[serde(rename = "zh-CN")]
60    zh_CN,
61    #[serde(rename = "zh-TW")]
62    zh_TW,
63    #[serde(rename = "zh-HK")]
64    zh_HK,
65    #[serde(rename = "tr-TR")]
66    tr_TR,
67}
68
69impl Locale {
70    pub const fn name(&self) -> &'static str {
71        match self {
72            Locale::auto => "Auto",
73            Locale::en => "English",
74            Locale::it_IT => "Italiano (Italia)",
75            Locale::fr_FR => "Français (France)",
76            Locale::es_ES => "Español (España)",
77            Locale::de_DE => "Deutsch (Deutschland)",
78            Locale::ja_JP => "日本語 (日本)",
79            Locale::zh_CN => "中文 (中国)",
80            Locale::zh_TW => "中文 (台灣)",
81            Locale::zh_HK => "中文 (香港)",
82            Locale::tr_TR => "Türkçe (Türkiye)",
83        }
84    }
85    pub fn language(&self) -> &'static str {
86        self.code().split('-').next().unwrap()
87    }
88    pub fn match_locale(&self, locale: &str) -> Option<Locale> {
89        // Try to match exact locale first
90        // Skip "auto" as it's not a valid locale code
91        for l in &Locale::VALUES[1..] {
92            if locale.starts_with(l.code()) {
93                return Some(*l);
94            }
95        }
96        // If no exact match, try to match by language code
97        for l in &Locale::VALUES[1..] {
98            if locale.starts_with(l.language()) {
99                return Some(*l);
100            }
101        }
102        None
103    }
104    pub fn apply(&self) {
105        match self {
106            Locale::auto => {
107                let locale = 'found: {
108                    for locale in sys_locale::get_locales() {
109                        if let Some(locale) = self.match_locale(&locale) {
110                            break 'found locale;
111                        }
112                    }
113                    Locale::en // Fallback to English if no match found
114                };
115                rust_i18n::set_locale(locale.code());
116            }
117            _ => {
118                rust_i18n::set_locale(self.code());
119            }
120        }
121    }
122}