chore: i18n (#5276)

* chore: notice i18n

* feat: add script to clean up unused i18n keys

* chore: cleanup i18n keys

* refactor(i18n/proxies): migrate proxies UI to structured locale keys

* chore: i18n for rule module

* chore: i18n for profile module

* chore: i18n for connections module

* chore: i18n for settings module

* chore: i18n for verge settings

* chore: i18n for theme settings

* chore: i18n for theme

* chore(i18n): components.home.*

* chore(i18n): remove unused i18n keys

* chore(i18n): components.profile.*

* chore(i18n): components.connection

* chore(i18n): pages.logs.*

* chore(i18n): pages.*.provider

* chore(i18n): components.settings.externalCors.*

* chore(i18n): components.settings.clash.*

* chore(i18n): components.settings.liteMode.*

* chore(i18n): components.settings.backup.*

* chore(i18n): components.settings.clash.port.*

* chore(i18n): components.settings.misc.*

* chore(i18n): components.settings.update.*

* chore(i18n): components.settings.sysproxy.*

* chore(i18n): components.settings.sysproxy.*

* chore(i18n): pages.profiles.notices/components.providers.notices

* refactor(notice): unify showNotice usage

* refactor(notice): add typed showNotice shortcuts, centralize defaults, and simplify subscriptions

* refactor: unify showNotice usage

* refactor(notice): unify showNotice API

* refactor(notice): unify showNotice usage

* chore(i18n): components.test.*

* chore(i18n): components.settings.dns.*

* chore(i18n): components.home.clashInfo.*

* chore(i18n): components.home.systemInfo.*

* chore(i18n): components.home.ipInfo/traffic.*

* chore(i18n): navigation.*

* refactor(i18n): remove pages.* namespace and migrate route texts under module-level page keys

* chore(i18n): common.*

* chore(i18n): common.*

* fix: change error handling in patch_profiles_config to return false when a switch is in progress

* fix: improve error handling in patch_profiles_config to prevent requests during profile switching

* fix: change error handling in patch_profiles_config to return false when a switch is in progress

fix: ensure CURRENT_SWITCHING_PROFILE is reset after config updates in perform_config_update and patch_profiles_config

* chore(i18n): restructure root-level locale keys into namespaces

* chore(i18n): add missing i18n keys

* docs: i18n guide

* chore: adjust i18n

* refactor(i18n): align UI actions and status labels with common keys

* refactor(i18n): unify two-name locale namespaces

* refactor(i18n/components): unify locale keys and update component references

* chore(i18n): add shared and entities namespaces to all locale files

* refactor(i18n): consolidate shared and entity namespaces across features

* chore(deps): update npm dependencies to ^7.3.5 (#5310)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* refactor(i18n): migrate shared editor modes and consolidate entities namespaces

* tmp

* refactor(i18n): flatten locales and move theme/validation strings

* docs: CONTRIBUTING_i18n.md

* refactor(i18n): restructure feedback and profile namespaces for better organization

* refactor(i18n): unify settings locale structure and update references

* refactor(i18n): reorganize locale keys for home, proxies, rules, connections, logs, unlock, and tests

* refactor(i18n/feedback/layout): unify shared toasts & normalize layout namespace

* refactor(i18n): centralize common UI strings in shared

* refactor(i18n): flatten headers and unify locale schema

* refactor(i18n): consolidate duplicate per-feature translations into shared namespace

* refactor(i18n): split locales into per-namespace files

* style: lint

* refactor(i18n): unify unlock UI translations under tests namespace

* feat(i18n): add type-checked translation keys

* style: eslint import order

* feat(i18n): replace ad-hoc loader with rust-i18n backend bundles

* chore(prebuild): remove locale-copy step

* fix(i18n, notice): propagate runtime params and update cleanup script path

* fix(i18n,notice): make locale formatting idempotent and guard early notice translations

* fix(i18n): resolve locale aliases and match OS codes correctly

* fix(unlock): use i18next-compatible double-brace interpolation in failure notice

* fix(i18n): route unlock error notices through translation keys

* fix(i18n): i18n types

* feat(i18n): localize upgrade notice for Clash core viewer

* fix(notice): ensure runtime overrides apply to prefix translations

* chore(i18n): replace literal notices with translation keys

* chore(i18n): types

* chore(i18n): regen typings before formatting to keep keys in sync

* chore(i18n): simply labels

* chore(i18n): adjust translation

* chore: remove eslint-plugin-i18next

* chore(i18n): add/refine Korean translations across frontend scopes and Rust backend (#5341)

* chore(i18n): translate settings.json (missed in previous pass) (#5343)

* chore(i18n): add/refine Korean translations across frontend scopes and Rust backend

* chore(i18n): add/refine Korean translations across frontend scopes and Rust backend

* fix(i18n-tauri): quote placeholder-leading value in ko.yml to prevent rust_i18n parse panic

* chore(i18n): translate settings.json (forgot to include previously)

---------

Co-authored-by: rozan <34974262+thelojan@users.noreply.github.com>
This commit is contained in:
Sline
2025-11-08 19:40:38 +08:00
committed by GitHub
co-authored by renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> rozan
parent c1a2e99f63
commit c8aa72186e
286 changed files with 24243 additions and 8867 deletions
+184 -14
View File
@@ -572,6 +572,12 @@ dependencies = [
"tokio",
]
[[package]]
name = "base62"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1adf9755786e27479693dedd3271691a92b5e242ab139cacb9fb8e7fb5381111"
[[package]]
name = "base64"
version = "0.13.1"
@@ -822,6 +828,16 @@ dependencies = [
"alloc-stdlib",
]
[[package]]
name = "bstr"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.19.0"
@@ -1131,6 +1147,7 @@ dependencies = [
"reqwest",
"reqwest_dav",
"runas",
"rust-i18n",
"scopeguard",
"serde",
"serde_json",
@@ -2847,7 +2864,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
dependencies = [
"heck 0.4.1",
"proc-macro-crate 2.0.2",
"proc-macro-crate 2.0.0",
"proc-macro-error",
"proc-macro2",
"quote",
@@ -2888,6 +2905,30 @@ dependencies = [
"xkeysym",
]
[[package]]
name = "globset"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "globwalk"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
dependencies = [
"bitflags 1.3.2",
"ignore",
"walkdir",
]
[[package]]
name = "gobject-sys"
version = "0.18.0"
@@ -3512,6 +3553,22 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "ignore"
version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata",
"same-file",
"walkdir",
"winapi-util",
]
[[package]]
name = "image"
version = "0.25.8"
@@ -3701,6 +3758,15 @@ dependencies = [
"waker-fn",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
@@ -4432,6 +4498,15 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "normpath"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf23ab2b905654b4cb177e30b629937b3868311d4e1cba859f899c041046e69b"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "notify-rust"
version = "4.11.7"
@@ -5554,11 +5629,10 @@ dependencies = [
[[package]]
name = "proc-macro-crate"
version = "2.0.2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24"
checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8"
dependencies = [
"toml_datetime 0.6.3",
"toml_edit 0.20.2",
]
@@ -6230,6 +6304,60 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "rust-i18n"
version = "3.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fda2551fdfaf6cc5ee283adc15e157047b92ae6535cf80f6d4962d05717dc332"
dependencies = [
"globwalk",
"once_cell",
"regex",
"rust-i18n-macro",
"rust-i18n-support",
"smallvec",
]
[[package]]
name = "rust-i18n-macro"
version = "3.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22baf7d7f56656d23ebe24f6bb57a5d40d2bce2a5f1c503e692b5b2fa450f965"
dependencies = [
"glob",
"once_cell",
"proc-macro2",
"quote",
"rust-i18n-support",
"serde",
"serde_json",
"serde_yaml",
"syn 2.0.108",
]
[[package]]
name = "rust-i18n-support"
version = "3.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940ed4f52bba4c0152056d771e563b7133ad9607d4384af016a134b58d758f19"
dependencies = [
"arc-swap",
"base62",
"globwalk",
"itertools 0.11.0",
"lazy_static",
"normpath",
"once_cell",
"proc-macro2",
"regex",
"serde",
"serde_json",
"serde_yaml",
"siphasher 1.0.1",
"toml 0.8.23",
"triomphe",
]
[[package]]
name = "rust-ini"
version = "0.21.3"
@@ -6672,6 +6800,19 @@ dependencies = [
"syn 2.0.108",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap 2.12.0",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "serde_yaml_ng"
version = "0.10.0"
@@ -7185,7 +7326,7 @@ dependencies = [
"cfg-expr",
"heck 0.5.0",
"pkg-config",
"toml 0.8.2",
"toml 0.8.23",
"version-compare",
]
@@ -8126,14 +8267,14 @@ dependencies = [
[[package]]
name = "toml"
version = "0.8.2"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.3",
"toml_edit 0.20.2",
"toml_datetime 0.6.11",
"toml_edit 0.22.27",
]
[[package]]
@@ -8153,9 +8294,9 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.3"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
@@ -8176,7 +8317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap 2.12.0",
"toml_datetime 0.6.3",
"toml_datetime 0.6.11",
"winnow 0.5.40",
]
@@ -8185,12 +8326,24 @@ name = "toml_edit"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
dependencies = [
"indexmap 2.12.0",
"toml_datetime 0.6.11",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap 2.12.0",
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.3",
"winnow 0.5.40",
"toml_datetime 0.6.11",
"toml_write",
"winnow 0.7.13",
]
[[package]]
@@ -8214,6 +8367,12 @@ dependencies = [
"winnow 0.7.13",
]
[[package]]
name = "toml_write"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "toml_writer"
version = "1.0.4"
@@ -8513,6 +8672,17 @@ dependencies = [
"petgraph",
]
[[package]]
name = "triomphe"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39"
dependencies = [
"arc-swap",
"serde",
"stable_deref_trait",
]
[[package]]
name = "try-lock"
version = "0.2.5"
+1
View File
@@ -87,6 +87,7 @@ clash_verge_service_ipc = { version = "2.0.21", features = [
"client",
], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" }
arc-swap = "1.7.1"
rust-i18n = "3.1.5"
[target.'cfg(windows)'.dependencies]
runas = "=1.2.0"
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: 대시보드
body: 대시보드 표시 상태가 업데이트되었습니다.
clashModeChanged:
title: 모드 전환
body: "{mode}(으)로 전환되었습니다."
systemProxyToggled:
title: 시스템 프록시
body: 시스템 프록시 상태가 업데이트되었습니다.
tunModeToggled:
title: TUN 모드
body: TUN 모드 상태가 업데이트되었습니다.
lightweightModeEntered:
title: 경량 모드
body: 경량 모드에 진입했습니다.
appQuit:
title: 곧 종료
body: Clash Verge가 곧 종료됩니다.
appHidden:
title: 앱이 숨겨짐
body: Clash Verge가 백그라운드에서 실행 중입니다.
service:
adminPrompt: 서비스를 설치하려면 관리자 권한이 필요합니다.
tray:
dashboard: 대시보드
ruleMode: 규칙 모드
globalMode: 전역 모드
directMode: 직접 모드
profiles: 프로필
proxies: 프록시
systemProxy: 시스템 프록시
tunMode: TUN 모드
closeAllConnections: 모든 연결 닫기
lightweightMode: 경량 모드
copyEnv: 환경 변수 복사
confDir: 구성 디렉터리
coreDir: 코어 디렉터리
logsDir: 로그 디렉터리
openDir: 디렉터리 열기
appLog: 애플리케이션 로그
coreLog: 코어 로그
restartClash: Clash 코어 재시작
restartApp: 애플리케이션 재시작
vergeVersion: Verge 버전
more: 더 보기
exit: 종료
tooltip:
systemProxy: 시스템 프록시
tun: TUN
profile: 프로필
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: 仪表板
body: 仪表板显示状态已更新。
clashModeChanged:
title: 模式切换
body: 已切换至 {mode}。
systemProxyToggled:
title: 系统代理
body: 系统代理状态已更新。
tunModeToggled:
title: TUN 模式
body: TUN 模式状态已更新。
lightweightModeEntered:
title: 轻量模式
body: 已进入轻量模式。
appQuit:
title: 即将退出
body: Clash Verge 即将退出。
appHidden:
title: 应用已隐藏
body: Clash Verge 正在后台运行。
service:
adminPrompt: 安装服务需要管理员权限
tray:
dashboard: 仪表板
ruleMode: 规则模式
globalMode: 全局模式
directMode: 直连模式
profiles: 订阅
proxies: 代理
systemProxy: 系统代理
tunMode: TUN 模式
closeAllConnections: 关闭所有连接
lightweightMode: 轻量模式
copyEnv: 复制环境变量
confDir: 配置目录
coreDir: 内核目录
logsDir: 日志目录
openDir: 打开目录
appLog: 应用日志
coreLog: 内核日志
restartClash: 重启 Clash 内核
restartApp: 重启应用
vergeVersion: Verge 版本
more: 更多
exit: 退出
tooltip:
systemProxy: 系统代理
tun: TUN
profile: 订阅
+52
View File
@@ -0,0 +1,52 @@
_version: 1
notifications:
dashboardToggled:
title: 儀表板
body: 儀表板顯示狀態已更新。
clashModeChanged:
title: 模式切換
body: 已切換至 {mode}。
systemProxyToggled:
title: 系統代理
body: 系統代理狀態已更新。
tunModeToggled:
title: TUN 模式
body: TUN 模式狀態已更新。
lightweightModeEntered:
title: 輕量模式
body: 已進入輕量模式。
appQuit:
title: 即將退出
body: Clash Verge 即將退出。
appHidden:
title: 應用已隱藏
body: Clash Verge 正在背景執行。
service:
adminPrompt: 安裝服務需要管理員權限
tray:
dashboard: 儀表板
ruleMode: 規則模式
globalMode: 全域模式
directMode: 直連模式
profiles: 訂閱
proxies: 代理
systemProxy: 系統代理
tunMode: TUN 模式
closeAllConnections: 關閉所有連線
lightweightMode: 輕量模式
copyEnv: 複製環境變數
confDir: 設定目錄
coreDir: 核心目錄
logsDir: 日誌目錄
openDir: 開啟目錄
appLog: 應用程式日誌
coreLog: 核心日誌
restartClash: 重新啟動 Clash 核心
restartApp: 重新啟動應用程式
vergeVersion: Verge 版本
more: 更多
exit: 離開
tooltip:
systemProxy: 系統代理
tun: TUN
profile: 訂閱
+2 -5
View File
@@ -1,8 +1,5 @@
use super::{CmdResult, StringifyErr};
use crate::{
core::service::{self, SERVICE_MANAGER, ServiceStatus},
utils::i18n::t,
};
use crate::core::service::{self, SERVICE_MANAGER, ServiceStatus};
use smartstring::SmartString;
async fn execute_service_operation_sync(status: ServiceStatus, op_type: &str) -> CmdResult {
@@ -13,7 +10,7 @@ async fn execute_service_operation_sync(status: ServiceStatus, op_type: &str) ->
.await
{
let emsg = format!("{} Service failed: {}", op_type, e);
return Err(SmartString::from(&*t(emsg.as_str()).await));
return Err(SmartString::from(emsg));
}
Ok(())
}
+6 -6
View File
@@ -227,8 +227,6 @@ fn linux_running_as_root() -> bool {
#[cfg(target_os = "macos")]
async fn uninstall_service() -> Result<()> {
use crate::utils::i18n::t;
logging!(info, Type::Service, "uninstall service");
let binary_path = dirs::service_path()?;
@@ -240,7 +238,9 @@ async fn uninstall_service() -> Result<()> {
let uninstall_shell: String = uninstall_path.to_string_lossy().into_owned();
let prompt = t("Service Administrator Prompt").await;
crate::utils::i18n::sync_locale().await;
let prompt = rust_i18n::t!("service.adminPrompt").to_string();
let command = format!(
r#"do shell script "sudo '{uninstall_shell}'" with administrator privileges with prompt "{prompt}""#
);
@@ -263,8 +263,6 @@ async fn uninstall_service() -> Result<()> {
#[cfg(target_os = "macos")]
async fn install_service() -> Result<()> {
use crate::utils::i18n::t;
logging!(info, Type::Service, "install service");
let binary_path = dirs::service_path()?;
@@ -276,7 +274,9 @@ async fn install_service() -> Result<()> {
let install_shell: String = install_path.to_string_lossy().into_owned();
let prompt = t("Service Administrator Prompt").await;
crate::utils::i18n::sync_locale().await;
let prompt = rust_i18n::t!("service.adminPrompt").to_string();
let command = format!(
r#"do shell script "sudo '{install_shell}'" with administrator privileges with prompt "{prompt}""#
);
+35 -27
View File
@@ -1,5 +1,12 @@
use crate::utils::i18n::t;
use std::sync::Arc;
use rust_i18n::t;
use std::{borrow::Cow, sync::Arc};
fn to_arc_str(value: Cow<'static, str>) -> Arc<str> {
match value {
Cow::Borrowed(s) => Arc::from(s),
Cow::Owned(s) => Arc::from(s.into_boxed_str()),
}
}
macro_rules! define_menu {
($($field:ident => $const_name:ident, $id:expr, $text:expr),+ $(,)?) => {
@@ -11,9 +18,10 @@ macro_rules! define_menu {
pub struct MenuIds;
impl MenuTexts {
pub async fn new() -> Self {
let ($($field,)+) = futures::join!($(t($text),)+);
Self { $($field,)+ }
pub fn new() -> Self {
Self {
$($field: to_arc_str(t!($text)),)+
}
}
}
@@ -24,26 +32,26 @@ macro_rules! define_menu {
}
define_menu! {
dashboard => DASHBOARD, "tray_dashboard", "Dashboard",
rule_mode => RULE_MODE, "tray_rule_mode", "Rule Mode",
global_mode => GLOBAL_MODE, "tray_global_mode", "Global Mode",
direct_mode => DIRECT_MODE, "tray_direct_mode", "Direct Mode",
profiles => PROFILES, "tray_profiles", "Profiles",
proxies => PROXIES, "tray_proxies", "Proxies",
system_proxy => SYSTEM_PROXY, "tray_system_proxy", "System Proxy",
tun_mode => TUN_MODE, "tray_tun_mode", "TUN Mode",
close_all_connections => CLOSE_ALL_CONNECTIONS, "tray_close_all_connections", "Close All Connections",
lightweight_mode => LIGHTWEIGHT_MODE, "tray_lightweight_mode", "LightWeight Mode",
copy_env => COPY_ENV, "tray_copy_env", "Copy Env",
conf_dir => CONF_DIR, "tray_conf_dir", "Conf Dir",
core_dir => CORE_DIR, "tray_core_dir", "Core Dir",
logs_dir => LOGS_DIR, "tray_logs_dir", "Logs Dir",
open_dir => OPEN_DIR, "tray_open_dir", "Open Dir",
app_log => APP_LOG, "tray_app_log", "Open App Log",
core_log => CORE_LOG, "tray_core_log", "Open Core Log",
restart_clash => RESTART_CLASH, "tray_restart_clash", "Restart Clash Core",
restart_app => RESTART_APP, "tray_restart_app", "Restart App",
verge_version => VERGE_VERSION, "tray_verge_version", "Verge Version",
more => MORE, "tray_more", "More",
exit => EXIT, "tray_exit", "Exit",
dashboard => DASHBOARD, "tray_dashboard", "tray.dashboard",
rule_mode => RULE_MODE, "tray_rule_mode", "tray.ruleMode",
global_mode => GLOBAL_MODE, "tray_global_mode", "tray.globalMode",
direct_mode => DIRECT_MODE, "tray_direct_mode", "tray.directMode",
profiles => PROFILES, "tray_profiles", "tray.profiles",
proxies => PROXIES, "tray_proxies", "tray.proxies",
system_proxy => SYSTEM_PROXY, "tray_system_proxy", "tray.systemProxy",
tun_mode => TUN_MODE, "tray_tun_mode", "tray.tunMode",
close_all_connections => CLOSE_ALL_CONNECTIONS, "tray_close_all_connections", "tray.closeAllConnections",
lightweight_mode => LIGHTWEIGHT_MODE, "tray_lightweight_mode", "tray.lightweightMode",
copy_env => COPY_ENV, "tray_copy_env", "tray.copyEnv",
conf_dir => CONF_DIR, "tray_conf_dir", "tray.confDir",
core_dir => CORE_DIR, "tray_core_dir", "tray.coreDir",
logs_dir => LOGS_DIR, "tray_logs_dir", "tray.logsDir",
open_dir => OPEN_DIR, "tray_open_dir", "tray.openDir",
app_log => APP_LOG, "tray_app_log", "tray.appLog",
core_log => CORE_LOG, "tray_core_log", "tray.coreLog",
restart_clash => RESTART_CLASH, "tray_restart_clash", "tray.restartClash",
restart_app => RESTART_APP, "tray_restart_app", "tray.restartApp",
verge_version => VERGE_VERSION, "tray_verge_version", "tray.vergeVersion",
more => MORE, "tray_more", "tray.more",
exit => EXIT, "tray_exit", "tray.exit",
}
+10 -6
View File
@@ -15,7 +15,7 @@ use crate::{
feat, logging,
module::lightweight::is_in_lightweight_mode,
singleton_lazy,
utils::{dirs::find_target_icons, i18n::t},
utils::{dirs::find_target_icons, i18n},
};
use super::handle;
@@ -438,6 +438,8 @@ impl Tray {
let app_handle = handle::Handle::app_handle();
i18n::sync_locale().await;
let verge = Config::verge().await.latest_arc();
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
@@ -464,9 +466,9 @@ impl Tray {
}
// Get localized strings before using them
let sys_proxy_text = t("SysProxy").await;
let tun_text = t("TUN").await;
let profile_text = t("Profile").await;
let sys_proxy_text = rust_i18n::t!("tray.tooltip.systemProxy");
let tun_text = rust_i18n::t!("tray.tooltip.tun");
let profile_text = rust_i18n::t!("tray.tooltip.profile");
let v = env!("CARGO_PKG_VERSION");
let reassembled_version = v.split_once('+').map_or_else(
@@ -622,7 +624,7 @@ async fn create_profile_menu_item(
CheckMenuItem::with_id(
&app_handle,
format!("profiles_{profile_uid}"),
t(profile_name).await,
profile_name.as_str(),
true,
is_current_profile,
None::<&str>,
@@ -817,6 +819,8 @@ async fn create_tray_menu(
) -> Result<tauri::menu::Menu<Wry>> {
let current_proxy_mode = mode.unwrap_or("");
i18n::sync_locale().await;
// 获取当前配置文件的选中代理组信息
let current_profile_selected = {
let profiles_config = Config::profiles().await;
@@ -877,7 +881,7 @@ async fn create_tray_menu(
create_profile_menu_item(app_handle, profile_uid_and_name).await?;
// Pre-fetch all localized strings
let texts = &MenuTexts::new().await;
let texts = MenuTexts::new();
// Convert to references only when needed
let profile_menu_items_refs: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
.iter()
+3
View File
@@ -25,12 +25,15 @@ use crate::{
use anyhow::Result;
use config::Config;
use once_cell::sync::OnceCell;
use rust_i18n::i18n;
use tauri::{AppHandle, Manager};
#[cfg(target_os = "macos")]
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_deep_link::DeepLinkExt;
use utils::logging::Type;
i18n!("locales", fallback = "zh");
pub static APP_HANDLE: OnceCell<AppHandle> = OnceCell::new();
/// Application initialization helper functions
mod app_init {
+78 -93
View File
@@ -1,43 +1,80 @@
use crate::{config::Config, utils::dirs};
use once_cell::sync::Lazy;
use smartstring::alias::String;
use std::{
collections::HashMap,
fs,
path::PathBuf,
sync::{Arc, RwLock},
};
use crate::config::Config;
use sys_locale;
const DEFAULT_LANGUAGE: &str = "zh";
type TranslationMap = (String, HashMap<String, Arc<str>>);
fn supported_languages_internal() -> Vec<&'static str> {
rust_i18n::available_locales!()
}
fn get_locales_dir() -> Option<PathBuf> {
dirs::app_resources_dir()
.map(|resource_path| resource_path.join("locales"))
.ok()
const fn fallback_language() -> &'static str {
DEFAULT_LANGUAGE
}
fn locale_alias(locale: &str) -> Option<&'static str> {
match locale {
"ja" | "ja-jp" | "jp" => Some("jp"),
"zh" | "zh-cn" | "zh-hans" | "zh-sg" | "zh-my" | "zh-chs" => Some("zh"),
"zh-tw" | "zh-hk" | "zh-hant" | "zh-mo" | "zh-cht" => Some("zhtw"),
_ => None,
}
}
fn resolve_supported_language(language: &str) -> Option<String> {
if language.is_empty() {
return None;
}
let normalized = language.to_lowercase().replace('_', "-");
let mut candidates: Vec<String> = Vec::new();
let mut push_candidate = |candidate: String| {
if !candidate.is_empty()
&& !candidates
.iter()
.any(|existing| existing.eq_ignore_ascii_case(&candidate))
{
candidates.push(candidate);
}
};
let segments: Vec<&str> = normalized.split('-').collect();
for i in (1..=segments.len()).rev() {
let prefix = segments[..i].join("-");
if let Some(alias) = locale_alias(&prefix) {
push_candidate(alias.to_string());
}
push_candidate(prefix);
}
let supported = supported_languages_internal();
candidates.into_iter().find(|candidate| {
supported
.iter()
.any(|&lang| lang.eq_ignore_ascii_case(candidate))
})
}
fn system_language() -> String {
sys_locale::get_locale()
.as_deref()
.and_then(resolve_supported_language)
.unwrap_or_else(|| fallback_language().to_string())
}
pub fn get_supported_languages() -> Vec<String> {
let mut languages = Vec::new();
supported_languages_internal()
.into_iter()
.map(|lang| lang.to_string())
.collect()
}
if let Some(locales_dir) = get_locales_dir()
&& let Ok(entries) = fs::read_dir(locales_dir)
{
for entry in entries.flatten() {
if let Some(file_name) = entry.file_name().to_str()
&& let Some(lang) = file_name.strip_suffix(".json")
{
languages.push(lang.into());
}
}
}
if languages.is_empty() {
languages.push(DEFAULT_LANGUAGE.into());
}
languages
pub fn set_locale(language: &str) {
let lang =
resolve_supported_language(language).unwrap_or_else(|| fallback_language().to_string());
rust_i18n::set_locale(&lang);
}
pub async fn current_language() -> String {
@@ -45,70 +82,18 @@ pub async fn current_language() -> String {
.await
.latest_arc()
.language
.as_deref()
.map(String::from)
.unwrap_or_else(get_system_language)
.clone()
.filter(|lang| !lang.is_empty())
.and_then(|lang| resolve_supported_language(&lang))
.unwrap_or_else(system_language)
}
static TRANSLATIONS: Lazy<RwLock<TranslationMap>> = Lazy::new(|| {
let lang = get_system_language();
let map = load_lang_file(&lang).unwrap_or_default();
RwLock::new((lang, map))
});
fn load_lang_file(lang: &str) -> Option<HashMap<String, Arc<str>>> {
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::<HashMap<String, String>>(&content).ok())
.map(|map| {
map.into_iter()
.map(|(k, v)| (k, Arc::from(v.as_str())))
.collect()
})
pub async fn sync_locale() -> String {
let language = current_language().await;
set_locale(&language);
language
}
fn get_system_language() -> String {
sys_locale::get_locale()
.map(|locale| locale.to_lowercase())
.and_then(|locale| locale.split(['_', '-']).next().map(String::from))
.filter(|lang| get_supported_languages().contains(lang))
.unwrap_or_else(|| DEFAULT_LANGUAGE.into())
}
pub async fn t(key: &str) -> Arc<str> {
let current_lang = current_language().await;
{
if let Ok(cache) = TRANSLATIONS.read()
&& cache.0 == current_lang
&& let Some(text) = cache.1.get(key)
{
return Arc::clone(text);
}
}
if let Some(new_map) = load_lang_file(&current_lang)
&& let Ok(mut cache) = TRANSLATIONS.write()
{
*cache = (current_lang.clone(), new_map);
if let Some(text) = cache.1.get(key) {
return Arc::clone(text);
}
}
if current_lang != DEFAULT_LANGUAGE
&& let Some(default_map) = load_lang_file(DEFAULT_LANGUAGE)
&& let Ok(mut cache) = TRANSLATIONS.write()
{
*cache = (DEFAULT_LANGUAGE.into(), default_map);
if let Some(text) = cache.1.get(key) {
return Arc::clone(text);
}
}
Arc::from(key)
pub const fn default_language() -> &'static str {
fallback_language()
}
+24 -29
View File
@@ -1,5 +1,4 @@
use crate::{core::handle, utils::i18n::t};
use crate::{core::handle, utils::i18n};
use tauri_plugin_notification::NotificationExt;
pub enum NotificationEvent<'a> {
@@ -27,48 +26,44 @@ fn notify(title: &str, body: &str) {
}
pub async fn notify_event<'a>(event: NotificationEvent<'a>) {
i18n::sync_locale().await;
match event {
NotificationEvent::DashboardToggled => {
notify(
&t("DashboardToggledTitle").await,
&t("DashboardToggledBody").await,
);
let title = rust_i18n::t!("notifications.dashboardToggled.title").to_string();
let body = rust_i18n::t!("notifications.dashboardToggled.body").to_string();
notify(&title, &body);
}
NotificationEvent::ClashModeChanged { mode } => {
notify(
&t("ClashModeChangedTitle").await,
&t_with_args("ClashModeChangedBody", mode).await,
);
let title = rust_i18n::t!("notifications.clashModeChanged.title").to_string();
let body = rust_i18n::t!("notifications.clashModeChanged.body").replace("{mode}", mode);
notify(&title, &body);
}
NotificationEvent::SystemProxyToggled => {
notify(
&t("SystemProxyToggledTitle").await,
&t("SystemProxyToggledBody").await,
);
let title = rust_i18n::t!("notifications.systemProxyToggled.title").to_string();
let body = rust_i18n::t!("notifications.systemProxyToggled.body").to_string();
notify(&title, &body);
}
NotificationEvent::TunModeToggled => {
notify(
&t("TunModeToggledTitle").await,
&t("TunModeToggledBody").await,
);
let title = rust_i18n::t!("notifications.tunModeToggled.title").to_string();
let body = rust_i18n::t!("notifications.tunModeToggled.body").to_string();
notify(&title, &body);
}
NotificationEvent::LightweightModeEntered => {
notify(
&t("LightweightModeEnteredTitle").await,
&t("LightweightModeEnteredBody").await,
);
let title = rust_i18n::t!("notifications.lightweightModeEntered.title").to_string();
let body = rust_i18n::t!("notifications.lightweightModeEntered.body").to_string();
notify(&title, &body);
}
NotificationEvent::AppQuit => {
notify(&t("AppQuitTitle").await, &t("AppQuitBody").await);
let title = rust_i18n::t!("notifications.appQuit.title").to_string();
let body = rust_i18n::t!("notifications.appQuit.body").to_string();
notify(&title, &body);
}
#[cfg(target_os = "macos")]
NotificationEvent::AppHidden => {
notify(&t("AppHiddenTitle").await, &t("AppHiddenBody").await);
let title = rust_i18n::t!("notifications.appHidden.title").to_string();
let body = rust_i18n::t!("notifications.appHidden.body").to_string();
notify(&title, &body);
}
}
}
// 辅助函数,带参数的i18n
async fn t_with_args(key: &str, mode: &str) -> String {
t(key).await.replace("{mode}", mode)
}
+1 -1
View File
@@ -11,7 +11,7 @@
"icons/icon.icns",
"icons/icon.ico"
],
"resources": ["resources", "resources/locales/*"],
"resources": ["resources"],
"publisher": "Clash Verge Rev",
"externalBin": ["sidecar/verge-mihomo", "sidecar/verge-mihomo-alpha"],
"copyright": "GNU General Public License v3.0",