From b8ad90ede39eda890c202b17be87674f92f2c2e7 Mon Sep 17 00:00:00 2001 From: Loren Eteval Date: Sat, 1 Aug 2026 22:51:39 +0800 Subject: [PATCH] Preserve literal plugin routing labels Signed-off-by: Loren Eteval --- Furious/Externals/GenTranslation.py | 9 ++++++++- Furious/Plugins/API.py | 1 + Furious/Plugins/Official/Hysteria1/Plugin.py | 3 ++- Furious/Plugins/Official/Xray/Plugin.py | 8 ++++++-- Furious/Plugins/README.md | 4 +++- Furious/Plugins/Registry.py | 4 ++++ Furious/TrayActions/Routing.py | 6 +++++- 7 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Furious/Externals/GenTranslation.py b/Furious/Externals/GenTranslation.py index dd04f67d..d1a8a77d 100644 --- a/Furious/Externals/GenTranslation.py +++ b/Furious/Externals/GenTranslation.py @@ -1048,7 +1048,6 @@ TRANSLATION = { }, "Routing": { "source": [ - "Furious.Plugins.Official.Xray.Plugin", "Furious.TrayActions.Routing" ], "RU": "Маршрутизация", @@ -2147,5 +2146,13 @@ TRANSLATION = { "RU": "Управление файлами ресурсов Xray-core...", "ZH": "管理Xray-core资源文件...", "isReviewed": "True" + }, + "Edit Routing...": { + "source": [ + "Furious.Plugins.Official.Xray.Plugin" + ], + "RU": "Редактировать маршрут...", + "ZH": "编辑路由...", + "isReviewed": "True" } } diff --git a/Furious/Plugins/API.py b/Furious/Plugins/API.py index f66472e7..23af2272 100644 --- a/Furious/Plugins/API.py +++ b/Furious/Plugins/API.py @@ -32,6 +32,7 @@ class PluginRouting: id: str displayName: str separatorBefore: bool = False + translatable: bool = False class FuriousPlugin: diff --git a/Furious/Plugins/Official/Hysteria1/Plugin.py b/Furious/Plugins/Official/Hysteria1/Plugin.py index 91fbcbc7..bb451504 100644 --- a/Furious/Plugins/Official/Hysteria1/Plugin.py +++ b/Furious/Plugins/Official/Hysteria1/Plugin.py @@ -83,7 +83,8 @@ class Hysteria1Plugin(FuriousPlugin): def routingOptions(self, config=None): """Return the routing modes supported by Hysteria 1.""" return tuple( - PluginRouting(routing.value, routing.value) for routing in AppBuiltinRouting + PluginRouting(routing.value, routing.value, translatable=True) + for routing in AppBuiltinRouting ) def startCore( diff --git a/Furious/Plugins/Official/Xray/Plugin.py b/Furious/Plugins/Official/Xray/Plugin.py index a9f2ffd1..c839b3e8 100644 --- a/Furious/Plugins/Official/Xray/Plugin.py +++ b/Furious/Plugins/Official/Xray/Plugin.py @@ -161,7 +161,7 @@ class XrayPlugin(FuriousPlugin): def createManagementActions(self, parent=None, **kwargs): """Create Xray routing and asset-management actions.""" # These modules require a fully initialized Furious.Qt package. - from Furious.Qt import AppQAction, AppQSeperator + from Furious.Qt import AppQAction, AppQSeperator, bootstrapIcon from Furious.Qt import gettext as _ from .UserRoutingWindow import UserRoutingWindow @@ -172,7 +172,8 @@ class XrayPlugin(FuriousPlugin): return ( AppQAction( - _('Routing'), + _('Edit Routing...'), + icon=bootstrapIcon('signpost.svg'), callback=routingEditor.show, ), AppQSeperator(), @@ -188,14 +189,17 @@ class XrayPlugin(FuriousPlugin): PluginRouting( AppBuiltinRouting.BypassMainlandChina.value, AppBuiltinRouting.BypassMainlandChina.value, + translatable=True, ), PluginRouting( AppBuiltinRouting.Global.value, AppBuiltinRouting.Global.value, + translatable=True, ), PluginRouting( AppBuiltinRouting.Custom.value, AppBuiltinRouting.Custom.value, + translatable=True, ), ] enabledProfiles = tuple( diff --git a/Furious/Plugins/README.md b/Furious/Plugins/README.md index 13cf98b6..2bf353e8 100644 --- a/Furious/Plugins/README.md +++ b/Furious/Plugins/README.md @@ -67,7 +67,9 @@ class ExamplePlugin(FuriousPlugin): supported by the given configuration. The tray Routing submenu follows the active server's plugin and is hidden when that plugin returns no routing modes. The host validates the current setting and falls back to the plugin's first -option before starting its core. +option before starting its core. Routing display names are treated as literal +text by default; set `translatable=True` only when the display name is an +application translation key. User-provided labels should remain literal. Configuration classes should subclass `ConfigFactory` and implement its table display, proxy endpoint, validity, serialization, and URI methods. Core process diff --git a/Furious/Plugins/Registry.py b/Furious/Plugins/Registry.py index a8c69993..2754cad6 100644 --- a/Furious/Plugins/Registry.py +++ b/Furious/Plugins/Registry.py @@ -228,6 +228,10 @@ class PluginRegistry: raise TypeError( 'plugin routing option display name must be a string' ) + if not isinstance(option.translatable, bool): + raise TypeError( + 'plugin routing option translatable flag must be a boolean' + ) optionId = option.id if optionId in optionIds: diff --git a/Furious/TrayActions/Routing.py b/Furious/TrayActions/Routing.py index b18daf22..9fb7897a 100644 --- a/Furious/TrayActions/Routing.py +++ b/Furious/TrayActions/Routing.py @@ -92,7 +92,11 @@ class RoutingAction(AppQAction): actions.append( RoutingChildAction( - _(option.displayName), + ( + _(option.displayName) + if option.translatable + else option.displayName + ), routingValue=option.id, checkable=True, checked=routing == option.id,