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                log::debug!("Language '{}' saved to config", lang.to_uppercase());
105                return Ok(());
106            }
107        }
108        Ok(())
109    }
110
111    fn update_language_in_toml(&self, content: &str, lang: &str) -> Result<String> {
112        let updated_content = if content.contains("[language]") {
113            content
114                .lines()
115                .map(|line| {
116                    if line.trim_start().starts_with("current =") {
117                        format!("current = \"{}\"", lang)
118                    } else {
119                        line.to_string()
120                    }
121                })
122                .collect::<Vec<_>>()
123                .join("\n")
124        } else {
125            format!("{}\n\n[language]\ncurrent = \"{}\"", content.trim(), lang)
126        };
127
128        Ok(updated_content)
129    }
130
131    pub async fn load_from_config(&self) -> Option<String> {
132        for path in &self.config_paths {
133            if path.exists() {
134                if let Ok(content) = tokio::fs::read_to_string(path).await {
135                    if let Some(lang) = self.extract_language_from_toml(&content) {
136                        return Some(lang);
137                    }
138                }
139            }
140        }
141        None
142    }
143
144    fn extract_language_from_toml(&self, content: &str) -> Option<String> {
145        let mut in_language_section = false;
146
147        for line in content.lines() {
148            let trimmed = line.trim();
149
150            if trimmed == "[language]" {
151                in_language_section = true;
152                continue;
153            }
154
155            if trimmed.starts_with('[') && trimmed.ends_with(']') && trimmed != "[language]" {
156                in_language_section = false;
157                continue;
158            }
159
160            if in_language_section && trimmed.starts_with("current =") {
161                if let Some(value_part) = trimmed.split('=').nth(1) {
162                    let cleaned = value_part.trim().trim_matches('"').trim_matches('\'');
163                    return Some(cleaned.to_string());
164                }
165            }
166        }
167        None
168    }
169
170    pub async fn load_and_apply_from_config(
171        &self,
172        config: &crate::core::config::Config,
173    ) -> Result<()> {
174        let lang = &config.language;
175
176        if let Err(e) = crate::i18n::set_language(lang) {
177            log::warn!(
178                "{}",
179                crate::i18n::get_translation(
180                    "system.config.language_set_failed",
181                    &[&e.to_string()]
182                )
183            );
184
185            let _ = crate::i18n::set_language(crate::i18n::DEFAULT_LANGUAGE);
186        }
187
188        Ok(())
189    }
190}
191
192impl Default for LanguageService {
193    fn default() -> Self {
194        Self::new()
195    }
196}