be27529054
弱网下(实测 RTT 2.2s/丢包)urltest 瞬断或内核 off,看门狗此前一判死就断开+要用户手动 重连(nodeUnhealthyError),体验差。改为:无备用节点时先自动重连**当前节点**(不换节点—— 弱网是本地网络问题,换节点无益还会来回横跳),连续失败超过 _kMaxAutoReconnect(3) 次才真报 「节点异常」。urltest 成功或用户主动连接即清零计数,只有持续失败才耗尽。 - _tryAutoReconnectCurrent(bounded 重连当前节点)+ _handleUnexpectedOff(kernel off 走同一逻辑)。 - _onNodeUnhealthy 无备用分支改调 _tryAutoReconnectCurrent;智能有备用仍优先切备用(不变)。 - 重连期给「网络波动,正在重连…」瞬态提示(nodeReconnecting,zh/en)。 - 测试:注入时钟 + 内存 SecureKV(避免 deviceId 挂起);kernel-off 连续判死→3 次自动重连后报 节点异常;路径B 无备用→自动重连当前节点不换节点。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
360 lines
10 KiB
Dart
360 lines
10 KiB
Dart
// strings_en.dart — English copy resources (single-language display)
|
||
//
|
||
// Red-line words forbidden: VPN / 翻墙 / 科学上网 / 突破封锁 / 自由穿越 / Go anywhere.
|
||
// Positioning: network acceleration / experience optimization + privacy.
|
||
// Plan numbers follow design/CLAUDE.md §7.
|
||
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 => 'Daily free time used up. Watch an ad to add time or upgrade.';
|
||
@override
|
||
String get adPlaying => 'Ad playing…';
|
||
@override
|
||
String adRewarded(int minutes) => '+$minutes min added';
|
||
@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 nodeSwitchTitle(String name) => 'Switch to $name?';
|
||
@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 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
|
||
List<String> get days7 => const ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
|
||
@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 & buy';
|
||
@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 autoConnectingTo(String name) => 'Auto-connecting to $name…';
|
||
@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 choosePlan => 'Choose plan';
|
||
@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
|
||
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 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 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 codeSentTo(String email) => 'Code sent to $email';
|
||
@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.';
|
||
}
|