Files
pangolin/client/lib/l10n/strings_en.dart
T
wangjia be27529054 feat(client): 弱网抖动先自动重连当前节点,不再动不动报「节点异常」(#18)
弱网下(实测 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>
2026-07-02 09:57:19 +08:00

360 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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.';
}