2930d76cf3
承接 FT-A(GET /v1/me/routing 含只读 system_locked_domains)。 - RoutingProfile 加只读 systemLockedDomains(fromJson 读/toJson 不输出); routing_screen 冲突检测扩展到域名类规则(domain/domain_suffix/domain_keyword 命中锁定域名 → systemLocked),ip_cidr 私网启发式保留。 - RoutingProfile.defaults() + RoutingProfileNotifier.resetToDefault()(复用 _persist:乐观更新/失败回滚/存成功后自动重连,保留只读 systemLockedDomains 不丢)+ routing_screen 加「重置默认」按钮与二次确认弹层(新增 3 个 l10n 键)。 - RoutingRule.copyWith 用哨兵支持 note 显式清空为 null;RoutingRule/Builtin/ RoutingProfile 加值相等 operator==/hashCode。 - T8 smartRouteSub 清理:grep 全仓发现 design/prototype/i18n/alias.json → gen_proto_i18n.mjs(CI 漂移闸)仍有活引用,按计划口径不删,详见 .superpowers/sdd/task-FTB-report.md。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
604 lines
19 KiB
Dart
604 lines
19 KiB
Dart
// strings_en.dart — English文案(自动生成 · 勿手改)
|
||
//
|
||
// GENERATED by design/codegen/gen_l10n_dart.mjs from design/i18n/strings.json.
|
||
// 改文案:改 strings.json 后重跑 codegen;新增 key 需同步 app_text.dart 抽象声明。
|
||
import 'app_text.dart';
|
||
|
||
class StringsEn extends AppText {
|
||
const StringsEn();
|
||
|
||
@override
|
||
AppLang get lang => AppLang.en;
|
||
|
||
@override
|
||
String get brand => 'Pangolin';
|
||
@override
|
||
String get online => '● Online';
|
||
@override
|
||
String get offline => '○ Offline';
|
||
@override
|
||
String get capOff => 'Tap to connect';
|
||
@override
|
||
String get capConnecting => 'Connecting…';
|
||
@override
|
||
String get capOn => 'Connected · Encrypted';
|
||
@override
|
||
String get connectNow => 'CONNECT';
|
||
@override
|
||
String get secure => 'SECURE';
|
||
@override
|
||
String get tabConnect => 'Connect';
|
||
@override
|
||
String get tabServers => 'Servers';
|
||
@override
|
||
String get tabStats => 'Stats';
|
||
@override
|
||
String get tabMe => 'Account';
|
||
@override
|
||
String get currentNode => 'Current node';
|
||
@override
|
||
String get download => 'Down';
|
||
@override
|
||
String get upload => 'Up';
|
||
@override
|
||
String get latency => 'Ping';
|
||
@override
|
||
String get quotaToday => 'Left today';
|
||
@override
|
||
String get minutes => 'min';
|
||
@override
|
||
String get quotaFree => 'Free · 10 min/day';
|
||
@override
|
||
String get planFreeTag => 'Free';
|
||
@override
|
||
String get watchAd => 'Watch ad to start';
|
||
@override
|
||
String get adUnlocked => 'Unlocked for today';
|
||
@override
|
||
String get quotaLeftLabel => 'Left';
|
||
@override
|
||
String get quotaUsedUp => 'Daily free time used up';
|
||
@override
|
||
String get watchAdMore => 'Watch ad for more time';
|
||
@override
|
||
String get quotaExhaustedNotice => 'You\'ve used today\'s free time. Upgrade to keep connecting.';
|
||
@override
|
||
String get adPlaying => 'Ad playing…';
|
||
@override
|
||
String get adFailed => 'Failed to add time, please retry';
|
||
@override
|
||
String get quotaDesktopTitle => 'Daily free time used up';
|
||
@override
|
||
String get quotaDesktopBody => 'Watch ads in the mobile app to add time, or upgrade for unlimited access.';
|
||
@override
|
||
String get gotIt => 'Got it';
|
||
@override
|
||
String get chooseNode => 'Choose server';
|
||
@override
|
||
String get searchPh => 'Search country / city';
|
||
@override
|
||
String get smartSelect => 'Smart select';
|
||
@override
|
||
String get nodeSwitchBody => 'This disconnects the current link and reconnects to this node.';
|
||
@override
|
||
String get nodeSwitchConfirm => 'Switch';
|
||
@override
|
||
String get nodeUnhealthySwitched => 'Current node unreachable — switched automatically';
|
||
@override
|
||
String get nodeUnhealthyError => 'Current node is unreachable — reconnect or switch node';
|
||
@override
|
||
String get nodeReconnecting => 'Network unstable, reconnecting…';
|
||
@override
|
||
String get routingRulesReconnecting => 'Rules updated, reconnecting…';
|
||
@override
|
||
String get smartSub => 'Picks the best node for your network';
|
||
@override
|
||
String get recommended => 'Recommended';
|
||
@override
|
||
String get statsTitle => 'Statistics';
|
||
@override
|
||
String get trafficMonth => 'Traffic';
|
||
@override
|
||
String get avgPing => 'Avg ping';
|
||
@override
|
||
String get durMonth => 'Time';
|
||
@override
|
||
String get weekTraffic => 'This week (GB)';
|
||
@override
|
||
String get byDevice => 'By device';
|
||
@override
|
||
String get noDeviceUsage => 'No device usage yet';
|
||
@override
|
||
String get periodMonth => 'This month';
|
||
@override
|
||
String get periodWeek => 'This week';
|
||
@override
|
||
String get periodToday => 'Today';
|
||
@override
|
||
String get statDown => 'Download';
|
||
@override
|
||
String get statUp => 'Upload';
|
||
@override
|
||
String get statDuration => 'Duration';
|
||
@override
|
||
String get chartTwoWeeks => 'Last 2 weeks';
|
||
@override
|
||
String get allDevices => 'All devices';
|
||
@override
|
||
String get meTitle => 'Account';
|
||
@override
|
||
String get proMember => 'PRO member';
|
||
@override
|
||
String get freePlanName => 'Free';
|
||
@override
|
||
String get expires => 'Expires';
|
||
@override
|
||
String get upgradeBtn => 'Renew / Upgrade';
|
||
@override
|
||
String get accInfoTitle => 'Account info';
|
||
@override
|
||
String get accEmail => 'Email';
|
||
@override
|
||
String get accPassword => 'Password';
|
||
@override
|
||
String get accChange => 'Change';
|
||
@override
|
||
String get deviceLimitTitle => 'Device limit reached';
|
||
@override
|
||
String get deviceLimitDesc => 'Your plan allows up to %s devices. Remove one to continue.';
|
||
@override
|
||
String get deviceLimitRemoveOldest => 'Remove least recently used';
|
||
@override
|
||
String get myDevices => 'My devices';
|
||
@override
|
||
String get devicesSub => 'Up to 3 devices on PRO';
|
||
@override
|
||
String get thisDevice => 'This device';
|
||
@override
|
||
String get remove => 'Remove';
|
||
@override
|
||
String get devOnline => 'Online';
|
||
@override
|
||
String get devOffline => 'Offline';
|
||
@override
|
||
String get devLastOnline => 'Last online';
|
||
@override
|
||
String get devNeverLogin => 'Never logged in';
|
||
@override
|
||
String get devForceLogout => 'Force logout';
|
||
@override
|
||
String get devClearLogin => 'Clear login info';
|
||
@override
|
||
String get devRename => 'Rename';
|
||
@override
|
||
String get devRenameHint => 'Device name';
|
||
@override
|
||
String get devSave => 'Save';
|
||
@override
|
||
String get devForceLogoutConfirm => 'This revokes the device\'s login session — it will be kicked offline and must sign in again. The device stays in the list.';
|
||
@override
|
||
String get devClearLoginConfirm => 'This removes the device from the list entirely and revokes its login session and data-plane credential. The device must sign in again to be used.';
|
||
@override
|
||
String get devCancel => 'Cancel';
|
||
@override
|
||
String get sessionRevokedTitle => 'This device was signed out';
|
||
@override
|
||
String get sessionRevokedBody => 'Your account signed this device out from another device. Please sign in again.';
|
||
@override
|
||
String get sessionRevokedOk => 'Sign in again';
|
||
@override
|
||
String get redeemEntry => 'Redeem';
|
||
@override
|
||
String get contactEntry => 'Contact us';
|
||
@override
|
||
String get signOut => 'Sign out';
|
||
@override
|
||
String get language => 'Language';
|
||
@override
|
||
String get darkAppearance => 'Dark appearance';
|
||
@override
|
||
String get stateOn => 'On';
|
||
@override
|
||
String get followLight => 'Off';
|
||
@override
|
||
String get protocol => 'Protocol';
|
||
@override
|
||
String get settingsTitle => 'Settings';
|
||
@override
|
||
String get autostart => 'Launch at startup';
|
||
@override
|
||
String get autostartSub => 'Run automatically on system start';
|
||
@override
|
||
String get autoConnect => 'Auto-connect';
|
||
@override
|
||
String get autoConnectSub => 'Connect automatically on launch';
|
||
@override
|
||
String get smartRoute => 'Smart routing';
|
||
@override
|
||
String get smartRouteSub => 'Accelerate abroad, direct at home';
|
||
@override
|
||
String get killSwitch => 'Kill Switch';
|
||
@override
|
||
String get killSwitchSub => 'Block traffic if the link drops';
|
||
@override
|
||
String get checkUpdate => 'Check for updates';
|
||
@override
|
||
String get webUserCenter => 'User Center (Web)';
|
||
@override
|
||
String get updateNotesFallback => 'This update includes fixes and stability improvements.';
|
||
@override
|
||
String get updateLater => 'Later';
|
||
@override
|
||
String get updateDownload => 'Download update';
|
||
@override
|
||
String get updateUpToDate => 'You are up to date';
|
||
@override
|
||
String get updateCheckFailed => 'Update check failed, try again later';
|
||
@override
|
||
String get freePlan => 'Free';
|
||
@override
|
||
String get proPlan => 'Pro';
|
||
@override
|
||
String get teamPlan => 'Team';
|
||
@override
|
||
String get perMonth => '/mo';
|
||
@override
|
||
String get current => 'Current';
|
||
@override
|
||
String get upgrade => 'Upgrade';
|
||
@override
|
||
String get choose => 'Choose';
|
||
@override
|
||
String get mostPopular => 'Popular';
|
||
@override
|
||
String get redeemTitle => 'Redeem & buy';
|
||
@override
|
||
String get redeemCodeTitle => 'Redeem a code';
|
||
@override
|
||
String get redeemPh => 'Enter activation code';
|
||
@override
|
||
String get redeemBtn => 'Redeem';
|
||
@override
|
||
String get redeemOk => 'Activated · PRO unlocked';
|
||
@override
|
||
String get buyTitle => 'Where to buy';
|
||
@override
|
||
String get buySub => 'In-app payment is unavailable. Get a code via:';
|
||
@override
|
||
String get chStore => 'Self-serve store';
|
||
@override
|
||
String get chEmail => 'Email support';
|
||
@override
|
||
String get contactTitle => 'Contact us';
|
||
@override
|
||
String get contactIntro => 'Need help? Reach us via any channel below — usually replies in minutes.';
|
||
@override
|
||
String get contactEmail => 'Email support';
|
||
@override
|
||
String get contactStore => 'Self-serve store';
|
||
@override
|
||
String get contactWebsite => 'Website';
|
||
@override
|
||
String get contactHoursTitle => 'Support hours';
|
||
@override
|
||
String get contactHours => 'Daily 9:00 – 24:00 (GMT+8)';
|
||
@override
|
||
String get authTagline => 'Fast · Stable · Effortless';
|
||
@override
|
||
String get tabLogin => 'Log in';
|
||
@override
|
||
String get tabRegister => 'Sign up';
|
||
@override
|
||
String get emailLabel => 'Email';
|
||
@override
|
||
String get emailPh => 'your@email.com';
|
||
@override
|
||
String get pwLabel => 'Password';
|
||
@override
|
||
String get pwPh => 'Enter password';
|
||
@override
|
||
String get setPwPh => 'Set a password (for multi-device login)';
|
||
@override
|
||
String get codeLabel => 'Verification code';
|
||
@override
|
||
String get sendCode => 'Send code';
|
||
@override
|
||
String get resend => 'Resend';
|
||
@override
|
||
String get doLogin => 'Log in';
|
||
@override
|
||
String get doNext => 'Next';
|
||
@override
|
||
String get doCreate => 'Create account';
|
||
@override
|
||
String get forgotPw => 'Forgot password?';
|
||
@override
|
||
String get tos => 'By continuing you agree to our Terms & Privacy Policy';
|
||
@override
|
||
String get obSkip => 'Skip';
|
||
@override
|
||
String get obNext => 'Next';
|
||
@override
|
||
String get obAllow => 'Allow & continue';
|
||
@override
|
||
String get obStart => 'Get started';
|
||
@override
|
||
String get ob1Title => 'Connect worldwide';
|
||
@override
|
||
String get ob1Sub => '80+ locations, auto-fastest routing, rock-solid.';
|
||
@override
|
||
String get ob2Title => 'Allow network setup';
|
||
@override
|
||
String get ob2Sub => 'The system will ask to add a network profile to build the encrypted tunnel.';
|
||
@override
|
||
String get ob3Title => 'Private by design';
|
||
@override
|
||
String get ob3Sub => 'End-to-end encrypted. We keep zero browsing logs.';
|
||
@override
|
||
String get purchaseTitle => 'Purchase';
|
||
@override
|
||
String get paymentTitle => 'Payment';
|
||
@override
|
||
String get buyNow => 'Buy now';
|
||
@override
|
||
String get payMethodAlipay => 'Alipay';
|
||
@override
|
||
String get payMethodCrypto => 'USDT (TRC20)';
|
||
@override
|
||
String get choosePayMethod => 'Choose payment method';
|
||
@override
|
||
String get proMonthly => 'Pro · Monthly';
|
||
@override
|
||
String get proQuarterly => 'Pro · Quarterly';
|
||
@override
|
||
String get proYearly => 'Pro · Yearly';
|
||
@override
|
||
String get perQuarter => '/quarter';
|
||
@override
|
||
String get perYear => '/year';
|
||
@override
|
||
String get payAmountLabel => 'Amount';
|
||
@override
|
||
String get payAddressLabel => 'Address';
|
||
@override
|
||
String get payNetworkLabel => 'Network';
|
||
@override
|
||
String get payExactAmountHint => 'Send the exact amount; activates automatically';
|
||
@override
|
||
String get copied => 'Copied';
|
||
@override
|
||
String get openAlipay => 'Pay with Alipay';
|
||
@override
|
||
String get openAlipayHint => 'Return after paying; this page refreshes automatically';
|
||
@override
|
||
String get openAlipayFailed => 'Couldn\'t open Alipay; check it\'s installed or try again';
|
||
@override
|
||
String get awaitingPayment => 'Awaiting payment';
|
||
@override
|
||
String get paySucceeded => 'Activated';
|
||
@override
|
||
String get payExpiresAt => 'Valid until';
|
||
@override
|
||
String get payDone => 'Done';
|
||
@override
|
||
String get payFailed => 'Payment failed';
|
||
@override
|
||
String get payRetry => 'Retry';
|
||
@override
|
||
String get switchPayMethod => 'Switch payment method';
|
||
@override
|
||
String get cancelOrder => 'Cancel order';
|
||
@override
|
||
String get qrNotSupported => 'Copy and open in Alipay';
|
||
@override
|
||
String get orderCreateFailed => 'Failed to create order, please retry';
|
||
@override
|
||
String get payUnsupportedRenderType => 'Unsupported, switch payment method';
|
||
@override
|
||
String get payNoActiveOrder => 'No active order';
|
||
@override
|
||
String get payChannel => 'Payment channel';
|
||
@override
|
||
String get payChannelUsdt => 'USDT · Crypto';
|
||
@override
|
||
String get payChannelCny => 'CNY · Alipay';
|
||
@override
|
||
String get perMonthSuffix => ' /mo';
|
||
@override
|
||
String get ordersTitle => 'My orders';
|
||
@override
|
||
String get orderDetailTitle => 'Order details';
|
||
@override
|
||
String get ordersEmpty => 'No orders yet';
|
||
@override
|
||
String get orderNoLabel => 'Order no.';
|
||
@override
|
||
String get orderPlanLabel => 'Plan';
|
||
@override
|
||
String get orderMethodLabel => 'Payment method';
|
||
@override
|
||
String get orderAmountLabel => 'Amount';
|
||
@override
|
||
String get orderCreatedLabel => 'Created';
|
||
@override
|
||
String get orderPaidLabel => 'Paid at';
|
||
@override
|
||
String get orderStatusPaid => 'Activated';
|
||
@override
|
||
String get orderStatusCanceled => 'Canceled';
|
||
@override
|
||
String get orderContinuePay => 'Continue payment';
|
||
@override
|
||
String get orderRefresh => 'Refresh';
|
||
@override
|
||
String get orderRebuy => 'Buy again';
|
||
@override
|
||
String get payMethodNezha => 'Nazhapay';
|
||
@override
|
||
String get inviteEntry => 'Invite friends, earn membership';
|
||
@override
|
||
String get inviteEntrySub => 'Friend signs up · both earn days';
|
||
@override
|
||
String get inviteTitle => 'Invite friends';
|
||
@override
|
||
String get inviteCodeLabel => 'Invite code';
|
||
@override
|
||
String get inviteLinkLabel => 'Invite link';
|
||
@override
|
||
String get inviteCopyCode => 'Copy code';
|
||
@override
|
||
String get inviteCopyLink => 'Copy link';
|
||
@override
|
||
String get inviteRewardsTitle => 'Rewards';
|
||
@override
|
||
String get inviteRewardRegister => 'Sign up · +3 days each';
|
||
@override
|
||
String get inviteRewardFirstBuy => 'First purchase · +7 days each';
|
||
@override
|
||
String get inviteRewardJoinTg => 'Join TG · +3 days';
|
||
@override
|
||
String get inviteProgressTitle => 'My rewards';
|
||
@override
|
||
String get inviteTasksTitle => 'More ways to earn';
|
||
@override
|
||
String get inviteJoinTgTitle => 'Join Telegram channel';
|
||
@override
|
||
String get inviteJoinTgBtn => 'Join channel';
|
||
@override
|
||
String get inviteVerifyBtn => 'Verify & claim';
|
||
@override
|
||
String get inviteTaskDone => 'Claimed +3 days';
|
||
@override
|
||
String get inviteStatInvited => 'Invited';
|
||
@override
|
||
String get inviteStatConverted => 'Converted';
|
||
@override
|
||
String get inviteStatDays => 'Days earned';
|
||
@override
|
||
String get inviteCodeFieldLabel => 'Invite code (optional)';
|
||
@override
|
||
String get notifTitle => 'Notifications';
|
||
@override
|
||
String get notifEmpty => 'No notifications';
|
||
@override
|
||
String get notifTypeNews => 'News';
|
||
@override
|
||
String get notifTypeFeature => 'Feature';
|
||
@override
|
||
String get notifTypeImportant => 'Important';
|
||
@override
|
||
String get notifTypeReward => 'Credited';
|
||
@override
|
||
String get notifTypeVersion => 'Update';
|
||
@override
|
||
String get notifTypePromo => 'Promo';
|
||
@override
|
||
String get settingsGroupConn => 'Connection';
|
||
@override
|
||
String get settingsGroupAppearance => 'Appearance';
|
||
@override
|
||
String get settingsGroupAbout => 'About';
|
||
@override
|
||
String get orderStatusExpired => 'Expired';
|
||
@override
|
||
String get promoBadge => 'Limited-time offer';
|
||
@override
|
||
String get promoOriginalPrice => 'Was';
|
||
@override
|
||
String get routingRulesTitle => 'Routing rules';
|
||
@override
|
||
String get routingRulesSub => 'Custom direct / tunnel / reject';
|
||
@override
|
||
String get routingLanDirect => 'LAN & private direct';
|
||
@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 routingTypeDomain => 'Domain';
|
||
@override
|
||
String get routingTypeDomainKeyword => 'Domain keyword';
|
||
@override
|
||
String get routingTypeIpCidr => 'IP range (CIDR)';
|
||
@override
|
||
String get routingTypeGeoip => 'GeoIP 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 routingRuleShadowed => 'Overridden by a rule above';
|
||
@override
|
||
String get routingModeNote => '"My rules" only take effect in Smart mode. Global / Direct mode ignores custom rules entirely (system-forced rules still apply).';
|
||
@override
|
||
String get routingRuleValueHint => 'e.g. example.com or 192.168.1.0/24';
|
||
@override
|
||
String get routingNoRules => 'No custom rules yet';
|
||
@override
|
||
String get routingAddRule => 'Add rule';
|
||
@override
|
||
String get routingImport => 'Import from text';
|
||
@override
|
||
String get routingReset => 'Reset defaults';
|
||
@override
|
||
String get routingResetConfirmTitle => 'Reset to defaults?';
|
||
@override
|
||
String get routingResetConfirmBody => 'This clears all your custom rules and restores the factory routing table.';
|
||
@override
|
||
String get routingResetConfirmAction => 'Reset';
|
||
@override
|
||
String get routingBack => 'Back to settings';
|
||
@override
|
||
List<String> get days7 => const ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
|
||
@override
|
||
List<String> get featsFree => const ['1 basic node only', '10 min per day', 'Watch an ad to start', '7-day free trial on sign-up'];
|
||
@override
|
||
List<String> get featsPro => const ['80+ locations', 'Unlimited · fast', '5 devices', 'Streaming optimized'];
|
||
@override
|
||
List<String> get featsTeam => const ['Everything in Pro', '10 seats', 'Central billing', 'Priority support'];
|
||
@override
|
||
String adRewarded(int minutes) => '+$minutes min added';
|
||
@override
|
||
String nodeSwitchTitle(String name) => 'Switch to $name?';
|
||
@override
|
||
String autoConnectingTo(String name) => 'Auto-connecting to $name…';
|
||
@override
|
||
String updateAvailableTitle(String version) => 'New version v$version available';
|
||
@override
|
||
String codeSentTo(String email) => 'Code sent to $email';
|
||
}
|