From b558202441a390d45472e4064fe04e7f76f28154 Mon Sep 17 00:00:00 2001 From: dongchengjie <37543964+dongchengjie@users.noreply.github.com> Date: Tue, 2 Jul 2024 17:04:22 +0800 Subject: [PATCH] chore: rule types locale --- .../profile/rules-editor-viewer.tsx | 396 +++++++++++------- src/locales/en.json | 59 ++- src/locales/fa.json | 49 +++ src/locales/ru.json | 55 ++- src/locales/zh.json | 44 +- 5 files changed, 436 insertions(+), 167 deletions(-) diff --git a/src/components/profile/rules-editor-viewer.tsx b/src/components/profile/rules-editor-viewer.tsx index e1fb272..902eec9 100644 --- a/src/components/profile/rules-editor-viewer.tsx +++ b/src/components/profile/rules-editor-viewer.tsx @@ -17,6 +17,7 @@ import { } from "@dnd-kit/sortable"; import { Autocomplete, + Box, Button, Dialog, DialogActions, @@ -43,100 +44,197 @@ interface Props { onChange?: (prev?: string, curr?: string) => void; } -const RuleTypeList = [ - "DOMAIN", - "DOMAIN-SUFFIX", - "DOMAIN-KEYWORD", - "DOMAIN-REGEX", - "GEOSITE", - "IP-CIDR", - "IP-SUFFIX", - "IP-ASN", - "GEOIP", - "SRC-GEOIP", - "SRC-IP-ASN", - "SRC-IP-CIDR", - "SRC-IP-SUFFIX", - "DST-PORT", - "SRC-PORT", - "IN-PORT", - "IN-TYPE", - "IN-USER", - "IN-NAME", - "PROCESS-PATH", - "PROCESS-PATH-REGEX", - "PROCESS-NAME", - "PROCESS-NAME-REGEX", - "UID", - "NETWORK", - "DSCP", - "RULE-SET", - "SUB-RULE", - "AND", - "OR", - "NOT", - "MATCH", -] as const; - -const NoResolveList = [ - "GEOIP", - "IP-ASN", - "IP-CIDR", - "IP-CIDR6", - "IP-SUFFIX", - "RULE-SET", -]; -const ExampleMap = { - DOMAIN: "example.com", - "DOMAIN-SUFFIX": "example.com", - "DOMAIN-KEYWORD": "example", - "DOMAIN-REGEX": "example.*", - GEOSITE: "youtube", - "IP-CIDR": "127.0.0.0/8", - "IP-SUFFIX": "8.8.8.8/24", - "IP-ASN": "13335", - GEOIP: "CN", - "SRC-GEOIP": "cn", - "SRC-IP-ASN": "9808", - "SRC-IP-CIDR": "192.168.1.201/32", - "SRC-IP-SUFFIX": "192.168.1.201/8", - "DST-PORT": "80", - "SRC-PORT": "7777", - "IN-PORT": "7890", - "IN-TYPE": "SOCKS/HTTP", - "IN-USER": "mihomo", - "IN-NAME": "ss", - "PROCESS-PATH": - getSystem() === "windows" - ? "C:Program FilesGoogleChromeApplicationchrome.exe" - : "/usr/bin/wget", - "PROCESS-PATH-REGEX": - getSystem() === "windows" ? "(?i).*Application\\chrome.*" : ".*bin/wget", - "PROCESS-NAME": getSystem() === "windows" ? "chrome.exe" : "curl", - "PROCESS-NAME-REGEX": ".*telegram.*", - UID: "1001", - NETWORK: "udp", - DSCP: "4", - "RULE-SET": "providername", - "SUB-RULE": "", - AND: "((DOMAIN,baidu.com),(NETWORK,UDP))", - OR: "((NETWORK,UDP),(DOMAIN,baidu.com))", - NOT: "((DOMAIN,baidu.com))", - MATCH: "", +const portValidator = (value: string): boolean => { + return new RegExp( + "^(?:[1-9]\\d{0,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$" + ).test(value); +}; +const ipv4CIDRValidator = (value: string): boolean => { + return new RegExp( + "^(?:(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))\\.){3}(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))(?:\\/(?:[12]?[0-9]|3[0-2]))$" + ).test(value); +}; +const ipv6CIDRValidator = (value: string): boolean => { + return new RegExp( + "^([0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){7}|::|:(?::[0-9a-fA-F]{1,4}){1,6}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,5}|(?:[0-9a-fA-F]{1,4}:){2}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){3}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){4}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){5}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,6}:)\\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])$" + ).test(value); }; -const BuiltinProxyPolicyList = ["DIRECT", "REJECT", "REJECT-DROP", "PASS"]; +const rules: { + name: string; + required?: boolean; + example?: string; + noResolve?: boolean; + validator?: (value: string) => boolean; +}[] = [ + { + name: "DOMAIN", + example: "example.com", + }, + { + name: "DOMAIN-SUFFIX", + example: "example.com", + }, + { + name: "DOMAIN-KEYWORD", + example: "example", + }, + { + name: "DOMAIN-REGEX", + example: "example.*", + }, + { + name: "GEOSITE", + example: "youtube", + }, + { + name: "GEOIP", + example: "CN", + noResolve: true, + }, + { + name: "SRC-GEOIP", + example: "CN", + }, + { + name: "IP-ASN", + example: "13335", + noResolve: true, + validator: (value) => (+value ? true : false), + }, + { + name: "SRC-IP-ASN", + example: "9808", + validator: (value) => (+value ? true : false), + }, + { + name: "IP-CIDR", + example: "127.0.0.0/8", + noResolve: true, + validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value), + }, + { + name: "IP-CIDR6", + example: "2620:0:2d0:200::7/32", + noResolve: true, + validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value), + }, + { + name: "SRC-IP-CIDR", + example: "192.168.1.201/32", + validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value), + }, + { + name: "IP-SUFFIX", + example: "8.8.8.8/24", + noResolve: true, + validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value), + }, + { + name: "SRC-IP-SUFFIX", + example: "192.168.1.201/8", + validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value), + }, + { + name: "SRC-PORT", + example: "7777", + validator: (value) => portValidator(value), + }, + { + name: "DST-PORT", + example: "80", + validator: (value) => portValidator(value), + }, + { + name: "IN-PORT", + example: "7890", + validator: (value) => portValidator(value), + }, + { + name: "DSCP", + example: "4", + }, + { + name: "PROCESS-NAME", + example: getSystem() === "windows" ? "chrome.exe" : "curl", + }, + { + name: "PROCESS-PATH", + example: + getSystem() === "windows" + ? "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" + : "/usr/bin/wget", + }, + { + name: "PROCESS-NAME-REGEX", + example: ".*telegram.*", + }, + { + name: "PROCESS-PATH-REGEX", + example: + getSystem() === "windows" ? "(?i).*Application\\chrome.*" : ".*bin/wget", + }, + { + name: "NETWORK", + example: "udp", + validator: (value) => ["tcp", "udp"].includes(value), + }, + { + name: "UID", + example: "1001", + validator: (value) => (+value ? true : false), + }, + { + name: "IN-TYPE", + example: "SOCKS/HTTP", + }, + { + name: "IN-USER", + example: "mihomo", + }, + { + name: "IN-NAME", + example: "ss", + }, + { + name: "SUB-RULE", + example: "(NETWORK,tcp)", + }, + { + name: "RULE-SET", + example: "providername", + noResolve: true, + }, + { + name: "AND", + example: "((DOMAIN,baidu.com),(NETWORK,UDP))", + }, + { + name: "OR", + example: "((NETWORK,UDP),(DOMAIN,baidu.com))", + }, + { + name: "NOT", + example: "((DOMAIN,baidu.com))", + }, + { + name: "MATCH", + required: false, + }, +]; + +const builtinProxyPolicies = ["DIRECT", "REJECT", "REJECT-DROP", "PASS"]; export const RulesEditorViewer = (props: Props) => { const { title, profileUid, property, open, onClose, onChange } = props; const { t } = useTranslation(); const [prevData, setPrevData] = useState(""); - const [ruleType, setRuleType] = - useState<(typeof RuleTypeList)[number]>("DOMAIN"); + + const [ruleType, setRuleType] = useState<(typeof rules)[number]>(rules[0]); const [ruleContent, setRuleContent] = useState(""); const [noResolve, setNoResolve] = useState(false); - const [proxyPolicy, setProxyPolicy] = useState("DIRECT"); + const [proxyPolicy, setProxyPolicy] = useState(builtinProxyPolicies[0]); const [proxyPolicyList, setProxyPolicyList] = useState([]); const [ruleList, setRuleList] = useState([]); const [ruleSetList, setRuleSetList] = useState([]); @@ -195,7 +293,7 @@ export const RulesEditorViewer = (props: Props) => { let ruleSetObj = yaml.load(data) as { "rule-providers": [] }; let subRuleObj = yaml.load(data) as { "sub-rules": [] }; setProxyPolicyList( - BuiltinProxyPolicyList.concat( + builtinProxyPolicies.concat( groupsObj["proxy-groups"] ? groupsObj["proxy-groups"].map((item: any) => item.name) : [] @@ -217,29 +315,17 @@ export const RulesEditorViewer = (props: Props) => { fetchProfile(); }, [open]); - const spliceRule = () => { - if (ruleContent === "") return ""; - // Check valid by regex - switch (ruleType) { - case "IP-CIDR": { - let v4regex = new RegExp( - "^((?:(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))\\.){3}(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5])))?:(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-5]{2}[0-3][0-5])$" - ); - let v6regex = new RegExp( - "^([0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){7}|::|:(?::[0-9a-fA-F]{1,4}){1,6}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,5}|(?:[0-9a-fA-F]{1,4}:){2}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){3}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){4}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){5}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,6}:)\\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])$" - ); - if (!v4regex.test(ruleContent) && !v6regex.test(ruleContent)) return ""; - - break; - } - default: - break; + const validateRule = () => { + if ((ruleType.required ?? true) && !ruleContent) { + throw new Error(t("Rule Condition Required")); } - return `${ruleType}${ - ruleType === "MATCH" ? "" : "," + ruleContent - },${proxyPolicy}${ - NoResolveList.includes(ruleType) && noResolve ? ",no-resolve" : "" - }`; + if (ruleType.validator && !ruleType.validator(ruleContent)) { + throw new Error(t("Invalid Rule")); + } + + return `${ruleType.name}${ + ruleContent ? "," + ruleContent : "" + },${proxyPolicy}${ruleType.noResolve && noResolve ? ",no-resolve" : ""}`; }; const onSave = useLockFn(async () => { @@ -274,49 +360,50 @@ export const RulesEditorViewer = (props: Props) => { { - if (v) setRuleType(v); - }} renderInput={(params) => } + options={rules} + value={ruleType} + getOptionLabel={(option) => option.name} + renderOption={(props, option) => ( +
  • + {option.name} +
  • + )} + onChange={(_, value) => value && setRuleType(value)} /> - + - {ruleType === "RULE-SET" && ( + + {ruleType.name === "RULE-SET" && ( } options={ruleSetList} - onChange={(_, v) => { - if (v) setRuleContent(v); - }} - renderInput={(params) => } + value={ruleContent} + onChange={(_, value) => value && setRuleContent(value)} /> )} - {ruleType === "SUB-RULE" && ( + {ruleType.name === "SUB-RULE" && ( { - if (v) setRuleContent(v); - }} renderInput={(params) => } + options={subRuleList} + value={ruleContent} + onChange={(_, value) => value && setRuleContent(value)} /> )} - {ruleType !== "RULE-SET" && ruleType !== "SUB-RULE" && ( + {ruleType.name !== "RULE-SET" && ruleType.name !== "SUB-RULE" && ( { - setRuleContent(e.target.value); - }} + required={ruleType.required ?? true} + error={(ruleType.required ?? true) && !ruleContent} + placeholder={ruleType.example} + onChange={(e) => setRuleContent(e.target.value)} /> )} @@ -325,22 +412,23 @@ export const RulesEditorViewer = (props: Props) => { { - if (v) setProxyPolicy(v); - }} renderInput={(params) => } + options={proxyPolicyList} + value={proxyPolicy} + renderOption={(props, option) => ( +
  • + {option} +
  • + )} + onChange={(_, value) => value && setProxyPolicy(value)} />
    - {NoResolveList.includes(ruleType) && ( + {ruleType.noResolve && ( { - setNoResolve(!noResolve); - }} + onChange={() => setNoResolve(!noResolve)} /> )} @@ -350,16 +438,18 @@ export const RulesEditorViewer = (props: Props) => { fullWidth variant="contained" onClick={() => { - let raw = spliceRule(); - if (raw === "") { - Notice.error(t("Invalid Rule")); - return; + try { + let raw = validateRule(); + console.log(raw); + + if (prependSeq.includes(raw)) return; + setPrependSeq([...prependSeq, raw]); + } catch (err: any) { + Notice.error(err.message || err.toString()); } - if (prependSeq.includes(raw)) return; - setPrependSeq([...prependSeq, raw]); }} > - {t("Add Prepend Rule")} + {t("Prepend Rule")} @@ -367,16 +457,16 @@ export const RulesEditorViewer = (props: Props) => { fullWidth variant="contained" onClick={() => { - let raw = spliceRule(); - if (raw === "") { - Notice.error(t("Invalid Rule")); - return; + try { + let raw = validateRule(); + if (appendSeq.includes(raw)) return; + setPrependSeq([...appendSeq, raw]); + } catch (err: any) { + Notice.error(err.message || err.toString()); } - if (appendSeq.includes(raw)) return; - setAppendSeq([...appendSeq, raw]); }} > - {t("Add Append Rule")} + {t("Append Rule")} diff --git a/src/locales/en.json b/src/locales/en.json index 1117e4e..94d3c24 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -54,12 +54,50 @@ "Edit Rules": "Edit Rules", "Rule Type": "Rule Type", "Rule Content": "Rule Content", - "Proxy Policy": "roxy Policy", + "Proxy Policy": "Proxy Policy", "No Resolve": "No Resolve", - "Add Prepend Rule": "Add Prepend Rule", - "Add Append Rule": "Add Append Rule", - "Invalid Rule": "Invalid Rule", + "Prepend Rule": "Prepend Rule", + "Append Rule": "Append Rule", "Delete Rule": "Delete Rule", + "Rule Condition Required": "Rule Condition Required", + "Invalid Rule": "Invalid Rule", + "DOMAIN": "Matches the full domain name", + "DOMAIN-SUFFIX": "Matches the domain suffix", + "DOMAIN-KEYWORD": "Matches the domain keyword", + "DOMAIN-REGEX": "Matches the domain using regular expressions", + "GEOSITE": "Matches domains within the Geosite", + "GEOIP": "Matches the country code of the IP address", + "SRC-GEOIP": "Matches the country code of the source IP address", + "IP-ASN": "Matches the IP address's ASN", + "SRC-IP-ASN": "Matches the source IP address's ASN", + "IP-CIDR": "Matches the IP address range", + "IP-CIDR6": "Matches the IPv6 address range", + "SRC-IP-CIDR": "Matches the source IP address range", + "IP-SUFFIX": "Matches the IP address suffix range", + "SRC-IP-SUFFIX": "Matches the source IP address suffix range", + "SRC-PORT": "Matches the source port range", + "DST-PORT": "Matches the destination port range", + "IN-PORT": "Matches the inbound port", + "DSCP": "DSCP marking (only for tproxy UDP inbound)", + "PROCESS-NAME": "Matches the process name (Android package name)", + "PROCESS-PATH": "Matches the full process path", + "PROCESS-NAME-REGEX": "Matches the full process name using regular expressions (Android package name)", + "PROCESS-PATH-REGEX": "Matches the full process path using regular expressions", + "NETWORK": "Matches the transport protocol (tcp/udp)", + "UID": "Matches the Linux USER ID", + "IN-TYPE": "Matches the inbound type", + "IN-USER": "Matches the inbound username", + "IN-NAME": "Matches the inbound name", + "SUB-RULE": "Sub-rule", + "RULE-SET": "Matches the rule set", + "AND": "Logical AND", + "OR": "Logical OR", + "NOT": "Logical NOT", + "MATCH": "Matches all requests", + "DIRECT": "Data goes directly outbound", + "REJECT": "Intercepts requests", + "REJECT-DROP": "Discards requests", + "PASS": "Skips this rule when matched", "Edit Groups": "Edit Proxy Groups", "Extend Config": "Extend Config", "Extend Script": "Extend Script", @@ -163,12 +201,20 @@ "Auto Launch": "Auto Launch", "Silent Start": "Silent Start", "Silent Start Info": "Start the program in background mode without displaying the panel", + "TG Channel": "Telegram Channel", + "Manual": "Manual", + "Github Repo": "Github Repo", "Clash Setting": "Clash Setting", - "Allow Lan": "Allow Lan", + "Allow Lan": "Allow LAN", "IPv6": "IPv6", "Log Level": "Log Level", "Port Config": "Port Config", "Random Port": "Random Port", + "Mixed Port": "Mixed Port", + "Socks Port": "Socks Port", + "Http Port": "Http(s) Port", + "Redir Port": "Redir Port", + "Tproxy Port": "Tproxy Port", "External": "External", "External Controller": "External Controller", "Core Secret": "Core Secret", @@ -186,9 +232,6 @@ "Open UWP tool": "Open UWP tool", "Open UWP tool Info": "Since Windows 8, UWP apps (such as Microsoft Store) are restricted from directly accessing local host network services, and this tool can be used to bypass this restriction", "Update GeoData": "Update GeoData", - "TG Channel": "Telegram Channel", - "Manual": "Manual", - "Github Repo": "Github Repo", "Verge Setting": "Verge Setting", "Language": "Language", "Theme Mode": "Theme Mode", diff --git a/src/locales/fa.json b/src/locales/fa.json index 3f85e28..640944a 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -50,6 +50,55 @@ "Expire Time": "زمان انقضا", "Create Profile": "ایجاد پروفایل", "Edit Profile": "ویرایش پروفایل", + "Edit Proxies": "ویرایش پروکسی‌ها", + "Edit Rules": "ویرایش قوانین", + "Rule Type": "نوع قانون", + "Rule Content": "محتوای قانون", + "Proxy Policy": "سیاست پروکسی", + "No Resolve": "بدون حل", + "Prepend Rule": "اضافه کردن قانون به ابتدا", + "Append Rule": "اضافه کردن قانون به انتها", + "Delete Rule": "حذف قانون", + "Rule Condition Required": "شرط قانون الزامی است", + "Invalid Rule": "قانون نامعتبر", + "DOMAIN": "مطابقت با نام کامل دامنه", + "DOMAIN-SUFFIX": "مطابقت با پسوند دامنه", + "DOMAIN-KEYWORD": "مطابقت با کلمه کلیدی دامنه", + "DOMAIN-REGEX": "مطابقت با دامنه با استفاده از عبارات منظم", + "GEOSITE": "مطابقت با دامنه‌های درون Geosite", + "GEOIP": "مطابقت با کد کشور IP", + "SRC-GEOIP": "مطابقت با کد کشور IP مبدا", + "IP-ASN": "مطابقت با ASN آدرس IP", + "SRC-IP-ASN": "مطابقت با ASN آدرس IP مبدا", + "IP-CIDR": "مطابقت با محدوده آدرس IP", + "IP-CIDR6": "مطابقت با محدوده آدرس IPv6", + "SRC-IP-CIDR": "مطابقت با محدوده آدرس IP مبدا", + "IP-SUFFIX": "مطابقت با محدوده پسوند آدرس IP", + "SRC-IP-SUFFIX": "مطابقت با محدوده پسوند آدرس IP مبدا", + "SRC-PORT": "مطابقت با محدوده پورت مبدا", + "DST-PORT": "مطابقت با محدوده پورت مقصد", + "IN-PORT": "مطابقت با پورت ورودی", + "DSCP": "علامت‌گذاری DSCP (فقط برای tproxy UDP ورودی)", + "PROCESS-NAME": "مطابقت با نام فرآیند (نام بسته Android)", + "PROCESS-PATH": "مطابقت با مسیر کامل فرآیند", + "PROCESS-NAME-REGEX": "مطابقت با نام فرآیند با استفاده از عبارات منظم (نام بسته Android)", + "PROCESS-PATH-REGEX": "مطابقت با مسیر کامل فرآیند با استفاده از عبارات منظم", + "NETWORK": "مطابقت با پروتکل انتقال (tcp/udp)", + "UID": "مطابقت با شناسه کاربری Linux", + "IN-TYPE": "مطابقت با نوع ورودی", + "IN-USER": "مطابقت با نام کاربری ورودی", + "IN-NAME": "مطابقت با نام ورودی", + "SUB-RULE": "قانون فرعی", + "RULE-SET": "مطابقت با مجموعه قوانین", + "AND": "منطق AND", + "OR": "منطق OR", + "NOT": "منطق NOT", + "MATCH": "مطابقت با تمام درخواست‌ها", + "DIRECT": "داده‌ها به صورت مستقیم خروجی می‌شوند", + "REJECT": "درخواست‌ها را متوقف می‌کند", + "REJECT-DROP": "درخواست‌ها را نادیده می‌گیرد", + "PASS": "این قانون را در صورت تطابق نادیده می‌گیرد", + "Edit Groups": "ویرایش گروه‌های پروکسی", "Extend Config": "توسعه پیکربندی", "Extend Script": "ادغام اسکریپت", "Global Merge": "تنظیمات گسترده‌ی سراسری", diff --git a/src/locales/ru.json b/src/locales/ru.json index 95a6042..faa882a 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -50,6 +50,55 @@ "Expire Time": "Время окончания", "Create Profile": "Создать профиль", "Edit Profile": "Изменить профиль", + "Edit Proxies": "Редактировать прокси", + "Edit Rules": "Редактировать правила", + "Rule Type": "Тип правила", + "Rule Content": "Содержимое правила", + "Proxy Policy": "Политика прокси", + "No Resolve": "Без разрешения", + "Prepend Rule": "Добавить правило в начало", + "Append Rule": "Добавить правило в конец", + "Delete Rule": "Удалить правило", + "Rule Condition Required": "Требуется условие правила", + "Invalid Rule": "Недействительное правило", + "DOMAIN": "Соответствует полному доменному имени", + "DOMAIN-SUFFIX": "Соответствует суффиксу домена", + "DOMAIN-KEYWORD": "Соответствует ключевому слову домена", + "DOMAIN-REGEX": "Соответствует домену с использованием регулярных выражений", + "GEOSITE": "Соответствует доменам в Geosite", + "GEOIP": "Соответствует коду страны IP-адреса", + "SRC-GEOIP": "Соответствует коду страны исходного IP-адреса", + "IP-ASN": "Соответствует ASN IP-адреса", + "SRC-IP-ASN": "Соответствует ASN исходного IP-адреса", + "IP-CIDR": "Соответствует диапазону IP-адресов", + "IP-CIDR6": "Соответствует диапазону IPv6-адресов", + "SRC-IP-CIDR": "Соответствует диапазону исходных IP-адресов", + "IP-SUFFIX": "Соответствует диапазону суффиксов IP-адресов", + "SRC-IP-SUFFIX": "Соответствует диапазону суффиксов исходных IP-адресов", + "SRC-PORT": "Соответствует диапазону исходных портов", + "DST-PORT": "Соответствует диапазону целевых портов", + "IN-PORT": "Соответствует входящему порту", + "DSCP": "Маркировка DSCP (только для tproxy UDP входящего)", + "PROCESS-NAME": "Соответствует имени процесса (имя пакета Android)", + "PROCESS-PATH": "Соответствует полному пути процесса", + "PROCESS-NAME-REGEX": "Соответствует имени процесса с использованием регулярных выражений (имя пакета Android)", + "PROCESS-PATH-REGEX": "Соответствует полному пути процесса с использованием регулярных выражений", + "NETWORK": "Соответствует транспортному протоколу (tcp/udp)", + "UID": "Соответствует USER ID в Linux", + "IN-TYPE": "Соответствует типу входящего соединения", + "IN-USER": "Соответствует имени пользователя входящего соединения", + "IN-NAME": "Соответствует имени входящего соединения", + "SUB-RULE": "Подправило", + "RULE-SET": "Соответствует набору правил", + "AND": "Логическое И", + "OR": "Логическое ИЛИ", + "NOT": "Логическое НЕ", + "MATCH": "Соответствует всем запросам", + "DIRECT": "Данные направляются напрямую наружу", + "REJECT": "Перехватывает запросы", + "REJECT-DROP": "Отклоняет запросы", + "PASS": "Пропускает это правило при совпадении", + "Edit Groups": "Редактировать группы прокси", "Extend Config": "Изменить Merge.", "Extend Script": "Изменить Script", "Global Merge": "Глобальный расширенный Настройки", @@ -152,6 +201,9 @@ "Auto Launch": "Автозапуск", "Silent Start": "Тихий запуск", "Silent Start Info": "Запускать программу в фоновом режиме без отображения панели", + "TG Channel": "Канал Telegram", + "Manual": "Документация", + "Github Repo": "GitHub репозиторий", "Clash Setting": "Настройки Clash", "Allow Lan": "Разрешить локальную сеть", "IPv6": "IPv6", @@ -180,9 +232,6 @@ "Open UWP tool": "Открыть UWP инструмент", "Open UWP tool Info": "С Windows 8 приложения UWP (такие как Microsoft Store) ограничены в прямом доступе к сетевым службам локального хоста, и этот инструмент позволяет обойти это ограничение", "Update GeoData": "Обновление GeoData", - "TG Channel": "Канал Telegram", - "Manual": "Документация", - "Github Repo": "GitHub репозиторий", "Verge Setting": "Настройки Verge", "Language": "Язык", "Theme Mode": "Режим темы", diff --git a/src/locales/zh.json b/src/locales/zh.json index 18fe8ec..cd49921 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -56,10 +56,48 @@ "Rule Content": "规则内容", "Proxy Policy": "代理策略", "No Resolve": "跳过DNS解析", - "Add Prepend Rule": "添加前置规则", - "Add Append Rule": "添加后置规则", - "Invalid Rule": "无效规则", + "Prepend Rule": "添加前置规则", + "Append Rule": "添加后置规则", "Delete Rule": "删除规则", + "Rule Condition Required": "规则条件缺失", + "Invalid Rule": "无效规则", + "DOMAIN": "匹配完整域名", + "DOMAIN-SUFFIX": "匹配域名后缀", + "DOMAIN-KEYWORD": "匹配域名关键字", + "DOMAIN-REGEX": "匹配域名正则表达式", + "GEOSITE": "匹配Geosite内的域名", + "GEOIP": "匹配IP所属国家代码", + "SRC-GEOIP": "匹配来源IP所属国家代码", + "IP-ASN": "匹配IP所属ASN", + "SRC-IP-ASN": "匹配来源IP所属ASN", + "IP-CIDR": "匹配IP地址范围", + "IP-CIDR6": "匹配IP地址范围", + "SRC-IP-CIDR": "匹配来源IP地址范围", + "IP-SUFFIX": "匹配IP后缀范围", + "SRC-IP-SUFFIX": "匹配来源IP后缀范围", + "SRC-PORT": "匹配请求来源端口范围", + "DST-PORT": "匹配请求目标端口范围", + "IN-PORT": "匹配入站端口", + "DSCP": "DSCP标记(仅限tproxy udp入站)", + "PROCESS-NAME": "匹配进程名称(Android包名)", + "PROCESS-PATH": "匹配完整进程路径", + "PROCESS-NAME-REGEX": "正则匹配完整进程名称(Android包名)", + "PROCESS-PATH-REGEX": "正则匹配完整进程路径", + "NETWORK": "匹配传输协议(tcp/udp)", + "UID": "匹配Linux USER ID", + "IN-TYPE": "匹配入站类型", + "IN-USER": "匹配入站用户名", + "IN-NAME": "匹配入站名称", + "SUB-RULE": "子规则", + "RULE-SET": "匹配规则集", + "AND": "逻辑和", + "OR": "逻辑或", + "NOT": "逻辑非", + "MATCH": "匹配所有请求", + "DIRECT": "直连", + "REJECT": "拦截请求", + "REJECT-DROP": "抛弃请求", + "PASS": "跳过此规则", "Edit Groups": "编辑代理组", "Extend Config": "扩展配置", "Extend Script": "扩展脚本",