From 64b1ec10c286c254e90c1766d763b4275dba3c50 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Mon, 27 Jul 2026 11:09:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(i18n):=20=E5=88=86=E6=B5=81=E8=A7=84?= =?UTF-8?q?=E5=88=99=E9=A1=B5=E9=9D=99=E6=80=81=E6=A0=87=E7=AD=BE=E6=8E=A5?= =?UTF-8?q?=E5=8D=95=E6=BA=90=20+=20=E4=BF=AE=20mobile=20=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E8=A1=8C=E6=A8=A1=E5=BC=8F=20pill=20=E8=AF=AD?= =?UTF-8?q?=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 路由页此前静态标签硬编码中文,EN 模式混语。全部接进 strings.json 单源: - 新增 26 个 routing* 产品 getter(6 语):代理模式/内置规则/国内直连/我的规则/ 添加规则/从文本导入/重置默认/最终/直连·走隧道·拒绝/域名后缀/GeoSite 规则集/返回设置… (复用 smartRoute;seg 三项 EN 取短词 Global/Smart/Direct 适配段宽)。 - app_text.dart +26 抽象声明;重生成 6 份 strings_*.dart;alias.json desktop +24 / mobile +19。 - markup 加 data-i18n;JS pill(直连/走隧道/拒绝、FINAL)统一改从生成的 I18N 单源取(_ri), 与静态 pill 同源。JS-owned 的模式说明 rtModeNote 保留 _t 双语(不加 data-i18n)。 - desktop LAN 行(此前未接)补接 rr.lan/rr.lanSub + 新 routingForcedPill。 - 修 mobile 设置行「Routing rules」的模式 pill:缺 data-i18n → EN 显中文,补 setSmartRoute (en=Smart routing,与 desktop 一致)。 验收:l10n 重生成+analyze 0 issue、原型 0 unresolved、生成器零 diff、244 测试全绿、无红线词。 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01A79VtQA1BwTuQN1ThpvYpo --- client/lib/l10n/app_text.dart | 24 +++ client/lib/l10n/strings_en.dart | 48 ++++++ client/lib/l10n/strings_es.dart | 48 ++++++ client/lib/l10n/strings_ja.dart | 48 ++++++ client/lib/l10n/strings_ko.dart | 48 ++++++ client/lib/l10n/strings_ru.dart | 48 ++++++ client/lib/l10n/strings_zh.dart | 48 ++++++ design/i18n/strings.json | 192 +++++++++++++++++++++++ design/prototype/i18n/alias.json | 45 +++++- design/prototype/screens/ui-desktop.html | 72 ++++++--- design/prototype/screens/ui-mobile.html | 72 ++++++--- 11 files changed, 650 insertions(+), 43 deletions(-) diff --git a/client/lib/l10n/app_text.dart b/client/lib/l10n/app_text.dart index 07c60d3..a5e3d5c 100644 --- a/client/lib/l10n/app_text.dart +++ b/client/lib/l10n/app_text.dart @@ -633,4 +633,28 @@ abstract class AppText { String get routingRulesSub; // 自定义直连 / 走隧道 / 拒绝 String get routingLanDirect; // 局域网 / 私网直连 / LAN & private direct String get routingLanForced; // 强制,不可关 / Always on + String get routingForcedPill; // 🔒 强制 / 🔒 Forced + String get routingProxyMode; // 代理模式 / Proxy mode + String get routingModeGlobal; // 全局代理 / Global + String get routingModeSmart; // 智能分流 / Smart + String get routingModeDirect; // 全部直连 / Direct + String get routingBuiltin; // 内置规则 / Built-in rules + String get routingCnDirect; // 国内直连 / China direct + String get routingMyRulesOrdered; // 我的规则 · 有序,首命中生效 / My rules · ordered, first match wins + String get routingMyRulesFirst; // 我的规则 · 首命中生效 / My rules · first match wins + String get routingFinalLong; // 最终(未命中任何规则) / Final (no rule matched) + String get routingFinalShort; // 最终(未命中) / Final (no match) + String get routingFinalFallback; // FINAL 兜底 / FINAL fallback + String get routingTypeDomainSuffix; // 域名后缀 / Domain suffix + String get routingTypeDomainSuffixCi; // 域名后缀 · CI 源 / Domain suffix · CI source + String get routingTypeDomainSuffixMirror; // 域名后缀 · 镜像 / Domain suffix · Mirror + String get routingTypeGeosite; // GeoSite 规则集 / GeoSite rule-set + String get routingActDirect; // 直连 / Direct + String get routingActProxy; // 走隧道 / Tunnel + String get routingActReject; // 拒绝 / Reject + String get routingForcedRow; // ⚠ 该域名由系统强制走隧道,此规则不生效 / ⚠ Forced to tunnel by the system; this rule is inactive + String get routingAddRule; // 添加规则 / Add rule + String get routingImport; // 从文本导入 / Import from text + String get routingReset; // 重置默认 / Reset defaults + String get routingBack; // 返回设置 / Back to settings } diff --git a/client/lib/l10n/strings_en.dart b/client/lib/l10n/strings_en.dart index 0d01a14..73c0d39 100644 --- a/client/lib/l10n/strings_en.dart +++ b/client/lib/l10n/strings_en.dart @@ -511,6 +511,54 @@ class StringsEn extends AppText { @override String get routingLanForced => 'Always on'; @override + String get routingForcedPill => '🔒 Forced'; + @override + String get routingProxyMode => 'Proxy mode'; + @override + String get routingModeGlobal => 'Global'; + @override + String get routingModeSmart => 'Smart'; + @override + String get routingModeDirect => 'Direct'; + @override + String get routingBuiltin => 'Built-in rules'; + @override + String get routingCnDirect => 'China direct'; + @override + String get routingMyRulesOrdered => 'My rules · ordered, first match wins'; + @override + String get routingMyRulesFirst => 'My rules · first match wins'; + @override + String get routingFinalLong => 'Final (no rule matched)'; + @override + String get routingFinalShort => 'Final (no match)'; + @override + String get routingFinalFallback => 'FINAL fallback'; + @override + String get routingTypeDomainSuffix => 'Domain suffix'; + @override + String get routingTypeDomainSuffixCi => 'Domain suffix · CI source'; + @override + String get routingTypeDomainSuffixMirror => 'Domain suffix · Mirror'; + @override + String get routingTypeGeosite => 'GeoSite rule-set'; + @override + String get routingActDirect => 'Direct'; + @override + String get routingActProxy => 'Tunnel'; + @override + String get routingActReject => 'Reject'; + @override + String get routingForcedRow => '⚠ Forced to tunnel by the system; this rule is inactive'; + @override + String get routingAddRule => 'Add rule'; + @override + String get routingImport => 'Import from text'; + @override + String get routingReset => 'Reset defaults'; + @override + String get routingBack => 'Back to settings'; + @override List get days7 => const ['M', 'T', 'W', 'T', 'F', 'S', 'S']; @override List get featsFree => const ['1 basic node only', '10 min per day', 'Watch an ad to start', '7-day free trial on sign-up']; diff --git a/client/lib/l10n/strings_es.dart b/client/lib/l10n/strings_es.dart index 37441fc..038c295 100644 --- a/client/lib/l10n/strings_es.dart +++ b/client/lib/l10n/strings_es.dart @@ -511,6 +511,54 @@ class StringsEs extends AppText { @override String get routingLanForced => 'Siempre activo'; @override + String get routingForcedPill => '🔒 Forzado'; + @override + String get routingProxyMode => 'Modo de proxy'; + @override + String get routingModeGlobal => 'Global'; + @override + String get routingModeSmart => 'Inteligente'; + @override + String get routingModeDirect => 'Directo'; + @override + String get routingBuiltin => 'Reglas integradas'; + @override + String get routingCnDirect => 'Directo nacional'; + @override + String get routingMyRulesOrdered => 'Mis reglas · en orden, gana la primera coincidencia'; + @override + String get routingMyRulesFirst => 'Mis reglas · gana la primera coincidencia'; + @override + String get routingFinalLong => 'Final (ninguna regla coincide)'; + @override + String get routingFinalShort => 'Final (sin coincidencia)'; + @override + String get routingFinalFallback => 'FINAL — reserva'; + @override + String get routingTypeDomainSuffix => 'Sufijo de dominio'; + @override + String get routingTypeDomainSuffixCi => 'Sufijo de dominio · fuente CI'; + @override + String get routingTypeDomainSuffixMirror => 'Sufijo de dominio · espejo'; + @override + String get routingTypeGeosite => 'Conjunto de reglas GeoSite'; + @override + String get routingActDirect => 'Directo'; + @override + String get routingActProxy => 'Túnel'; + @override + String get routingActReject => 'Rechazar'; + @override + String get routingForcedRow => '⚠ El sistema fuerza el túnel para este dominio; esta regla no se aplica'; + @override + String get routingAddRule => 'Añadir regla'; + @override + String get routingImport => 'Importar desde texto'; + @override + String get routingReset => 'Restablecer'; + @override + String get routingBack => 'Volver a ajustes'; + @override List get days7 => const ['L', 'M', 'X', 'J', 'V', 'S', 'D']; @override List get featsFree => const ['Solo 1 nodo básico', '10 min al día', 'Mira un anuncio para empezar', 'Prueba gratis de 7 días al registrarte']; diff --git a/client/lib/l10n/strings_ja.dart b/client/lib/l10n/strings_ja.dart index c3d0893..8d4eab2 100644 --- a/client/lib/l10n/strings_ja.dart +++ b/client/lib/l10n/strings_ja.dart @@ -511,6 +511,54 @@ class StringsJa extends AppText { @override String get routingLanForced => '常時オン'; @override + String get routingForcedPill => '🔒 強制'; + @override + String get routingProxyMode => 'プロキシモード'; + @override + String get routingModeGlobal => 'グローバル'; + @override + String get routingModeSmart => 'スマート'; + @override + String get routingModeDirect => '直接'; + @override + String get routingBuiltin => '組み込みルール'; + @override + String get routingCnDirect => '国内は直接接続'; + @override + String get routingMyRulesOrdered => 'マイルール · 順序あり、最初の一致が有効'; + @override + String get routingMyRulesFirst => 'マイルール · 最初の一致が有効'; + @override + String get routingFinalLong => '最終(どのルールにも一致しない)'; + @override + String get routingFinalShort => '最終(一致なし)'; + @override + String get routingFinalFallback => 'FINAL フォールバック'; + @override + String get routingTypeDomainSuffix => 'ドメインサフィックス'; + @override + String get routingTypeDomainSuffixCi => 'ドメインサフィックス · CI ソース'; + @override + String get routingTypeDomainSuffixMirror => 'ドメインサフィックス · ミラー'; + @override + String get routingTypeGeosite => 'GeoSite ルールセット'; + @override + String get routingActDirect => '直接接続'; + @override + String get routingActProxy => 'トンネル'; + @override + String get routingActReject => '拒否'; + @override + String get routingForcedRow => '⚠ このドメインはシステムにより強制的にトンネル経由となり、このルールは無効です'; + @override + String get routingAddRule => 'ルールを追加'; + @override + String get routingImport => 'テキストから読み込み'; + @override + String get routingReset => 'デフォルトに戻す'; + @override + String get routingBack => '設定に戻る'; + @override List get days7 => const ['月', '火', '水', '木', '金', '土', '日']; @override List get featsFree => const ['基本ノード1つのみ', '1日10分', '広告を見て開始', '登録で7日間無料トライアル']; diff --git a/client/lib/l10n/strings_ko.dart b/client/lib/l10n/strings_ko.dart index 378cb02..1d4cb9c 100644 --- a/client/lib/l10n/strings_ko.dart +++ b/client/lib/l10n/strings_ko.dart @@ -511,6 +511,54 @@ class StringsKo extends AppText { @override String get routingLanForced => '항상 켜짐'; @override + String get routingForcedPill => '🔒 강제'; + @override + String get routingProxyMode => '프록시 모드'; + @override + String get routingModeGlobal => '전체'; + @override + String get routingModeSmart => '스마트'; + @override + String get routingModeDirect => '직접'; + @override + String get routingBuiltin => '기본 규칙'; + @override + String get routingCnDirect => '국내 직접 연결'; + @override + String get routingMyRulesOrdered => '내 규칙 · 순서대로, 첫 일치 적용'; + @override + String get routingMyRulesFirst => '내 규칙 · 첫 일치 적용'; + @override + String get routingFinalLong => '최종(일치하는 규칙 없음)'; + @override + String get routingFinalShort => '최종(일치 없음)'; + @override + String get routingFinalFallback => 'FINAL 폴백'; + @override + String get routingTypeDomainSuffix => '도메인 접미사'; + @override + String get routingTypeDomainSuffixCi => '도메인 접미사 · CI 소스'; + @override + String get routingTypeDomainSuffixMirror => '도메인 접미사 · 미러'; + @override + String get routingTypeGeosite => 'GeoSite 규칙 세트'; + @override + String get routingActDirect => '직접 연결'; + @override + String get routingActProxy => '터널'; + @override + String get routingActReject => '거부'; + @override + String get routingForcedRow => '⚠ 이 도메인은 시스템에 의해 강제로 터널을 사용하며, 이 규칙은 적용되지 않습니다'; + @override + String get routingAddRule => '규칙 추가'; + @override + String get routingImport => '텍스트에서 가져오기'; + @override + String get routingReset => '기본값 재설정'; + @override + String get routingBack => '설정으로 돌아가기'; + @override List get days7 => const ['월', '화', '수', '목', '금', '토', '일']; @override List get featsFree => const ['기본 노드 1개', '하루 10분', '광고 시청 후 시작', '가입 시 7일 무료 체험']; diff --git a/client/lib/l10n/strings_ru.dart b/client/lib/l10n/strings_ru.dart index 494d189..e72bb9f 100644 --- a/client/lib/l10n/strings_ru.dart +++ b/client/lib/l10n/strings_ru.dart @@ -511,6 +511,54 @@ class StringsRu extends AppText { @override String get routingLanForced => 'Всегда включено'; @override + String get routingForcedPill => '🔒 Принудительно'; + @override + String get routingProxyMode => 'Режим прокси'; + @override + String get routingModeGlobal => 'Глобально'; + @override + String get routingModeSmart => 'Умный'; + @override + String get routingModeDirect => 'Напрямую'; + @override + String get routingBuiltin => 'Встроенные правила'; + @override + String get routingCnDirect => 'Прямой доступ внутри страны'; + @override + String get routingMyRulesOrdered => 'Мои правила · по порядку, срабатывает первое совпадение'; + @override + String get routingMyRulesFirst => 'Мои правила · срабатывает первое совпадение'; + @override + String get routingFinalLong => 'Итог (ни одно правило не совпало)'; + @override + String get routingFinalShort => 'Итог (нет совпадений)'; + @override + String get routingFinalFallback => 'FINAL — резерв'; + @override + String get routingTypeDomainSuffix => 'Суффикс домена'; + @override + String get routingTypeDomainSuffixCi => 'Суффикс домена · CI-источник'; + @override + String get routingTypeDomainSuffixMirror => 'Суффикс домена · зеркало'; + @override + String get routingTypeGeosite => 'Набор правил GeoSite'; + @override + String get routingActDirect => 'Напрямую'; + @override + String get routingActProxy => 'Туннель'; + @override + String get routingActReject => 'Отклонить'; + @override + String get routingForcedRow => '⚠ Домен принудительно направлен через туннель системой; правило неактивно'; + @override + String get routingAddRule => 'Добавить правило'; + @override + String get routingImport => 'Импорт из текста'; + @override + String get routingReset => 'Сбросить настройки'; + @override + String get routingBack => 'Назад к настройкам'; + @override List get days7 => const ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']; @override List get featsFree => const ['Только 1 базовый узел', '10 минут в день', 'Смотрите рекламу, чтобы начать', '7 дней бесплатно при регистрации']; diff --git a/client/lib/l10n/strings_zh.dart b/client/lib/l10n/strings_zh.dart index 855db05..094e2df 100644 --- a/client/lib/l10n/strings_zh.dart +++ b/client/lib/l10n/strings_zh.dart @@ -511,6 +511,54 @@ class StringsZh extends AppText { @override String get routingLanForced => '强制,不可关'; @override + String get routingForcedPill => '🔒 强制'; + @override + String get routingProxyMode => '代理模式'; + @override + String get routingModeGlobal => '全局代理'; + @override + String get routingModeSmart => '智能分流'; + @override + String get routingModeDirect => '全部直连'; + @override + String get routingBuiltin => '内置规则'; + @override + String get routingCnDirect => '国内直连'; + @override + String get routingMyRulesOrdered => '我的规则 · 有序,首命中生效'; + @override + String get routingMyRulesFirst => '我的规则 · 首命中生效'; + @override + String get routingFinalLong => '最终(未命中任何规则)'; + @override + String get routingFinalShort => '最终(未命中)'; + @override + String get routingFinalFallback => 'FINAL 兜底'; + @override + String get routingTypeDomainSuffix => '域名后缀'; + @override + String get routingTypeDomainSuffixCi => '域名后缀 · CI 源'; + @override + String get routingTypeDomainSuffixMirror => '域名后缀 · 镜像'; + @override + String get routingTypeGeosite => 'GeoSite 规则集'; + @override + String get routingActDirect => '直连'; + @override + String get routingActProxy => '走隧道'; + @override + String get routingActReject => '拒绝'; + @override + String get routingForcedRow => '⚠ 该域名由系统强制走隧道,此规则不生效'; + @override + String get routingAddRule => '添加规则'; + @override + String get routingImport => '从文本导入'; + @override + String get routingReset => '重置默认'; + @override + String get routingBack => '返回设置'; + @override List get days7 => const ['一', '二', '三', '四', '五', '六', '日']; @override List get featsFree => const ['仅 1 个基础节点', '每日 10 分钟时长', '使用前观看广告', '注册享 7 天免费试用']; diff --git a/design/i18n/strings.json b/design/i18n/strings.json index 255087f..c6f7bda 100644 --- a/design/i18n/strings.json +++ b/design/i18n/strings.json @@ -2000,6 +2000,198 @@ "ko": "항상 켜짐", "ru": "Всегда включено", "es": "Siempre activo" + }, + "routingForcedPill": { + "zh": "🔒 强制", + "en": "🔒 Forced", + "ja": "🔒 強制", + "ko": "🔒 강제", + "ru": "🔒 Принудительно", + "es": "🔒 Forzado" + }, + "routingProxyMode": { + "zh": "代理模式", + "en": "Proxy mode", + "ja": "プロキシモード", + "ko": "프록시 모드", + "ru": "Режим прокси", + "es": "Modo de proxy" + }, + "routingModeGlobal": { + "zh": "全局代理", + "en": "Global", + "ja": "グローバル", + "ko": "전체", + "ru": "Глобально", + "es": "Global" + }, + "routingModeSmart": { + "zh": "智能分流", + "en": "Smart", + "ja": "スマート", + "ko": "스마트", + "ru": "Умный", + "es": "Inteligente" + }, + "routingModeDirect": { + "zh": "全部直连", + "en": "Direct", + "ja": "直接", + "ko": "직접", + "ru": "Напрямую", + "es": "Directo" + }, + "routingBuiltin": { + "zh": "内置规则", + "en": "Built-in rules", + "ja": "組み込みルール", + "ko": "기본 규칙", + "ru": "Встроенные правила", + "es": "Reglas integradas" + }, + "routingCnDirect": { + "zh": "国内直连", + "en": "China direct", + "ja": "国内は直接接続", + "ko": "국내 직접 연결", + "ru": "Прямой доступ внутри страны", + "es": "Directo nacional" + }, + "routingMyRulesOrdered": { + "zh": "我的规则 · 有序,首命中生效", + "en": "My rules · ordered, first match wins", + "ja": "マイルール · 順序あり、最初の一致が有効", + "ko": "내 규칙 · 순서대로, 첫 일치 적용", + "ru": "Мои правила · по порядку, срабатывает первое совпадение", + "es": "Mis reglas · en orden, gana la primera coincidencia" + }, + "routingMyRulesFirst": { + "zh": "我的规则 · 首命中生效", + "en": "My rules · first match wins", + "ja": "マイルール · 最初の一致が有効", + "ko": "내 규칙 · 첫 일치 적용", + "ru": "Мои правила · срабатывает первое совпадение", + "es": "Mis reglas · gana la primera coincidencia" + }, + "routingFinalLong": { + "zh": "最终(未命中任何规则)", + "en": "Final (no rule matched)", + "ja": "最終(どのルールにも一致しない)", + "ko": "최종(일치하는 규칙 없음)", + "ru": "Итог (ни одно правило не совпало)", + "es": "Final (ninguna regla coincide)" + }, + "routingFinalShort": { + "zh": "最终(未命中)", + "en": "Final (no match)", + "ja": "最終(一致なし)", + "ko": "최종(일치 없음)", + "ru": "Итог (нет совпадений)", + "es": "Final (sin coincidencia)" + }, + "routingFinalFallback": { + "zh": "FINAL 兜底", + "en": "FINAL fallback", + "ja": "FINAL フォールバック", + "ko": "FINAL 폴백", + "ru": "FINAL — резерв", + "es": "FINAL — reserva" + }, + "routingTypeDomainSuffix": { + "zh": "域名后缀", + "en": "Domain suffix", + "ja": "ドメインサフィックス", + "ko": "도메인 접미사", + "ru": "Суффикс домена", + "es": "Sufijo de dominio" + }, + "routingTypeDomainSuffixCi": { + "zh": "域名后缀 · CI 源", + "en": "Domain suffix · CI source", + "ja": "ドメインサフィックス · CI ソース", + "ko": "도메인 접미사 · CI 소스", + "ru": "Суффикс домена · CI-источник", + "es": "Sufijo de dominio · fuente CI" + }, + "routingTypeDomainSuffixMirror": { + "zh": "域名后缀 · 镜像", + "en": "Domain suffix · Mirror", + "ja": "ドメインサフィックス · ミラー", + "ko": "도메인 접미사 · 미러", + "ru": "Суффикс домена · зеркало", + "es": "Sufijo de dominio · espejo" + }, + "routingTypeGeosite": { + "zh": "GeoSite 规则集", + "en": "GeoSite rule-set", + "ja": "GeoSite ルールセット", + "ko": "GeoSite 규칙 세트", + "ru": "Набор правил GeoSite", + "es": "Conjunto de reglas GeoSite" + }, + "routingActDirect": { + "zh": "直连", + "en": "Direct", + "ja": "直接接続", + "ko": "직접 연결", + "ru": "Напрямую", + "es": "Directo" + }, + "routingActProxy": { + "zh": "走隧道", + "en": "Tunnel", + "ja": "トンネル", + "ko": "터널", + "ru": "Туннель", + "es": "Túnel" + }, + "routingActReject": { + "zh": "拒绝", + "en": "Reject", + "ja": "拒否", + "ko": "거부", + "ru": "Отклонить", + "es": "Rechazar" + }, + "routingForcedRow": { + "zh": "⚠ 该域名由系统强制走隧道,此规则不生效", + "en": "⚠ Forced to tunnel by the system; this rule is inactive", + "ja": "⚠ このドメインはシステムにより強制的にトンネル経由となり、このルールは無効です", + "ko": "⚠ 이 도메인은 시스템에 의해 강제로 터널을 사용하며, 이 규칙은 적용되지 않습니다", + "ru": "⚠ Домен принудительно направлен через туннель системой; правило неактивно", + "es": "⚠ El sistema fuerza el túnel para este dominio; esta regla no se aplica" + }, + "routingAddRule": { + "zh": "添加规则", + "en": "Add rule", + "ja": "ルールを追加", + "ko": "규칙 추가", + "ru": "Добавить правило", + "es": "Añadir regla" + }, + "routingImport": { + "zh": "从文本导入", + "en": "Import from text", + "ja": "テキストから読み込み", + "ko": "텍스트에서 가져오기", + "ru": "Импорт из текста", + "es": "Importar desde texto" + }, + "routingReset": { + "zh": "重置默认", + "en": "Reset defaults", + "ja": "デフォルトに戻す", + "ko": "기본값 재설정", + "ru": "Сбросить настройки", + "es": "Restablecer" + }, + "routingBack": { + "zh": "返回设置", + "en": "Back to settings", + "ja": "設定に戻る", + "ko": "설정으로 돌아가기", + "ru": "Назад к настройкам", + "es": "Volver a ajustes" } }, "lists": { diff --git a/design/prototype/i18n/alias.json b/design/prototype/i18n/alias.json index b3abc73..064939f 100644 --- a/design/prototype/i18n/alias.json +++ b/design/prototype/i18n/alias.json @@ -129,7 +129,31 @@ "login.signupTitle": "tabRegister", "nav.routing": "routingRulesTitle", "set.routing": "routingRulesTitle", - "set.routingSub": "routingRulesSub" + "set.routingSub": "routingRulesSub", + "rr.back": "routingBack", + "rr.proxyMode": "routingProxyMode", + "rr.modeGlobal": "routingModeGlobal", + "rr.modeSmart": "routingModeSmart", + "rr.modeDirect": "routingModeDirect", + "rr.builtin": "routingBuiltin", + "rr.cnDirect": "routingCnDirect", + "rr.lan": "routingLanDirect", + "rr.lanSub": "routingLanForced", + "rr.forcedPill": "routingForcedPill", + "rr.myRulesOrdered": "routingMyRulesOrdered", + "rr.finalLong": "routingFinalLong", + "rr.finalFallback": "routingFinalFallback", + "rr.typeDomainSuffix": "routingTypeDomainSuffix", + "rr.typeDomainSuffixCi": "routingTypeDomainSuffixCi", + "rr.typeDomainSuffixMirror": "routingTypeDomainSuffixMirror", + "rr.typeGeosite": "routingTypeGeosite", + "rr.actDirect": "routingActDirect", + "rr.actProxy": "routingActProxy", + "rr.actReject": "routingActReject", + "rr.forcedRow": "routingForcedRow", + "rr.addRule": "routingAddRule", + "rr.import": "routingImport", + "rr.reset": "routingReset" }, "mobile": { "brandName": "brand", @@ -274,6 +298,23 @@ "redeemBtn": "redeemBtn", "navRouting": "routingRulesTitle", "rr.lan": "routingLanDirect", - "rr.lanSub": "routingLanForced" + "rr.lanSub": "routingLanForced", + "rr.proxyMode": "routingProxyMode", + "rr.modeGlobal": "routingModeGlobal", + "rr.modeSmart": "routingModeSmart", + "rr.modeDirect": "routingModeDirect", + "rr.builtin": "routingBuiltin", + "rr.cnDirect": "routingCnDirect", + "rr.myRulesFirst": "routingMyRulesFirst", + "rr.finalShort": "routingFinalShort", + "rr.finalFallback": "routingFinalFallback", + "rr.typeDomainSuffix": "routingTypeDomainSuffix", + "rr.typeGeosite": "routingTypeGeosite", + "rr.actDirect": "routingActDirect", + "rr.actProxy": "routingActProxy", + "rr.actReject": "routingActReject", + "rr.addRule": "routingAddRule", + "rr.import": "routingImport", + "rr.reset": "routingReset" } } diff --git a/design/prototype/screens/ui-desktop.html b/design/prototype/screens/ui-desktop.html index 1b8ef93..cce4729 100644 --- a/design/prototype/screens/ui-desktop.html +++ b/design/prototype/screens/ui-desktop.html @@ -745,36 +745,36 @@
- -
代理模式
+ +
代理模式
- - - + + +

下方「我的规则」仅在智能分流模式生效。全局代理 / 全部直连为一刀切模式,忽略自定义规则(系统强制层仍保留)。

-
内置规则
+
内置规则
-
国内直连
GeoIP / GeoSite CN
-
局域网 / 私网直连
强制,不可关
🔒 强制
+
国内直连
GeoIP / GeoSite CN
+
局域网 / 私网直连
强制,不可关
🔒 强制
-
我的规则 · 有序,首命中生效
+
我的规则 · 有序,首命中生效
-
git.51yanmei.com
域名后缀 · CI 源
直连
-
aliyun.com
域名后缀 · 镜像
直连
-
github.com
域名后缀
走隧道
-
category-ads
GeoSite 规则集
拒绝
-
git.yanmeiai.com
⚠ 该域名由系统强制走隧道,此规则不生效
直连
+
git.51yanmei.com
域名后缀 · CI 源
直连
+
aliyun.com
域名后缀 · 镜像
直连
+
github.com
域名后缀
走隧道
+
category-ads
GeoSite 规则集
拒绝
+
git.yanmeiai.com
⚠ 该域名由系统强制走隧道,此规则不生效
直连
- - - + + +
-
最终(未命中任何规则)
-
FINAL 兜底
走隧道
+
最终(未命中任何规则)
+
FINAL 兜底
走隧道
@@ -1381,6 +1381,30 @@ var I18N = { 'rd.label':{zh:'兑换码',en:'Redemption code'}, 'rd.ph':{zh:'输入激活码',en:'Enter activation code'}, 'rd.title':{zh:'兑换激活码',en:'Redeem a code'}, + 'rr.actDirect':{zh:'直连',en:'Direct'}, + 'rr.actProxy':{zh:'走隧道',en:'Tunnel'}, + 'rr.actReject':{zh:'拒绝',en:'Reject'}, + 'rr.addRule':{zh:'添加规则',en:'Add rule'}, + 'rr.back':{zh:'返回设置',en:'Back to settings'}, + 'rr.builtin':{zh:'内置规则',en:'Built-in rules'}, + 'rr.cnDirect':{zh:'国内直连',en:'China direct'}, + 'rr.finalFallback':{zh:'FINAL 兜底',en:'FINAL fallback'}, + 'rr.finalLong':{zh:'最终(未命中任何规则)',en:'Final (no rule matched)'}, + 'rr.forcedPill':{zh:'🔒 强制',en:'🔒 Forced'}, + 'rr.forcedRow':{zh:'⚠ 该域名由系统强制走隧道,此规则不生效',en:'⚠ Forced to tunnel by the system; this rule is inactive'}, + 'rr.import':{zh:'从文本导入',en:'Import from text'}, + 'rr.lan':{zh:'局域网 / 私网直连',en:'LAN & private direct'}, + 'rr.lanSub':{zh:'强制,不可关',en:'Always on'}, + 'rr.modeDirect':{zh:'全部直连',en:'Direct'}, + 'rr.modeGlobal':{zh:'全局代理',en:'Global'}, + 'rr.modeSmart':{zh:'智能分流',en:'Smart'}, + 'rr.myRulesOrdered':{zh:'我的规则 · 有序,首命中生效',en:'My rules · ordered, first match wins'}, + 'rr.proxyMode':{zh:'代理模式',en:'Proxy mode'}, + 'rr.reset':{zh:'重置默认',en:'Reset defaults'}, + 'rr.typeDomainSuffix':{zh:'域名后缀',en:'Domain suffix'}, + 'rr.typeDomainSuffixCi':{zh:'域名后缀 · CI 源',en:'Domain suffix · CI source'}, + 'rr.typeDomainSuffixMirror':{zh:'域名后缀 · 镜像',en:'Domain suffix · Mirror'}, + 'rr.typeGeosite':{zh:'GeoSite 规则集',en:'GeoSite rule-set'}, 'set.autoconnect':{zh:'自动连接',en:'Auto-connect'}, 'set.autoconnectSub':{zh:'启动时自动连接节点',en:'Connect automatically on launch'}, 'set.autostart':{zh:'开机自启',en:'Launch at startup'}, @@ -2026,6 +2050,8 @@ var _rtBack=document.getElementById('rtBack'); if(_rtBack) _rtBack.addEventListe (function(){ if(!S.routeMode) S.routeMode='smart'; function _t(zh,en){ return S.lang==='zh'?zh:en; } + // 从生成的 I18N 单源取值(与静态 data-i18n pill 同源),S.lang 恒为 zh/en + function _ri(k){ var o=I18N[k]; return o ? (o[S.lang]||o.en) : ''; } function _esc(s){ return String(s).replace(/&/g,'&').replace(//g,'>'); } var INP='padding:10px 12px;border-radius:var(--radius-md);border:1px solid var(--border);background:var(--surface);color:var(--fg1);font-size:14px;font-family:inherit;width:100%;box-sizing:border-box'; @@ -2034,7 +2060,7 @@ var _rtBack=document.getElementById('rtBack'); if(_rtBack) _rtBack.addEventListe document.querySelectorAll('#rtModeSeg button').forEach(function(b){ b.classList.toggle('is-active', b.dataset.mode===mode); }); ['rtCnRow','rtMyRules'].forEach(function(id){ var el=document.getElementById(id); if(!el) return; el.style.opacity=smart?'':'.4'; el.style.pointerEvents=smart?'':'none'; }); var fp=document.getElementById('rtFinalPill'); - if(fp){ if(mode==='direct'){ fp.textContent=_t('直连','Direct'); fp.className='pill is-success'; } else { fp.textContent=_t('走隧道','Tunnel'); fp.className='pill is-warning'; } } + if(fp){ if(mode==='direct'){ fp.textContent=_ri('rr.actDirect'); fp.className='pill is-success'; } else { fp.textContent=_ri('rr.actProxy'); fp.className='pill is-warning'; } } var note=document.getElementById('rtModeNote'); if(note){ if(smart) note.innerHTML=_t('下方「我的规则」仅在智能分流模式生效。全局代理 / 全部直连为一刀切模式,忽略自定义规则(系统强制层仍保留)。','Rules below apply only in Smart routing. Global / Direct are one-size modes that ignore custom rules (system-forced layer stays).'); @@ -2048,9 +2074,9 @@ var _rtBack=document.getElementById('rtBack'); if(_rtBack) _rtBack.addEventListe function closeModal(id){ var m=document.getElementById(id); if(m) m.classList.remove('is-open'); } function pillFor(act){ - if(act==='proxy') return ''+_t('走隧道','Tunnel')+''; - if(act==='reject') return ''+_t('拒绝','Reject')+''; - return ''+_t('直连','Direct')+''; + if(act==='proxy') return ''+_ri('rr.actProxy')+''; + if(act==='reject') return ''+_ri('rr.actReject')+''; + return ''+_ri('rr.actDirect')+''; } var TYPE={domain_suffix:['域名后缀','Domain suffix'],domain:['域名','Domain'],ip_cidr:['IP-CIDR','IP-CIDR'],geosite:['GeoSite','GeoSite'],geoip:['GeoIP','GeoIP']}; function addRuleRow(type,val,act,prepend){ diff --git a/design/prototype/screens/ui-mobile.html b/design/prototype/screens/ui-mobile.html index 2d1ebe2..babdf4c 100644 --- a/design/prototype/screens/ui-mobile.html +++ b/design/prototype/screens/ui-mobile.html @@ -869,7 +869,7 @@
分流规则
-
智能分流
+
智能分流
@@ -928,33 +928,33 @@
-
代理模式
+
代理模式
- - - + + +

「我的规则」仅在智能分流模式生效;全局 / 直连为一刀切,忽略自定义规则(系统强制层仍保留)。

-
内置规则
+
内置规则
-
国内直连
GeoIP / GeoSite CN
+
国内直连
GeoIP / GeoSite CN
局域网 / 私网直连
强制,不可关
🔒
-
我的规则 · 首命中生效
+
我的规则 · 首命中生效
-
git.51yanmei.com
域名后缀
直连
-
github.com
域名后缀
走隧道
-
category-ads
GeoSite 规则集
拒绝
+
git.51yanmei.com
域名后缀
直连
+
github.com
域名后缀
走隧道
+
category-ads
GeoSite 规则集
拒绝
- +
- - + +
-
最终(未命中)
-
FINAL 兜底
走隧道
+
最终(未命中)
+
FINAL 兜底
走隧道
@@ -1501,9 +1501,26 @@ const I18N = { relYday:'昨天', renew:'续费 / 升级', rowBuyPlan:'购买套餐', + 'rr.actDirect':'直连', + 'rr.actProxy':'走隧道', + 'rr.actReject':'拒绝', + 'rr.addRule':'添加规则', + 'rr.builtin':'内置规则', + 'rr.cnDirect':'国内直连', + 'rr.finalFallback':'FINAL 兜底', + 'rr.finalShort':'最终(未命中)', 'rr.forced':'🔒', + 'rr.import':'从文本导入', 'rr.lan':'局域网 / 私网直连', 'rr.lanSub':'强制,不可关', + 'rr.modeDirect':'全部直连', + 'rr.modeGlobal':'全局代理', + 'rr.modeSmart':'智能分流', + 'rr.myRulesFirst':'我的规则 · 首命中生效', + 'rr.proxyMode':'代理模式', + 'rr.reset':'重置默认', + 'rr.typeDomainSuffix':'域名后缀', + 'rr.typeGeosite':'GeoSite 规则集', searchPh:'搜索国家 / 城市', secAccount:'账户信息', secBilling:'购买 · 订单 · 兑换', @@ -1750,9 +1767,26 @@ const I18N = { relYday:'yesterday', renew:'Renew / Upgrade', rowBuyPlan:'Purchase', + 'rr.actDirect':'Direct', + 'rr.actProxy':'Tunnel', + 'rr.actReject':'Reject', + 'rr.addRule':'Add rule', + 'rr.builtin':'Built-in rules', + 'rr.cnDirect':'China direct', + 'rr.finalFallback':'FINAL fallback', + 'rr.finalShort':'Final (no match)', 'rr.forced':'🔒', + 'rr.import':'Import from text', 'rr.lan':'LAN & private direct', 'rr.lanSub':'Always on', + 'rr.modeDirect':'Direct', + 'rr.modeGlobal':'Global', + 'rr.modeSmart':'Smart', + 'rr.myRulesFirst':'My rules · first match wins', + 'rr.proxyMode':'Proxy mode', + 'rr.reset':'Reset defaults', + 'rr.typeDomainSuffix':'Domain suffix', + 'rr.typeGeosite':'GeoSite rule-set', searchPh:'Search country / city', secAccount:'Account info', secBilling:'Buy · Orders · Redeem', @@ -2455,6 +2489,8 @@ $('#rowRouting').addEventListener('click', ()=>{ subOpen('routing'); renderRouti (function(){ if(!state.routeMode) state.routeMode='smart'; const _t=(zh,en)=>state.lang==='zh'?zh:en; + // 从生成的 I18N 单源取值(与静态 data-i18n pill 同源),state.lang 恒为 zh/en + const _ri=k=>{ const o=I18N[state.lang]; return o&&o[k]!=null?o[k]:''; }; const _esc=s=>String(s).replace(/&/g,'&').replace(//g,'>'); const INP='padding:10px 12px;border-radius:var(--radius-md);border:1px solid var(--border);background:var(--bg-subtle);color:var(--fg1);font-size:14px;font-family:inherit;width:100%;box-sizing:border-box'; @@ -2463,7 +2499,7 @@ $('#rowRouting').addEventListener('click', ()=>{ subOpen('routing'); renderRouti document.querySelectorAll('#rtModeSeg button').forEach(b=>b.classList.toggle('is-active', b.dataset.mode===mode)); ['rtCnRow','rtMyRules'].forEach(id=>{ const el=document.getElementById(id); if(!el) return; el.style.opacity=smart?'':'.4'; el.style.pointerEvents=smart?'':'none'; }); const fp=document.getElementById('rtFinalPill'); - if(fp){ if(mode==='direct'){ fp.textContent=_t('直连','Direct'); fp.className='pill is-success'; } else { fp.textContent=_t('走隧道','Tunnel'); fp.className='pill is-warning'; } } + if(fp){ if(mode==='direct'){ fp.textContent=_ri('rr.actDirect'); fp.className='pill is-success'; } else { fp.textContent=_ri('rr.actProxy'); fp.className='pill is-warning'; } } const note=document.getElementById('rtModeNote'); if(note){ if(smart) note.innerHTML=_t('「我的规则」仅在智能分流模式生效;全局 / 直连为一刀切,忽略自定义规则(系统强制层仍保留)。','Rules apply only in Smart routing; Global / Direct are one-size modes that ignore custom rules (system-forced layer stays).'); @@ -2475,7 +2511,7 @@ $('#rowRouting').addEventListener('click', ()=>{ subOpen('routing'); renderRouti const openM=id=>{ const m=document.getElementById(id); if(m){ m.classList.add('is-open'); m.onclick=e=>{ if(e.target===m) m.classList.remove('is-open'); }; } }; const closeM=id=>{ const m=document.getElementById(id); if(m) m.classList.remove('is-open'); }; - const pillFor=act=> act==='proxy'?''+_t('走隧道','Tunnel')+'' : act==='reject'?''+_t('拒绝','Reject')+'' : ''+_t('直连','Direct')+''; + const pillFor=act=> act==='proxy'?''+_ri('rr.actProxy')+'' : act==='reject'?''+_ri('rr.actReject')+'' : ''+_ri('rr.actDirect')+''; const TYPE={domain_suffix:['域名后缀','Domain suffix'],domain:['域名','Domain'],ip_cidr:['IP-CIDR','IP-CIDR'],geosite:['GeoSite','GeoSite'],geoip:['GeoIP','GeoIP']}; function addRuleRow(type,val,act,prepend){ const list=document.getElementById('rtRulesList'); if(!list) return;