feat(sub): add Happ client integration, routing presets, and app management (#6434)

* feat(sub): add Happ client integration, routing presets, and app management

Implement comprehensive Happ proxy client integration according to official developer specifications.

- Fix header emission on disabled routing and hidden settings to send explicit '0' headers rather than omitting, allowing Happ clients to reset cached settings.
- Add support for 'happ://routing/off' deeplink in routing validation.
- Preserve '?serverDescription=' query parameters in link fragments without escaping to support Happ server subtitles across VMess, VLESS, Trojan and SS.
- Add Happ application management headers: ProviderID, New-Url, Fallback-Url, Sub-Info banners, Sub-Expire notifications, No-Limit mode, hardware ID enforcement, TUN modes/types, route exclusions, APNS exclusions, and per-app proxy settings.
- Add curated routing presets (Iran Bypass, China Direct, AdBlock, Global) and interactive visual rule generator in frontend settings.
- Synchronize all 13 translation locales with native Persian, Russian, and Chinese translations.

* fix(sub): keep Happ header overrides behind the auto-detect opt-in

The Routing-Enable/Hide-Settings off values were emitted on the
User-Agent alone, so every panel that upgraded would push
"Routing-Enable: 0" — documented by happ.su as disabling routing
globally — to every Happ client without the operator enabling anything.
They now ride subHappAutoDetect like every other Happ header.

Two further mismatches against the vendor spec:

- serverDescription was written as a key of the VMess base64 JSON
  object. happ.su documents it as a "#Title?serverDescription=<base64>"
  link parameter or a JSON "meta" entry, so the caption never reached
  Happ while every other VMess consumer received an unknown key.
  Dropped rather than moved: emitting the documented form is unsafe
  here because our own parser base64-decodes the whole VMess body
  (internal/util/link/outbound.go).

- The TUN Mode dropdown stored the literal "default", forwarded as
  "Tun-Mode: default", where happ.su documents system|gvisor only. It
  now stores the unset value so no header is sent. TUN Type "default"
  is a documented value and is unchanged.

Each fix carries a test that fails without it.
This commit is contained in:
Pejman Yousefi
2026-09-10 17:16:12 +03:30
committed by GitHub
parent d5ab84e8d5
commit 1456658028
35 changed files with 3460 additions and 51 deletions
+186
View File
@@ -220,6 +220,76 @@
"subExpiredTemplate": {
"type": "string"
},
"subHappAlwaysHwid": {
"type": "boolean"
},
"subHappAutoConnect": {
"type": "boolean"
},
"subHappAutoConnectType": {
"type": "string"
},
"subHappAutoDetect": {
"description": "Happ client customization settings (app-management / routing / UX).",
"type": "boolean"
},
"subHappColorProfile": {
"type": "string"
},
"subHappExcludeApns": {
"type": "boolean"
},
"subHappExcludeRoutes": {
"type": "string"
},
"subHappFallbackUrl": {
"type": "string"
},
"subHappNewUrl": {
"type": "string"
},
"subHappNoLimit": {
"type": "boolean"
},
"subHappNotificationExpire": {
"type": "boolean"
},
"subHappPerAppList": {
"type": "string"
},
"subHappPerAppMode": {
"type": "string"
},
"subHappPingType": {
"type": "string"
},
"subHappProviderId": {
"type": "string"
},
"subHappSubExpire": {
"type": "boolean"
},
"subHappSubExpireButtonLink": {
"type": "string"
},
"subHappSubInfoButtonLink": {
"type": "string"
},
"subHappSubInfoButtonText": {
"type": "string"
},
"subHappSubInfoColor": {
"type": "string"
},
"subHappSubInfoText": {
"type": "string"
},
"subHappTunMode": {
"type": "string"
},
"subHappTunType": {
"type": "string"
},
"subHideSettings": {
"type": "boolean"
},
@@ -443,6 +513,29 @@
"subEnableRouting",
"subEncrypt",
"subExpiredTemplate",
"subHappAlwaysHwid",
"subHappAutoConnect",
"subHappAutoConnectType",
"subHappAutoDetect",
"subHappColorProfile",
"subHappExcludeApns",
"subHappExcludeRoutes",
"subHappFallbackUrl",
"subHappNewUrl",
"subHappNoLimit",
"subHappNotificationExpire",
"subHappPerAppList",
"subHappPerAppMode",
"subHappPingType",
"subHappProviderId",
"subHappSubExpire",
"subHappSubExpireButtonLink",
"subHappSubInfoButtonLink",
"subHappSubInfoButtonText",
"subHappSubInfoColor",
"subHappSubInfoText",
"subHappTunMode",
"subHappTunType",
"subHideSettings",
"subIncyEnableRouting",
"subIncyRoutingRules",
@@ -711,6 +804,76 @@
"subExpiredTemplate": {
"type": "string"
},
"subHappAlwaysHwid": {
"type": "boolean"
},
"subHappAutoConnect": {
"type": "boolean"
},
"subHappAutoConnectType": {
"type": "string"
},
"subHappAutoDetect": {
"description": "Happ client customization settings (app-management / routing / UX).",
"type": "boolean"
},
"subHappColorProfile": {
"type": "string"
},
"subHappExcludeApns": {
"type": "boolean"
},
"subHappExcludeRoutes": {
"type": "string"
},
"subHappFallbackUrl": {
"type": "string"
},
"subHappNewUrl": {
"type": "string"
},
"subHappNoLimit": {
"type": "boolean"
},
"subHappNotificationExpire": {
"type": "boolean"
},
"subHappPerAppList": {
"type": "string"
},
"subHappPerAppMode": {
"type": "string"
},
"subHappPingType": {
"type": "string"
},
"subHappProviderId": {
"type": "string"
},
"subHappSubExpire": {
"type": "boolean"
},
"subHappSubExpireButtonLink": {
"type": "string"
},
"subHappSubInfoButtonLink": {
"type": "string"
},
"subHappSubInfoButtonText": {
"type": "string"
},
"subHappSubInfoColor": {
"type": "string"
},
"subHappSubInfoText": {
"type": "string"
},
"subHappTunMode": {
"type": "string"
},
"subHappTunType": {
"type": "string"
},
"subHideSettings": {
"type": "boolean"
},
@@ -941,6 +1104,29 @@
"subEnableRouting",
"subEncrypt",
"subExpiredTemplate",
"subHappAlwaysHwid",
"subHappAutoConnect",
"subHappAutoConnectType",
"subHappAutoDetect",
"subHappColorProfile",
"subHappExcludeApns",
"subHappExcludeRoutes",
"subHappFallbackUrl",
"subHappNewUrl",
"subHappNoLimit",
"subHappNotificationExpire",
"subHappPerAppList",
"subHappPerAppMode",
"subHappPingType",
"subHappProviderId",
"subHappSubExpire",
"subHappSubExpireButtonLink",
"subHappSubInfoButtonLink",
"subHappSubInfoButtonText",
"subHappSubInfoColor",
"subHappSubInfoText",
"subHappTunMode",
"subHappTunType",
"subHideSettings",
"subIncyEnableRouting",
"subIncyRoutingRules",
+186
View File
@@ -220,6 +220,76 @@
"subExpiredTemplate": {
"type": "string"
},
"subHappAlwaysHwid": {
"type": "boolean"
},
"subHappAutoConnect": {
"type": "boolean"
},
"subHappAutoConnectType": {
"type": "string"
},
"subHappAutoDetect": {
"description": "Happ client customization settings (app-management / routing / UX).",
"type": "boolean"
},
"subHappColorProfile": {
"type": "string"
},
"subHappExcludeApns": {
"type": "boolean"
},
"subHappExcludeRoutes": {
"type": "string"
},
"subHappFallbackUrl": {
"type": "string"
},
"subHappNewUrl": {
"type": "string"
},
"subHappNoLimit": {
"type": "boolean"
},
"subHappNotificationExpire": {
"type": "boolean"
},
"subHappPerAppList": {
"type": "string"
},
"subHappPerAppMode": {
"type": "string"
},
"subHappPingType": {
"type": "string"
},
"subHappProviderId": {
"type": "string"
},
"subHappSubExpire": {
"type": "boolean"
},
"subHappSubExpireButtonLink": {
"type": "string"
},
"subHappSubInfoButtonLink": {
"type": "string"
},
"subHappSubInfoButtonText": {
"type": "string"
},
"subHappSubInfoColor": {
"type": "string"
},
"subHappSubInfoText": {
"type": "string"
},
"subHappTunMode": {
"type": "string"
},
"subHappTunType": {
"type": "string"
},
"subHideSettings": {
"type": "boolean"
},
@@ -443,6 +513,29 @@
"subEnableRouting",
"subEncrypt",
"subExpiredTemplate",
"subHappAlwaysHwid",
"subHappAutoConnect",
"subHappAutoConnectType",
"subHappAutoDetect",
"subHappColorProfile",
"subHappExcludeApns",
"subHappExcludeRoutes",
"subHappFallbackUrl",
"subHappNewUrl",
"subHappNoLimit",
"subHappNotificationExpire",
"subHappPerAppList",
"subHappPerAppMode",
"subHappPingType",
"subHappProviderId",
"subHappSubExpire",
"subHappSubExpireButtonLink",
"subHappSubInfoButtonLink",
"subHappSubInfoButtonText",
"subHappSubInfoColor",
"subHappSubInfoText",
"subHappTunMode",
"subHappTunType",
"subHideSettings",
"subIncyEnableRouting",
"subIncyRoutingRules",
@@ -711,6 +804,76 @@
"subExpiredTemplate": {
"type": "string"
},
"subHappAlwaysHwid": {
"type": "boolean"
},
"subHappAutoConnect": {
"type": "boolean"
},
"subHappAutoConnectType": {
"type": "string"
},
"subHappAutoDetect": {
"description": "Happ client customization settings (app-management / routing / UX).",
"type": "boolean"
},
"subHappColorProfile": {
"type": "string"
},
"subHappExcludeApns": {
"type": "boolean"
},
"subHappExcludeRoutes": {
"type": "string"
},
"subHappFallbackUrl": {
"type": "string"
},
"subHappNewUrl": {
"type": "string"
},
"subHappNoLimit": {
"type": "boolean"
},
"subHappNotificationExpire": {
"type": "boolean"
},
"subHappPerAppList": {
"type": "string"
},
"subHappPerAppMode": {
"type": "string"
},
"subHappPingType": {
"type": "string"
},
"subHappProviderId": {
"type": "string"
},
"subHappSubExpire": {
"type": "boolean"
},
"subHappSubExpireButtonLink": {
"type": "string"
},
"subHappSubInfoButtonLink": {
"type": "string"
},
"subHappSubInfoButtonText": {
"type": "string"
},
"subHappSubInfoColor": {
"type": "string"
},
"subHappSubInfoText": {
"type": "string"
},
"subHappTunMode": {
"type": "string"
},
"subHappTunType": {
"type": "string"
},
"subHideSettings": {
"type": "boolean"
},
@@ -941,6 +1104,29 @@
"subEnableRouting",
"subEncrypt",
"subExpiredTemplate",
"subHappAlwaysHwid",
"subHappAutoConnect",
"subHappAutoConnectType",
"subHappAutoDetect",
"subHappColorProfile",
"subHappExcludeApns",
"subHappExcludeRoutes",
"subHappFallbackUrl",
"subHappNewUrl",
"subHappNoLimit",
"subHappNotificationExpire",
"subHappPerAppList",
"subHappPerAppMode",
"subHappPingType",
"subHappProviderId",
"subHappSubExpire",
"subHappSubExpireButtonLink",
"subHappSubInfoButtonLink",
"subHappSubInfoButtonText",
"subHappSubInfoColor",
"subHappSubInfoText",
"subHappTunMode",
"subHappTunType",
"subHideSettings",
"subIncyEnableRouting",
"subIncyRoutingRules",
+46
View File
@@ -59,6 +59,29 @@ export const EXAMPLES: Record<string, unknown> = {
"subEnableRouting": false,
"subEncrypt": false,
"subExpiredTemplate": "",
"subHappAlwaysHwid": false,
"subHappAutoConnect": false,
"subHappAutoConnectType": "",
"subHappAutoDetect": false,
"subHappColorProfile": "",
"subHappExcludeApns": false,
"subHappExcludeRoutes": "",
"subHappFallbackUrl": "",
"subHappNewUrl": "",
"subHappNoLimit": false,
"subHappNotificationExpire": false,
"subHappPerAppList": "",
"subHappPerAppMode": "",
"subHappPingType": "",
"subHappProviderId": "",
"subHappSubExpire": false,
"subHappSubExpireButtonLink": "",
"subHappSubInfoButtonLink": "",
"subHappSubInfoButtonText": "",
"subHappSubInfoColor": "",
"subHappSubInfoText": "",
"subHappTunMode": "",
"subHappTunType": "",
"subHideSettings": false,
"subIncyEnableRouting": false,
"subIncyRoutingRules": "",
@@ -176,6 +199,29 @@ export const EXAMPLES: Record<string, unknown> = {
"subEnableRouting": false,
"subEncrypt": false,
"subExpiredTemplate": "",
"subHappAlwaysHwid": false,
"subHappAutoConnect": false,
"subHappAutoConnectType": "",
"subHappAutoDetect": false,
"subHappColorProfile": "",
"subHappExcludeApns": false,
"subHappExcludeRoutes": "",
"subHappFallbackUrl": "",
"subHappNewUrl": "",
"subHappNoLimit": false,
"subHappNotificationExpire": false,
"subHappPerAppList": "",
"subHappPerAppMode": "",
"subHappPingType": "",
"subHappProviderId": "",
"subHappSubExpire": false,
"subHappSubExpireButtonLink": "",
"subHappSubInfoButtonLink": "",
"subHappSubInfoButtonText": "",
"subHappSubInfoColor": "",
"subHappSubInfoText": "",
"subHappTunMode": "",
"subHappTunType": "",
"subHideSettings": false,
"subIncyEnableRouting": false,
"subIncyRoutingRules": "",
+186
View File
@@ -194,6 +194,76 @@ export const SCHEMAS: Record<string, unknown> = {
"subExpiredTemplate": {
"type": "string"
},
"subHappAlwaysHwid": {
"type": "boolean"
},
"subHappAutoConnect": {
"type": "boolean"
},
"subHappAutoConnectType": {
"type": "string"
},
"subHappAutoDetect": {
"description": "Happ client customization settings (app-management / routing / UX).",
"type": "boolean"
},
"subHappColorProfile": {
"type": "string"
},
"subHappExcludeApns": {
"type": "boolean"
},
"subHappExcludeRoutes": {
"type": "string"
},
"subHappFallbackUrl": {
"type": "string"
},
"subHappNewUrl": {
"type": "string"
},
"subHappNoLimit": {
"type": "boolean"
},
"subHappNotificationExpire": {
"type": "boolean"
},
"subHappPerAppList": {
"type": "string"
},
"subHappPerAppMode": {
"type": "string"
},
"subHappPingType": {
"type": "string"
},
"subHappProviderId": {
"type": "string"
},
"subHappSubExpire": {
"type": "boolean"
},
"subHappSubExpireButtonLink": {
"type": "string"
},
"subHappSubInfoButtonLink": {
"type": "string"
},
"subHappSubInfoButtonText": {
"type": "string"
},
"subHappSubInfoColor": {
"type": "string"
},
"subHappSubInfoText": {
"type": "string"
},
"subHappTunMode": {
"type": "string"
},
"subHappTunType": {
"type": "string"
},
"subHideSettings": {
"type": "boolean"
},
@@ -417,6 +487,29 @@ export const SCHEMAS: Record<string, unknown> = {
"subEnableRouting",
"subEncrypt",
"subExpiredTemplate",
"subHappAlwaysHwid",
"subHappAutoConnect",
"subHappAutoConnectType",
"subHappAutoDetect",
"subHappColorProfile",
"subHappExcludeApns",
"subHappExcludeRoutes",
"subHappFallbackUrl",
"subHappNewUrl",
"subHappNoLimit",
"subHappNotificationExpire",
"subHappPerAppList",
"subHappPerAppMode",
"subHappPingType",
"subHappProviderId",
"subHappSubExpire",
"subHappSubExpireButtonLink",
"subHappSubInfoButtonLink",
"subHappSubInfoButtonText",
"subHappSubInfoColor",
"subHappSubInfoText",
"subHappTunMode",
"subHappTunType",
"subHideSettings",
"subIncyEnableRouting",
"subIncyRoutingRules",
@@ -685,6 +778,76 @@ export const SCHEMAS: Record<string, unknown> = {
"subExpiredTemplate": {
"type": "string"
},
"subHappAlwaysHwid": {
"type": "boolean"
},
"subHappAutoConnect": {
"type": "boolean"
},
"subHappAutoConnectType": {
"type": "string"
},
"subHappAutoDetect": {
"description": "Happ client customization settings (app-management / routing / UX).",
"type": "boolean"
},
"subHappColorProfile": {
"type": "string"
},
"subHappExcludeApns": {
"type": "boolean"
},
"subHappExcludeRoutes": {
"type": "string"
},
"subHappFallbackUrl": {
"type": "string"
},
"subHappNewUrl": {
"type": "string"
},
"subHappNoLimit": {
"type": "boolean"
},
"subHappNotificationExpire": {
"type": "boolean"
},
"subHappPerAppList": {
"type": "string"
},
"subHappPerAppMode": {
"type": "string"
},
"subHappPingType": {
"type": "string"
},
"subHappProviderId": {
"type": "string"
},
"subHappSubExpire": {
"type": "boolean"
},
"subHappSubExpireButtonLink": {
"type": "string"
},
"subHappSubInfoButtonLink": {
"type": "string"
},
"subHappSubInfoButtonText": {
"type": "string"
},
"subHappSubInfoColor": {
"type": "string"
},
"subHappSubInfoText": {
"type": "string"
},
"subHappTunMode": {
"type": "string"
},
"subHappTunType": {
"type": "string"
},
"subHideSettings": {
"type": "boolean"
},
@@ -915,6 +1078,29 @@ export const SCHEMAS: Record<string, unknown> = {
"subEnableRouting",
"subEncrypt",
"subExpiredTemplate",
"subHappAlwaysHwid",
"subHappAutoConnect",
"subHappAutoConnectType",
"subHappAutoDetect",
"subHappColorProfile",
"subHappExcludeApns",
"subHappExcludeRoutes",
"subHappFallbackUrl",
"subHappNewUrl",
"subHappNoLimit",
"subHappNotificationExpire",
"subHappPerAppList",
"subHappPerAppMode",
"subHappPingType",
"subHappProviderId",
"subHappSubExpire",
"subHappSubExpireButtonLink",
"subHappSubInfoButtonLink",
"subHappSubInfoButtonText",
"subHappSubInfoColor",
"subHappSubInfoText",
"subHappTunMode",
"subHappTunType",
"subHideSettings",
"subIncyEnableRouting",
"subIncyRoutingRules",
+46
View File
@@ -67,6 +67,29 @@ export interface AllSetting {
subEnableRouting: boolean;
subEncrypt: boolean;
subExpiredTemplate: string;
subHappAlwaysHwid: boolean;
subHappAutoConnect: boolean;
subHappAutoConnectType: string;
subHappAutoDetect: boolean;
subHappColorProfile: string;
subHappExcludeApns: boolean;
subHappExcludeRoutes: string;
subHappFallbackUrl: string;
subHappNewUrl: string;
subHappNoLimit: boolean;
subHappNotificationExpire: boolean;
subHappPerAppList: string;
subHappPerAppMode: string;
subHappPingType: string;
subHappProviderId: string;
subHappSubExpire: boolean;
subHappSubExpireButtonLink: string;
subHappSubInfoButtonLink: string;
subHappSubInfoButtonText: string;
subHappSubInfoColor: string;
subHappSubInfoText: string;
subHappTunMode: string;
subHappTunType: string;
subHideSettings: boolean;
subIncyEnableRouting: boolean;
subIncyRoutingRules: string;
@@ -185,6 +208,29 @@ export interface AllSettingView {
subEnableRouting: boolean;
subEncrypt: boolean;
subExpiredTemplate: string;
subHappAlwaysHwid: boolean;
subHappAutoConnect: boolean;
subHappAutoConnectType: string;
subHappAutoDetect: boolean;
subHappColorProfile: string;
subHappExcludeApns: boolean;
subHappExcludeRoutes: string;
subHappFallbackUrl: string;
subHappNewUrl: string;
subHappNoLimit: boolean;
subHappNotificationExpire: boolean;
subHappPerAppList: string;
subHappPerAppMode: string;
subHappPingType: string;
subHappProviderId: string;
subHappSubExpire: boolean;
subHappSubExpireButtonLink: string;
subHappSubInfoButtonLink: string;
subHappSubInfoButtonText: string;
subHappSubInfoColor: string;
subHappSubInfoText: string;
subHappTunMode: string;
subHappTunType: string;
subHideSettings: boolean;
subIncyEnableRouting: boolean;
subIncyRoutingRules: string;
+46
View File
@@ -83,6 +83,29 @@ export const AllSettingSchema = z.object({
subEnableRouting: z.boolean(),
subEncrypt: z.boolean(),
subExpiredTemplate: z.string(),
subHappAlwaysHwid: z.boolean(),
subHappAutoConnect: z.boolean(),
subHappAutoConnectType: z.string(),
subHappAutoDetect: z.boolean(),
subHappColorProfile: z.string(),
subHappExcludeApns: z.boolean(),
subHappExcludeRoutes: z.string(),
subHappFallbackUrl: z.string(),
subHappNewUrl: z.string(),
subHappNoLimit: z.boolean(),
subHappNotificationExpire: z.boolean(),
subHappPerAppList: z.string(),
subHappPerAppMode: z.string(),
subHappPingType: z.string(),
subHappProviderId: z.string(),
subHappSubExpire: z.boolean(),
subHappSubExpireButtonLink: z.string(),
subHappSubInfoButtonLink: z.string(),
subHappSubInfoButtonText: z.string(),
subHappSubInfoColor: z.string(),
subHappSubInfoText: z.string(),
subHappTunMode: z.string(),
subHappTunType: z.string(),
subHideSettings: z.boolean(),
subIncyEnableRouting: z.boolean(),
subIncyRoutingRules: z.string(),
@@ -202,6 +225,29 @@ export const AllSettingViewSchema = z.object({
subEnableRouting: z.boolean(),
subEncrypt: z.boolean(),
subExpiredTemplate: z.string(),
subHappAlwaysHwid: z.boolean(),
subHappAutoConnect: z.boolean(),
subHappAutoConnectType: z.string(),
subHappAutoDetect: z.boolean(),
subHappColorProfile: z.string(),
subHappExcludeApns: z.boolean(),
subHappExcludeRoutes: z.string(),
subHappFallbackUrl: z.string(),
subHappNewUrl: z.string(),
subHappNoLimit: z.boolean(),
subHappNotificationExpire: z.boolean(),
subHappPerAppList: z.string(),
subHappPerAppMode: z.string(),
subHappPingType: z.string(),
subHappProviderId: z.string(),
subHappSubExpire: z.boolean(),
subHappSubExpireButtonLink: z.string(),
subHappSubInfoButtonLink: z.string(),
subHappSubInfoButtonText: z.string(),
subHappSubInfoColor: z.string(),
subHappSubInfoText: z.string(),
subHappTunMode: z.string(),
subHappTunType: z.string(),
subHideSettings: z.boolean(),
subIncyEnableRouting: z.boolean(),
subIncyRoutingRules: z.string(),
+23
View File
@@ -73,6 +73,29 @@ export class AllSetting {
subJsonObservatory = '';
subThemeDir = '';
subHideSettings = false;
subHappAutoDetect = false;
subHappProviderId = '';
subHappNewUrl = '';
subHappFallbackUrl = '';
subHappSubInfoColor = 'blue';
subHappSubInfoText = '';
subHappSubInfoButtonText = '';
subHappSubInfoButtonLink = '';
subHappSubExpire = false;
subHappSubExpireButtonLink = '';
subHappNotificationExpire = false;
subHappNoLimit = false;
subHappAlwaysHwid = false;
subHappTunMode = '';
subHappTunType = '';
subHappExcludeRoutes = '';
subHappExcludeApns = false;
subHappColorProfile = '';
subHappPingType = '';
subHappAutoConnect = false;
subHappAutoConnectType = 'lowestdelay';
subHappPerAppMode = 'off';
subHappPerAppList = '';
timeLocation = 'Local';
@@ -0,0 +1,637 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Input, Modal, Select, Space, Switch, Tabs, message } from 'antd';
import {
BranchesOutlined,
BuildOutlined,
CloudSyncOutlined,
DesktopOutlined,
MobileOutlined,
NotificationOutlined,
ThunderboltOutlined,
} from '@ant-design/icons';
import type { AllSetting } from '@/models/setting';
import { SettingListItem } from '@/components/ui';
import { buildHappPresetDeeplink, parseList, toBase64Utf8 } from './happPresets';
interface HappSettingsContentProps {
allSetting: AllSetting;
updateSetting: (patch: Partial<AllSetting>) => void;
isMobile: boolean;
remoteSourceBadge: (val: string) => React.ReactNode;
}
export default function HappSettingsContent({
allSetting,
updateSetting,
isMobile,
remoteSourceBadge,
}: HappSettingsContentProps) {
const { t } = useTranslation();
const [selectedPreset, setSelectedPreset] = useState<string>('iran-bypass');
const [isModalOpen, setIsModalOpen] = useState(false);
const [directDomains, setDirectDomains] = useState('');
const [proxyDomains, setProxyDomains] = useState('');
const [blockDomains, setBlockDomains] = useState('');
const [directIPs, setDirectIPs] = useState('');
const [proxyIPs, setProxyIPs] = useState('');
const [blockIPs, setBlockIPs] = useState('');
const applyPreset = () => {
const payload = buildHappPresetDeeplink(selectedPreset);
if (payload) {
updateSetting({ subRoutingRules: payload });
message.success(t('pages.settings.subHappPresetApplied'));
}
};
const handleBuildDeeplink = () => {
interface FieldRule {
type: string;
outboundTag: string;
domain?: string[];
ip?: string[];
network?: string;
}
const rules: FieldRule[] = [];
const bDom = parseList(blockDomains);
const bIp = parseList(blockIPs);
if (bDom.length > 0 || bIp.length > 0) {
rules.push({
type: 'field',
outboundTag: 'block',
...(bDom.length > 0 ? { domain: bDom } : {}),
...(bIp.length > 0 ? { ip: bIp } : {}),
});
}
const dDom = parseList(directDomains);
const dIp = parseList(directIPs);
if (dDom.length > 0 || dIp.length > 0) {
rules.push({
type: 'field',
outboundTag: 'direct',
...(dDom.length > 0 ? { domain: dDom } : {}),
...(dIp.length > 0 ? { ip: dIp } : {}),
});
}
const pDom = parseList(proxyDomains);
const pIp = parseList(proxyIPs);
if (pDom.length > 0 || pIp.length > 0) {
rules.push({
type: 'field',
outboundTag: 'proxy',
...(pDom.length > 0 ? { domain: pDom } : {}),
...(pIp.length > 0 ? { ip: pIp } : {}),
});
}
rules.push({
type: 'field',
outboundTag: 'proxy',
network: 'tcp,udp',
});
const deeplink = 'happ://routing/onadd/' + toBase64Utf8(JSON.stringify({ rules }));
updateSetting({ subRoutingRules: deeplink });
setIsModalOpen(false);
message.success(t('pages.settings.subHappDeeplinkGenerated'));
};
return (
<>
<Tabs
type="card"
size="small"
items={[
{
key: 'routing',
label: (
<span>
<BranchesOutlined /> {!isMobile && t('pages.settings.subHappGroupRouting')}
</span>
),
children: (
<>
<SettingListItem
paddings="small"
title={t('pages.settings.subEnableRouting')}
description={t('pages.settings.subEnableRoutingDesc')}
>
<Switch
checked={allSetting.subEnableRouting}
onChange={(v) => updateSetting({ subEnableRouting: v })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappPresets')}
description={t('pages.settings.subHappPresetsDesc')}
>
<Space orientation="horizontal" style={{ width: '100%' }}>
<Select
value={selectedPreset}
style={{ minWidth: 170 }}
onChange={setSelectedPreset}
options={[
{ value: 'iran-bypass', label: t('pages.settings.subHappPresetIran') },
{ value: 'china-direct', label: t('pages.settings.subHappPresetChina') },
{ value: 'adblock', label: t('pages.settings.subHappPresetAdblock') },
{ value: 'global', label: t('pages.settings.subHappPresetGlobal') },
{ value: 'off', label: t('pages.settings.subHappPresetOff') },
]}
/>
<Button type="primary" onClick={applyPreset}>
{t('pages.settings.subHappPresets')}
</Button>
</Space>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappVisualBuilder')}
description={t('pages.settings.subHappVisualBuilderDesc')}
>
<Button icon={<BuildOutlined />} onClick={() => setIsModalOpen(true)}>
{t('pages.settings.subHappVisualBuilder')}
</Button>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subRoutingRules')}
badge={remoteSourceBadge(allSetting.subRoutingRules)}
description={t('pages.settings.subRoutingRulesDesc')}
>
<Input.TextArea
value={allSetting.subRoutingRules}
rows={4}
placeholder="happ://routing/onadd/... or https://.../DEFAULT.DEEPLINK"
onChange={(e) => updateSetting({ subRoutingRules: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappNoLimit')}
description={t('pages.settings.subHappNoLimitDesc')}
>
<Switch
checked={allSetting.subHappNoLimit}
onChange={(v) => updateSetting({ subHappNoLimit: v })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHideSettings')}
description={t('pages.settings.subHideSettingsDesc')}
>
<Switch
checked={allSetting.subHideSettings}
onChange={(v) => updateSetting({ subHideSettings: v })}
/>
</SettingListItem>
</>
),
},
{
key: 'banners',
label: (
<span>
<NotificationOutlined /> {!isMobile && t('pages.settings.subHappGroupBanners')}
</span>
),
children: (
<>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappSubInfoText')}
description={t('pages.settings.subHappSubInfoTextDesc')}
>
<Input
value={allSetting.subHappSubInfoText}
maxLength={200}
placeholder="Welcome to our high-speed network!"
onChange={(e) => updateSetting({ subHappSubInfoText: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappSubInfoColor')}
description={t('pages.settings.subHappSubInfoColorDesc')}
>
<Select
value={allSetting.subHappSubInfoColor || 'blue'}
style={{ width: '100%' }}
onChange={(v) => updateSetting({ subHappSubInfoColor: v })}
options={[
{ value: 'blue', label: t('pages.settings.subHappColorBlue') },
{ value: 'green', label: t('pages.settings.subHappColorGreen') },
{ value: 'red', label: t('pages.settings.subHappColorRed') },
]}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappSubInfoButtonText')}
description={t('pages.settings.subHappSubInfoButtonTextDesc')}
>
<Input
value={allSetting.subHappSubInfoButtonText}
maxLength={25}
placeholder="Support Channel"
onChange={(e) => updateSetting({ subHappSubInfoButtonText: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappSubInfoButtonLink')}
description={t('pages.settings.subHappSubInfoButtonLinkDesc')}
>
<Input
value={allSetting.subHappSubInfoButtonLink}
placeholder="https://t.me/your_channel"
onChange={(e) => updateSetting({ subHappSubInfoButtonLink: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappSubExpire')}
description={t('pages.settings.subHappSubExpireDesc')}
>
<Switch
checked={allSetting.subHappSubExpire}
onChange={(v) => updateSetting({ subHappSubExpire: v })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappSubExpireButtonLink')}
description={t('pages.settings.subHappSubExpireButtonLinkDesc')}
>
<Input
value={allSetting.subHappSubExpireButtonLink}
placeholder="https://example.com/renew"
onChange={(e) => updateSetting({ subHappSubExpireButtonLink: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappNotificationExpire')}
description={t('pages.settings.subHappNotificationExpireDesc')}
>
<Switch
checked={allSetting.subHappNotificationExpire}
onChange={(v) => updateSetting({ subHappNotificationExpire: v })}
/>
</SettingListItem>
</>
),
},
{
key: 'network',
label: (
<span>
<ThunderboltOutlined /> {!isMobile && t('pages.settings.subHappGroupNetwork')}
</span>
),
children: (
<>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappTunMode')}
description={t('pages.settings.subHappTunModeDesc')}
>
<Select
value={allSetting.subHappTunMode}
style={{ width: '100%' }}
onChange={(v) => updateSetting({ subHappTunMode: v })}
options={[
// happ.su documents tun-mode as system|gvisor only, so
// Default is the unset state rather than a third value.
{ value: '', label: t('pages.settings.subHappTunModeDefault') },
{ value: 'system', label: t('pages.settings.subHappTunModeSystem') },
{ value: 'gvisor', label: t('pages.settings.subHappTunModeGvisor') },
]}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappTunType')}
description={t('pages.settings.subHappTunTypeDesc')}
>
<Select
value={allSetting.subHappTunType || 'default'}
style={{ width: '100%' }}
onChange={(v) => updateSetting({ subHappTunType: v })}
options={[
{ value: 'singbox', label: t('pages.settings.subHappTunTypeSingbox') },
{ value: 'tun2proxy', label: t('pages.settings.subHappTunTypeTun2proxy') },
{ value: 'default', label: t('pages.settings.subHappTunTypeDefault') },
{ value: 'xray', label: t('pages.settings.subHappTunTypeXray') },
]}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappExcludeRoutes')}
description={t('pages.settings.subHappExcludeRoutesDesc')}
>
<Input
value={allSetting.subHappExcludeRoutes}
placeholder="192.168.0.0/16, 10.0.0.0/8"
onChange={(e) => updateSetting({ subHappExcludeRoutes: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappExcludeApns')}
description={t('pages.settings.subHappExcludeApnsDesc')}
>
<Switch
checked={allSetting.subHappExcludeApns}
onChange={(v) => updateSetting({ subHappExcludeApns: v })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappPingType')}
description={t('pages.settings.subHappPingTypeDesc')}
>
<Select
value={allSetting.subHappPingType || 'proxy'}
style={{ width: '100%' }}
onChange={(v) => updateSetting({ subHappPingType: v })}
options={[
{ value: 'proxy', label: t('pages.settings.subHappPingProxy') },
{ value: 'proxy-head', label: t('pages.settings.subHappPingProxyHead') },
{ value: 'tcp', label: t('pages.settings.subHappPingTcp') },
{ value: 'icmp', label: t('pages.settings.subHappPingIcmp') },
]}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappAutoConnect')}
description={t('pages.settings.subHappAutoConnectDesc')}
>
<Switch
checked={allSetting.subHappAutoConnect}
onChange={(v) => updateSetting({ subHappAutoConnect: v })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappAutoConnectType')}
description={t('pages.settings.subHappAutoConnectTypeDesc')}
>
<Select
value={allSetting.subHappAutoConnectType || 'lowestdelay'}
style={{ width: '100%' }}
onChange={(v) => updateSetting({ subHappAutoConnectType: v })}
options={[
{
value: 'lowestdelay',
label: t('pages.settings.subHappAutoConnectLowestDelay'),
},
{ value: 'lastused', label: t('pages.settings.subHappAutoConnectLastUsed') },
{ value: 'random', label: t('pages.settings.subHappAutoConnectRandom') },
]}
/>
</SettingListItem>
</>
),
},
{
key: 'themes',
label: (
<span>
<DesktopOutlined /> {!isMobile && t('pages.settings.subHappGroupThemes')}
</span>
),
children: (
<>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappColorProfile')}
description={t('pages.settings.subHappColorProfileDesc')}
>
<Input
value={allSetting.subHappColorProfile}
placeholder="default, violet, turquoise, cyberpunk, or custom JSON"
onChange={(e) => updateSetting({ subHappColorProfile: e.target.value })}
/>
</SettingListItem>
</>
),
},
{
key: 'failover',
label: (
<span>
<CloudSyncOutlined /> {!isMobile && t('pages.settings.subHappGroupFailover')}
</span>
),
children: (
<>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappAutoDetect')}
description={t('pages.settings.subHappAutoDetectDesc')}
>
<Switch
checked={allSetting.subHappAutoDetect}
onChange={(v) => updateSetting({ subHappAutoDetect: v })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappProviderId')}
description={t('pages.settings.subHappProviderIdDesc')}
>
<Input
value={allSetting.subHappProviderId}
placeholder="my-happ-provider"
onChange={(e) => updateSetting({ subHappProviderId: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappNewUrl')}
description={t('pages.settings.subHappNewUrlDesc')}
>
<Input
value={allSetting.subHappNewUrl}
placeholder="https://new-domain.com/sub/..."
onChange={(e) => updateSetting({ subHappNewUrl: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappFallbackUrl')}
description={t('pages.settings.subHappFallbackUrlDesc')}
>
<Input
value={allSetting.subHappFallbackUrl}
placeholder="https://backup-domain.com/sub/..."
onChange={(e) => updateSetting({ subHappFallbackUrl: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappAlwaysHwid')}
description={t('pages.settings.subHappAlwaysHwidDesc')}
>
<Switch
checked={allSetting.subHappAlwaysHwid}
onChange={(v) => updateSetting({ subHappAlwaysHwid: v })}
/>
</SettingListItem>
</>
),
},
{
key: 'android',
label: (
<span>
<MobileOutlined /> {!isMobile && t('pages.settings.subHappGroupAndroid')}
</span>
),
children: (
<>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappPerAppMode')}
description={t('pages.settings.subHappPerAppModeDesc')}
>
<Select
value={allSetting.subHappPerAppMode || 'off'}
style={{ width: '100%' }}
onChange={(v) => updateSetting({ subHappPerAppMode: v })}
options={[
{ value: 'off', label: t('pages.settings.subHappPerAppOff') },
{ value: 'on', label: t('pages.settings.subHappPerAppOn') },
{ value: 'bypass', label: t('pages.settings.subHappPerAppBypass') },
]}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappPerAppList')}
description={t('pages.settings.subHappPerAppListDesc')}
>
<Input.TextArea
value={allSetting.subHappPerAppList}
rows={4}
placeholder="org.telegram.messenger, com.google.android.youtube"
onChange={(e) => updateSetting({ subHappPerAppList: e.target.value })}
/>
</SettingListItem>
</>
),
},
]}
/>
<Modal
title={t('pages.settings.subHappModalTitle')}
open={isModalOpen}
onCancel={() => setIsModalOpen(false)}
onOk={handleBuildDeeplink}
okText={t('pages.settings.subHappBuildDeeplink')}
width={650}
>
<Space direction="vertical" style={{ width: '100%', marginTop: 12 }} size="middle">
<div>
<div style={{ fontWeight: 600, marginBottom: 4 }}>
{t('pages.settings.subHappDirectDomains')}
</div>
<Input.TextArea
rows={2}
value={directDomains}
placeholder="domain:ir, domain:cn, example.local"
onChange={(e) => setDirectDomains(e.target.value)}
/>
</div>
<div>
<div style={{ fontWeight: 600, marginBottom: 4 }}>
{t('pages.settings.subHappProxyDomains')}
</div>
<Input.TextArea
rows={2}
value={proxyDomains}
placeholder="geosite:google, youtube.com"
onChange={(e) => setProxyDomains(e.target.value)}
/>
</div>
<div>
<div style={{ fontWeight: 600, marginBottom: 4 }}>
{t('pages.settings.subHappBlockDomains')}
</div>
<Input.TextArea
rows={2}
value={blockDomains}
placeholder="geosite:category-ads-all, analytics.google.com"
onChange={(e) => setBlockDomains(e.target.value)}
/>
</div>
<div>
<div style={{ fontWeight: 600, marginBottom: 4 }}>
{t('pages.settings.subHappDirectIPs')}
</div>
<Input.TextArea
rows={2}
value={directIPs}
placeholder="geoip:ir, 192.168.0.0/16, 10.0.0.0/8"
onChange={(e) => setDirectIPs(e.target.value)}
/>
</div>
<div>
<div style={{ fontWeight: 600, marginBottom: 4 }}>
{t('pages.settings.subHappProxyIPs')}
</div>
<Input.TextArea
rows={2}
value={proxyIPs}
placeholder="1.1.1.1/32, 8.8.8.8/32"
onChange={(e) => setProxyIPs(e.target.value)}
/>
</div>
<div>
<div style={{ fontWeight: 600, marginBottom: 4 }}>
{t('pages.settings.subHappBlockIPs')}
</div>
<Input.TextArea
rows={2}
value={blockIPs}
placeholder="geoip:phishing, 0.0.0.0/8"
onChange={(e) => setBlockIPs(e.target.value)}
/>
</div>
</Space>
</Modal>
</>
);
}
@@ -17,6 +17,7 @@ import { RemarkTemplateField } from '@/components/form';
import { useMediaQuery } from '@/hooks/useMediaQuery';
import { catTabLabel } from './catTabLabel';
import { sanitizePath, normalizePath } from './uriPath';
import HappSettingsContent from './HappSettingsContent';
interface SubscriptionGeneralTabProps {
allSetting: AllSetting;
@@ -344,40 +345,12 @@ export default function SubscriptionGeneralTab({
key: '5',
label: catTabLabel(<BranchesOutlined />, 'Happ', isMobile),
children: (
<>
<SettingListItem
paddings="small"
title={t('pages.settings.subEnableRouting')}
description={t('pages.settings.subEnableRoutingDesc')}
>
<Switch
checked={allSetting.subEnableRouting}
onChange={(v) => updateSetting({ subEnableRouting: v })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subRoutingRules')}
badge={remoteSourceBadge(allSetting.subRoutingRules)}
description={t('pages.settings.subRoutingRulesDesc')}
>
<Input.TextArea
value={allSetting.subRoutingRules}
placeholder="happ://routing/onadd/... or https://.../DEFAULT.DEEPLINK"
onChange={(e) => updateSetting({ subRoutingRules: e.target.value })}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHideSettings')}
description={t('pages.settings.subHideSettingsDesc')}
>
<Switch
checked={allSetting.subHideSettings}
onChange={(v) => updateSetting({ subHideSettings: v })}
/>
</SettingListItem>
</>
<HappSettingsContent
allSetting={allSetting}
updateSetting={updateSetting}
isMobile={isMobile}
remoteSourceBadge={remoteSourceBadge}
/>
),
},
{
+118
View File
@@ -0,0 +1,118 @@
// Encode UTF-8 string to standard Base64 safe for browser environments.
export function toBase64Utf8(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, p1) =>
String.fromCharCode(Number(`0x${p1}`)),
),
);
}
// Splits multiline or comma-separated string into clean unique token arrays.
export function parseList(input: string): string[] {
return input
.split(/[\n,]+/)
.map((s) => s.trim())
.filter(Boolean);
}
// Build standard Happ routing deeplink or special state for curated presets.
export function buildHappPresetDeeplink(preset: string): string {
switch (preset) {
case 'off':
return 'happ://routing/off';
case 'iran-bypass':
return (
'happ://routing/onadd/' +
toBase64Utf8(
JSON.stringify({
rules: [
{
type: 'field',
outboundTag: 'direct',
domain: ['domain:ir', 'regexp:.*\\.ir$'],
ip: ['geoip:ir', 'geoip:private'],
},
{
type: 'field',
outboundTag: 'block',
domain: ['geosite:category-ads-all'],
},
{
type: 'field',
outboundTag: 'proxy',
network: 'tcp,udp',
},
],
}),
)
);
case 'china-direct':
return (
'happ://routing/onadd/' +
toBase64Utf8(
JSON.stringify({
rules: [
{
type: 'field',
outboundTag: 'direct',
domain: ['domain:cn', 'geosite:cn'],
ip: ['geoip:cn', 'geoip:private'],
},
{
type: 'field',
outboundTag: 'block',
domain: ['geosite:category-ads-all'],
},
{
type: 'field',
outboundTag: 'proxy',
network: 'tcp,udp',
},
],
}),
)
);
case 'adblock':
return (
'happ://routing/onadd/' +
toBase64Utf8(
JSON.stringify({
rules: [
{
type: 'field',
outboundTag: 'block',
domain: ['geosite:category-ads-all'],
},
{
type: 'field',
outboundTag: 'direct',
ip: ['geoip:private'],
},
{
type: 'field',
outboundTag: 'proxy',
network: 'tcp,udp',
},
],
}),
)
);
case 'global':
return (
'happ://routing/onadd/' +
toBase64Utf8(
JSON.stringify({
rules: [
{
type: 'field',
outboundTag: 'proxy',
network: 'tcp,udp',
},
],
}),
)
);
default:
return '';
}
}
+23
View File
@@ -77,6 +77,29 @@ export const AllSettingSchema = z
subJsonFinalMask: z.string().optional(),
subJsonObservatory: z.string().optional(),
subHideSettings: z.boolean().optional(),
subHappAutoDetect: z.boolean().optional(),
subHappProviderId: z.string().optional(),
subHappNewUrl: z.string().optional(),
subHappFallbackUrl: z.string().optional(),
subHappSubInfoColor: z.string().optional(),
subHappSubInfoText: z.string().optional(),
subHappSubInfoButtonText: z.string().optional(),
subHappSubInfoButtonLink: z.string().optional(),
subHappSubExpire: z.boolean().optional(),
subHappSubExpireButtonLink: z.string().optional(),
subHappNotificationExpire: z.boolean().optional(),
subHappNoLimit: z.boolean().optional(),
subHappAlwaysHwid: z.boolean().optional(),
subHappTunMode: z.string().optional(),
subHappTunType: z.string().optional(),
subHappExcludeRoutes: z.string().optional(),
subHappExcludeApns: z.boolean().optional(),
subHappColorProfile: z.string().optional(),
subHappPingType: z.string().optional(),
subHappAutoConnect: z.boolean().optional(),
subHappAutoConnectType: z.string().optional(),
subHappPerAppMode: z.string().optional(),
subHappPerAppList: z.string().optional(),
timeLocation: z.string().optional(),
ldapEnable: z.boolean().optional(),
ldapHost: z.string().optional(),
+85
View File
@@ -0,0 +1,85 @@
import { describe, expect, it } from 'vitest';
import { buildHappPresetDeeplink, parseList, toBase64Utf8 } from '@/pages/settings/happPresets';
describe('Happ presets and helpers', () => {
it('correctly parses comma and newline separated lists', () => {
const raw = 'domain:ir\nregexp:.*\\.ir$\n, example.com, , google.com';
const parsed = parseList(raw);
expect(parsed).toEqual(['domain:ir', 'regexp:.*\\.ir$', 'example.com', 'google.com']);
});
it('generates valid happ://routing/off for off preset', () => {
const link = buildHappPresetDeeplink('off');
expect(link).toBe('happ://routing/off');
});
it('generates valid base64 payload for iran-bypass preset', () => {
const link = buildHappPresetDeeplink('iran-bypass');
expect(link.startsWith('happ://routing/onadd/')).toBe(true);
const b64 = link.replace('happ://routing/onadd/', '');
const jsonStr = atob(b64);
const parsed = JSON.parse(jsonStr);
expect(parsed).toHaveProperty('rules');
expect(Array.isArray(parsed.rules)).toBe(true);
const directRule = parsed.rules.find(
(r: { outboundTag: string }) => r.outboundTag === 'direct',
);
expect(directRule).toBeDefined();
expect(directRule.domain).toContain('domain:ir');
expect(directRule.ip).toContain('geoip:ir');
const blockRule = parsed.rules.find((r: { outboundTag: string }) => r.outboundTag === 'block');
expect(blockRule).toBeDefined();
expect(blockRule.domain).toContain('geosite:category-ads-all');
});
it('generates valid base64 payload for china-direct preset', () => {
const link = buildHappPresetDeeplink('china-direct');
expect(link.startsWith('happ://routing/onadd/')).toBe(true);
const b64 = link.replace('happ://routing/onadd/', '');
const jsonStr = atob(b64);
const parsed = JSON.parse(jsonStr);
const directRule = parsed.rules.find(
(r: { outboundTag: string }) => r.outboundTag === 'direct',
);
expect(directRule.domain).toContain('domain:cn');
expect(directRule.ip).toContain('geoip:cn');
});
it('generates valid base64 payload for adblock preset', () => {
const link = buildHappPresetDeeplink('adblock');
const b64 = link.replace('happ://routing/onadd/', '');
const jsonStr = atob(b64);
const parsed = JSON.parse(jsonStr);
const blockRule = parsed.rules.find((r: { outboundTag: string }) => r.outboundTag === 'block');
expect(blockRule.domain).toContain('geosite:category-ads-all');
});
it('generates valid base64 payload for global preset', () => {
const link = buildHappPresetDeeplink('global');
const b64 = link.replace('happ://routing/onadd/', '');
const jsonStr = atob(b64);
const parsed = JSON.parse(jsonStr);
expect(parsed.rules[0].outboundTag).toBe('proxy');
expect(parsed.rules[0].network).toBe('tcp,udp');
});
it('encodes unicode properly via toBase64Utf8', () => {
const text = 'فیلترشکن و روتینگ';
const b64 = toBase64Utf8(text);
const decoded = decodeURIComponent(
Array.prototype.map
.call(atob(b64), (c: string) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join(''),
);
expect(decoded).toBe(text);
});
});
@@ -0,0 +1,72 @@
import { describe, expect, it, vi } from 'vitest';
import { fireEvent } from '@testing-library/react';
import HappSettingsContent from '@/pages/settings/HappSettingsContent';
import { AllSetting } from '@/models/setting';
import { renderWithProviders } from './test-utils';
function openTab(name: string) {
const tab = Array.from(document.querySelectorAll('.ant-tabs-tab')).find((t) =>
(t.textContent ?? '').includes(name),
);
if (!tab) throw new Error(`tab '${name}' not found`);
fireEvent.click(tab);
}
function selectFor(title: string): HTMLElement {
const row = Array.from(document.querySelectorAll('.ant-select')).find((s) =>
(s.closest('li,div[class*="setting"]')?.textContent ?? '').includes(title),
);
if (!row) throw new Error(`select for '${title}' not found`);
return row as HTMLElement;
}
function clickOption(text: string) {
const option = Array.from(document.querySelectorAll('.ant-select-item-option')).find(
(o) => (o.textContent ?? '').trim() === text,
);
if (!option) throw new Error(`option '${text}' not found`);
fireEvent.click(option);
}
describe('Happ TUN Mode select', () => {
// happ.su documents tun-mode as system|gvisor only, so the Default entry has
// to mean "send no header", the same state a fresh panel ships with.
it('stores the empty value for Default so no Tun-Mode header is emitted', () => {
const updateSetting = vi.fn();
const allSetting = new AllSetting();
allSetting.subHappTunMode = 'gvisor';
renderWithProviders(
<HappSettingsContent
allSetting={allSetting}
updateSetting={updateSetting}
isMobile={false}
remoteSourceBadge={() => null}
/>,
);
openTab('Network');
const select = selectFor('TUN Mode');
fireEvent.mouseDown(select.querySelector('.ant-select-selector') ?? select);
clickOption('Default');
expect(updateSetting).toHaveBeenCalledWith({ subHappTunMode: '' });
});
it('labels the unset state Default rather than leaving the control blank', () => {
renderWithProviders(
<HappSettingsContent
allSetting={new AllSetting()}
updateSetting={vi.fn()}
isMobile={false}
remoteSourceBadge={() => null}
/>,
);
openTab('Network');
const select = selectFor('TUN Mode');
expect(select.querySelector('.ant-select-content')?.textContent).toBe('Default');
});
});
+16 -2
View File
@@ -52,6 +52,7 @@ type SUBController struct {
subEnableRouting bool
subRoutingRules string
subHideSettings bool
happConfig HappConfig
subIncyEnableRouting bool
subIncyRoutingRules string
@@ -110,6 +111,7 @@ type subControllerConfig struct {
subEnableRouting bool
subRoutingRules string
subHideSettings bool
happConfig HappConfig
subIncyEnableRouting bool
subIncyRoutingRules string
@@ -229,6 +231,10 @@ func WithSUBIncyRoutingRules(value string) SUBControllerOption {
return func(config *subControllerConfig) { config.subIncyRoutingRules = value }
}
func WithSUBHappConfig(value HappConfig) SUBControllerOption {
return func(config *subControllerConfig) { config.happConfig = value }
}
func defaultSUBControllerConfig() subControllerConfig {
return subControllerConfig{
subPath: "/sub/",
@@ -258,6 +264,7 @@ func NewSUBController(g *gin.RouterGroup, options ...SUBControllerOption) *SUBCo
subEnableRouting: config.subEnableRouting,
subRoutingRules: config.subRoutingRules,
subHideSettings: config.subHideSettings,
happConfig: config.happConfig,
subIncyEnableRouting: config.subIncyEnableRouting,
subIncyRoutingRules: config.subIncyRoutingRules,
@@ -845,16 +852,23 @@ func (a *SUBController) ApplyCommonHeaders(
c.Writer.Header().Set("Announce", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileAnnounce)))
}
// Advanced (Happ). Routing stays independent of the enable flag; remote
// values come only from the validated cache and never delay this response.
rules, remote, routingErr := resolveRoutingSource(remoteRoutingHapp, profileRoutingRules)
// The off values undo a previously pushed setting, so they ride the same
// opt-in as every other Happ header rather than reaching every Happ client.
happManaged := a.happConfig.AutoDetect && c.Request != nil && IsHappClient(c.GetHeader("User-Agent"))
if profileEnableRouting {
c.Writer.Header().Set("Routing-Enable", "true")
} else if happManaged {
c.Writer.Header().Set("Routing-Enable", "0")
}
if (routingErr == nil || !remote) && strings.TrimSpace(rules) != "" {
c.Writer.Header().Set("Routing", rules)
}
if profileHideSettings {
c.Writer.Header().Set("Hide-Settings", "1")
} else if happManaged {
c.Writer.Header().Set("Hide-Settings", "0")
}
ApplyHappHeaders(c, a.happConfig, happManaged)
}
+26
View File
@@ -116,6 +116,32 @@ func TestBuildEndpointVmessLinks(t *testing.T) {
}
}
// happ.su documents serverDescription as a "#title?serverDescription=<base64>"
// link parameter, never a key of the VMess object, so nothing may leak into it.
func TestBuildEndpointVmessLinks_HostServerDescription(t *testing.T) {
s := &SubService{}
in := &model.Inbound{Remark: "ib"}
baseObj := map[string]any{"v": "2", "add": "base.example.com", "port": 443, "type": "none", "id": "uid", "scy": "auto", "net": "tcp", "tls": "none"}
host := &model.Host{Address: "a.example.com", Port: 8443, ServerDescription: "Berlin premium"}
eps := []ShareEndpoint{externalProxyToEndpoint(hostToExternalProxyMap(host, "a.example.com", 8443))}
got := s.buildEndpointVmessLinks(eps, baseObj, in, "user", "tcp")
raw, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(got, "vmess://"))
if err != nil {
t.Fatalf("decode vmess link: %v", err)
}
var obj map[string]any
if err := json.Unmarshal(raw, &obj); err != nil {
t.Fatalf("unmarshal vmess object: %v", err)
}
if obj["add"] != "a.example.com" {
t.Fatalf("host endpoint not applied: add = %v", obj["add"])
}
if value, ok := obj["serverDescription"]; ok {
t.Fatalf("VMess object carries serverDescription = %v; it is not a VMess object key", value)
}
}
// N5 — a host's Final Mask is appended to the inbound's own fm param (#5831).
func TestBuildEndpointLinks_HostFinalMaskMerge(t *testing.T) {
s := &SubService{}
+141
View File
@@ -0,0 +1,141 @@
package sub
import (
"regexp"
"strings"
"github.com/gin-gonic/gin"
)
var happUserAgentRegex = regexp.MustCompile(`(?i)\bhapp\b`)
// HappConfig holds all Happ client customization parameters.
type HappConfig struct {
AutoDetect bool
ProviderId string
NewUrl string
FallbackUrl string
SubInfoColor string
SubInfoText string
SubInfoButtonText string
SubInfoButtonLink string
SubExpire bool
SubExpireButtonLink string
NotificationExpire bool
NoLimit bool
AlwaysHwid bool
TunMode string
TunType string
ExcludeRoutes string
ExcludeApns bool
ColorProfile string
PingType string
AutoConnect bool
AutoConnectType string
PerAppMode string
PerAppList string
}
// IsHappClient checks if the client user-agent identifies as Happ.
func IsHappClient(userAgent string) bool {
return happUserAgentRegex.MatchString(userAgent)
}
// ApplyHappHeaders sets standard and advanced Happ subscription headers.
func ApplyHappHeaders(c *gin.Context, cfg HappConfig, isHapp bool) {
if c == nil || c.Writer == nil || !cfg.AutoDetect || !isHapp {
return
}
if cfg.ProviderId != "" {
c.Writer.Header().Set("ProviderID", cfg.ProviderId)
}
if cfg.NewUrl != "" {
c.Writer.Header().Set("New-Url", cfg.NewUrl)
}
if cfg.FallbackUrl != "" {
c.Writer.Header().Set("Fallback-Url", cfg.FallbackUrl)
}
if text := strings.TrimSpace(cfg.SubInfoText); text != "" {
color := strings.TrimSpace(cfg.SubInfoColor)
switch strings.ToLower(color) {
case "primary", "info":
color = "blue"
case "success":
color = "green"
case "warning", "danger":
color = "red"
case "":
color = "blue"
}
c.Writer.Header().Set("Sub-Info-Color", color)
c.Writer.Header().Set("Sub-Info-Text", text)
if btnText := strings.TrimSpace(cfg.SubInfoButtonText); btnText != "" {
c.Writer.Header().Set("Sub-Info-Button-Text", btnText)
}
if btnLink := strings.TrimSpace(cfg.SubInfoButtonLink); btnLink != "" {
c.Writer.Header().Set("Sub-Info-Button-Link", btnLink)
}
}
if cfg.SubExpire {
c.Writer.Header().Set("Sub-Expire", "1")
if link := strings.TrimSpace(cfg.SubExpireButtonLink); link != "" {
c.Writer.Header().Set("Sub-Expire-Button-Link", link)
}
}
if cfg.NotificationExpire {
c.Writer.Header().Set("Notification-Subs-Expire", "1")
}
if cfg.NoLimit {
c.Writer.Header().Set("No-Limit-Enabled", "1")
}
if cfg.AlwaysHwid {
c.Writer.Header().Set("Subscription-Always-Hwid-Enable", "1")
}
if cfg.TunMode != "" {
c.Writer.Header().Set("Tun-Mode", cfg.TunMode)
}
if cfg.TunType != "" {
c.Writer.Header().Set("Tun-Type", cfg.TunType)
}
if routes := strings.TrimSpace(cfg.ExcludeRoutes); routes != "" {
c.Writer.Header().Set("Exclude-Routes", routes)
}
if cfg.ExcludeApns {
c.Writer.Header().Set("Exclude-Apns-Enable", "true")
}
if profile := strings.TrimSpace(cfg.ColorProfile); profile != "" {
profile = strings.ReplaceAll(strings.ReplaceAll(profile, "\r", ""), "\n", "")
c.Writer.Header().Set("Color-Profile", profile)
}
if ping := strings.TrimSpace(cfg.PingType); ping != "" {
if strings.EqualFold(ping, "http") {
ping = "proxy"
}
c.Writer.Header().Set("Ping-Type", ping)
}
if cfg.AutoConnect {
c.Writer.Header().Set("Subscription-Autoconnect", "1")
autoType := strings.TrimSpace(cfg.AutoConnectType)
switch strings.ToLower(autoType) {
case "fastest":
autoType = "lowestdelay"
case "last":
autoType = "lastused"
}
if autoType != "" {
c.Writer.Header().Set("Subscription-Autoconnect-Type", autoType)
}
}
if mode := strings.TrimSpace(cfg.PerAppMode); mode != "" && mode != "off" {
switch strings.ToLower(mode) {
case "include":
mode = "on"
case "exclude":
mode = "bypass"
}
c.Writer.Header().Set("Per-App-Proxy-Mode", mode)
if list := strings.TrimSpace(cfg.PerAppList); list != "" {
c.Writer.Header().Set("Per-App-Proxy-List", list)
}
}
}
+227
View File
@@ -0,0 +1,227 @@
package sub
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestNormalizeHappRouting_Off(t *testing.T) {
got, err := normalizeHappRouting([]byte("happ://routing/off"))
if err != nil {
t.Fatalf("normalizeHappRouting(off) error: %v", err)
}
if got != "happ://routing/off" {
t.Fatalf("normalizeHappRouting(off) = %q, want happ://routing/off", got)
}
}
func TestApplyCommonHeaders_HappClientHeaders(t *testing.T) {
gin.SetMode(gin.TestMode)
cfg := HappConfig{
AutoDetect: true,
ProviderId: "pid-test-123",
NewUrl: "https://new.example.com/sub",
FallbackUrl: "https://backup.example.com/sub",
SubInfoColor: "primary",
SubInfoText: "Welcome to VIP Network",
SubInfoButtonText: "Telegram",
SubInfoButtonLink: "https://t.me/example",
SubExpire: true,
SubExpireButtonLink: "https://renew.example.com",
NotificationExpire: true,
NoLimit: true,
AlwaysHwid: true,
TunMode: "gvisor",
TunType: "singbox",
ExcludeRoutes: "192.168.1.0/24, 10.0.0.0/8",
ExcludeApns: true,
ColorProfile: "{\"serverRowBackgroundColor\":\n\"#21003D67\"}",
PingType: "http",
AutoConnect: true,
AutoConnectType: "fastest",
PerAppMode: "include",
PerAppList: "com.google.chrome,com.meta.instagram",
}
controller := &SUBController{happConfig: cfg}
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodGet, "/sub/test", nil)
ctx.Request.Header.Set("User-Agent", "Happ/1.2.0 (iPhone; iOS 17.5)")
controller.ApplyCommonHeaders(ctx, "upload=0; download=100; total=1000; expire=1800000000", "12", "MyTitle", "", "", "", false, "", false)
h := recorder.Header()
if h.Get("Routing-Enable") != "0" {
t.Fatalf("Routing-Enable = %q, want 0 for Happ with disabled routing", h.Get("Routing-Enable"))
}
if h.Get("Hide-Settings") != "0" {
t.Fatalf("Hide-Settings = %q, want 0 for Happ with disabled hideSettings", h.Get("Hide-Settings"))
}
if h.Get("ProviderID") != "pid-test-123" {
t.Fatalf("ProviderID = %q, want pid-test-123", h.Get("ProviderID"))
}
if h.Get("New-Url") != "https://new.example.com/sub" {
t.Fatalf("New-Url = %q", h.Get("New-Url"))
}
if h.Get("Fallback-Url") != "https://backup.example.com/sub" {
t.Fatalf("Fallback-Url = %q", h.Get("Fallback-Url"))
}
if h.Get("Sub-Info-Color") != "blue" || h.Get("Sub-Info-Text") != "Welcome to VIP Network" {
t.Fatalf("Sub-Info = %s / %s, want blue / Welcome to VIP Network", h.Get("Sub-Info-Color"), h.Get("Sub-Info-Text"))
}
if h.Get("Sub-Info-Button-Text") != "Telegram" || h.Get("Sub-Info-Button-Link") != "https://t.me/example" {
t.Fatalf("Sub-Info button = %s / %s", h.Get("Sub-Info-Button-Text"), h.Get("Sub-Info-Button-Link"))
}
if h.Get("Sub-Expire") != "1" || h.Get("Sub-Expire-Button-Link") != "https://renew.example.com" {
t.Fatalf("Sub-Expire = %s / %s", h.Get("Sub-Expire"), h.Get("Sub-Expire-Button-Link"))
}
if h.Get("Notification-Subs-Expire") != "1" {
t.Fatalf("Notification-Subs-Expire = %q", h.Get("Notification-Subs-Expire"))
}
if h.Get("No-Limit-Enabled") != "1" {
t.Fatalf("No-Limit-Enabled = %q", h.Get("No-Limit-Enabled"))
}
if h.Get("Subscription-Always-Hwid-Enable") != "1" {
t.Fatalf("Subscription-Always-Hwid-Enable = %q", h.Get("Subscription-Always-Hwid-Enable"))
}
if h.Get("Tun-Mode") != "gvisor" || h.Get("Tun-Type") != "singbox" {
t.Fatalf("Tun mode/type = %s / %s", h.Get("Tun-Mode"), h.Get("Tun-Type"))
}
if h.Get("Exclude-Routes") != "192.168.1.0/24, 10.0.0.0/8" || h.Get("Exclude-Apns-Enable") != "true" {
t.Fatalf("Exclude routes/apns = %s / %s", h.Get("Exclude-Routes"), h.Get("Exclude-Apns-Enable"))
}
if wantProfile := "{\"serverRowBackgroundColor\":\"#21003D67\"}"; h.Get("Color-Profile") != wantProfile {
t.Fatalf("Color-Profile = %q, want %q", h.Get("Color-Profile"), wantProfile)
}
if h.Get("Ping-Type") != "proxy" {
t.Fatalf("Ping-Type = %q, want proxy for http alias", h.Get("Ping-Type"))
}
if h.Get("Subscription-Autoconnect") != "1" || h.Get("Subscription-Autoconnect-Type") != "lowestdelay" {
t.Fatalf("Autoconnect = %s / %s, want 1 / lowestdelay for fastest alias", h.Get("Subscription-Autoconnect"), h.Get("Subscription-Autoconnect-Type"))
}
if h.Get("Per-App-Proxy-Mode") != "on" || h.Get("Per-App-Proxy-List") != "com.google.chrome,com.meta.instagram" {
t.Fatalf("Per-App = %s / %s, want on / com.google.chrome,com.meta.instagram", h.Get("Per-App-Proxy-Mode"), h.Get("Per-App-Proxy-List"))
}
}
func TestApplyHappHeaders_Gating(t *testing.T) {
gin.SetMode(gin.TestMode)
cfg := HappConfig{
AutoDetect: true,
ProviderId: "pid-secret",
SubInfoText: "Banner",
TunMode: "system",
}
t.Run("non-Happ User-Agent receives no headers", func(t *testing.T) {
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodGet, "/sub/test", nil)
ctx.Request.Header.Set("User-Agent", "v2rayNG/1.8.5")
controller := &SUBController{happConfig: cfg}
controller.ApplyCommonHeaders(ctx, "", "", "Title", "", "", "", false, "", false)
if got := recorder.Header().Get("ProviderID"); got != "" {
t.Fatalf("ProviderID emitted to non-Happ client: %q", got)
}
if got := recorder.Header().Get("Sub-Info-Text"); got != "" {
t.Fatalf("Sub-Info-Text emitted to non-Happ client: %q", got)
}
if got := recorder.Header().Get("Tun-Mode"); got != "" {
t.Fatalf("Tun-Mode emitted to non-Happ client: %q", got)
}
})
t.Run("AutoDetect disabled suppresses Happ headers", func(t *testing.T) {
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodGet, "/sub/test", nil)
ctx.Request.Header.Set("User-Agent", "Happ/1.2.0 (Android)")
disabledCfg := cfg
disabledCfg.AutoDetect = false
controller := &SUBController{happConfig: disabledCfg}
controller.ApplyCommonHeaders(ctx, "", "", "Title", "", "", "", false, "", false)
if got := recorder.Header().Get("ProviderID"); got != "" {
t.Fatalf("ProviderID emitted when AutoDetect is false: %q", got)
}
if got := recorder.Header().Get("Sub-Info-Text"); got != "" {
t.Fatalf("Sub-Info-Text emitted when AutoDetect is false: %q", got)
}
// happ.su documents routing-enable 0/false as "disables routing
// globally", so it must stay behind the same opt-in as the rest.
if got := recorder.Header().Get("Routing-Enable"); got != "" {
t.Fatalf("Routing-Enable emitted when AutoDetect is false: %q", got)
}
if got := recorder.Header().Get("Hide-Settings"); got != "" {
t.Fatalf("Hide-Settings emitted when AutoDetect is false: %q", got)
}
})
}
func TestApplyHappHeaders_Aliases(t *testing.T) {
gin.SetMode(gin.TestMode)
tests := []struct {
name string
cfg HappConfig
wantHeader string
wantValue string
}{
{
name: "color warning maps to red",
cfg: HappConfig{AutoDetect: true, SubInfoText: "Alert", SubInfoColor: "warning"},
wantHeader: "Sub-Info-Color",
wantValue: "red",
},
{
name: "color danger maps to red",
cfg: HappConfig{AutoDetect: true, SubInfoText: "Alert", SubInfoColor: "danger"},
wantHeader: "Sub-Info-Color",
wantValue: "red",
},
{
name: "color success maps to green",
cfg: HappConfig{AutoDetect: true, SubInfoText: "Ok", SubInfoColor: "success"},
wantHeader: "Sub-Info-Color",
wantValue: "green",
},
{
name: "autoconnect last maps to lastused",
cfg: HappConfig{AutoDetect: true, AutoConnect: true, AutoConnectType: "last"},
wantHeader: "Subscription-Autoconnect-Type",
wantValue: "lastused",
},
{
name: "per-app exclude maps to bypass",
cfg: HappConfig{AutoDetect: true, PerAppMode: "exclude", PerAppList: "app.id"},
wantHeader: "Per-App-Proxy-Mode",
wantValue: "bypass",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodGet, "/sub/test", nil)
ctx.Request.Header.Set("User-Agent", "Happ/1.2.0 (iOS)")
controller := &SUBController{happConfig: tc.cfg}
controller.ApplyCommonHeaders(ctx, "", "", "Title", "", "", "", false, "", false)
if got := recorder.Header().Get(tc.wantHeader); got != tc.wantValue {
t.Fatalf("%s = %q, want %q", tc.wantHeader, got, tc.wantValue)
}
})
}
}
+3
View File
@@ -369,6 +369,9 @@ func normalizeHappRouting(body []byte) (string, error) {
}
return "happ://routing/onadd/" + base64.StdEncoding.EncodeToString(compact), nil
}
if text == "happ://routing/off" {
return text, nil
}
if strings.ContainsAny(text, "\r\n") {
return "", errors.New("Happ deeplink must be a single line")
}
+1 -2
View File
@@ -2166,8 +2166,7 @@ func appendQueryAndFragment(link string, params map[string]string, fragment, sec
if fragment != "" {
sb.WriteByte('#')
// Match the frontend's encodeURIComponent(remark): spaces become
// %20 (not + as in query strings).
// Match the frontend's encodeURIComponent(remark): spaces become %20.
sb.WriteString(strings.ReplaceAll(url.QueryEscape(fragment), "+", "%20"))
}
return sb.String()
+26
View File
@@ -215,6 +215,31 @@ func (s *Server) initRouter() (*gin.Engine, error) {
SubIncyRoutingRules = ""
}
happCfg := HappConfig{}
happCfg.AutoDetect, _ = s.settingService.GetSubHappAutoDetect()
happCfg.ProviderId, _ = s.settingService.GetSubHappProviderId()
happCfg.NewUrl, _ = s.settingService.GetSubHappNewUrl()
happCfg.FallbackUrl, _ = s.settingService.GetSubHappFallbackUrl()
happCfg.SubInfoColor, _ = s.settingService.GetSubHappSubInfoColor()
happCfg.SubInfoText, _ = s.settingService.GetSubHappSubInfoText()
happCfg.SubInfoButtonText, _ = s.settingService.GetSubHappSubInfoButtonText()
happCfg.SubInfoButtonLink, _ = s.settingService.GetSubHappSubInfoButtonLink()
happCfg.SubExpire, _ = s.settingService.GetSubHappSubExpire()
happCfg.SubExpireButtonLink, _ = s.settingService.GetSubHappSubExpireButtonLink()
happCfg.NotificationExpire, _ = s.settingService.GetSubHappNotificationExpire()
happCfg.NoLimit, _ = s.settingService.GetSubHappNoLimit()
happCfg.AlwaysHwid, _ = s.settingService.GetSubHappAlwaysHwid()
happCfg.TunMode, _ = s.settingService.GetSubHappTunMode()
happCfg.TunType, _ = s.settingService.GetSubHappTunType()
happCfg.ExcludeRoutes, _ = s.settingService.GetSubHappExcludeRoutes()
happCfg.ExcludeApns, _ = s.settingService.GetSubHappExcludeApns()
happCfg.ColorProfile, _ = s.settingService.GetSubHappColorProfile()
happCfg.PingType, _ = s.settingService.GetSubHappPingType()
happCfg.AutoConnect, _ = s.settingService.GetSubHappAutoConnect()
happCfg.AutoConnectType, _ = s.settingService.GetSubHappAutoConnectType()
happCfg.PerAppMode, _ = s.settingService.GetSubHappPerAppMode()
happCfg.PerAppList, _ = s.settingService.GetSubHappPerAppList()
// set per-request localizer from headers/cookies
engine.Use(locale.LocalizerMiddleware())
@@ -296,6 +321,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
WithSUBEnableRouting(SubEnableRouting),
WithSUBRoutingRules(SubRoutingRules),
WithSUBHideSettings(SubHideSettings),
WithSUBHappConfig(happCfg),
WithSUBIncyEnableRouting(SubIncyEnableRouting),
WithSUBIncyRoutingRules(SubIncyRoutingRules),
)
+25
View File
@@ -112,6 +112,31 @@ type AllSetting struct {
SubThemeDir string `json:"subThemeDir" form:"subThemeDir"`
SubHideSettings bool `json:"subHideSettings" form:"subHideSettings"`
// Happ client customization settings (app-management / routing / UX).
SubHappAutoDetect bool `json:"subHappAutoDetect" form:"subHappAutoDetect"`
SubHappProviderId string `json:"subHappProviderId" form:"subHappProviderId"`
SubHappNewUrl string `json:"subHappNewUrl" form:"subHappNewUrl"`
SubHappFallbackUrl string `json:"subHappFallbackUrl" form:"subHappFallbackUrl"`
SubHappSubInfoColor string `json:"subHappSubInfoColor" form:"subHappSubInfoColor"`
SubHappSubInfoText string `json:"subHappSubInfoText" form:"subHappSubInfoText"`
SubHappSubInfoButtonText string `json:"subHappSubInfoButtonText" form:"subHappSubInfoButtonText"`
SubHappSubInfoButtonLink string `json:"subHappSubInfoButtonLink" form:"subHappSubInfoButtonLink"`
SubHappSubExpire bool `json:"subHappSubExpire" form:"subHappSubExpire"`
SubHappSubExpireButtonLink string `json:"subHappSubExpireButtonLink" form:"subHappSubExpireButtonLink"`
SubHappNotificationExpire bool `json:"subHappNotificationExpire" form:"subHappNotificationExpire"`
SubHappNoLimit bool `json:"subHappNoLimit" form:"subHappNoLimit"`
SubHappAlwaysHwid bool `json:"subHappAlwaysHwid" form:"subHappAlwaysHwid"`
SubHappTunMode string `json:"subHappTunMode" form:"subHappTunMode"`
SubHappTunType string `json:"subHappTunType" form:"subHappTunType"`
SubHappExcludeRoutes string `json:"subHappExcludeRoutes" form:"subHappExcludeRoutes"`
SubHappExcludeApns bool `json:"subHappExcludeApns" form:"subHappExcludeApns"`
SubHappColorProfile string `json:"subHappColorProfile" form:"subHappColorProfile"`
SubHappPingType string `json:"subHappPingType" form:"subHappPingType"`
SubHappAutoConnect bool `json:"subHappAutoConnect" form:"subHappAutoConnect"`
SubHappAutoConnectType string `json:"subHappAutoConnectType" form:"subHappAutoConnectType"`
SubHappPerAppMode string `json:"subHappPerAppMode" form:"subHappPerAppMode"`
SubHappPerAppList string `json:"subHappPerAppList" form:"subHappPerAppList"`
LdapEnable bool `json:"ldapEnable" form:"ldapEnable"`
LdapHost string `json:"ldapHost" form:"ldapHost"`
LdapPort int `json:"ldapPort" form:"ldapPort" validate:"gte=0,lte=65535"`
+125
View File
@@ -102,6 +102,29 @@ var defaultValueMap = map[string]string{
"subEnableRouting": "false",
"subRoutingRules": "",
"subHideSettings": "false",
"subHappAutoDetect": "false",
"subHappProviderId": "",
"subHappNewUrl": "",
"subHappFallbackUrl": "",
"subHappSubInfoColor": "blue",
"subHappSubInfoText": "",
"subHappSubInfoButtonText": "",
"subHappSubInfoButtonLink": "",
"subHappSubExpire": "false",
"subHappSubExpireButtonLink": "",
"subHappNotificationExpire": "false",
"subHappNoLimit": "false",
"subHappAlwaysHwid": "false",
"subHappTunMode": "",
"subHappTunType": "",
"subHappExcludeRoutes": "",
"subHappExcludeApns": "false",
"subHappColorProfile": "",
"subHappPingType": "",
"subHappAutoConnect": "false",
"subHappAutoConnectType": "lowestdelay",
"subHappPerAppMode": "off",
"subHappPerAppList": "",
"subIncyEnableRouting": "false",
"subIncyRoutingRules": "",
"subListen": "",
@@ -820,6 +843,98 @@ func (s *SettingService) GetSubHideSettings() (bool, error) {
return s.getBool("subHideSettings")
}
func (s *SettingService) GetSubHappAutoDetect() (bool, error) {
return s.getBool("subHappAutoDetect")
}
func (s *SettingService) GetSubHappProviderId() (string, error) {
return s.getString("subHappProviderId")
}
func (s *SettingService) GetSubHappNewUrl() (string, error) {
return s.getString("subHappNewUrl")
}
func (s *SettingService) GetSubHappFallbackUrl() (string, error) {
return s.getString("subHappFallbackUrl")
}
func (s *SettingService) GetSubHappSubInfoColor() (string, error) {
return s.getString("subHappSubInfoColor")
}
func (s *SettingService) GetSubHappSubInfoText() (string, error) {
return s.getString("subHappSubInfoText")
}
func (s *SettingService) GetSubHappSubInfoButtonText() (string, error) {
return s.getString("subHappSubInfoButtonText")
}
func (s *SettingService) GetSubHappSubInfoButtonLink() (string, error) {
return s.getString("subHappSubInfoButtonLink")
}
func (s *SettingService) GetSubHappSubExpire() (bool, error) {
return s.getBool("subHappSubExpire")
}
func (s *SettingService) GetSubHappSubExpireButtonLink() (string, error) {
return s.getString("subHappSubExpireButtonLink")
}
func (s *SettingService) GetSubHappNotificationExpire() (bool, error) {
return s.getBool("subHappNotificationExpire")
}
func (s *SettingService) GetSubHappNoLimit() (bool, error) {
return s.getBool("subHappNoLimit")
}
func (s *SettingService) GetSubHappAlwaysHwid() (bool, error) {
return s.getBool("subHappAlwaysHwid")
}
func (s *SettingService) GetSubHappTunMode() (string, error) {
return s.getString("subHappTunMode")
}
func (s *SettingService) GetSubHappTunType() (string, error) {
return s.getString("subHappTunType")
}
func (s *SettingService) GetSubHappExcludeRoutes() (string, error) {
return s.getString("subHappExcludeRoutes")
}
func (s *SettingService) GetSubHappExcludeApns() (bool, error) {
return s.getBool("subHappExcludeApns")
}
func (s *SettingService) GetSubHappColorProfile() (string, error) {
return s.getString("subHappColorProfile")
}
func (s *SettingService) GetSubHappPingType() (string, error) {
return s.getString("subHappPingType")
}
func (s *SettingService) GetSubHappAutoConnect() (bool, error) {
return s.getBool("subHappAutoConnect")
}
func (s *SettingService) GetSubHappAutoConnectType() (string, error) {
return s.getString("subHappAutoConnectType")
}
func (s *SettingService) GetSubHappPerAppMode() (string, error) {
return s.getString("subHappPerAppMode")
}
func (s *SettingService) GetSubHappPerAppList() (string, error) {
return s.getString("subHappPerAppList")
}
func (s *SettingService) GetSubIncyEnableRouting() (bool, error) {
return s.getBool("subIncyEnableRouting")
}
@@ -1363,6 +1478,16 @@ func validateSettingsURLs(allSetting *entity.AllSetting) error {
// the scheme instead of forcing SanitizeHTTPURL's http(s)-only rule.
allSetting.SubSupportUrl = common.EnsureURLScheme(allSetting.SubSupportUrl)
allSetting.SubProfileUrl = common.EnsureURLScheme(allSetting.SubProfileUrl)
for _, ptr := range []*string{
&allSetting.SubHappNewUrl,
&allSetting.SubHappFallbackUrl,
&allSetting.SubHappSubInfoButtonLink,
&allSetting.SubHappSubExpireButtonLink,
} {
if strings.TrimSpace(*ptr) != "" {
*ptr = common.EnsureURLScheme(strings.TrimSpace(*ptr))
}
}
for name, value := range map[string]*string{
"Happ routing source": &allSetting.SubRoutingRules,
"Clash/Mihomo routing source": &allSetting.SubClashRules,
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "قالب انتهاء الصلاحية",
"subExpiredTemplateDesc": "قالب التكوين الوهمي عند انتهاء صلاحية اشتراك المستخدم.",
"subTrafficDepletedTemplate": "قالب نفاد البيانات",
"subTrafficDepletedTemplateDesc": "قالب التكوين الوهمي عند استهلاك حصة بيانات المستخدم بالكامل."
"subTrafficDepletedTemplateDesc": "قالب التكوين الوهمي عند استهلاك حصة بيانات المستخدم بالكامل.",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "تعطيل التوجيه (happ://routing/off)",
"subHappColorBlue": "أزرق (قياسي / افتراضي)",
"subHappColorGreen": "أخضر (نجاح)",
"subHappColorRed": "أحمر (تحذير / خطر)",
"subHappTunModeDefault": "افتراضي",
"subHappTunModeSystem": "النظام (حزمة نظام التشغيل القياسية)",
"subHappTunModeGvisor": "gVisor (حزمة مساحة المستخدم)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "افتراضي (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "عبر البروكسي (زمن استجابة GET)",
"subHappPingProxyHead": "عبر البروكسي (زمن استجابة HEAD)",
"subHappPingTcp": "بينج مصافحة TCP",
"subHappPingIcmp": "بينج ICMP",
"subHappAutoConnectLowestDelay": "أقل تأخير (العقدة الأسرع)",
"subHappAutoConnectLastUsed": "آخر عقدة تم استخدامها",
"subHappAutoConnectRandom": "عقدة عشوائية",
"subHappPerAppOff": "إيقاف",
"subHappPerAppOn": "تشغيل (بروكسي للتطبيقات المحددة فقط)",
"subHappPerAppBypass": "تجاوز (استثناء التطبيقات المحددة)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"save": "احفظ",
+93 -1
View File
@@ -1584,7 +1584,99 @@
"subExpiredTemplate": "Expired Template",
"subExpiredTemplateDesc": "Template for the dummy config when the subscriber account has expired.",
"subTrafficDepletedTemplate": "Traffic Depleted Template",
"subTrafficDepletedTemplateDesc": "Template for the dummy config when subscriber traffic quota is exhausted."
"subTrafficDepletedTemplateDesc": "Template for the dummy config when subscriber traffic quota is exhausted.",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "Disable Routing (happ://routing/off)",
"subHappColorBlue": "Blue (Standard / Default)",
"subHappColorGreen": "Green (Success)",
"subHappColorRed": "Red (Warning / Danger)",
"subHappTunModeDefault": "Default",
"subHappTunModeSystem": "System (Standard OS Stack)",
"subHappTunModeGvisor": "gVisor (Userspace Stack)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "Default (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "via Proxy (GET Latency)",
"subHappPingProxyHead": "via Proxy (HEAD Latency)",
"subHappPingTcp": "TCP Handshake Ping",
"subHappPingIcmp": "ICMP Ping",
"subHappAutoConnectLowestDelay": "Lowest Delay (Fastest Node)",
"subHappAutoConnectLastUsed": "Last Used Node",
"subHappAutoConnectRandom": "Random Node",
"subHappPerAppOff": "Off",
"subHappPerAppOn": "On (Proxy Only Listed Apps)",
"subHappPerAppBypass": "Bypass (Exclude Listed Apps)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"save": "Save",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "Plantilla de expirado",
"subExpiredTemplateDesc": "Plantilla para el nodo ficticio cuando la suscripción ha expirado.",
"subTrafficDepletedTemplate": "Plantilla de tráfico agotado",
"subTrafficDepletedTemplateDesc": "Plantilla para el nodo ficticio cuando el límite de tráfico se ha agotado."
"subTrafficDepletedTemplateDesc": "Plantilla para el nodo ficticio cuando el límite de tráfico se ha agotado.",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "Desactivar enrutamiento (happ://routing/off)",
"subHappColorBlue": "Azul (estándar / predeterminado)",
"subHappColorGreen": "Verde (éxito)",
"subHappColorRed": "Rojo (advertencia / peligro)",
"subHappTunModeDefault": "Predeterminado",
"subHappTunModeSystem": "Sistema (pila estándar del SO)",
"subHappTunModeGvisor": "gVisor (pila de espacio de usuario)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "Predeterminado (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "Vía proxy (latencia GET)",
"subHappPingProxyHead": "Vía proxy (latencia HEAD)",
"subHappPingTcp": "Ping de saludo TCP",
"subHappPingIcmp": "Ping ICMP",
"subHappAutoConnectLowestDelay": "Menor latencia (nodo más rápido)",
"subHappAutoConnectLastUsed": "Último nodo utilizado",
"subHappAutoConnectRandom": "Nodo aleatorio",
"subHappPerAppOff": "Desactivado",
"subHappPerAppOn": "Activado (solo apps de la lista)",
"subHappPerAppBypass": "Omitir (excluir apps de la lista)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"save": "Guardar configuración",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "قالب پیام انقضا",
"subExpiredTemplateDesc": "قالب کانفیگ نمایشی زمانی که اشتراک کاربر منقضی شده است.",
"subTrafficDepletedTemplate": "قالب پیام اتمام حجم",
"subTrafficDepletedTemplateDesc": "قالب کانفیگ نمایشی زمانی که حجم اشتراک کاربر به پایان رسیده است."
"subTrafficDepletedTemplateDesc": "قالب کانفیگ نمایشی زمانی که حجم اشتراک کاربر به پایان رسیده است.",
"subHappAutoDetect": "تشخیص خودکار کلاینت Happ",
"subHappAutoDetectDesc": "تزریق خودکار هدرها و روتینگ Happ هنگامی که User-Agent کلاینت مربوط به برنامه Happ باشد.",
"subHappProviderId": "شناسه ارائه‌دهنده (Provider ID)",
"subHappProviderIdDesc": "شناسه یکتا جهت مدیریت کلاینت Happ، اتصال به کانفیگ ریموت و مهاجرت کاربران.",
"subHappNewUrl": "لینک اشتراک جدید (مهاجرت)",
"subHappNewUrlDesc": "آدرس اشتراک جدید جهت انتقال خودکار؛ برنامه Happ پس از دریافت، آدرس اشتراک را به این لینک تغییر می‌دهد.",
"subHappFallbackUrl": "لینک اشتراک پشتیبان (Fallback)",
"subHappFallbackUrlDesc": "آدرس اشتراک جایگزین در صورت در دسترس نبودن سرور اصلی اشتراک.",
"subHappSubInfoText": "متن بنر اعلانات",
"subHappSubInfoTextDesc": "پیام اعلان سفارشی در بالای صفحه برنامه Happ (حداکثر ۲۰۰ نویسه).",
"subHappSubInfoColor": "رنگ بنر اعلانات",
"subHappSubInfoColorDesc": "تم رنگی بنر اعلان (پیش‌فرض، اطلاع‌رسانی، موفقیت، هشدار یا اخطار).",
"subHappSubInfoButtonText": "متن دکمه بنر",
"subHappSubInfoButtonTextDesc": "عنوان دکمه اقدام در بنر اعلان (حداکثر ۲۵ نویسه).",
"subHappSubInfoButtonLink": "لینک دکمه بنر",
"subHappSubInfoButtonLinkDesc": "آدرسی که با کلیک روی دکمه بنر باز می‌شود.",
"subHappSubExpire": "بنر اشتراک منقضی‌شده",
"subHappSubExpireDesc": "نمایش بنر تمدید در برنامه Happ در صورت اتمام ترافیک یا زمان اشتراک کاربر.",
"subHappSubExpireButtonLink": "لینک تمدید اشتراک",
"subHappSubExpireButtonLinkDesc": "آدرس صفحه خرید یا تمدید اشتراک منقضی‌شده.",
"subHappNotificationExpire": "اعلان انقضای اشتراک",
"subHappNotificationExpireDesc": "نمایش هشدار انقضای اشتراک به کاربر پیش از پایان اعتبار در برنامه Happ.",
"subHappNoLimit": "حذف محدودیت تعداد قوانین",
"subHappNoLimitDesc": "اجازه اعمال تعداد نامحدود قوانین روتینگ بدون برش خوردن روی سیستم‌های تلفن همراه.",
"subHappAlwaysHwid": "الزام شناسه سخت‌افزاری (HWID)",
"subHappAlwaysHwidDesc": "قفل کردن درخواست‌های اشتراک به شناسه سخت‌افزاری دستگاه جهت جلوگیری از اشتراک‌گذاری اکانت.",
"subHappTunMode": "حالت تونل (TUN Mode)",
"subHappTunModeDesc": "حالت رابط شبکه مجازی TUN در برنامه Happ (پیش‌فرض، سیستمی یا سخت‌گیرانه).",
"subHappTunType": "موتور شبکه TUN",
"subHappTunTypeDesc": "پشته شبکه مورد استفاده برای TUN (سیستمی، gVisor یا ترکیبی).",
"subHappExcludeRoutes": "مستثنی کردن مسیرهای CIDR",
"subHappExcludeRoutesDesc": "رنج‌های IP جدا شده با کاما جهت دور زدن تونل VPN (مانند 192.168.0.0/16, 10.0.0.0/8).",
"subHappExcludeApns": "مستثنی کردن سرویس‌های اعلان اپل (APNs)",
"subHappExcludeApnsDesc": "دور زدن سرویس‌های اعلان اپل برای اطمینان از دریافت پایدار ناتیفیکیشن‌ها در iOS.",
"subHappColorProfile": "پروفایل رنگ و پوسته",
"subHappColorProfileDesc": "پوسته ظاهری برنامه Happ (بنفش، فیروزه‌ای، سایبرپانک یا JSON سفارشی).",
"subHappPingType": "روش تست پینگ",
"subHappPingTypeDesc": "پروتکل اندازه‌گیری تأخیر گره‌ها در برنامه Happ (icmp، tcp یا http).",
"subHappAutoConnect": "اتصال خودکار هنگام اجرا",
"subHappAutoConnectDesc": "اتصال خودکار به وی‌پی‌ان با باز شدن برنامه Happ.",
"subHappAutoConnectType": "راهبرد اتصال خودکار",
"subHappAutoConnectTypeDesc": "هدف اتصال خودکار: سریع‌ترین سرور یا آخرین سرور استفاده‌شده.",
"subHappPerAppMode": "پراکسی انتخابی برنامه‌ها در اندروید",
"subHappPerAppModeDesc": "مدیریت عبور ترافیک برنامه‌های اندروید: خاموش، عبور فقط برنامه‌های منتخب یا مستثنی کردن آن‌ها.",
"subHappPerAppList": "نام بسته‌های برنامه‌های اندروید",
"subHappPerAppListDesc": "نام بسته‌های اپلیکیشن‌های اندروید جدا شده با کاما (مانند com.telegram.messenger).",
"subHappPresetIran": "دور زدن سایت‌های ایران (Iran Bypass)",
"subHappPresetChina": "دور زدن چین (China Direct)",
"subHappPresetAdblock": "مسدودسازی تبلیغات (AdBlock)",
"subHappPresetGlobal": "پراکسی کل ترافیک (Global)",
"subHappPresetOff": "غیرفعال‌سازی روتینگ (happ://routing/off)",
"subHappColorBlue": "آبی (استاندارد / پیش‌فرض)",
"subHappColorGreen": "سبز (موفقیت)",
"subHappColorRed": "قرمز (هشدار / خطر)",
"subHappTunModeDefault": "پیش‌فرض",
"subHappTunModeSystem": "سیستم (استک استاندارد سیستم‌عامل)",
"subHappTunModeGvisor": "gVisor (استک فضای کاربری)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "پیش‌فرض (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "از طریق پروکسی (تأخیر GET)",
"subHappPingProxyHead": "از طریق پروکسی (تأخیر HEAD)",
"subHappPingTcp": "پینگ دست‌دادن TCP",
"subHappPingIcmp": "پینگ ICMP",
"subHappAutoConnectLowestDelay": "کمترین تأخیر (سریع‌ترین نود)",
"subHappAutoConnectLastUsed": "آخرین نود استفاده‌شده",
"subHappAutoConnectRandom": "نود تصادفی",
"subHappPerAppOff": "خاموش",
"subHappPerAppOn": "روشن (فقط برنامه‌های فهرست‌شده)",
"subHappPerAppBypass": "بای‌پس (مستثنی‌کردن برنامه‌های فهرست‌شده)",
"subHappPresetApplied": "الگوی روتینگ Happ اعمال شد",
"subHappPresets": "الگوهای آماده روتینگ",
"subHappPresetsDesc": "الگوهای آماده و بهینه‌سازی‌شده برای روتینگ در برنامه Happ.",
"subHappVisualBuilder": "سازنده بصری قوانین",
"subHappVisualBuilderDesc": "ایجاد آسان دیپ‌لینک روتینگ سفارشی بر اساس دامنه‌ها و آی‌پی‌ها.",
"subHappBuildDeeplink": "تولید دیپ‌لینک",
"subHappModalTitle": "سازنده بصری قوانین روتینگ Happ",
"subHappDirectDomains": "دامنه‌های مستقیم (Direct)",
"subHappProxyDomains": "دامنه‌های پراکسی (Proxy)",
"subHappBlockDomains": "دامنه‌های مسدود (Block)",
"subHappDirectIPs": "آی‌پی‌های مستقیم (Direct CIDRs)",
"subHappProxyIPs": "آی‌پی‌های پراکسی (Proxy CIDRs)",
"subHappBlockIPs": "آی‌پی‌های مسدود (Block CIDRs)",
"subHappDeeplinkGenerated": "دیپ‌لینک تولید و در قوانین روتینگ اعمال شد",
"subHappGroupRouting": "قوانین و روتینگ",
"subHappGroupBanners": "اعلانات و بنرهای هوشمند",
"subHappGroupNetwork": "تنظیمات شبکه و TUN",
"subHappGroupThemes": "ظاهر و پوسته برنامه",
"subHappGroupFailover": "مهاجرت و مدیریت کلاینت",
"subHappGroupAndroid": "پراکسی برنامه‌های اندروید"
},
"xray": {
"save": "ذخیره",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "Templat Kedaluwarsa",
"subExpiredTemplateDesc": "Templat untuk konfigurasi dummy saat akun langganan telah kedaluwarsa.",
"subTrafficDepletedTemplate": "Templat Kuota Habis",
"subTrafficDepletedTemplateDesc": "Templat untuk konfigurasi dummy saat kuota data langganan telah habis."
"subTrafficDepletedTemplateDesc": "Templat untuk konfigurasi dummy saat kuota data langganan telah habis.",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "Nonaktifkan Perutean (happ://routing/off)",
"subHappColorBlue": "Biru (Standar / Default)",
"subHappColorGreen": "Hijau (Sukses)",
"subHappColorRed": "Merah (Peringatan / Bahaya)",
"subHappTunModeDefault": "Default",
"subHappTunModeSystem": "Sistem (Stack OS Standar)",
"subHappTunModeGvisor": "gVisor (Stack Userspace)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "Default (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "Melalui Proxy (Latensi GET)",
"subHappPingProxyHead": "Melalui Proxy (Latensi HEAD)",
"subHappPingTcp": "Ping Handshake TCP",
"subHappPingIcmp": "Ping ICMP",
"subHappAutoConnectLowestDelay": "Latensi Terendah (Node Tercepat)",
"subHappAutoConnectLastUsed": "Node Terakhir Digunakan",
"subHappAutoConnectRandom": "Node Acak",
"subHappPerAppOff": "Mati",
"subHappPerAppOn": "Nyala (Hanya Proksikan Aplikasi Terdaftar)",
"subHappPerAppBypass": "Bypass (Kecualikan Aplikasi Terdaftar)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"save": "Simpan",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "期限切れテンプレート",
"subExpiredTemplateDesc": "サブスクリプションの有効期限が切れた際のダミー構成用テンプレート。",
"subTrafficDepletedTemplate": "通信量超過テンプレート",
"subTrafficDepletedTemplateDesc": "サブスクリプションの通信量が上限に達した際のダミー構成用テンプレート。"
"subTrafficDepletedTemplateDesc": "サブスクリプションの通信量が上限に達した際のダミー構成用テンプレート。",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "ルーティングを無効化 (happ://routing/off)",
"subHappColorBlue": "ブルー (標準 / デフォルト)",
"subHappColorGreen": "グリーン (成功)",
"subHappColorRed": "レッド (警告 / 危険)",
"subHappTunModeDefault": "デフォルト",
"subHappTunModeSystem": "システム (標準OSスタック)",
"subHappTunModeGvisor": "gVisor (ユーザー空間スタック)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "デフォルト (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "プロキシ経由 (GET遅延)",
"subHappPingProxyHead": "プロキシ経由 (HEAD遅延)",
"subHappPingTcp": "TCPハンドシェイク Ping",
"subHappPingIcmp": "ICMP Ping",
"subHappAutoConnectLowestDelay": "最小遅延 (最速ノード)",
"subHappAutoConnectLastUsed": "最後に使用したノード",
"subHappAutoConnectRandom": "ランダムノード",
"subHappPerAppOff": "オフ",
"subHappPerAppOn": "オン (リストされたアプリのみプロキシ)",
"subHappPerAppBypass": "バイパス (リストされたアプリを除外)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"importRules": "ルールをインポート",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "Modelo expirado",
"subExpiredTemplateDesc": "Modelo para a configuração fictícia quando a assinatura expirou.",
"subTrafficDepletedTemplate": "Modelo de tráfego esgotado",
"subTrafficDepletedTemplateDesc": "Modelo para a configuração fictícia quando a cota de tráfego foi esgotada."
"subTrafficDepletedTemplateDesc": "Modelo para a configuração fictícia quando a cota de tráfego foi esgotada.",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "Desativar roteamento (happ://routing/off)",
"subHappColorBlue": "Azul (padrão)",
"subHappColorGreen": "Verde (sucesso)",
"subHappColorRed": "Vermelho (aviso / perigo)",
"subHappTunModeDefault": "Padrão",
"subHappTunModeSystem": "Sistema (pilha padrão do SO)",
"subHappTunModeGvisor": "gVisor (pilha de espaço do usuário)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "Padrão (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "Via proxy (latência GET)",
"subHappPingProxyHead": "Via proxy (latência HEAD)",
"subHappPingTcp": "Ping de handshake TCP",
"subHappPingIcmp": "Ping ICMP",
"subHappAutoConnectLowestDelay": "Menor latência (nó mais rápido)",
"subHappAutoConnectLastUsed": "Último nó usado",
"subHappAutoConnectRandom": "Nó aleatório",
"subHappPerAppOff": "Desativado",
"subHappPerAppOn": "Ativado (apenas apps listados)",
"subHappPerAppBypass": "Desviar (excluir apps listados)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"importRules": "Importar regras",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "Шаблон истекшей подписки",
"subExpiredTemplateDesc": "Шаблон для фиктивного узла, когда срок действия подписки истек.",
"subTrafficDepletedTemplate": "Шаблон исчерпания трафика",
"subTrafficDepletedTemplateDesc": "Шаблон для фиктивного узла, когда лимит трафика исчерпан."
"subTrafficDepletedTemplateDesc": "Шаблон для фиктивного узла, когда лимит трафика исчерпан.",
"subHappAutoDetect": "Автоопределение заголовков Happ",
"subHappAutoDetectDesc": "Автоматически добавлять заголовки и маршрутизацию для клиентов с User-Agent Happ.",
"subHappProviderId": "Идентификатор провайдера (Provider ID)",
"subHappProviderIdDesc": "Уникальный идентификатор для управления приложением, удаленной конфигурации и миграции.",
"subHappNewUrl": "Новый URL подписки (Миграция)",
"subHappNewUrlDesc": "Новый адрес подписки. При получении клиент Happ автоматически переключится на данный URL.",
"subHappFallbackUrl": "Резервный URL подписки (Fallback)",
"subHappFallbackUrlDesc": "Резервный адрес подписки на случай недоступности основного сервера.",
"subHappSubInfoText": "Текст информационного баннера",
"subHappSubInfoTextDesc": "Пользовательский баннер в верхней части приложения Happ (до 200 символов).",
"subHappSubInfoColor": "Цвет баннера",
"subHappSubInfoColorDesc": "Цветовая тема информационного блока: blue (синий), green (зеленый), red (красный).",
"subHappSubInfoButtonText": "Текст кнопки баннера",
"subHappSubInfoButtonTextDesc": "Название кнопки действия в информационном блоке (до 25 символов).",
"subHappSubInfoButtonLink": "Ссылка кнопки баннера",
"subHappSubInfoButtonLinkDesc": "URL-адрес, открывающийся при нажатии на кнопку баннера.",
"subHappSubExpire": "Баннер об окончании подписки",
"subHappSubExpireDesc": "Отображать баннер о скором окончании или истечении срока действия подписки.",
"subHappSubExpireButtonLink": "Ссылка для продления подписки",
"subHappSubExpireButtonLinkDesc": "URL для кнопки «Продлить» при истечении срока подписки.",
"subHappNotificationExpire": "Уведомление об окончании подписки",
"subHappNotificationExpireDesc": "Отправлять напоминания пользователю за 3 дня до окончания подписки.",
"subHappNoLimit": "Режим без ограничений (No Limit)",
"subHappNoLimitDesc": "Увеличивает лимит оперативной памяти и снимает ограничения на количество правил.",
"subHappAlwaysHwid": "Обязательный HWID",
"subHappAlwaysHwidDesc": "Запрещает пользователю отключать передачу идентификатора устройства (HWID).",
"subHappTunMode": "Режим TUN",
"subHappTunModeDesc": "Сетевой стек для TUN: system (системный) или gvisor (пользовательский стек).",
"subHappTunType": "Ядро туنнеля (TUN Type)",
"subHappTunTypeDesc": "Выбор ядра туннеля: singbox, tun2proxy, default (Happ TUN) или xray.",
"subHappExcludeRoutes": "Исключения маршрутов (CIDR)",
"subHappExcludeRoutesDesc": "Список подсетей и IP-адресов через запятую, трафик которых идет мимо туннеля.",
"subHappExcludeApns": "Исключить push-уведомления Apple (APNS)",
"subHappExcludeApnsDesc": "Трафик уведомлений Apple направляется напрямую для надежной доставки на iOS.",
"subHappColorProfile": "Цветовая тема клиента",
"subHappColorProfileDesc": "Тема оформления интерфейса Happ: violet, turquoise, cyberpunk или свой JSON.",
"subHappPingType": "Метод проверки пинга",
"subHappPingTypeDesc": "Тип проверки задержки: via Proxy (GET), via Proxy (HEAD), TCP или ICMP.",
"subHappAutoConnect": "Автоподключение при запуске",
"subHappAutoConnectDesc": "Автоматически подключаться к серверу при запуске приложения.",
"subHappAutoConnectType": "Критерий автоподключения",
"subHappAutoConnectTypeDesc": "Сервер для автоподключения: lowestdelay (наименьший пинг), lastused (последний) или random.",
"subHappPerAppMode": "Прокси для приложений (Android)",
"subHappPerAppModeDesc": "Режим раздельного туннелирования: off (выкл), on (только выбранные) или bypass (все кроме выбранных).",
"subHappPerAppList": "Пакеты приложений Android",
"subHappPerAppListDesc": "Список идентификаторов пакетов через запятую (например, org.telegram.messenger).",
"subHappPresetIran": "Обход сайтов Ирана (Iran Bypass)",
"subHappPresetChina": "Обход сайтов Китая (China Direct)",
"subHappPresetAdblock": "Блокировка рекламы (AdBlock)",
"subHappPresetGlobal": "Полный прокси (Global)",
"subHappPresetOff": "Отключить маршрутизацию (happ://routing/off)",
"subHappColorBlue": "Синий (стандартный / по умолчанию)",
"subHappColorGreen": "Зеленый (успех)",
"subHappColorRed": "Красный (предупреждение / опасность)",
"subHappTunModeDefault": "По умолчанию",
"subHappTunModeSystem": "Системный (стандартный стек ОС)",
"subHappTunModeGvisor": "gVisor (пользовательский стек)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "По умолчанию (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "Через прокси (задержка GET)",
"subHappPingProxyHead": "Через прокси (задержка HEAD)",
"subHappPingTcp": "TCP-рукопожатие (пинг)",
"subHappPingIcmp": "ICMP-пинг",
"subHappAutoConnectLowestDelay": "Минимальная задержка (быстрый узел)",
"subHappAutoConnectLastUsed": "Последний использованный узел",
"subHappAutoConnectRandom": "Случайный узел",
"subHappPerAppOff": "Выкл",
"subHappPerAppOn": "Вкл (только выбранные приложения)",
"subHappPerAppBypass": "Обход (исключить выбранные приложения)",
"subHappPresetApplied": "Пресет маршрутизации Happ успешно применен",
"subHappPresets": "Пресеты маршрутизации",
"subHappPresetsDesc": "Готовые конфигурации маршрутизации для клиентов Happ.",
"subHappVisualBuilder": "Визуальный конструктор правил",
"subHappVisualBuilderDesc": "Генератор диплинков маршрутизации на основе списков доменов и IP.",
"subHappBuildDeeplink": "Сгенерировать диплинк",
"subHappModalTitle": "Визуальный конструктор правил маршрутизации Happ",
"subHappDirectDomains": "Прямые домены (Direct)",
"subHappProxyDomains": "Проксируемые домены (Proxy)",
"subHappBlockDomains": "Заблокированные домены (Block)",
"subHappDirectIPs": "Прямые IP / CIDR",
"subHappProxyIPs": "Проксируемые IP / CIDR",
"subHappBlockIPs": "Заблокированные IP / CIDR",
"subHappDeeplinkGenerated": "Диплинк сгенерирован и применен к правилам маршрутизации",
"subHappGroupRouting": "Маршрутизация и правила",
"subHappGroupBanners": "Баннеры и уведомления",
"subHappGroupNetwork": "Сетевые настройки и TUN",
"subHappGroupThemes": "Внешний вид и темы",
"subHappGroupFailover": "Миграция и управление",
"subHappGroupAndroid": "Прокси приложений Android"
},
"xray": {
"importRules": "Импорт правил",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "Süresi Dolmuş Şablonu",
"subExpiredTemplateDesc": "Abonelik süresi dolduğunda sahte yapılandırma için kullanılacak şablon.",
"subTrafficDepletedTemplate": "Trafik Tükendi Şablonu",
"subTrafficDepletedTemplateDesc": "Abonelik trafik kotası bittiğinde sahte yapılandırma için kullanılacak şablon."
"subTrafficDepletedTemplateDesc": "Abonelik trafik kotası bittiğinde sahte yapılandırma için kullanılacak şablon.",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "Yönlendirmeyi Devre Dışı Bırak (happ://routing/off)",
"subHappColorBlue": "Mavi (Standart / Varsayılan)",
"subHappColorGreen": "Yeşil (Başarılı)",
"subHappColorRed": "Kırmızı (Uyarı / Tehlike)",
"subHappTunModeDefault": "Varsayılan",
"subHappTunModeSystem": "Sistem (Standart İşletim Sistemi Yığını)",
"subHappTunModeGvisor": "gVisor (Kullanıcı Alanı Yığını)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "Varsayılan (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "Proxy Üzerinden (GET Gecikmesi)",
"subHappPingProxyHead": "Proxy Üzerinden (HEAD Gecikmesi)",
"subHappPingTcp": "TCP El Sıkışma Pingi",
"subHappPingIcmp": "ICMP Ping",
"subHappAutoConnectLowestDelay": "En Düşük Gecikme (En Hızlı Düğüm)",
"subHappAutoConnectLastUsed": "Son Kullanılan Düğüm",
"subHappAutoConnectRandom": "Rastgele Düğüm",
"subHappPerAppOff": "Kapalı",
"subHappPerAppOn": "Açık (Yalnızca Listelenen Uygulamalar)",
"subHappPerAppBypass": "Atla (Listelenen Uygulamaları Hariç Tut)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"save": "Kaydet",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "Шаблон закінчення терміну",
"subExpiredTemplateDesc": "Шаблон для фіктивного вузла, коли термін дії підписки закінчився.",
"subTrafficDepletedTemplate": "Шаблон вичерпання трафіку",
"subTrafficDepletedTemplateDesc": "Шаблон для фіктивного вузла, коли ліміт трафіку вичерпано."
"subTrafficDepletedTemplateDesc": "Шаблон для фіктивного вузла, коли ліміт трафіку вичерпано.",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "Вимкнути маршрутизацію (happ://routing/off)",
"subHappColorBlue": "Синій (стандартний / за замовчуванням)",
"subHappColorGreen": "Зелений (успіх)",
"subHappColorRed": "Червоний (попередження / небезпека)",
"subHappTunModeDefault": "За замовчуванням",
"subHappTunModeSystem": "Системний (стандартний стек ОС)",
"subHappTunModeGvisor": "gVisor (користувацький стек)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "За замовчуванням (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "Через проксі (затримка GET)",
"subHappPingProxyHead": "Через проксі (затримка HEAD)",
"subHappPingTcp": "TCP-рукостискання (пінг)",
"subHappPingIcmp": "ICMP-пінг",
"subHappAutoConnectLowestDelay": "Найменша затримка (найшвидший вузол)",
"subHappAutoConnectLastUsed": "Останній використаний вузол",
"subHappAutoConnectRandom": "Випадковий вузол",
"subHappPerAppOff": "Вимк",
"subHappPerAppOn": "Увімк (тільки обрані програми)",
"subHappPerAppBypass": "Обхід (виключити обрані програми)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"save": "Зберегти",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "Mẫu hết hạn",
"subExpiredTemplateDesc": "Mẫu cho cấu hình ảo khi tài khoản đăng ký đã hết hạn.",
"subTrafficDepletedTemplate": "Mẫu hết dung lượng",
"subTrafficDepletedTemplateDesc": "Mẫu cho cấu hình ảo khi tài khoản đăng ký đã dùng hết dung lượng."
"subTrafficDepletedTemplateDesc": "Mẫu cho cấu hình ảo khi tài khoản đăng ký đã dùng hết dung lượng.",
"subHappAutoDetect": "Happ Header Auto-Detection",
"subHappAutoDetectDesc": "Automatically inject Happ routing and headers when client User-Agent indicates Happ.",
"subHappProviderId": "Provider ID",
"subHappProviderIdDesc": "Unique provider identifier for Happ client management, remote configuration binding, and migration.",
"subHappNewUrl": "New Subscription URL",
"subHappNewUrlDesc": "Target URL for automatic client migration. When set, Happ clients will migrate to this subscription link.",
"subHappFallbackUrl": "Fallback Subscription URL",
"subHappFallbackUrlDesc": "Backup subscription address used by Happ if the primary subscription URL becomes unreachable.",
"subHappSubInfoText": "Banner Announcement Text",
"subHappSubInfoTextDesc": "Custom announcement banner displayed at the top of the Happ client (max 200 characters).",
"subHappSubInfoColor": "Banner Accent Color",
"subHappSubInfoColorDesc": "Color theme style for the announcement banner.",
"subHappSubInfoButtonText": "Banner Button Text",
"subHappSubInfoButtonTextDesc": "Button label displayed inside the announcement banner (max 25 characters).",
"subHappSubInfoButtonLink": "Banner Button Link",
"subHappSubInfoButtonLinkDesc": "Target URL opened when the user clicks the banner action button.",
"subHappSubExpire": "Expired Subscription Banner",
"subHappSubExpireDesc": "Show an expired subscription banner in Happ when the user's traffic or validity has ended.",
"subHappSubExpireButtonLink": "Renewal Link",
"subHappSubExpireButtonLinkDesc": "Target URL opened when the user clicks the renewal button on an expired subscription.",
"subHappNotificationExpire": "Expiration Notifications",
"subHappNotificationExpireDesc": "Instruct Happ to alert the user in advance before their subscription expires.",
"subHappNoLimit": "Bypass Rule Limit",
"subHappNoLimitDesc": "Allow routing rule count to exceed default mobile platform limits without truncation.",
"subHappAlwaysHwid": "Enforce Hardware ID (HWID)",
"subHappAlwaysHwidDesc": "Bind subscription sessions to client device hardware identifier for enhanced security.",
"subHappTunMode": "TUN Mode",
"subHappTunModeDesc": "TUN virtual network interface routing mode: default, system, or strict.",
"subHappTunType": "TUN Engine",
"subHappTunTypeDesc": "Underlying network stack implementation for TUN: system, gVisor, or mixed.",
"subHappExcludeRoutes": "Exclude CIDR Routes",
"subHappExcludeRoutesDesc": "Comma-separated IP CIDRs (e.g. 192.168.0.0/16, 10.0.0.0/8) to bypass the VPN tunnel.",
"subHappExcludeApns": "Exclude Apple APNs",
"subHappExcludeApnsDesc": "Bypass Apple Push Notification services to maintain reliable background notifications on iOS.",
"subHappColorProfile": "Client Color Theme",
"subHappColorProfileDesc": "Client color theme profile: default, violet, turquoise, cyberpunk, or custom JSON theme.",
"subHappPingType": "Latency Ping Method",
"subHappPingTypeDesc": "Measurement protocol used by Happ for node latency testing: icmp, tcp, or http.",
"subHappAutoConnect": "Auto-Connect on Launch",
"subHappAutoConnectDesc": "Instruct Happ to automatically connect to VPN when the application starts.",
"subHappAutoConnectType": "Auto-Connect Target",
"subHappAutoConnectTypeDesc": "Connection target strategy for auto-connect: fastest node or last used node.",
"subHappPerAppMode": "Android Per-App Proxy Mode",
"subHappPerAppModeDesc": "Control Android application routing: off, include (proxy only listed apps), or exclude (bypass listed apps).",
"subHappPerAppList": "Android Package Names",
"subHappPerAppListDesc": "Comma-separated package names of Android applications to include or exclude (e.g. com.telegram.messenger).",
"subHappPresetIran": "Iran Bypass",
"subHappPresetChina": "China Direct",
"subHappPresetAdblock": "AdBlock",
"subHappPresetGlobal": "Full Proxy",
"subHappPresetOff": "Tắt định tuyến (happ://routing/off)",
"subHappColorBlue": "Xanh dương (Tiêu chuẩn / Mặc định)",
"subHappColorGreen": "Xanh lá (Thành công)",
"subHappColorRed": "Đỏ (Cảnh báo / Nguy hiểm)",
"subHappTunModeDefault": "Mặc định",
"subHappTunModeSystem": "Hệ thống (Ngăn xếp hệ điều hành tiêu chuẩn)",
"subHappTunModeGvisor": "gVisor (Ngăn xếp không gian người dùng)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "Mặc định (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "Qua Proxy (Độ trễ GET)",
"subHappPingProxyHead": "Qua Proxy (Độ trễ HEAD)",
"subHappPingTcp": "Ping bắt tay TCP",
"subHappPingIcmp": "Ping ICMP",
"subHappAutoConnectLowestDelay": "Độ trễ thấp nhất (Nút nhanh nhất)",
"subHappAutoConnectLastUsed": "Nút sử dụng gần nhất",
"subHappAutoConnectRandom": "Nút ngẫu nhiên",
"subHappPerAppOff": "Tắt",
"subHappPerAppOn": "Bật (Chỉ proxy ứng dụng trong danh sách)",
"subHappPerAppBypass": "Bỏ qua (Loại trừ ứng dụng trong danh sách)",
"subHappPresetApplied": "Happ preset applied to routing rules",
"subHappPresets": "Routing Presets",
"subHappPresetsDesc": "Pre-configured routing rule presets tailored for Happ clients.",
"subHappVisualBuilder": "Visual Rule Generator",
"subHappVisualBuilderDesc": "Create custom routing deeplink from domain and IP lists.",
"subHappBuildDeeplink": "Generate Deeplink",
"subHappModalTitle": "Happ Visual Routing Rule Generator",
"subHappDirectDomains": "Direct Domains (Bypass)",
"subHappProxyDomains": "Proxy Domains (Tunnel)",
"subHappBlockDomains": "Blocked Domains (Ad/Malware)",
"subHappDirectIPs": "Direct IPs / CIDRs",
"subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy"
},
"xray": {
"importRules": "Nhập quy tắc",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "过期提示模板",
"subExpiredTemplateDesc": "用户订阅过期时提示配置的备注模板。",
"subTrafficDepletedTemplate": "流量耗尽模板",
"subTrafficDepletedTemplateDesc": "用户订阅流量用尽时提示配置的备注模板。"
"subTrafficDepletedTemplateDesc": "用户订阅流量用尽时提示配置的备注模板。",
"subHappAutoDetect": "Happ 客户端请求头自动识别",
"subHappAutoDetectDesc": "当客户端 User-Agent 包含 Happ 时自动注入专属路由规则与响应头。",
"subHappProviderId": "服务提供商标识 (Provider ID)",
"subHappProviderIdDesc": "Happ 客户端管理、远程配置绑定与订阅迁移所需的唯一标识符。",
"subHappNewUrl": "新订阅地址 (迁移)",
"subHappNewUrlDesc": "自动迁移的新订阅 URL。客户端收到后会自动将订阅地址切换为此链接。",
"subHappFallbackUrl": "备用订阅地址 (Fallback)",
"subHappFallbackUrlDesc": "主订阅服务器不可用时 Happ 自动切换的备份订阅链接。",
"subHappSubInfoText": "横幅公告文本",
"subHappSubInfoTextDesc": "显示在 Happ 客户端顶部的自定义通知横幅(最多 200 字符)。",
"subHappSubInfoColor": "横幅配色主题",
"subHappSubInfoColorDesc": "横幅主题颜色:blue (默认蓝色)、green (绿色)、red (红色警告)。",
"subHappSubInfoButtonText": "横幅按钮文字",
"subHappSubInfoButtonTextDesc": "通知横幅内的操作按钮标题(最多 25 字符)。",
"subHappSubInfoButtonLink": "横幅按钮链接",
"subHappSubInfoButtonLinkDesc": "点击横幅按钮时打开的目标网址或 DeepLink。",
"subHappSubExpire": "订阅到期提醒横幅",
"subHappSubExpireDesc": "当用户订阅即将到期或已过期时在 Happ 中显示续费提示横幅。",
"subHappSubExpireButtonLink": "续费链接",
"subHappSubExpireButtonLinkDesc": "到期横幅中点击「续费」按钮时跳转的支付或购买页面。",
"subHappNotificationExpire": "订阅到期推送提醒",
"subHappNotificationExpireDesc": "在订阅到期前 3 天向用户发送每日一次的客户端到期提醒。",
"subHappNoLimit": "解除规则数量限制 (No Limit)",
"subHappNoLimitDesc": "提升内核内存上限,允许应用超出移动端默认数量的复杂路由规则。",
"subHappAlwaysHwid": "强制绑定硬件标识 (HWID)",
"subHappAlwaysHwidDesc": "禁止客户端关闭硬件标识符上报,强化多设备防盗刷安全。",
"subHappTunMode": "TUN 运行模式",
"subHappTunModeDesc": "TUN 网络接口栈:system (系统网络栈) 或 gvisor (用户态协议栈)。",
"subHappTunType": "TUN 隧道内核 (TUN Type)",
"subHappTunTypeDesc": "隧道实现引擎:singbox、tun2proxy、default (Happ 原生) 或 xray。",
"subHappExcludeRoutes": "排除直连网段 (CIDR)",
"subHappExcludeRoutesDesc": "逗号分隔的 IP/CIDR 地址段,此网段流量不走 VPN 隧道直连。",
"subHappExcludeApns": "排除苹果推送服务 (APNs)",
"subHappExcludeApnsDesc": "让 Apple APNs 流量直连,保障 iOS 设备在后台稳定接收推送通知。",
"subHappColorProfile": "客户端外观主题",
"subHappColorProfileDesc": "Happ 界面配色主题:violet、turquoise、cyberpunk 或自定义 JSON 调色板。",
"subHappPingType": "延迟测速协议",
"subHappPingTypeDesc": "节点测速协议:via Proxy (GET)、via Proxy (HEAD)、TCP 握手或 ICMP。",
"subHappAutoConnect": "启动时自动连接",
"subHappAutoConnectDesc": "Happ 客户端打开时自动连接代理节点。",
"subHappAutoConnectType": "自动连接策略",
"subHappAutoConnectTypeDesc": "连接目标选择:lowestdelay (最低延迟)、lastused (上次使用) 或 random。",
"subHappPerAppMode": "Android 分应用代理",
"subHappPerAppModeDesc": "分应用代理模式:off (关闭)、on (仅代理选定应用) 或 bypass (绕过选定应用)。",
"subHappPerAppList": "Android 应用包名列表",
"subHappPerAppListDesc": "以逗号分隔的应用包名(例如 org.telegram.messenger, com.google.android.youtube)。",
"subHappPresetIran": "伊朗直连规则 (Iran Bypass)",
"subHappPresetChina": "大陆直连规则 (China Direct)",
"subHappPresetAdblock": "广告拦截规则 (AdBlock)",
"subHappPresetGlobal": "全局代理规则 (Global)",
"subHappPresetOff": "禁用路由 (happ://routing/off)",
"subHappColorBlue": "蓝色 (标准 / 默认)",
"subHappColorGreen": "绿色 (成功)",
"subHappColorRed": "红色 (警告 / 危险)",
"subHappTunModeDefault": "默认",
"subHappTunModeSystem": "系统 (标准操作系统协议栈)",
"subHappTunModeGvisor": "gVisor (用户空间协议栈)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "默认 (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "经由代理 (GET 延迟)",
"subHappPingProxyHead": "经由代理 (HEAD 延迟)",
"subHappPingTcp": "TCP 握手 Ping",
"subHappPingIcmp": "ICMP Ping",
"subHappAutoConnectLowestDelay": "最低延迟 (最快节点)",
"subHappAutoConnectLastUsed": "最后使用的节点",
"subHappAutoConnectRandom": "随机节点",
"subHappPerAppOff": "关闭",
"subHappPerAppOn": "开启 (仅代理列表中的应用)",
"subHappPerAppBypass": "绕过 (排除列表中的应用)",
"subHappPresetApplied": "Happ 预设规则已应用",
"subHappPresets": "预设路由规则",
"subHappPresetsDesc": "专为 Happ 客户端预设调优的常用分流规则。",
"subHappVisualBuilder": "可视化规则生成器",
"subHappVisualBuilderDesc": "通过域名与 IP 列表快速生成自定义 Happ 路由 DeepLink。",
"subHappBuildDeeplink": "生成 DeepLink",
"subHappModalTitle": "Happ 路由规则可视化生成器",
"subHappDirectDomains": "直连域名 (Direct)",
"subHappProxyDomains": "代理域名 (Proxy)",
"subHappBlockDomains": "阻止域名 (Block)",
"subHappDirectIPs": "直连 IP / CIDR",
"subHappProxyIPs": "代理 IP / CIDR",
"subHappBlockIPs": "阻止 IP / CIDR",
"subHappDeeplinkGenerated": "DeepLink 已生成并填入路由规则",
"subHappGroupRouting": "路由分流与规则",
"subHappGroupBanners": "横幅公告与通知",
"subHappGroupNetwork": "网络与 TUN 引擎",
"subHappGroupThemes": "界面与主题外观",
"subHappGroupFailover": "迁移与客户端管理",
"subHappGroupAndroid": "Android 分应用代理"
},
"xray": {
"importRules": "导入规则",
+93 -1
View File
@@ -1466,7 +1466,99 @@
"subExpiredTemplate": "過期提示範本",
"subExpiredTemplateDesc": "使用者訂閱過期時提示配置的備註範本。",
"subTrafficDepletedTemplate": "流量耗盡範本",
"subTrafficDepletedTemplateDesc": "使用者訂閱流量用盡時提示配置的備註範本。"
"subTrafficDepletedTemplateDesc": "使用者訂閱流量用盡時提示配置的備註範本。",
"subHappAutoDetect": "Happ 客户端请求头自动识别",
"subHappAutoDetectDesc": "当客户端 User-Agent 包含 Happ 时自动注入专属路由规则与响应头。",
"subHappProviderId": "服务提供商标识 (Provider ID)",
"subHappProviderIdDesc": "Happ 客户端管理、远程配置绑定与订阅迁移所需的唯一标识符。",
"subHappNewUrl": "新订阅地址 (迁移)",
"subHappNewUrlDesc": "自动迁移的新订阅 URL。客户端收到后会自动将订阅地址切换为此链接。",
"subHappFallbackUrl": "备用订阅地址 (Fallback)",
"subHappFallbackUrlDesc": "主订阅服务器不可用时 Happ 自动切换的备份订阅链接。",
"subHappSubInfoText": "横幅公告文本",
"subHappSubInfoTextDesc": "显示在 Happ 客户端顶部的自定义通知横幅(最多 200 字符)。",
"subHappSubInfoColor": "横幅配色主题",
"subHappSubInfoColorDesc": "横幅主题颜色:blue (默认蓝色)、green (绿色)、red (红色警告)。",
"subHappSubInfoButtonText": "横幅按钮文字",
"subHappSubInfoButtonTextDesc": "通知横幅内的操作按钮标题(最多 25 字符)。",
"subHappSubInfoButtonLink": "横幅按钮链接",
"subHappSubInfoButtonLinkDesc": "点击横幅按钮时打开的目标网址或 DeepLink。",
"subHappSubExpire": "订阅到期提醒横幅",
"subHappSubExpireDesc": "当用户订阅即将到期或已过期时在 Happ 中显示续费提示横幅。",
"subHappSubExpireButtonLink": "续费链接",
"subHappSubExpireButtonLinkDesc": "到期横幅中点击「续费」按钮时跳转的支付或购买页面。",
"subHappNotificationExpire": "订阅到期推送提醒",
"subHappNotificationExpireDesc": "在订阅到期前 3 天向用户发送每日一次的客户端到期提醒。",
"subHappNoLimit": "解除规则数量限制 (No Limit)",
"subHappNoLimitDesc": "提升内核内存上限,允许应用超出移动端默认数量的复杂路由规则。",
"subHappAlwaysHwid": "强制绑定硬件标识 (HWID)",
"subHappAlwaysHwidDesc": "禁止客户端关闭硬件标识符上报,强化多设备防盗刷安全。",
"subHappTunMode": "TUN 运行模式",
"subHappTunModeDesc": "TUN 网络接口栈:system (系统网络栈) 或 gvisor (用户态协议栈)。",
"subHappTunType": "TUN 隧道内核 (TUN Type)",
"subHappTunTypeDesc": "隧道实现引擎:singbox、tun2proxy、default (Happ 原生) 或 xray。",
"subHappExcludeRoutes": "排除直连网段 (CIDR)",
"subHappExcludeRoutesDesc": "逗号分隔的 IP/CIDR 地址段,此网段流量不走 VPN 隧道直连。",
"subHappExcludeApns": "排除苹果推送服务 (APNs)",
"subHappExcludeApnsDesc": "让 Apple APNs 流量直连,保障 iOS 设备在后台稳定接收推送通知。",
"subHappColorProfile": "客户端外观主题",
"subHappColorProfileDesc": "Happ 界面配色主题:violet、turquoise、cyberpunk 或自定义 JSON 调色板。",
"subHappPingType": "延迟测速协议",
"subHappPingTypeDesc": "节点测速协议:via Proxy (GET)、via Proxy (HEAD)、TCP 握手或 ICMP。",
"subHappAutoConnect": "启动时自动连接",
"subHappAutoConnectDesc": "Happ 客户端打开时自动连接代理节点。",
"subHappAutoConnectType": "自动连接策略",
"subHappAutoConnectTypeDesc": "连接目标选择:lowestdelay (最低延迟)、lastused (上次使用) 或 random。",
"subHappPerAppMode": "Android 分应用代理",
"subHappPerAppModeDesc": "分应用代理模式:off (关闭)、on (仅代理选定应用) 或 bypass (绕过选定应用)。",
"subHappPerAppList": "Android 应用包名列表",
"subHappPerAppListDesc": "以逗号分隔的应用包名(例如 org.telegram.messenger, com.google.android.youtube)。",
"subHappPresetIran": "伊朗直连规则 (Iran Bypass)",
"subHappPresetChina": "大陆直连规则 (China Direct)",
"subHappPresetAdblock": "广告拦截规则 (AdBlock)",
"subHappPresetGlobal": "全局代理规则 (Global)",
"subHappPresetOff": "停用路由 (happ://routing/off)",
"subHappColorBlue": "藍色 (標準 / 預設)",
"subHappColorGreen": "綠色 (成功)",
"subHappColorRed": "紅色 (警告 / 危險)",
"subHappTunModeDefault": "預設",
"subHappTunModeSystem": "系統 (標準作業系統協議棧)",
"subHappTunModeGvisor": "gVisor (使用者空間協議棧)",
"subHappTunTypeSingbox": "sing-box",
"subHappTunTypeTun2proxy": "tun2proxy",
"subHappTunTypeDefault": "預設 (Happ TUN)",
"subHappTunTypeXray": "Xray TUN",
"subHappPingProxy": "經由代理 (GET 延遲)",
"subHappPingProxyHead": "經由代理 (HEAD 延遲)",
"subHappPingTcp": "TCP 握手 Ping",
"subHappPingIcmp": "ICMP Ping",
"subHappAutoConnectLowestDelay": "最低延遲 (最快節點)",
"subHappAutoConnectLastUsed": "最後使用的節點",
"subHappAutoConnectRandom": "隨機節點",
"subHappPerAppOff": "關閉",
"subHappPerAppOn": "開啟 (僅代理列表中的應用)",
"subHappPerAppBypass": "繞過 (排除列表中的應用)",
"subHappPresetApplied": "Happ 预设规则已应用",
"subHappPresets": "预设路由规则",
"subHappPresetsDesc": "专为 Happ 客户端预设调优的常用分流规则。",
"subHappVisualBuilder": "可视化规则生成器",
"subHappVisualBuilderDesc": "通过域名与 IP 列表快速生成自定义 Happ 路由 DeepLink。",
"subHappBuildDeeplink": "生成 DeepLink",
"subHappModalTitle": "Happ 路由规则可视化生成器",
"subHappDirectDomains": "直连域名 (Direct)",
"subHappProxyDomains": "代理域名 (Proxy)",
"subHappBlockDomains": "阻止域名 (Block)",
"subHappDirectIPs": "直连 IP / CIDR",
"subHappProxyIPs": "代理 IP / CIDR",
"subHappBlockIPs": "阻止 IP / CIDR",
"subHappDeeplinkGenerated": "DeepLink 已生成并填入路由规则",
"subHappGroupRouting": "路由分流与规则",
"subHappGroupBanners": "横幅公告与通知",
"subHappGroupNetwork": "网络与 TUN 引擎",
"subHappGroupThemes": "界面与主题外观",
"subHappGroupFailover": "迁移与客户端管理",
"subHappGroupAndroid": "Android 分应用代理"
},
"xray": {
"save": "儲存",