rush_sync_server/commands/lang/
mod.rs

1use crate::core::prelude::*;
2use crate::i18n::{get_available_languages, get_current_language, set_language};
3
4pub mod command;
5pub use command::LanguageCommand;
6
7#[derive(Debug)]
8pub struct LanguageService {
9    config_paths: Vec<std::path::PathBuf>,
10}
11
12impl LanguageService {
13    pub fn new() -> Self {
14        Self {
15            config_paths: crate::setup::setup_toml::get_config_paths(),
16        }
17    }
18
19    pub fn show_status(&self) -> String {
20        let current_lang = get_current_language();
21        let available_langs = get_available_languages().join(", ");
22
23        let current = crate::i18n::get_command_translation(
24            "system.commands.language.current",
25            &[&current_lang],
26        );
27        let available = crate::i18n::get_command_translation(
28            "system.commands.language.available",
29            &[&available_langs],
30        );
31
32        format!("{}\n{}", current, available)
33    }
34
35    pub async fn change_language(&mut self, lang: &str) -> Result<String> {
36        match set_language(lang) {
37            Ok(()) => {
38                if let Err(e) = self.save_to_config(lang).await {
39                    log::error!("Failed to save language config: {}", e);
40                }
41
42                Ok(self.create_save_message(
43                    lang,
44                    &crate::i18n::get_command_translation(
45                        "system.commands.language.changed",
46                        &[&lang.to_uppercase()],
47                    ),
48                ))
49            }
50            Err(e) => Ok(crate::i18n::get_command_translation(
51                "system.commands.language.invalid",
52                &[&e.to_string()],
53            )),
54        }
55    }
56
57    pub fn switch_language_only(&self, lang: &str) -> Result<()> {
58        set_language(lang)
59    }
60
61    pub async fn process_save_message(message: &str) -> Option<String> {
62        if !message.starts_with("__SAVE_LANGUAGE__") {
63            return None;
64        }
65
66        let parts: Vec<&str> = message.split("__MESSAGE__").collect();
67        if parts.len() != 2 {
68            return None;
69        }
70
71        let lang_part = parts[0].replace("__SAVE_LANGUAGE__", "");
72        let display_message = parts[1];
73
74        let service = LanguageService::new();
75        if let Err(e) = service.save_to_config(&lang_part).await {
76            log::error!("Failed to save language config: {}", e);
77        }
78
79        Some(display_message.to_string())
80    }
81
82    pub fn get_available(&self) -> Vec<String> {
83        get_available_languages()
84    }
85
86    pub fn get_current(&self) -> String {
87        get_current_language()
88    }
89
90    fn create_save_message(&self, lang: &str, display_text: &str) -> String {
91        format!("__SAVE_LANGUAGE__{}__MESSAGE__{}", lang, display_text)
92    }
93
94    async fn save_to_config(&self, lang: &str) -> Result<()> {
95        for path in &self.config_paths {
96            if path.exists() {
97                let content = tokio::fs::read_to_string(path)
98                    .await
99                    .map_err(AppError::Io)?;
100                let updated_content = self.update_language_in_toml(&content, lang)?;
101                tokio::fs::write(path, updated_content)
102                    .await
103                    .map_err(AppError::Io)?;
104                return Ok(());
105            }
106        }
107        Ok(())
108    }
109
110    fn update_language_in_toml(&self, content: &str, lang: &str) -> Result<String> {
111        let updated_content = if content.contains("[language]") {
112            content
113                .lines()
114                .map(|line| {
115                    if line.trim_start().starts_with("current =") {
116                        format!("current = \"{}\"", lang)
117                    } else {
118                        line.to_string()
119                    }
120                })
121                .collect::<Vec<_>>()
122                .join("\n")
123        } else {
124            format!("{}\n\n[language]\ncurrent = \"{}\"", content.trim(), lang)
125        };
126
127        Ok(updated_content)
128    }
129
130    pub async fn load_from_config(&self) -> Option<String> {
131        for path in &self.config_paths {
132            if path.exists() {
133                if let Ok(content) = tokio::fs::read_to_string(path).await {
134                    if let Some(lang) = self.extract_language_from_toml(&content) {
135                        return Some(lang);
136                    }
137                }
138            }
139        }
140        None
141    }
142
143    fn extract_language_from_toml(&self, content: &str) -> Option<String> {
144        let mut in_language_section = false;
145
146        for line in content.lines() {
147            let trimmed = line.trim();
148
149            if trimmed == "[language]" {
150                in_language_section = true;
151                continue;
152            }
153
154            if trimmed.starts_with('[') && trimmed.ends_with(']') && trimmed != "[language]" {
155                in_language_section = false;
156                continue;
157            }
158
159            if in_language_section && trimmed.starts_with("current =") {
160                if let Some(value_part) = trimmed.split('=').nth(1) {
161                    let cleaned = value_part.trim().trim_matches('"').trim_matches('\'');
162                    return Some(cleaned.to_string());
163                }
164            }
165        }
166        None
167    }
168
169    pub async fn load_and_apply_from_config(
170        &self,
171        config: &crate::core::config::Config,
172    ) -> Result<()> {
173        let lang = &config.language;
174
175        if let Err(e) = crate::i18n::set_language(lang) {
176            log::warn!(
177                "{}",
178                crate::i18n::get_translation(
179                    "system.config.language_set_failed",
180                    &[&e.to_string()]
181                )
182            );
183
184            let _ = crate::i18n::set_language(crate::i18n::DEFAULT_LANGUAGE);
185        }
186
187        Ok(())
188    }
189}
190
191impl Default for LanguageService {
192    fn default() -> Self {
193        Self::new()
194    }
195}