mcvm_shared/lang/
mod.rs

1/// Translation for multiple languages
2pub mod translate;
3
4#[cfg(feature = "schema")]
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8/// A language supported by mcvm. Includes all languages in Minecraft.
9#[allow(missing_docs)]
10#[derive(Deserialize, Serialize, Copy, Clone, Eq, PartialEq, Debug, Hash)]
11#[cfg_attr(feature = "schema", derive(JsonSchema))]
12#[serde(rename_all = "snake_case")]
13pub enum Language {
14	Afrikaans,
15	Arabic,
16	Asturian,
17	Azerbaijani,
18	Bashkir,
19	Bavarian,
20	Belarusian,
21	Bulgarian,
22	Breton,
23	Brabantian,
24	Bosnian,
25	Catalan,
26	Czech,
27	Welsh,
28	Danish,
29	AustrianGerman,
30	SwissGerman,
31	German,
32	Greek,
33	AustralianEnglish,
34	CanadianEnglish,
35	BritishEnglish,
36	NewZealandEnglish,
37	PirateSpeak,
38	UpsideDown,
39	AmericanEnglish,
40	Anglish,
41	Shakespearean,
42	Esperanto,
43	ArgentinianSpanish,
44	ChileanSpanish,
45	EcuadorianSpanish,
46	EuropeanSpanish,
47	MexicanSpanish,
48	UruguayanSpanish,
49	VenezuelanSpanish,
50	Andalusian,
51	Estonian,
52	Basque,
53	Persian,
54	Finnish,
55	Filipino,
56	Faroese,
57	CanadianFrench,
58	EuropeanFrench,
59	EastFranconian,
60	Friulian,
61	Frisian,
62	Irish,
63	ScottishGaelic,
64	Galician,
65	Hawaiian,
66	Hebrew,
67	Hindi,
68	Croatian,
69	Hungarian,
70	Armenian,
71	Indonesian,
72	Igbo,
73	Ido,
74	Icelandic,
75	Interslavic,
76	Italian,
77	Japanese,
78	Lojban,
79	Georgian,
80	Kazakh,
81	Kannada,
82	Korean,
83	Kolsch,
84	Cornish,
85	Latin,
86	Luxembourgish,
87	Limburgish,
88	Lombard,
89	Lolcat,
90	Lithuanian,
91	Latvian,
92	ClassicalChinese,
93	Macedonian,
94	Mongolian,
95	Malay,
96	Maltese,
97	Nahuatl,
98	LowGerman,
99	DutchFlemish,
100	Dutch,
101	NorwegianNynorsk,
102	NorwegianBokmal,
103	Occitan,
104	Elfdalian,
105	Polish,
106	BrazilianPortuguese,
107	EuropeanPortuguese,
108	Quenya,
109	Romanian,
110	RussianPreRevolutionary,
111	Russian,
112	Rusyn,
113	NorthernSami,
114	Slovak,
115	Slovenian,
116	Somali,
117	Albanian,
118	Serbian,
119	Swedish,
120	UpperSaxonGerman,
121	Silesian,
122	Tamil,
123	Thai,
124	Tagalog,
125	Klingon,
126	TokiPona,
127	Turkish,
128	Tatar,
129	Ukrainian,
130	Valencian,
131	Venetian,
132	Vietnamese,
133	Yiddish,
134	Yoruba,
135	ChineseSimplified,
136	ChineseTraditionalHongKong,
137	ChineseTraditionalTaiwan,
138	MalayJawi,
139}
140
141impl Default for Language {
142	fn default() -> Self {
143		match sys_locale::get_locale() {
144			Some(locale) => extract_locale_language(&locale).unwrap_or(Self::AmericanEnglish),
145			None => Self::AmericanEnglish,
146		}
147	}
148}
149
150impl Language {
151	/// Parse a Language from a string
152	pub fn parse_from_str(string: &str) -> Option<Self> {
153		match string {
154			"afrikaans" => Some(Self::Afrikaans),
155			"arabic" => Some(Self::Arabic),
156			"asturian" => Some(Self::Asturian),
157			"azerbaijani" => Some(Self::Azerbaijani),
158			"bashkir" => Some(Self::Bashkir),
159			"bavarian" => Some(Self::Bavarian),
160			"belarusian" => Some(Self::Belarusian),
161			"bulgarian" => Some(Self::Bulgarian),
162			"breton" => Some(Self::Breton),
163			"brabantian" => Some(Self::Brabantian),
164			"bosnian" => Some(Self::Bosnian),
165			"catalan" => Some(Self::Catalan),
166			"czech" => Some(Self::Czech),
167			"welsh" => Some(Self::Welsh),
168			"danish" => Some(Self::Danish),
169			"austrian_german" => Some(Self::AustrianGerman),
170			"swiss_german" => Some(Self::SwissGerman),
171			"german" => Some(Self::German),
172			"greek" => Some(Self::Greek),
173			"australian_english" => Some(Self::AustralianEnglish),
174			"canadian_english" => Some(Self::CanadianEnglish),
175			"british_english" => Some(Self::BritishEnglish),
176			"new_zealand_english" => Some(Self::NewZealandEnglish),
177			"pirate_speak" => Some(Self::PirateSpeak),
178			"upside_down" => Some(Self::UpsideDown),
179			"american_english" => Some(Self::AmericanEnglish),
180			"anglish" => Some(Self::Anglish),
181			"shakespearean" => Some(Self::Shakespearean),
182			"esperanto" => Some(Self::Esperanto),
183			"argentinian_spanish" => Some(Self::ArgentinianSpanish),
184			"chilean_spanish" => Some(Self::ChileanSpanish),
185			"ecuadorian_spanish" => Some(Self::EcuadorianSpanish),
186			"european_spanish" => Some(Self::EuropeanSpanish),
187			"mexican_spanish" => Some(Self::MexicanSpanish),
188			"uruguayan_spanish" => Some(Self::UruguayanSpanish),
189			"venezuelan_spanish" => Some(Self::VenezuelanSpanish),
190			"andalusian" => Some(Self::Andalusian),
191			"estonian" => Some(Self::Estonian),
192			"basque" => Some(Self::Basque),
193			"persian" => Some(Self::Persian),
194			"finnish" => Some(Self::Finnish),
195			"filipino" => Some(Self::Filipino),
196			"faroese" => Some(Self::Faroese),
197			"canadian_french" => Some(Self::CanadianFrench),
198			"european_french" => Some(Self::EuropeanFrench),
199			"east_franconian" => Some(Self::EastFranconian),
200			"friulian" => Some(Self::Friulian),
201			"frisian" => Some(Self::Frisian),
202			"irish" => Some(Self::Irish),
203			"scottish_gaelic" => Some(Self::ScottishGaelic),
204			"galician" => Some(Self::Galician),
205			"hawaiian" => Some(Self::Hawaiian),
206			"hebrew" => Some(Self::Hebrew),
207			"hindi" => Some(Self::Hindi),
208			"croatian" => Some(Self::Croatian),
209			"hungarian" => Some(Self::Hungarian),
210			"armenian" => Some(Self::Armenian),
211			"indonesian" => Some(Self::Indonesian),
212			"igbo" => Some(Self::Igbo),
213			"ido" => Some(Self::Ido),
214			"icelandic" => Some(Self::Icelandic),
215			"interslavic" => Some(Self::Interslavic),
216			"italian" => Some(Self::Italian),
217			"japanese" => Some(Self::Japanese),
218			"lojban" => Some(Self::Lojban),
219			"georgian" => Some(Self::Georgian),
220			"kazakh" => Some(Self::Kazakh),
221			"kannada" => Some(Self::Kannada),
222			"korean" => Some(Self::Korean),
223			"kolsch" => Some(Self::Kolsch),
224			"cornish" => Some(Self::Cornish),
225			"latin" => Some(Self::Latin),
226			"luxembourgish" => Some(Self::Luxembourgish),
227			"limburgish" => Some(Self::Limburgish),
228			"lombard" => Some(Self::Lombard),
229			"lolcat" => Some(Self::Lolcat),
230			"lithuanian" => Some(Self::Lithuanian),
231			"latvian" => Some(Self::Latvian),
232			"classical_chinese" => Some(Self::ClassicalChinese),
233			"macedonian" => Some(Self::Macedonian),
234			"mongolian" => Some(Self::Mongolian),
235			"malay" => Some(Self::Malay),
236			"maltese" => Some(Self::Maltese),
237			"nahuatl" => Some(Self::Nahuatl),
238			"low_german" => Some(Self::LowGerman),
239			"dutch_flemish" => Some(Self::DutchFlemish),
240			"dutch" => Some(Self::Dutch),
241			"norwegian_nynorsk" => Some(Self::NorwegianNynorsk),
242			"norwegian_bokmal" => Some(Self::NorwegianBokmal),
243			"occitan" => Some(Self::Occitan),
244			"elfdalian" => Some(Self::Elfdalian),
245			"polish" => Some(Self::Polish),
246			"brazilian_portuguese" => Some(Self::BrazilianPortuguese),
247			"european_portuguese" => Some(Self::EuropeanPortuguese),
248			"quenya" => Some(Self::Quenya),
249			"romanian" => Some(Self::Romanian),
250			"russian_pre_revolutionary" => Some(Self::RussianPreRevolutionary),
251			"russian" => Some(Self::Russian),
252			"rusyn" => Some(Self::Rusyn),
253			"northern_sami" => Some(Self::NorthernSami),
254			"slovak" => Some(Self::Slovak),
255			"slovenian" => Some(Self::Slovenian),
256			"somali" => Some(Self::Somali),
257			"albanian" => Some(Self::Albanian),
258			"serbian" => Some(Self::Serbian),
259			"swedish" => Some(Self::Swedish),
260			"upper_saxon_german" => Some(Self::UpperSaxonGerman),
261			"silesian" => Some(Self::Silesian),
262			"tamil" => Some(Self::Tamil),
263			"thai" => Some(Self::Thai),
264			"tagalog" => Some(Self::Tagalog),
265			"klingon" => Some(Self::Klingon),
266			"toki_pona" => Some(Self::TokiPona),
267			"turkish" => Some(Self::Turkish),
268			"tatar" => Some(Self::Tatar),
269			"ukrainian" => Some(Self::Ukrainian),
270			"valencian" => Some(Self::Valencian),
271			"venetian" => Some(Self::Venetian),
272			"vietnamese" => Some(Self::Vietnamese),
273			"yiddish" => Some(Self::Yiddish),
274			"yoruba" => Some(Self::Yoruba),
275			"chinese_simplified" => Some(Self::ChineseSimplified),
276			"chinese_traditional_hong_kong" => Some(Self::ChineseTraditionalHongKong),
277			"chinese_traditional_taiwan" => Some(Self::ChineseTraditionalTaiwan),
278			"malay_jawi" => Some(Self::MalayJawi),
279			_ => None,
280		}
281	}
282}
283
284/// Extract a `Language` value from a locale. Not all locales and languages are supported
285pub fn extract_locale_language(locale: &str) -> Option<Language> {
286	let locale = canonicalize_locale(locale);
287	match locale.as_str() {
288		"de_AT" => Some(Language::AustrianGerman),
289		"de_CH" => Some(Language::SwissGerman),
290		"en_AU" => Some(Language::AustralianEnglish),
291		"en_CA" => Some(Language::CanadianEnglish),
292		"en_GB" => Some(Language::BritishEnglish),
293		"en_NZ" => Some(Language::NewZealandEnglish),
294		"en_US" | "C" | "POSIX" => Some(Language::AmericanEnglish),
295		"es_AR" => Some(Language::ArgentinianSpanish),
296		"es_CL" => Some(Language::ChileanSpanish),
297		"es_EC" => Some(Language::EcuadorianSpanish),
298		"es_ES" => Some(Language::EuropeanSpanish),
299		"es_MX" => Some(Language::MexicanSpanish),
300		"es_UY" => Some(Language::UruguayanSpanish),
301		"es_VE" => Some(Language::VenezuelanSpanish),
302		"fr_CA" => Some(Language::CanadianFrench),
303		"fr_FR" => Some(Language::EuropeanFrench),
304		"nl_BE" => Some(Language::DutchFlemish),
305		"nl_NL" => Some(Language::Dutch),
306		"pt_BR" => Some(Language::BrazilianPortuguese),
307		"pt_PT" => Some(Language::EuropeanPortuguese),
308		"zh_CN" => Some(Language::ChineseSimplified),
309		"zh_HK" => Some(Language::ChineseTraditionalHongKong),
310		"zh_TW" => Some(Language::ChineseTraditionalTaiwan),
311		// Use only the language if the region is unknown
312		other => {
313			let first_part = extract_locale_first_part(other);
314			match first_part {
315				"af" => Some(Language::Afrikaans),
316				"ar" => Some(Language::Arabic),
317				"az" => Some(Language::Azerbaijani),
318				"be" => Some(Language::Belarusian),
319				"bg" => Some(Language::Bulgarian),
320				"bs" => Some(Language::Bosnian),
321				"ca" => Some(Language::Catalan),
322				"cs" => Some(Language::Czech),
323				"da" => Some(Language::Danish),
324				"de" => Some(Language::German),
325				"el" => Some(Language::Greek),
326				"en" => Some(Language::AmericanEnglish),
327				"es" => Some(Language::EuropeanSpanish),
328				"et" => Some(Language::Estonian),
329				"fi" => Some(Language::Finnish),
330				"fr" => Some(Language::EuropeanFrench),
331				"he" => Some(Language::Hebrew),
332				"hi" => Some(Language::Hindi),
333				"hr" => Some(Language::Croatian),
334				"hu" => Some(Language::Hungarian),
335				"hy" => Some(Language::Armenian),
336				"id" => Some(Language::Indonesian),
337				"is" => Some(Language::Icelandic),
338				"it" => Some(Language::Italian),
339				"ja" => Some(Language::Japanese),
340				"ka" => Some(Language::Georgian),
341				"kk" => Some(Language::Kazakh),
342				"kn" => Some(Language::Kannada),
343				"ko" => Some(Language::Korean),
344				"lt" => Some(Language::Lithuanian),
345				"lv" => Some(Language::Latvian),
346				"mk" => Some(Language::Macedonian),
347				"ms" => Some(Language::Malay),
348				"mt" => Some(Language::Maltese),
349				"nb" => Some(Language::NorwegianBokmal),
350				"nl" => Some(Language::Dutch),
351				"nn" => Some(Language::NorwegianNynorsk),
352				"pl" => Some(Language::Polish),
353				"pt" => Some(Language::EuropeanPortuguese),
354				"ro" => Some(Language::Romanian),
355				"ru" => Some(Language::Russian),
356				"sk" => Some(Language::Slovak),
357				"sl" => Some(Language::Slovenian),
358				"sq" => Some(Language::Albanian),
359				"sr" => Some(Language::Serbian),
360				"sv" => Some(Language::Swedish),
361				"ta" => Some(Language::Tamil),
362				"th" => Some(Language::Thai),
363				"tr" => Some(Language::Turkish),
364				"uk" => Some(Language::Ukrainian),
365				"vi" => Some(Language::Vietnamese),
366				"zh" => Some(Language::ChineseSimplified),
367				_ => None,
368			}
369		}
370	}
371}
372
373/// Extract the first part, the language, from the language tag.
374/// Should be canonicalized first to use an underscore
375pub fn extract_locale_first_part(locale: &str) -> &str {
376	if let Some(underscore_location) = locale.find('_') {
377		locale.split_at(underscore_location).0
378	} else {
379		locale
380	}
381}
382
383/// Canonicalize a language tag
384pub fn canonicalize_locale(locale: &str) -> String {
385	let mut locale = strip_locale(locale).to_string();
386	locale = locale.replace('-', "_");
387	locale
388}
389
390/// Strip extensions and other stuff from an IETF language tag
391pub fn strip_locale(locale: &str) -> &str {
392	if let Some(dot_location) = locale.find('.') {
393		locale.split_at(dot_location).0
394	} else {
395		locale
396	}
397}
398
399#[cfg(test)]
400mod tests {
401	use super::*;
402
403	#[test]
404	fn test_locale_canonicalization() {
405		assert_eq!(canonicalize_locale("az"), "az".to_string());
406		assert_eq!(canonicalize_locale("az-ZA"), "az_ZA".to_string());
407		assert_eq!(canonicalize_locale("az-ZA.UTF-8"), "az_ZA".to_string());
408	}
409
410	#[test]
411	fn test_language_extraction() {
412		assert_eq!(
413			extract_locale_language("C"),
414			Some(Language::AmericanEnglish)
415		);
416		assert_eq!(extract_locale_language("af-ZA"), Some(Language::Afrikaans));
417		assert_eq!(
418			extract_locale_language("de-CH"),
419			Some(Language::SwissGerman)
420		);
421		assert_eq!(extract_locale_language("de_FOO"), Some(Language::German));
422	}
423}