From f947fbd6c654cc7eefe3b7e8ad9ef11fe0dde30e Mon Sep 17 00:00:00 2001 From: Misfit-s Date: Thu, 4 Jun 2026 22:55:51 +0300 Subject: [PATCH] feat(Clash): Add routing rules and enable routing option for Clash subscriptions (#4904) * feat(clash): add routing rules and enable routing option for Clash/Mihomo subscriptions Allows adding custom YAML blocks and placeholders to Clash exports. Why: Shifting routing to the client prevents server IP exposure for DIRECT traffic and reduces unnecessary server bandwidth/CPU usage. * fix --------- Co-authored-by: Misfit-s <> --- frontend/src/generated/types.ts | 4 + frontend/src/generated/zod.ts | 4 + frontend/src/models/setting.ts | 2 + frontend/src/pages/api-docs/endpoints.ts | 2 +- .../pages/settings/SubscriptionGeneralTab.tsx | 14 +++ frontend/src/schemas/setting.ts | 2 + sub/sub.go | 12 +- sub/subClashService.go | 119 ++++++++++++++++-- sub/subController.go | 4 +- web/entity/entity.go | 2 + web/service/setting.go | 10 ++ web/translation/ar-EG.json | 4 + web/translation/en-US.json | 4 + web/translation/es-ES.json | 4 + web/translation/fa-IR.json | 4 + web/translation/id-ID.json | 4 + web/translation/ja-JP.json | 4 + web/translation/pt-BR.json | 4 + web/translation/ru-RU.json | 4 + web/translation/tr-TR.json | 4 + web/translation/uk-UA.json | 4 + web/translation/vi-VN.json | 4 + web/translation/zh-CN.json | 4 + web/translation/zh-TW.json | 4 + 24 files changed, 212 insertions(+), 15 deletions(-) diff --git a/frontend/src/generated/types.ts b/frontend/src/generated/types.ts index 23da8193..5e85e98c 100644 --- a/frontend/src/generated/types.ts +++ b/frontend/src/generated/types.ts @@ -34,7 +34,9 @@ export interface AllSetting { subAnnounce: string; subCertFile: string; subClashEnable: boolean; + subClashEnableRouting: boolean; subClashPath: string; + subClashRules: string; subClashURI: string; subDomain: string; subEmailInRemark: boolean; @@ -121,7 +123,9 @@ export interface AllSettingView { subAnnounce: string; subCertFile: string; subClashEnable: boolean; + subClashEnableRouting: boolean; subClashPath: string; + subClashRules: string; subClashURI: string; subDomain: string; subEmailInRemark: boolean; diff --git a/frontend/src/generated/zod.ts b/frontend/src/generated/zod.ts index e64c26f9..470eb756 100644 --- a/frontend/src/generated/zod.ts +++ b/frontend/src/generated/zod.ts @@ -36,7 +36,9 @@ export const AllSettingSchema = z.object({ subAnnounce: z.string(), subCertFile: z.string(), subClashEnable: z.boolean(), + subClashEnableRouting: z.boolean(), subClashPath: z.string(), + subClashRules: z.string(), subClashURI: z.string(), subDomain: z.string(), subEmailInRemark: z.boolean(), @@ -124,7 +126,9 @@ export const AllSettingViewSchema = z.object({ subAnnounce: z.string(), subCertFile: z.string(), subClashEnable: z.boolean(), + subClashEnableRouting: z.boolean(), subClashPath: z.string(), + subClashRules: z.string(), subClashURI: z.string(), subDomain: z.string(), subEmailInRemark: z.boolean(), diff --git a/frontend/src/models/setting.ts b/frontend/src/models/setting.ts index fcbe1ec1..a8b40d44 100644 --- a/frontend/src/models/setting.ts +++ b/frontend/src/models/setting.ts @@ -55,6 +55,8 @@ export class AllSetting { subURI = ''; subJsonURI = ''; subClashURI = ''; + subClashEnableRouting = false; + subClashRules = ''; subJsonFragment = ''; subJsonNoises = ''; subJsonMux = ''; diff --git a/frontend/src/pages/api-docs/endpoints.ts b/frontend/src/pages/api-docs/endpoints.ts index 2233e211..3d6e4c02 100644 --- a/frontend/src/pages/api-docs/endpoints.ts +++ b/frontend/src/pages/api-docs/endpoints.ts @@ -1114,7 +1114,7 @@ export const sections: readonly Section[] = [ { method: 'GET', path: '/{clashPath}:subid', - summary: 'Return subscription as a Clash/Mihomo-compatible YAML config. Only when Clash subscription is enabled in settings. Default path: /clash/:subid.', + summary: 'Return subscription as a Clash/Mihomo-compatible YAML config, including configured global Clash routing rules. Only when Clash subscription is enabled in settings. Default path: /clash/:subid.', params: [ { name: 'subid', in: 'path', type: 'string', desc: 'Client subscription ID.' }, ], diff --git a/frontend/src/pages/settings/SubscriptionGeneralTab.tsx b/frontend/src/pages/settings/SubscriptionGeneralTab.tsx index ec37b827..88b6e160 100644 --- a/frontend/src/pages/settings/SubscriptionGeneralTab.tsx +++ b/frontend/src/pages/settings/SubscriptionGeneralTab.tsx @@ -166,6 +166,20 @@ export default function SubscriptionGeneralTab({ allSetting, updateSetting }: Su updateSetting({ subRoutingRules: e.target.value })} /> + + Clash / Mihomo + + + updateSetting({ subClashEnableRouting: v })} /> + + + updateSetting({ subClashRules: e.target.value })} + /> + ), }, diff --git a/frontend/src/schemas/setting.ts b/frontend/src/schemas/setting.ts index 66d061df..b2341569 100644 --- a/frontend/src/schemas/setting.ts +++ b/frontend/src/schemas/setting.ts @@ -59,6 +59,8 @@ export const AllSettingSchema = z.object({ subURI: z.string().optional(), subJsonURI: z.string().optional(), subClashURI: z.string().optional(), + subClashEnableRouting: z.boolean().optional(), + subClashRules: z.string().optional(), subJsonFragment: z.string().optional(), subJsonNoises: z.string().optional(), subJsonMux: z.string().optional(), diff --git a/sub/sub.go b/sub/sub.go index 667ea006..9109947a 100644 --- a/sub/sub.go +++ b/sub/sub.go @@ -140,6 +140,16 @@ func (s *Server) initRouter() (*gin.Engine, error) { SubJsonRules = "" } + SubClashEnableRouting, err := s.settingService.GetSubClashEnableRouting() + if err != nil { + SubClashEnableRouting = false + } + + SubClashRules, err := s.settingService.GetSubClashRules() + if err != nil { + SubClashRules = "" + } + SubTitle, err := s.settingService.GetSubTitle() if err != nil { SubTitle = "" @@ -226,7 +236,7 @@ func (s *Server) initRouter() (*gin.Engine, error) { s.sub = NewSUBController( g, LinksPath, JsonPath, ClashPath, subJsonEnable, subClashEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates, - SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubTitle, SubSupportUrl, + SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubClashEnableRouting, SubClashRules, SubTitle, SubSupportUrl, SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules) return engine, nil diff --git a/sub/subClashService.go b/sub/subClashService.go index 1dc61d67..c15639bf 100644 --- a/sub/subClashService.go +++ b/sub/subClashService.go @@ -15,17 +15,13 @@ import ( type SubClashService struct { inboundService service.InboundService + enableRouting bool + clashRules string SubService *SubService } -type ClashConfig struct { - Proxies []map[string]any `yaml:"proxies"` - ProxyGroups []map[string]any `yaml:"proxy-groups"` - Rules []string `yaml:"rules"` -} - -func NewSubClashService(subService *SubService) *SubClashService { - return &SubClashService{SubService: subService} +func NewSubClashService(enableRouting bool, clashRules string, subService *SubService) *SubClashService { + return &SubClashService{enableRouting: enableRouting, clashRules: clashRules, SubService: subService} } func (s *SubClashService) GetClash(subId string, host string) (string, string, error) { @@ -76,14 +72,20 @@ func (s *SubClashService) GetClash(subId string, host string) (string, string, e } proxyNames = append(proxyNames, "DIRECT") - config := ClashConfig{ - Proxies: proxies, - ProxyGroups: []map[string]any{{ + config := map[string]any{ + "proxies": proxies, + "proxy-groups": []map[string]any{{ "name": "PROXY", "type": "select", "proxies": proxyNames, }}, - Rules: []string{"MATCH,PROXY"}, + "rules": []string{"MATCH,PROXY"}, + } + + if s.enableRouting { + if err := mergeClashRulesYAML(config, s.clashRules); err != nil { + return "", "", err + } } finalYAML, err := yaml.Marshal(config) @@ -554,3 +556,96 @@ func cloneMap(src map[string]any) map[string]any { maps.Copy(dst, src) return dst } + +func mergeClashRulesYAML(base map[string]any, raw string) error { + raw = strings.TrimSpace(raw) + if raw == "" { + return nil + } + + var custom any + if err := yaml.Unmarshal([]byte(raw), &custom); err != nil { + mergeClashRules(base, linesToClashRules(raw)) + return nil + } + + switch typed := custom.(type) { + case []any: + mergeClashRules(base, typed) + case map[string]any: + if rules, ok := typed["rules"]; ok { + if ruleList, ok := asAnySlice(rules); ok { + mergeClashRules(base, ruleList) + } + } + default: + mergeClashRules(base, linesToClashRules(raw)) + } + + return nil +} + +func mergeClashRules(base map[string]any, customRules []any) { + if len(customRules) == 0 { + return + } + + baseRules, _ := asAnySlice(base["rules"]) + if hasClashMatchRule(customRules) { + base["rules"] = customRules + return + } + + merged := make([]any, 0, len(customRules)+len(baseRules)) + merged = append(merged, customRules...) + merged = append(merged, baseRules...) + base["rules"] = merged +} + +func asAnySlice(value any) ([]any, bool) { + switch typed := value.(type) { + case []any: + return typed, true + case []string: + out := make([]any, 0, len(typed)) + for _, item := range typed { + out = append(out, item) + } + return out, true + case []map[string]any: + out := make([]any, 0, len(typed)) + for _, item := range typed { + out = append(out, item) + } + return out, true + default: + return nil, false + } +} + +func hasClashMatchRule(rules []any) bool { + for _, rule := range rules { + ruleText, ok := rule.(string) + if !ok { + continue + } + parts := strings.SplitN(ruleText, ",", 2) + if strings.EqualFold(strings.TrimSpace(parts[0]), "MATCH") { + return true + } + } + return false +} + +func linesToClashRules(raw string) []any { + lines := strings.Split(raw, "\n") + rules := make([]any, 0, len(lines)) + for _, line := range lines { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + rules = append(rules, line) + } + return rules +} diff --git a/sub/subController.go b/sub/subController.go index 05569a54..77609992 100644 --- a/sub/subController.go +++ b/sub/subController.go @@ -66,6 +66,8 @@ func NewSUBController( jsonNoise string, jsonMux string, jsonRules string, + clashEnableRouting bool, + clashRules string, subTitle string, subSupportUrl string, subProfileUrl string, @@ -91,7 +93,7 @@ func NewSUBController( subService: sub, subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub), - subClashService: NewSubClashService(sub), + subClashService: NewSubClashService(clashEnableRouting, clashRules, sub), } a.initRouter(g) return a diff --git a/web/entity/entity.go b/web/entity/entity.go index ef9a39e9..de32f15b 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -83,6 +83,8 @@ type AllSetting struct { SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"` // Enable Clash/Mihomo subscription endpoint SubClashPath string `json:"subClashPath" form:"subClashPath"` // Path for Clash/Mihomo subscription endpoint SubClashURI string `json:"subClashURI" form:"subClashURI"` // Clash/Mihomo subscription server URI + SubClashEnableRouting bool `json:"subClashEnableRouting" form:"subClashEnableRouting"` // Enable global routing rules for Clash/Mihomo + SubClashRules string `json:"subClashRules" form:"subClashRules"` // Clash/Mihomo global routing rules SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"` // JSON subscription fragment configuration SubJsonNoises string `json:"subJsonNoises" form:"subJsonNoises"` // JSON subscription noise configuration SubJsonMux string `json:"subJsonMux" form:"subJsonMux"` // JSON subscription mux configuration diff --git a/web/service/setting.go b/web/service/setting.go index 098ec039..dced01e7 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -79,6 +79,8 @@ var defaultValueMap = map[string]string{ "subClashEnable": "false", "subClashPath": "/clash/", "subClashURI": "", + "subClashEnableRouting": "false", + "subClashRules": "", "subJsonFragment": "", "subJsonNoises": "", "subJsonMux": "", @@ -658,6 +660,14 @@ func (s *SettingService) GetSubClashURI() (string, error) { return s.getString("subClashURI") } +func (s *SettingService) GetSubClashEnableRouting() (bool, error) { + return s.getBool("subClashEnableRouting") +} + +func (s *SettingService) GetSubClashRules() (string, error) { + return s.getString("subClashRules") +} + func (s *SettingService) GetSubJsonFragment() (string, error) { return s.getString("subJsonFragment") } diff --git a/web/translation/ar-EG.json b/web/translation/ar-EG.json index f7e25337..13ac8f65 100644 --- a/web/translation/ar-EG.json +++ b/web/translation/ar-EG.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "إعداد عام لتمكين التوجيه (Routing) في عميل VPN. (فقط لـ Happ)", "subRoutingRules": "قواعد التوجيه", "subRoutingRulesDesc": "قواعد التوجيه العامة لعميل VPN. (فقط لـ Happ)", + "subClashEnableRouting": "تفعيل التوجيه", + "subClashEnableRoutingDesc": "تضمين قواعد توجيه Clash/Mihomo العامة في اشتراكات YAML المُنشأة.", + "subClashRoutingRules": "قواعد التوجيه العامة", + "subClashRoutingRulesDesc": "قواعد Clash/Mihomo التي تُضاف في بداية كل اشتراك YAML قبل MATCH,PROXY.", "subListen": "IP الاستماع", "subListenDesc": "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)", "subPort": "بورت الاستماع", diff --git a/web/translation/en-US.json b/web/translation/en-US.json index 443a8f0e..70720c9e 100644 --- a/web/translation/en-US.json +++ b/web/translation/en-US.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "Global setting to enable routing in the VPN client. (Only for Happ)", "subRoutingRules": "Routing rules", "subRoutingRulesDesc": "Global routing rules for the VPN client. (Only for Happ)", + "subClashEnableRouting": "Enable routing", + "subClashEnableRoutingDesc": "Include global Clash/Mihomo routing rules in generated YAML subscriptions.", + "subClashRoutingRules": "Global routing rules", + "subClashRoutingRulesDesc": "Default Clash/Mihomo rules prepended to every generated YAML subscription before MATCH,PROXY.", "subListen": "Listen IP", "subListenDesc": "The IP address for the subscription service. (leave blank to listen on all IPs)", "subPort": "Listen Port", diff --git a/web/translation/es-ES.json b/web/translation/es-ES.json index 2283e34a..408cd506 100644 --- a/web/translation/es-ES.json +++ b/web/translation/es-ES.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "Configuración global para habilitar el enrutamiento en el cliente VPN. (Solo para Happ)", "subRoutingRules": "Reglas de enrutamiento", "subRoutingRulesDesc": "Reglas de enrutamiento globales para el cliente VPN. (Solo para Happ)", + "subClashEnableRouting": "Habilitar enrutamiento", + "subClashEnableRoutingDesc": "Incluir reglas globales de enrutamiento Clash/Mihomo en las suscripciones YAML generadas.", + "subClashRoutingRules": "Reglas globales de enrutamiento", + "subClashRoutingRulesDesc": "Reglas Clash/Mihomo agregadas al inicio de cada suscripción YAML antes de MATCH,PROXY.", "subListen": "Listening IP", "subListenDesc": "Dejar en blanco por defecto para monitorear todas las IPs.", "subPort": "Puerto de Suscripción", diff --git a/web/translation/fa-IR.json b/web/translation/fa-IR.json index 87f181f7..07ba6131 100644 --- a/web/translation/fa-IR.json +++ b/web/translation/fa-IR.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "تنظیمات سراسری برای فعال‌سازی مسیریابی در کلاینت VPN. (فقط برای Happ)", "subRoutingRules": "قوانین مسیریابی", "subRoutingRulesDesc": "قوانین مسیریابی سراسری برای کلاینت VPN. (فقط برای Happ)", + "subClashEnableRouting": "فعال‌سازی مسیریابی", + "subClashEnableRoutingDesc": "قوانین مسیریابی سراسری Clash/Mihomo را در اشتراک‌های YAML تولیدشده وارد کن.", + "subClashRoutingRules": "قوانین مسیریابی سراسری", + "subClashRoutingRulesDesc": "قوانین Clash/Mihomo که پیش از MATCH,PROXY به ابتدای هر اشتراک YAML افزوده می‌شوند.", "subListen": "آدرس آی‌پی", "subListenDesc": "آدرس آی‌پی برای سرویس سابسکریپشن. برای گوش دادن به‌تمام آی‌پی‌ها خالی‌بگذارید", "subPort": "پورت", diff --git a/web/translation/id-ID.json b/web/translation/id-ID.json index 523ad689..9d30abaf 100644 --- a/web/translation/id-ID.json +++ b/web/translation/id-ID.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "Pengaturan global untuk mengaktifkan perutean (routing) di klien VPN. (Hanya untuk Happ)", "subRoutingRules": "Aturan routing", "subRoutingRulesDesc": "Aturan routing global untuk klien VPN. (Hanya untuk Happ)", + "subClashEnableRouting": "Aktifkan routing", + "subClashEnableRoutingDesc": "Sertakan aturan routing global Clash/Mihomo dalam langganan YAML yang dibuat.", + "subClashRoutingRules": "Aturan routing global", + "subClashRoutingRulesDesc": "Aturan Clash/Mihomo yang ditambahkan di awal setiap langganan YAML sebelum MATCH,PROXY.", "subListen": "IP Pendengar", "subListenDesc": "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)", "subPort": "Port Pendengar", diff --git a/web/translation/ja-JP.json b/web/translation/ja-JP.json index 8cc74116..110519ff 100644 --- a/web/translation/ja-JP.json +++ b/web/translation/ja-JP.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "VPNクライアントでルーティングを有効にするためのグローバル設定。(Happのみ)", "subRoutingRules": "ルーティングルール", "subRoutingRulesDesc": "VPNクライアントのグローバルルーティングルール。(Happのみ)", + "subClashEnableRouting": "ルーティングを有効化", + "subClashEnableRoutingDesc": "生成されたYAMLサブスクリプションにClash/Mihomoのグローバルルーティングルールを含めます。", + "subClashRoutingRules": "グローバルルーティングルール", + "subClashRoutingRulesDesc": "各YAMLサブスクリプションのMATCH,PROXYより前に追加されるClash/Mihomoルール。", "subListen": "監視IP", "subListenDesc": "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)", "subPort": "監視ポート", diff --git a/web/translation/pt-BR.json b/web/translation/pt-BR.json index 7bc5a7ef..6f650b7b 100644 --- a/web/translation/pt-BR.json +++ b/web/translation/pt-BR.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "Configuração global para habilitar o roteamento no cliente VPN. (Apenas para Happ)", "subRoutingRules": "Regras de roteamento", "subRoutingRulesDesc": "Regras de roteamento globais para o cliente VPN. (Apenas para Happ)", + "subClashEnableRouting": "Ativar roteamento", + "subClashEnableRoutingDesc": "Incluir regras globais de roteamento Clash/Mihomo nas assinaturas YAML geradas.", + "subClashRoutingRules": "Regras globais de roteamento", + "subClashRoutingRulesDesc": "Regras Clash/Mihomo adicionadas ao início de cada assinatura YAML antes de MATCH,PROXY.", "subListen": "IP de Escuta", "subListenDesc": "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)", "subPort": "Porta de Escuta", diff --git a/web/translation/ru-RU.json b/web/translation/ru-RU.json index 226df950..edc652f6 100644 --- a/web/translation/ru-RU.json +++ b/web/translation/ru-RU.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "Глобальная настройка для включения маршрутизации в VPN-клиенте. (Только для Happ)", "subRoutingRules": "Правила маршрутизации", "subRoutingRulesDesc": "Глобальные правила маршрутизации для VPN-клиента. (Только для Happ)", + "subClashEnableRouting": "Включить маршрутизацию", + "subClashEnableRoutingDesc": "Добавлять глобальные правила маршрутизации Clash/Mihomo в сгенерированные YAML-подписки.", + "subClashRoutingRules": "Глобальные правила маршрутизации", + "subClashRoutingRulesDesc": "Правила Clash/Mihomo, добавляемые в начало каждой YAML-подписки перед MATCH,PROXY.", "subListen": "Прослушивание IP", "subListenDesc": "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса", "subPort": "Порт подписки", diff --git a/web/translation/tr-TR.json b/web/translation/tr-TR.json index 69b52e5e..e61de644 100644 --- a/web/translation/tr-TR.json +++ b/web/translation/tr-TR.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "VPN istemcisinde yönlendirmeyi etkinleştirmek için genel ayar. (Yalnızca Happ için)", "subRoutingRules": "Yönlendirme kuralları", "subRoutingRulesDesc": "VPN istemcisi için genel yönlendirme kuralları. (Yalnızca Happ için)", + "subClashEnableRouting": "Yönlendirmeyi etkinleştir", + "subClashEnableRoutingDesc": "Oluşturulan YAML aboneliklerine genel Clash/Mihomo yönlendirme kurallarını ekle.", + "subClashRoutingRules": "Genel yönlendirme kuralları", + "subClashRoutingRulesDesc": "Her YAML aboneliğinin başına MATCH,PROXY öncesinde eklenen Clash/Mihomo kuralları.", "subListen": "Dinleme IP", "subListenDesc": "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)", "subPort": "Dinleme Portu", diff --git a/web/translation/uk-UA.json b/web/translation/uk-UA.json index b386306a..91606d6d 100644 --- a/web/translation/uk-UA.json +++ b/web/translation/uk-UA.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті. (Тільки для Happ)", "subRoutingRules": "Правила маршрутизації", "subRoutingRulesDesc": "Глобальні правила маршрутизації для VPN-клієнта. (Тільки для Happ)", + "subClashEnableRouting": "Увімкнути маршрутизацію", + "subClashEnableRoutingDesc": "Додавати глобальні правила маршрутизації Clash/Mihomo до згенерованих YAML-підписок.", + "subClashRoutingRules": "Глобальні правила маршрутизації", + "subClashRoutingRulesDesc": "Правила Clash/Mihomo, що додаються на початок кожної YAML-підписки перед MATCH,PROXY.", "subListen": "Слухати IP", "subListenDesc": "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)", "subPort": "Слухати порт", diff --git a/web/translation/vi-VN.json b/web/translation/vi-VN.json index 12c620af..db45b23a 100644 --- a/web/translation/vi-VN.json +++ b/web/translation/vi-VN.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "Cài đặt toàn cục để bật định tuyến trong ứng dụng khách VPN. (Chỉ dành cho Happ)", "subRoutingRules": "Quy tắc định tuyến", "subRoutingRulesDesc": "Quy tắc định tuyến toàn cầu cho client VPN. (Chỉ dành cho Happ)", + "subClashEnableRouting": "Bật định tuyến", + "subClashEnableRoutingDesc": "Bao gồm quy tắc định tuyến Clash/Mihomo toàn cầu trong các đăng ký YAML được tạo.", + "subClashRoutingRules": "Quy tắc định tuyến toàn cầu", + "subClashRoutingRulesDesc": "Quy tắc Clash/Mihomo được thêm vào đầu mỗi đăng ký YAML trước MATCH,PROXY.", "subListen": "Listening IP", "subListenDesc": "Mặc định để trống để nghe tất cả các IP", "subPort": "Cổng gói đăng ký", diff --git a/web/translation/zh-CN.json b/web/translation/zh-CN.json index d7076703..5df7df7a 100644 --- a/web/translation/zh-CN.json +++ b/web/translation/zh-CN.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "在 VPN 客户端中启用路由的全局设置。(僅限 Happ)", "subRoutingRules": "路由規則", "subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ)", + "subClashEnableRouting": "启用路由", + "subClashEnableRoutingDesc": "在生成的 YAML 订阅中包含 Clash/Mihomo 全局路由规则。", + "subClashRoutingRules": "全局路由规则", + "subClashRoutingRulesDesc": "添加到每个 YAML 订阅开头、MATCH,PROXY 之前的 Clash/Mihomo 规则。", "subListen": "监听 IP", "subListenDesc": "订阅服务监听的 IP 地址(留空表示监听所有 IP)", "subPort": "监听端口", diff --git a/web/translation/zh-TW.json b/web/translation/zh-TW.json index 813f53c2..0090f40b 100644 --- a/web/translation/zh-TW.json +++ b/web/translation/zh-TW.json @@ -1004,6 +1004,10 @@ "subEnableRoutingDesc": "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ)", "subRoutingRules": "路由規則", "subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ)", + "subClashEnableRouting": "啟用路由", + "subClashEnableRoutingDesc": "在產生的 YAML 訂閱中包含 Clash/Mihomo 全域路由規則。", + "subClashRoutingRules": "全域路由規則", + "subClashRoutingRulesDesc": "加入到每個 YAML 訂閱開頭、MATCH,PROXY 之前的 Clash/Mihomo 規則。", "subListen": "監聽 IP", "subListenDesc": "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)", "subPort": "監聽埠",