Skip to main content

gh_download/
i18n.rs

1use std::ffi::OsString;
2
3use serde::Deserialize;
4
5use clap::ValueEnum;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Deserialize)]
8#[serde(rename_all = "lowercase")]
9pub enum Language {
10    En,
11    Zh,
12}
13
14impl Language {
15    pub fn detect(
16        explicit: Option<Language>,
17        lc_all: Option<&str>,
18        lc_messages: Option<&str>,
19        lang: Option<&str>,
20    ) -> Self {
21        explicit.unwrap_or_else(|| {
22            let locale = first_non_empty([lc_all, lc_messages, lang]);
23            if locale.map(locale_indicates_chinese).unwrap_or(false) {
24                Language::Zh
25            } else {
26                Language::En
27            }
28        })
29    }
30
31    pub fn is_chinese(self) -> bool {
32        matches!(self, Language::Zh)
33    }
34}
35
36pub fn detect_language_from_args_and_env(
37    args: &[OsString],
38    lc_all: Option<&str>,
39    lc_messages: Option<&str>,
40    lang: Option<&str>,
41) -> Language {
42    Language::detect(parse_language_override(args), lc_all, lc_messages, lang)
43}
44
45pub(crate) fn parse_language_override(args: &[OsString]) -> Option<Language> {
46    let mut iter = args.iter().skip(1);
47    while let Some(arg) = iter.next() {
48        let value = arg.to_string_lossy();
49        if let Some(raw) = value.strip_prefix("--lang=") {
50            return parse_language_value(raw);
51        }
52        if value == "--lang" {
53            if let Some(next) = iter.next() {
54                return parse_language_value(&next.to_string_lossy());
55            }
56        }
57    }
58    None
59}
60
61pub(crate) fn parse_language_value(value: &str) -> Option<Language> {
62    if value.eq_ignore_ascii_case("zh") {
63        Some(Language::Zh)
64    } else if value.eq_ignore_ascii_case("en") {
65        Some(Language::En)
66    } else {
67        None
68    }
69}
70
71fn first_non_empty<const N: usize>(values: [Option<&str>; N]) -> Option<&str> {
72    values
73        .into_iter()
74        .flatten()
75        .map(str::trim)
76        .find(|value| !value.is_empty())
77}
78
79fn locale_indicates_chinese(locale: &str) -> bool {
80    locale.to_ascii_lowercase().contains("zh")
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn language_defaults_to_english_when_locale_is_not_chinese() {
89        assert_eq!(
90            Language::detect(None, None, None, Some("en_US.UTF-8")),
91            Language::En
92        );
93    }
94
95    #[test]
96    fn language_switches_to_chinese_from_locale() {
97        assert_eq!(
98            Language::detect(None, None, None, Some("zh_CN.UTF-8")),
99            Language::Zh
100        );
101    }
102
103    #[test]
104    fn explicit_language_overrides_locale() {
105        assert_eq!(
106            Language::detect(Some(Language::En), Some("zh_CN.UTF-8"), None, None),
107            Language::En
108        );
109    }
110}