1use super::{Lang, LangCode};
2use crate::{IsDefault, MuxError, Result};
3use std::{env, str::FromStr};
4
5#[macro_export]
7macro_rules! lang {
8 ($x:ident) => {
9 $crate::Lang::Code($crate::LangCode::$x)
10 };
11}
12
13impl Lang {
14 pub(crate) fn new(s: impl AsRef<str>) -> Lang {
15 let s = s.as_ref();
16 match get_code(s) {
17 Some(c) => Lang::Code(c),
18 None => Lang::Other(s.into()),
19 }
20 }
21}
22
23impl LangCode {
24 pub(crate) fn init() -> Self {
25 Self::get_from_system_locale().unwrap_or_default()
26 }
27
28 pub(crate) fn get(s: &str) -> Option<LangCode> {
29 get_code(s)
30 }
31
32 fn get_from_system_locale() -> Option<Self> {
33 let locale = env::var("LC_ALL")
34 .ok()
35 .or_else(|| env::var("LANG").ok())
36 .or_else(|| env::var("LC_MESSAGES").ok())
37 .or_else(|| get_system_locale_fallback())?;
38
39 return get_code(&locale);
40
41 fn get_system_locale_fallback() -> Option<String> {
42 #[cfg(windows)]
43 {
44 use std::ffi::OsString;
45 use std::os::windows::ffi::OsStringExt;
46 use winapi::um::winnls::GetUserDefaultLocaleName;
47
48 const LOCALE_NAME_MAX_LENGTH: usize = 85;
49 let mut buffer = [0u16; LOCALE_NAME_MAX_LENGTH];
50
51 let len = unsafe {
52 GetUserDefaultLocaleName(buffer.as_mut_ptr(), LOCALE_NAME_MAX_LENGTH as i32)
53 };
54
55 if len > 0 {
56 let os_str = OsString::from_wide(&buffer[..(len as usize - 1)]);
57 os_str.into_string().ok()
58 } else {
59 None
60 }
61 }
62
63 #[cfg(unix)]
64 {
65 None
66 }
67 }
68 }
69}
70
71impl Default for Lang {
72 fn default() -> Lang {
73 Lang::Code(LangCode::default())
74 }
75}
76impl IsDefault for Lang {
77 fn is_default(&self) -> bool {
78 matches!(self, Lang::Code(c) if c.is_default())
79 }
80}
81
82impl FromStr for Lang {
83 type Err = MuxError;
84
85 fn from_str(s: &str) -> Result<Lang> {
86 Ok(Lang::new(s))
87 }
88}
89impl FromStr for LangCode {
90 type Err = MuxError;
91
92 fn from_str(s: &str) -> Result<LangCode> {
93 get_code(s).ok_or_else(|| err!("Not found a valid language code"))
94 }
95}
96
97fn get_code(s: &str) -> Option<LangCode> {
98 fn str_to_ascii_words(s: &str) -> impl Iterator<Item = &str> {
99 use lazy_regex::{Lazy, Regex, regex};
100 static REGEX_ASCII_WORD: &Lazy<Regex> = regex!(r"[a-zA-Z]+");
101 REGEX_ASCII_WORD.find_iter(s).map(|mat| mat.as_str())
102 }
103
104 let mut buf = [0u8; 3];
105 str_to_ascii_words(s).find_map(|s| {
106 let len = s.len();
107 if !matches!(len, 2 | 3) {
108 return None;
109 }
110 for (dst, src) in buf[..len].iter_mut().zip(s.bytes()) {
111 *dst = src.to_ascii_lowercase();
112 }
113
114 let code = match &buf[..len] {
115 b"und" => LangCode::Und,
116 b"aa" | b"aar" => LangCode::Aar,
117 b"ab" | b"abk" => LangCode::Abk,
118 b"af" | b"afr" => LangCode::Afr,
119 b"ak" | b"aka" => LangCode::Aka,
120 b"sq" | b"sqi" | b"alb" => LangCode::Alb,
121 b"am" | b"amh" => LangCode::Amh,
122 b"ar" | b"ara" => LangCode::Ara,
123 b"an" | b"arg" => LangCode::Arg,
124 b"hy" | b"hye" | b"arm" => LangCode::Arm,
125 b"as" | b"asm" => LangCode::Asm,
126 b"av" | b"ava" => LangCode::Ava,
127 b"ae" | b"ave" => LangCode::Ave,
128 b"ay" | b"aym" => LangCode::Aym,
129 b"az" | b"aze" => LangCode::Aze,
130 b"ba" | b"bak" => LangCode::Bak,
131 b"bm" | b"bam" => LangCode::Bam,
132 b"eu" | b"eus" | b"baq" => LangCode::Baq,
133 b"be" | b"bel" => LangCode::Bel,
134 b"bn" | b"ben" => LangCode::Ben,
135 b"bi" | b"bis" => LangCode::Bis,
136 b"bs" | b"bos" => LangCode::Bos,
137 b"br" | b"bre" => LangCode::Bre,
138 b"bg" | b"bul" => LangCode::Bul,
139 b"my" | b"mya" | b"bur" => LangCode::Bur,
140 b"ca" | b"cat" => LangCode::Cat,
141 b"ch" | b"cha" => LangCode::Cha,
142 b"ce" | b"che" => LangCode::Che,
143 b"zh" | b"zho" | b"chi" => LangCode::Chi,
144 b"cu" | b"chu" => LangCode::Chu,
145 b"cv" | b"chv" => LangCode::Chv,
146 b"kw" | b"cor" => LangCode::Cor,
147 b"co" | b"cos" => LangCode::Cos,
148 b"cr" | b"cre" => LangCode::Cre,
149 b"cs" | b"ces" | b"cze" => LangCode::Cze,
150 b"da" | b"dan" => LangCode::Dan,
151 b"dv" | b"div" => LangCode::Div,
152 b"nl" | b"nld" | b"dut" => LangCode::Dut,
153 b"dz" | b"dzo" => LangCode::Dzo,
154 b"en" | b"eng" => LangCode::Eng,
155 b"eo" | b"epo" => LangCode::Epo,
156 b"et" | b"est" => LangCode::Est,
157 b"ee" | b"ewe" => LangCode::Ewe,
158 b"fo" | b"fao" => LangCode::Fao,
159 b"fj" | b"fij" => LangCode::Fij,
160 b"fi" | b"fin" => LangCode::Fin,
161 b"fr" | b"fra" | b"fre" => LangCode::Fre,
162 b"fy" | b"fry" => LangCode::Fry,
163 b"ff" | b"ful" => LangCode::Ful,
164 b"ka" | b"kat" | b"geo" => LangCode::Geo,
165 b"de" | b"der" | b"ger" => LangCode::Ger,
166 b"gd" | b"gla" => LangCode::Gla,
167 b"ga" | b"gle" => LangCode::Gle,
168 b"gl" | b"glg" => LangCode::Glg,
169 b"gb" | b"glv" => LangCode::Glv,
170 b"el" | b"ell" | b"gre" => LangCode::Gre,
171 b"gn" | b"grn" => LangCode::Grn,
172 b"gu" | b"guj" => LangCode::Guj,
173 b"ht" | b"hat" => LangCode::Hat,
174 b"ha" | b"hau" => LangCode::Hau,
175 b"sh" | b"hbs" => LangCode::Hbs,
176 b"he" | b"heb" => LangCode::Heb,
177 b"hz" | b"her" => LangCode::Her,
178 b"hi" | b"hin" => LangCode::Hin,
179 b"ho" | b"hmo" => LangCode::Hmo,
180 b"hr" | b"hrv" => LangCode::Hrv,
181 b"hu" | b"hun" => LangCode::Hun,
182 b"ig" | b"ibo" => LangCode::Ibo,
183 b"is" | b"isl" | b"ice" => LangCode::Ice,
184 b"io" | b"ido" => LangCode::Ido,
185 b"ii" | b"iii" => LangCode::Iii,
186 b"iu" | b"iku" => LangCode::Iku,
187 b"ie" | b"ile" => LangCode::Ile,
188 b"ia" | b"ina" => LangCode::Ina,
189 b"id" | b"ind" => LangCode::Ind,
190 b"ik" | b"ipk" => LangCode::Ipk,
191 b"it" | b"ita" => LangCode::Ita,
192 b"jv" | b"jav" => LangCode::Jav,
193 b"ja" | b"jpn" => LangCode::Jpn,
194 b"kl" | b"kal" => LangCode::Kal,
195 b"kn" | b"kan" => LangCode::Kan,
196 b"ks" | b"kas" => LangCode::Kas,
197 b"kr" | b"kau" => LangCode::Kau,
198 b"kk" | b"kaz" => LangCode::Kaz,
199 b"km" | b"khm" => LangCode::Khm,
200 b"ki" | b"kik" => LangCode::Kik,
201 b"rw" | b"kin" => LangCode::Kin,
202 b"ky" | b"kir" => LangCode::Kir,
203 b"kv" | b"kom" => LangCode::Kom,
204 b"kg" | b"kon" => LangCode::Kon,
205 b"ko" | b"kor" => LangCode::Kor,
206 b"kj" | b"kua" => LangCode::Kua,
207 b"ku" | b"kur" => LangCode::Kur,
208 b"lo" | b"lao" => LangCode::Lao,
209 b"la" | b"lat" => LangCode::Lat,
210 b"lv" | b"lav" => LangCode::Lav,
211 b"li" | b"lim" => LangCode::Lim,
212 b"ln" | b"lin" => LangCode::Lin,
213 b"lt" | b"lit" => LangCode::Lit,
214 b"lb" | b"ltz" => LangCode::Ltz,
215 b"lu" | b"lub" => LangCode::Lub,
216 b"lg" | b"lug" => LangCode::Lug,
217 b"mk" | b"mkd" | b"mac" => LangCode::Mac,
218 b"mh" | b"mah" => LangCode::Mah,
219 b"ml" | b"mal" => LangCode::Mal,
220 b"mi" | b"mri" | b"mao" => LangCode::Mao,
221 b"mr" | b"mar" => LangCode::Mar,
222 b"ms" | b"msa" | b"may" => LangCode::May,
223 b"mg" | b"mlg" => LangCode::Mlg,
224 b"mt" | b"mlt" => LangCode::Mlt,
225 b"mn" | b"mon" => LangCode::Mon,
226 b"na" | b"nau" => LangCode::Nau,
227 b"nv" | b"nav" => LangCode::Nav,
228 b"nr" | b"nbl" => LangCode::Nbl,
229 b"nd" | b"nde" => LangCode::Nde,
230 b"ng" | b"ndo" => LangCode::Ndo,
231 b"ne" | b"nep" => LangCode::Nep,
232 b"nn" | b"nno" => LangCode::Nno,
233 b"nb" | b"nob" => LangCode::Nob,
234 b"no" | b"nor" => LangCode::Nor,
235 b"ny" | b"nya" => LangCode::Nya,
236 b"oc" | b"oci" => LangCode::Oci,
237 b"oj" | b"oji" => LangCode::Oji,
238 b"or" | b"ori" => LangCode::Ori,
239 b"om" | b"orm" => LangCode::Orm,
240 b"os" | b"oss" => LangCode::Oss,
241 b"pa" | b"pan" => LangCode::Pan,
242 b"fa" | b"fas" | b"per" => LangCode::Per,
243 b"pi" | b"pli" => LangCode::Pli,
244 b"pl" | b"pol" => LangCode::Pol,
245 b"pt" | b"por" => LangCode::Por,
246 b"ps" | b"pus" => LangCode::Pus,
247 b"qu" | b"que" => LangCode::Que,
248 b"rm" | b"roh" => LangCode::Roh,
249 b"ro" | b"ron" | b"rum" => LangCode::Rum,
250 b"rn" | b"run" => LangCode::Run,
251 b"ru" | b"rus" => LangCode::Rus,
252 b"sg" | b"sag" => LangCode::Sag,
253 b"sa" | b"san" => LangCode::San,
254 b"si" | b"sin" => LangCode::Sin,
255 b"sk" | b"slk" | b"slo" => LangCode::Slo,
256 b"sl" | b"slv" => LangCode::Slv,
257 b"se" | b"sme" => LangCode::Sme,
258 b"sm" | b"smo" => LangCode::Smo,
259 b"sn" | b"sna" => LangCode::Sna,
260 b"sd" | b"snd" => LangCode::Snd,
261 b"so" | b"som" => LangCode::Som,
262 b"st" | b"sot" => LangCode::Sot,
263 b"es" | b"spa" => LangCode::Spa,
264 b"sc" | b"srd" => LangCode::Srd,
265 b"sr" | b"srp" => LangCode::Srp,
266 b"ss" | b"ssw" => LangCode::Ssw,
267 b"su" | b"sun" => LangCode::Sun,
268 b"sw" | b"swa" => LangCode::Swa,
269 b"sv" | b"swe" => LangCode::Swe,
270 b"ty" | b"tah" => LangCode::Tah,
271 b"ta" | b"tam" => LangCode::Tam,
272 b"tt" | b"tat" => LangCode::Tat,
273 b"te" | b"tel" => LangCode::Tel,
274 b"tg" | b"tgk" => LangCode::Tgk,
275 b"tl" | b"tgl" => LangCode::Tgl,
276 b"th" | b"tha" => LangCode::Tha,
277 b"bo" | b"bod" | b"tib" => LangCode::Tib,
278 b"ti" | b"tir" => LangCode::Tir,
279 b"to" | b"ton" => LangCode::Ton,
280 b"tn" | b"tsn" => LangCode::Tsn,
281 b"ts" | b"tso" => LangCode::Tso,
282 b"tk" | b"tuk" => LangCode::Tuk,
283 b"tr" | b"tur" => LangCode::Tur,
284 b"tw" | b"twi" => LangCode::Twi,
285 b"ug" | b"uig" => LangCode::Uig,
286 b"uk" | b"ukr" => LangCode::Ukr,
287 b"ur" | b"urd" => LangCode::Urd,
288 b"uz" | b"uzb" => LangCode::Uzb,
289 b"ve" | b"ven" => LangCode::Ven,
290 b"vi" | b"vie" => LangCode::Vie,
291 b"vo" | b"vol" => LangCode::Vol,
292 b"cy" | b"cym" | b"wel" => LangCode::Wel,
293 b"wa" | b"wln" => LangCode::Wln,
294 b"wo" | b"wol" => LangCode::Wol,
295 b"xh" | b"xho" => LangCode::Xho,
296 b"yi" | b"yid" => LangCode::Yid,
297 b"yo" | b"yor" => LangCode::Yor,
298 b"za" | b"zha" => LangCode::Zha,
299 b"zu" | b"zul" => LangCode::Zul,
300 _ => return None,
301 };
302
303 Some(code)
304 })
305}