rush_sync_server/commands/lang/
mod.rs

1// =====================================================
2// FILE: src/commands/lang/mod.rs - VEREINFACHTES LANGUAGE-SYSTEM
3// =====================================================
4
5use crate::core::prelude::*;
6use crate::i18n::{get_available_languages, get_current_language, set_language};
7
8pub mod command;
9pub use command::LanguageCommand;
10
11// ✅ VEREINFACHT: Alle Language-Logik in einer Struktur
12#[derive(Debug)] // ✅ NEU
13pub struct LanguageService {
14    config_paths: Vec<std::path::PathBuf>,
15}
16
17impl LanguageService {
18    /// Erstellt neuen LanguageService
19    pub fn new() -> Self {
20        Self {
21            config_paths: crate::setup::setup_toml::get_config_paths(),
22        }
23    }
24
25    /// Zeigt aktuellen Status und verfügbare Sprachen
26    pub fn show_status(&self) -> String {
27        let current_lang = get_current_language();
28        let available_langs = get_available_languages().join(", ");
29
30        let current = crate::i18n::get_command_translation(
31            "system.commands.language.current",
32            &[&current_lang],
33        );
34        let available = crate::i18n::get_command_translation(
35            "system.commands.language.available",
36            &[&available_langs],
37        );
38
39        format!("{}\n{}", current, available)
40    }
41
42    /// Ändert die Sprache komplett (i18n + Config + Persistence)
43    pub async fn change_language(&mut self, lang: &str) -> Result<String> {
44        // ✅ 1. VALIDIERUNG + i18n setzen
45        match set_language(lang) {
46            Ok(()) => {
47                // ✅ 2. CONFIG PERSISTIEREN
48                if let Err(e) = self.save_to_config(lang).await {
49                    log::error!("Failed to save language config: {}", e);
50                    // Trotzdem Success, da i18n gesetzt wurde
51                }
52
53                // ✅ 3. SUCCESS MESSAGE (in neuer Sprache!)
54                Ok(self.create_save_message(
55                    lang,
56                    &crate::i18n::get_command_translation(
57                        "system.commands.language.changed",
58                        &[&lang.to_uppercase()],
59                    ),
60                ))
61            }
62            Err(e) => {
63                // ✅ 4. ERROR MESSAGE
64                Ok(crate::i18n::get_command_translation(
65                    "system.commands.language.invalid",
66                    &[&e.to_string()],
67                ))
68            }
69        }
70    }
71
72    /// Direkter Language-Switch ohne Config-Save (für sync calls)
73    pub fn switch_language_only(&self, lang: &str) -> Result<()> {
74        set_language(lang)
75    }
76
77    /// Verarbeitet __SAVE_LANGUAGE__ Messages von screen.rs
78    pub async fn process_save_message(message: &str) -> Option<String> {
79        if !message.starts_with("__SAVE_LANGUAGE__") {
80            return None;
81        }
82
83        let parts: Vec<&str> = message.split("__MESSAGE__").collect();
84        if parts.len() != 2 {
85            return None;
86        }
87
88        let lang_part = parts[0].replace("__SAVE_LANGUAGE__", "");
89        let display_message = parts[1];
90
91        // ✅ CONFIG SPEICHERN
92        let service = LanguageService::new();
93        if let Err(e) = service.save_to_config(&lang_part).await {
94            log::error!("Failed to save language config: {}", e);
95        }
96
97        Some(display_message.to_string())
98    }
99
100    /// Gibt verfügbare Sprachen zurück
101    pub fn get_available(&self) -> Vec<String> {
102        get_available_languages()
103    }
104
105    /// Gibt aktuelle Sprache zurück
106    pub fn get_current(&self) -> String {
107        get_current_language()
108    }
109
110    // ✅ PRIVATE HELPERS
111
112    /// Erstellt das spezielle Save-Message Format für screen.rs
113    fn create_save_message(&self, lang: &str, display_text: &str) -> String {
114        format!("__SAVE_LANGUAGE__{}__MESSAGE__{}", lang, display_text)
115    }
116
117    /// Speichert Sprache in Config-Datei
118    async fn save_to_config(&self, lang: &str) -> Result<()> {
119        for path in &self.config_paths {
120            if path.exists() {
121                let content = tokio::fs::read_to_string(path)
122                    .await
123                    .map_err(AppError::Io)?;
124                let updated_content = self.update_language_in_toml(&content, lang)?;
125                tokio::fs::write(path, updated_content)
126                    .await
127                    .map_err(AppError::Io)?;
128                log::debug!("Language '{}' saved to config", lang.to_uppercase());
129                return Ok(());
130            }
131        }
132        Ok(())
133    }
134
135    /// Updated language.current in TOML-Inhalt
136    fn update_language_in_toml(&self, content: &str, lang: &str) -> Result<String> {
137        let updated_content = if content.contains("[language]") {
138            // Bestehende current = Zeile ersetzen
139            content
140                .lines()
141                .map(|line| {
142                    if line.trim_start().starts_with("current =") {
143                        format!("current = \"{}\"", lang)
144                    } else {
145                        line.to_string()
146                    }
147                })
148                .collect::<Vec<_>>()
149                .join("\n")
150        } else {
151            // Language section hinzufügen
152            format!("{}\n\n[language]\ncurrent = \"{}\"", content.trim(), lang)
153        };
154
155        Ok(updated_content)
156    }
157
158    /// Lädt Sprache aus Config beim Startup
159    pub async fn load_from_config(&self) -> Option<String> {
160        for path in &self.config_paths {
161            if path.exists() {
162                if let Ok(content) = tokio::fs::read_to_string(path).await {
163                    if let Some(lang) = self.extract_language_from_toml(&content) {
164                        return Some(lang);
165                    }
166                }
167            }
168        }
169        None
170    }
171
172    /// Extrahiert language.current aus TOML String
173    fn extract_language_from_toml(&self, content: &str) -> Option<String> {
174        let mut in_language_section = false;
175
176        for line in content.lines() {
177            let trimmed = line.trim();
178
179            if trimmed == "[language]" {
180                in_language_section = true;
181                continue;
182            }
183
184            if trimmed.starts_with('[') && trimmed.ends_with(']') && trimmed != "[language]" {
185                in_language_section = false;
186                continue;
187            }
188
189            if in_language_section && trimmed.starts_with("current =") {
190                if let Some(value_part) = trimmed.split('=').nth(1) {
191                    let cleaned = value_part.trim().trim_matches('"').trim_matches('\'');
192                    return Some(cleaned.to_string());
193                }
194            }
195        }
196        None
197    }
198
199    /// ✅ NEU: Ersatz für LanguageConfig::load_and_apply_from_config
200    pub async fn load_and_apply_from_config(
201        &self,
202        config: &crate::core::config::Config,
203    ) -> Result<()> {
204        let lang = &config.language;
205
206        // ✅ SETZE i18n basierend auf Config
207        if let Err(e) = crate::i18n::set_language(lang) {
208            log::warn!(
209                "{}",
210                crate::i18n::get_translation(
211                    "system.config.language_set_failed",
212                    &[&e.to_string()]
213                )
214            );
215
216            // ✅ FALLBACK auf DEFAULT
217            let _ = crate::i18n::set_language(crate::i18n::DEFAULT_LANGUAGE);
218        }
219
220        Ok(())
221    }
222}
223
224impl Default for LanguageService {
225    fn default() -> Self {
226        Self::new()
227    }
228}