1use std::fmt::Display;
2use std::path::Path;
3use std::str::FromStr;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
8#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
9#[non_exhaustive]
10pub enum Language {
11 #[default]
12 #[cfg_attr(feature = "serde", serde(rename = "en"))]
13 English,
14 #[cfg_attr(feature = "serde", serde(rename = "de"))]
15 German,
16 #[cfg_attr(feature = "serde", serde(rename = "fr"))]
17 French,
18 #[cfg_attr(feature = "serde", serde(rename = "ro"))]
19 Romanian,
20 #[cfg_attr(feature = "serde", serde(rename = "ar"))]
21 Arabic,
22 #[cfg_attr(feature = "serde", serde(rename = "bg"))]
23 Bulgarian,
24 #[cfg_attr(feature = "serde", serde(rename = "ru"))]
25 Russian,
26 #[cfg_attr(feature = "serde", serde(rename = "fi"))]
27 Finnish,
28 #[cfg_attr(feature = "serde", serde(rename = "tr"))]
29 Turkish,
30 #[cfg_attr(feature = "serde", serde(rename = "sl"))]
31 Slovenian,
32}
33impl Language {
34 pub const SEPARATOR: char = 'l';
35 #[inline]
36 fn check(s: &str) -> Self {
37 if s.len() < 3 { return Self::default()}
38 match &s[s.len() - 3..] {
39 ".en" => Self::English,
40 ".de" => Self::German,
41 ".fr" => Self::French,
42 ".ro" => Self::Romanian,
43 ".ar" => Self::Arabic,
44 ".bg" => Self::Bulgarian,
45 ".ru" => Self::Russian,
46 ".fi" => Self::Finnish,
47 ".tr" => Self::Turkish,
48 ".sl" => Self::Slovenian,
49 _ => Self::default(),
50 }
51 }
52 #[must_use]
53 pub fn from_rel_path(mut s: &str) -> Self {
54 s = s.strip_suffix(".tex").unwrap_or(s);
55 Self::check(s)
56 }
57 pub fn from_file(path: &Path) -> Self {
58 path.file_stem()
59 .and_then(|s| s.to_str())
60 .map_or_else(Self::default, Self::check)
61 }
62
63 #[must_use]
64 pub const fn flag_unicode(&self) -> &'static str {
65 match self {
66 Self::English => "🇬🇧",
67 Self::German => "🇩🇪",
68 Self::French => "🇫🇷",
69 Self::Romanian => "🇷🇴",
70 Self::Arabic => "🇦🇪",
71 Self::Bulgarian => "🇧🇬",
72 Self::Russian => "🇷🇺",
73 Self::Finnish => "🇫🇮",
74 Self::Turkish => "🇹🇷",
75 Self::Slovenian => "🇸🇮",
76 }
77 }
78
79 #[must_use]
80 pub const fn flag_svg(&self) -> &'static str {
81 match self {
83 Self::English => r##"<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-gb" viewBox="0 0 640 480">
84 <path fill="#012169" d="M0 0h640v480H0z"/>
85 <path fill="#FFF" d="m75 0 244 181L562 0h78v62L400 241l240 178v61h-80L320 301 81 480H0v-60l239-178L0 64V0z"/>
86 <path fill="#C8102E" d="m424 281 216 159v40L369 281zm-184 20 6 35L54 480H0zM640 0v3L391 191l2-44L590 0zM0 0l239 176h-60L0 42z"/>
87 <path fill="#FFF" d="M241 0v480h160V0zM0 160v160h640V160z"/>
88 <path fill="#C8102E" d="M0 193v96h640v-96zM273 0v480h96V0z"/>
89</svg>"##,
90 Self::German => r##"<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-de" viewBox="0 0 640 480">
91 <path fill="#fc0" d="M0 320h640v160H0z"/>
92 <path fill="#000001" d="M0 0h640v160H0z"/>
93 <path fill="red" d="M0 160h640v160H0z"/>
94</svg>
95"##,
96 Self::French => r##"
97<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-fr" viewBox="0 0 640 480">
98 <path fill="#fff" d="M0 0h640v480H0z"/>
99 <path fill="#000091" d="M0 0h213.3v480H0z"/>
100 <path fill="#e1000f" d="M426.7 0H640v480H426.7z"/>
101</svg>
102"##,
103 Self::Romanian => r##"
104<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-ro" viewBox="0 0 640 480">
105 <g fill-rule="evenodd" stroke-width="1pt">
106 <path fill="#00319c" d="M0 0h213.3v480H0z"/>
107 <path fill="#ffde00" d="M213.3 0h213.4v480H213.3z"/>
108 <path fill="#de2110" d="M426.7 0H640v480H426.7z"/>
109 </g>
110</svg>
111"##,
112 Self::Arabic => r##"
113<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-ae" viewBox="0 0 640 480">
114 <path fill="#00732f" d="M0 0h640v160H0z"/>
115 <path fill="#fff" d="M0 160h640v160H0z"/>
116 <path fill="#000001" d="M0 320h640v160H0z"/>
117 <path fill="red" d="M0 0h220v480H0z"/>
118</svg>
119"##,
120 Self::Bulgarian => r##"<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-bg" viewBox="0 0 640 480">
121 <path fill="#fff" d="M0 0h640v160H0z"/>
122 <path fill="#00966e" d="M0 160h640v160H0z"/>
123 <path fill="#d62612" d="M0 320h640v160H0z"/>
124</svg>"##,
125 Self::Russian => r##"
126<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-ru" viewBox="0 0 640 480">
127 <path fill="#fff" d="M0 0h640v160H0z"/>
128 <path fill="#0039a6" d="M0 160h640v160H0z"/>
129 <path fill="#d52b1e" d="M0 320h640v160H0z"/>
130</svg>"##,
131 Self::Finnish => r##"<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-fi" viewBox="0 0 640 480">
132 <path fill="#fff" d="M0 0h640v480H0z"/>
133 <path fill="#002f6c" d="M0 174.5h640v131H0z"/>
134 <path fill="#002f6c" d="M175.5 0h130.9v480h-131z"/>
135</svg>"##,
136 Self::Turkish => r##"
137<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-tr" viewBox="0 0 640 480">
138 <g fill-rule="evenodd">
139 <path fill="#e30a17" d="M0 0h640v480H0z"/>
140 <path fill="#fff" d="M407 247.5c0 66.2-54.6 119.9-122 119.9s-122-53.7-122-120 54.6-119.8 122-119.8 122 53.7 122 119.9"/>
141 <path fill="#e30a17" d="M413 247.5c0 53-43.6 95.9-97.5 95.9s-97.6-43-97.6-96 43.7-95.8 97.6-95.8 97.6 42.9 97.6 95.9z"/>
142 <path fill="#fff" d="m430.7 191.5-1 44.3-41.3 11.2 40.8 14.5-1 40.7 26.5-31.8 40.2 14-23.2-34.1 28.3-33.9-43.5 12-25.8-37z"/>
143 </g>
144</svg>
145"##,
146 Self::Slovenian => r##"
147<svg width="1.5em" xmlns="http://www.w3.org/2000/svg" id="flag-icons-si" viewBox="0 0 640 480">
148 <defs>
149 <clipPath id="si-a">
150 <path fill-opacity=".7" d="M-15 0h682.6v512H-15.1z"/>
151 </clipPath>
152 </defs>
153 <g fill-rule="evenodd" stroke-width="1pt" clip-path="url(#si-a)" transform="translate(14.1)scale(.9375)">
154 <path fill="#fff" d="M-62 0H962v512H-62z"/>
155 <path fill="#d50000" d="M-62 341.3H962V512H-62z"/>
156 <path fill="#0000bf" d="M-62 170.7H962v170.6H-62z"/>
157 <path fill="#d50000" d="M228.4 93c-4 61.6-6.4 95.4-15.7 111-10.2 16.8-20 29.1-59.7 44-39.6-14.9-49.4-27.2-59.6-44-9.4-15.6-11.7-49.4-15.7-111l5.8-2c11.8-3.6 20.6-6.5 27.1-7.8 9.3-2 17.3-4.2 42.3-4.7 25 .4 33 2.8 42.3 4.8 6.4 1.4 15.6 4 27.3 7.7z"/>
158 <path fill="#0000bf" d="M222.6 91c-3.8 61.5-7 89.7-12 103.2-9.6 23.2-24.8 35.9-57.6 48-32.8-12.1-48-24.8-57.7-48-5-13.6-8-41.7-11.8-103.3 11.6-3.6 20.6-6.4 27.1-7.7 9.3-2 17.3-4.3 42.3-4.7 25 .4 33 2.7 42.3 4.7a284 284 0 0 1 27.4 7.7z"/>
159 <path fill="#ffdf00" d="m153 109.8 1.5 3.7 7 1-4.5 2.7 4.3 2.9-6.3 1-2 3.4-2-3.5-6-.8 4-3-4.2-2.7 6.7-1z"/>
160 <path fill="#fff" d="m208.3 179.6-3.9-3-2.7-4.6-5.4-4.7-2.9-4.7-5.4-4.9-2.6-4.7-3-2.3-1.8-1.9-5 4.3-2.6 4.7-3.3 3-3.7-2.9-2.7-4.8-10.3-18.3-10.3 18.3-2.7 4.8-3.7 2.9-3.3-3-2.7-4.7-4.9-4.3-1.9 1.8-2.9 2.4-2.6 4.7-5.4 4.9-2.9 4.7-5.4 4.7-2.7 4.6-3.9 3a65.8 65.8 0 0 0 18.6 36.3 107 107 0 0 0 36.6 20.5 104.1 104.1 0 0 0 36.8-20.5c5.8-6 16.6-19.3 18.6-36.3"/>
161 <path fill="#ffdf00" d="m169.4 83.9 1.6 3.7 7 1-4.6 2.7 4.4 2.9-6.3 1-2 3.4-2-3.5-6-.8 4-3-4.2-2.7 6.6-1zm-33 0 1.6 3.7 7 .9-4.5 2.7 4.3 2.9-6.3 1-2 3.4-2-3.4-6-.9 4-3-4.2-2.7 6.7-1z"/>
162 <path fill="#0000bf" d="M199.7 203h-7.4l-7-.5-8.3-4h-9.4l-8.1 4-6.5.6-6.4-.6-8.1-4H129l-8.4 4-6.9.6-7.6-.1-3.6-6.2.1-.2 11.2 1.9 6.9-.5 8.3-4.1h9.4l8.2 4 6.4.6 6.5-.6 8.1-4h9.4l8.4 4 6.9.6 10.8-2 .2.4zm-86.4 9.5 7.4-.5 8.3-4h9.4l8.2 4 6.4.5 6.4-.5 8.2-4h9.4l8.3 4 7.5.5 4.8-6h-.1l-5.2 1.4-6.9-.5-8.3-4h-9.4l-8.2 4-6.4.6-6.5-.6-8.1-4H129l-8.4 4-6.9.6-5-1.3v.2l4.5 5.6z"/>
163 </g>
164</svg>
165"##,
166 }
167 }
168}
169impl Display for Language {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 write!(f, "{}", Into::<&'static str>::into(*self))
172 }
173}
174impl From<Language> for &'static str {
175 fn from(value: Language) -> Self {
176 match value {
177 Language::English => "en",
178 Language::German => "de",
179 Language::French => "fr",
180 Language::Romanian => "ro",
181 Language::Arabic => "ar",
182 Language::Bulgarian => "bg",
183 Language::Russian => "ru",
184 Language::Finnish => "fi",
185 Language::Turkish => "tr",
186 Language::Slovenian => "sl",
187 }
188 }
189}
190impl FromStr for Language {
191 type Err = ();
192 fn from_str(s: &str) -> Result<Self, ()> {
193 match s {
194 "en" => Ok(Self::English),
195 "de" => Ok(Self::German),
196 "fr" => Ok(Self::French),
197 "ro" => Ok(Self::Romanian),
198 "ar" => Ok(Self::Arabic),
199 "bg" => Ok(Self::Bulgarian),
200 "ru" => Ok(Self::Russian),
201 "fi" => Ok(Self::Finnish),
202 "tr" => Ok(Self::Turkish),
203 "sl" => Ok(Self::Slovenian),
204 _ => Err(()),
205 }
206 }
207}