diff --git a/src-tauri/src/cmd/service.rs b/src-tauri/src/cmd/service.rs index c2f557de..75d1a544 100644 --- a/src-tauri/src/cmd/service.rs +++ b/src-tauri/src/cmd/service.rs @@ -3,6 +3,7 @@ use crate::{ core::service::{self, SERVICE_MANAGER, ServiceStatus}, utils::i18n::t, }; +use smartstring::SmartString; async fn execute_service_operation_sync(status: ServiceStatus, op_type: &str) -> CmdResult { if let Err(e) = SERVICE_MANAGER @@ -12,7 +13,7 @@ async fn execute_service_operation_sync(status: ServiceStatus, op_type: &str) -> .await { let emsg = format!("{} Service failed: {}", op_type, e); - return Err(t(emsg.as_str()).await); + return Err(SmartString::from(&*t(emsg.as_str()).await)); } Ok(()) } diff --git a/src-tauri/src/core/tray/menu_def.rs b/src-tauri/src/core/tray/menu_def.rs index 636665ac..c2acbe25 100644 --- a/src-tauri/src/core/tray/menu_def.rs +++ b/src-tauri/src/core/tray/menu_def.rs @@ -1,11 +1,11 @@ use crate::utils::i18n::t; -use smartstring::alias::String; +use std::sync::Arc; macro_rules! define_menu { ($($field:ident => $const_name:ident, $id:expr, $text:expr),+ $(,)?) => { #[derive(Debug)] pub struct MenuTexts { - $(pub $field: String,)+ + $(pub $field: Arc,)+ } pub struct MenuIds; diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index 64534031..6b4eef42 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -23,6 +23,7 @@ use futures::future::join_all; use parking_lot::Mutex; use smartstring::alias::String; use std::collections::HashMap; +use std::sync::Arc; use std::{ fs, sync::atomic::{AtomicBool, Ordering}, @@ -798,7 +799,7 @@ fn create_proxy_menu_item( app_handle: &AppHandle, show_proxy_groups_inline: bool, proxy_submenus: Vec>, - proxies_text: &String, + proxies_text: &Arc, ) -> Result { // 创建代理主菜单 let (proxies_submenu, inline_proxy_items) = if show_proxy_groups_inline { diff --git a/src-tauri/src/utils/i18n.rs b/src-tauri/src/utils/i18n.rs index e7a52caa..83f6140e 100644 --- a/src-tauri/src/utils/i18n.rs +++ b/src-tauri/src/utils/i18n.rs @@ -1,12 +1,18 @@ use crate::{config::Config, utils::dirs}; use once_cell::sync::Lazy; -use serde_json::Value; use smartstring::alias::String; -use std::{fs, path::PathBuf, sync::RwLock}; +use std::{ + collections::HashMap, + fs, + path::PathBuf, + sync::{Arc, RwLock}, +}; use sys_locale; const DEFAULT_LANGUAGE: &str = "zh"; +type TranslationMap = (String, HashMap>); + fn get_locales_dir() -> Option { dirs::app_resources_dir() .map(|resource_path| resource_path.join("locales")) @@ -44,18 +50,23 @@ pub async fn current_language() -> String { .unwrap_or_else(get_system_language) } -static TRANSLATIONS: Lazy)>> = Lazy::new(|| { +static TRANSLATIONS: Lazy> = Lazy::new(|| { let lang = get_system_language(); - let json = load_lang_file(&lang).unwrap_or_else(|| Value::Object(Default::default())); - RwLock::new((lang, Box::new(json))) + let map = load_lang_file(&lang).unwrap_or_default(); + RwLock::new((lang, map)) }); -fn load_lang_file(lang: &str) -> Option { +fn load_lang_file(lang: &str) -> Option>> { let locales_dir = get_locales_dir()?; let file_path = locales_dir.join(format!("{lang}.json")); fs::read_to_string(file_path) .ok() - .and_then(|content| serde_json::from_str(&content).ok()) + .and_then(|content| serde_json::from_str::>(&content).ok()) + .map(|map| { + map.into_iter() + .map(|(k, v)| (k, Arc::from(v.as_str()))) + .collect() + }) } fn get_system_language() -> String { @@ -66,38 +77,38 @@ fn get_system_language() -> String { .unwrap_or_else(|| DEFAULT_LANGUAGE.into()) } -pub async fn t(key: &str) -> String { +pub async fn t(key: &str) -> Arc { let current_lang = current_language().await; { if let Ok(cache) = TRANSLATIONS.read() && cache.0 == current_lang - && let Some(text) = cache.1.get(key).and_then(|val| val.as_str()) + && let Some(text) = cache.1.get(key) { - return text.into(); + return Arc::clone(text); } } - if let Some(new_json) = load_lang_file(¤t_lang) + if let Some(new_map) = load_lang_file(¤t_lang) && let Ok(mut cache) = TRANSLATIONS.write() { - *cache = (current_lang.clone(), Box::new(new_json)); + *cache = (current_lang.clone(), new_map); - if let Some(text) = cache.1.get(key).and_then(|val| val.as_str()) { - return text.into(); + if let Some(text) = cache.1.get(key) { + return Arc::clone(text); } } if current_lang != DEFAULT_LANGUAGE - && let Some(default_json) = load_lang_file(DEFAULT_LANGUAGE) + && let Some(default_map) = load_lang_file(DEFAULT_LANGUAGE) && let Ok(mut cache) = TRANSLATIONS.write() { - *cache = (DEFAULT_LANGUAGE.into(), Box::new(default_json)); + *cache = (DEFAULT_LANGUAGE.into(), default_map); - if let Some(text) = cache.1.get(key).and_then(|val| val.as_str()) { - return text.into(); + if let Some(text) = cache.1.get(key) { + return Arc::clone(text); } } - key.into() + Arc::from(key) }