mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
refactor(i18n): optimize translation handling with Arc<str> for better memory efficiency
refactor(tray): change menu text storage to use Arc<str> for improved performance refactor(service): utilize SmartString for error messages to enhance memory management
This commit is contained in:
@@ -3,6 +3,7 @@ use crate::{
|
|||||||
core::service::{self, SERVICE_MANAGER, ServiceStatus},
|
core::service::{self, SERVICE_MANAGER, ServiceStatus},
|
||||||
utils::i18n::t,
|
utils::i18n::t,
|
||||||
};
|
};
|
||||||
|
use smartstring::SmartString;
|
||||||
|
|
||||||
async fn execute_service_operation_sync(status: ServiceStatus, op_type: &str) -> CmdResult {
|
async fn execute_service_operation_sync(status: ServiceStatus, op_type: &str) -> CmdResult {
|
||||||
if let Err(e) = SERVICE_MANAGER
|
if let Err(e) = SERVICE_MANAGER
|
||||||
@@ -12,7 +13,7 @@ async fn execute_service_operation_sync(status: ServiceStatus, op_type: &str) ->
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
let emsg = format!("{} Service failed: {}", op_type, e);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::utils::i18n::t;
|
use crate::utils::i18n::t;
|
||||||
use smartstring::alias::String;
|
use std::sync::Arc;
|
||||||
|
|
||||||
macro_rules! define_menu {
|
macro_rules! define_menu {
|
||||||
($($field:ident => $const_name:ident, $id:expr, $text:expr),+ $(,)?) => {
|
($($field:ident => $const_name:ident, $id:expr, $text:expr),+ $(,)?) => {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MenuTexts {
|
pub struct MenuTexts {
|
||||||
$(pub $field: String,)+
|
$(pub $field: Arc<str>,)+
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MenuIds;
|
pub struct MenuIds;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use futures::future::join_all;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use smartstring::alias::String;
|
use smartstring::alias::String;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
@@ -798,7 +799,7 @@ fn create_proxy_menu_item(
|
|||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
show_proxy_groups_inline: bool,
|
show_proxy_groups_inline: bool,
|
||||||
proxy_submenus: Vec<Submenu<Wry>>,
|
proxy_submenus: Vec<Submenu<Wry>>,
|
||||||
proxies_text: &String,
|
proxies_text: &Arc<str>,
|
||||||
) -> Result<ProxyMenuItem> {
|
) -> Result<ProxyMenuItem> {
|
||||||
// 创建代理主菜单
|
// 创建代理主菜单
|
||||||
let (proxies_submenu, inline_proxy_items) = if show_proxy_groups_inline {
|
let (proxies_submenu, inline_proxy_items) = if show_proxy_groups_inline {
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
use crate::{config::Config, utils::dirs};
|
use crate::{config::Config, utils::dirs};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde_json::Value;
|
|
||||||
use smartstring::alias::String;
|
use smartstring::alias::String;
|
||||||
use std::{fs, path::PathBuf, sync::RwLock};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
};
|
||||||
use sys_locale;
|
use sys_locale;
|
||||||
|
|
||||||
const DEFAULT_LANGUAGE: &str = "zh";
|
const DEFAULT_LANGUAGE: &str = "zh";
|
||||||
|
|
||||||
|
type TranslationMap = (String, HashMap<String, Arc<str>>);
|
||||||
|
|
||||||
fn get_locales_dir() -> Option<PathBuf> {
|
fn get_locales_dir() -> Option<PathBuf> {
|
||||||
dirs::app_resources_dir()
|
dirs::app_resources_dir()
|
||||||
.map(|resource_path| resource_path.join("locales"))
|
.map(|resource_path| resource_path.join("locales"))
|
||||||
@@ -44,18 +50,23 @@ pub async fn current_language() -> String {
|
|||||||
.unwrap_or_else(get_system_language)
|
.unwrap_or_else(get_system_language)
|
||||||
}
|
}
|
||||||
|
|
||||||
static TRANSLATIONS: Lazy<RwLock<(String, Box<Value>)>> = Lazy::new(|| {
|
static TRANSLATIONS: Lazy<RwLock<TranslationMap>> = Lazy::new(|| {
|
||||||
let lang = get_system_language();
|
let lang = get_system_language();
|
||||||
let json = load_lang_file(&lang).unwrap_or_else(|| Value::Object(Default::default()));
|
let map = load_lang_file(&lang).unwrap_or_default();
|
||||||
RwLock::new((lang, Box::new(json)))
|
RwLock::new((lang, map))
|
||||||
});
|
});
|
||||||
|
|
||||||
fn load_lang_file(lang: &str) -> Option<Value> {
|
fn load_lang_file(lang: &str) -> Option<HashMap<String, Arc<str>>> {
|
||||||
let locales_dir = get_locales_dir()?;
|
let locales_dir = get_locales_dir()?;
|
||||||
let file_path = locales_dir.join(format!("{lang}.json"));
|
let file_path = locales_dir.join(format!("{lang}.json"));
|
||||||
fs::read_to_string(file_path)
|
fs::read_to_string(file_path)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|content| serde_json::from_str(&content).ok())
|
.and_then(|content| serde_json::from_str::<HashMap<String, String>>(&content).ok())
|
||||||
|
.map(|map| {
|
||||||
|
map.into_iter()
|
||||||
|
.map(|(k, v)| (k, Arc::from(v.as_str())))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_system_language() -> String {
|
fn get_system_language() -> String {
|
||||||
@@ -66,38 +77,38 @@ fn get_system_language() -> String {
|
|||||||
.unwrap_or_else(|| DEFAULT_LANGUAGE.into())
|
.unwrap_or_else(|| DEFAULT_LANGUAGE.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn t(key: &str) -> String {
|
pub async fn t(key: &str) -> Arc<str> {
|
||||||
let current_lang = current_language().await;
|
let current_lang = current_language().await;
|
||||||
|
|
||||||
{
|
{
|
||||||
if let Ok(cache) = TRANSLATIONS.read()
|
if let Ok(cache) = TRANSLATIONS.read()
|
||||||
&& cache.0 == current_lang
|
&& 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()
|
&& 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()) {
|
if let Some(text) = cache.1.get(key) {
|
||||||
return text.into();
|
return Arc::clone(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if current_lang != DEFAULT_LANGUAGE
|
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()
|
&& 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()) {
|
if let Some(text) = cache.1.get(key) {
|
||||||
return text.into();
|
return Arc::clone(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
key.into()
|
Arc::from(key)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user