feat(i18n): l10n codegen — strings.json 单源生成 6 份 strings_*.dart
② 单源 codegen 落地:
- strings.json 补全 lists 段(days7/featsFree/featsPro/featsTeam),
之前 extraction 漏了 4 个 List<String> getter;现 246 getter+4 list+5 method×6 语。
- design/codegen/gen_l10n_dart.mjs:strings.json → strings_{zh,en,ja,ko,ru,es}.dart,
getter 转义 \$、method 保留 $var 插值,dart format 定型(保证漂移闸零 diff)。
- app_text.dart 保持手写(enum/abstract 声明/AppLangMisc 逻辑),编译器强制 concrete⊆abstract 兜底。
- 往返无损:生成的 Dart 重新抽取 == strings.json 逐字节一致;flutter analyze 0 issue、flutter test 244 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A79VtQA1BwTuQN1ThpvYpo
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
// strings_en.dart — English copy resources (single-language display)
|
||||
// strings_en.dart — English文案(自动生成 · 勿手改)
|
||||
//
|
||||
// Red-line words forbidden — see design/CLAUDE.md §1 rule 13 (no censorship-circumvention terms).
|
||||
// Positioning: network acceleration / experience optimization + privacy.
|
||||
// Plan numbers follow design/CLAUDE.md §7.
|
||||
// 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 {
|
||||
@@ -27,7 +26,6 @@ class StringsEn extends AppText {
|
||||
String get connectNow => 'CONNECT';
|
||||
@override
|
||||
String get secure => 'SECURE';
|
||||
|
||||
@override
|
||||
String get tabConnect => 'Connect';
|
||||
@override
|
||||
@@ -36,7 +34,6 @@ class StringsEn extends AppText {
|
||||
String get tabStats => 'Stats';
|
||||
@override
|
||||
String get tabMe => 'Account';
|
||||
|
||||
@override
|
||||
String get currentNode => 'Current node';
|
||||
@override
|
||||
@@ -45,7 +42,6 @@ class StringsEn extends AppText {
|
||||
String get upload => 'Up';
|
||||
@override
|
||||
String get latency => 'Ping';
|
||||
|
||||
@override
|
||||
String get quotaToday => 'Left today';
|
||||
@override
|
||||
@@ -65,20 +61,19 @@ class StringsEn extends AppText {
|
||||
@override
|
||||
String get watchAdMore => 'Watch ad for more time';
|
||||
@override
|
||||
String get quotaExhaustedNotice => "You've used today's free time. Upgrade to keep connecting.";
|
||||
String get quotaExhaustedNotice =>
|
||||
'You\'ve used today\'s free time. Upgrade to keep connecting.';
|
||||
@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.';
|
||||
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
|
||||
@@ -86,22 +81,22 @@ class StringsEn extends AppText {
|
||||
@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.';
|
||||
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';
|
||||
String get nodeUnhealthySwitched =>
|
||||
'Current node unreachable — switched automatically';
|
||||
@override
|
||||
String get nodeUnhealthyError => 'Current node is unreachable — reconnect or switch node';
|
||||
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
|
||||
@@ -113,8 +108,6 @@ class StringsEn extends AppText {
|
||||
@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';
|
||||
@@ -134,7 +127,6 @@ class StringsEn extends AppText {
|
||||
String get chartTwoWeeks => 'Last 2 weeks';
|
||||
@override
|
||||
String get allDevices => 'All devices';
|
||||
|
||||
@override
|
||||
String get meTitle => 'Account';
|
||||
@override
|
||||
@@ -156,7 +148,8 @@ class StringsEn extends AppText {
|
||||
@override
|
||||
String get deviceLimitTitle => 'Device limit reached';
|
||||
@override
|
||||
String get deviceLimitDesc => 'Your plan allows up to %s devices. Remove one to continue.';
|
||||
String get deviceLimitDesc =>
|
||||
'Your plan allows up to %s devices. Remove one to continue.';
|
||||
@override
|
||||
String get deviceLimitRemoveOldest => 'Remove least recently used';
|
||||
@override
|
||||
@@ -186,15 +179,18 @@ class StringsEn extends AppText {
|
||||
@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.';
|
||||
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.';
|
||||
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.';
|
||||
String get sessionRevokedBody =>
|
||||
'Your account signed this device out from another device. Please sign in again.';
|
||||
@override
|
||||
String get sessionRevokedOk => 'Sign in again';
|
||||
@override
|
||||
@@ -224,8 +220,6 @@ class StringsEn extends AppText {
|
||||
@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';
|
||||
@@ -238,9 +232,8 @@ class StringsEn extends AppText {
|
||||
@override
|
||||
String get webUserCenter => 'User Center (Web)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => 'New version v$version available';
|
||||
@override
|
||||
String get updateNotesFallback => 'This update includes fixes and stability improvements.';
|
||||
String get updateNotesFallback =>
|
||||
'This update includes fixes and stability improvements.';
|
||||
@override
|
||||
String get updateLater => 'Later';
|
||||
@override
|
||||
@@ -249,7 +242,6 @@ class StringsEn extends AppText {
|
||||
String get updateUpToDate => 'You are up to date';
|
||||
@override
|
||||
String get updateCheckFailed => 'Update check failed, try again later';
|
||||
|
||||
@override
|
||||
String get freePlan => 'Free';
|
||||
@override
|
||||
@@ -266,20 +258,6 @@ class StringsEn extends AppText {
|
||||
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
|
||||
@@ -298,7 +276,6 @@ class StringsEn extends AppText {
|
||||
String get chStore => 'Self-serve store';
|
||||
@override
|
||||
String get chEmail => 'Email support';
|
||||
|
||||
@override
|
||||
String get contactTitle => 'Contact us';
|
||||
@override
|
||||
@@ -314,7 +291,6 @@ class StringsEn extends AppText {
|
||||
String get contactHoursTitle => 'Support hours';
|
||||
@override
|
||||
String get contactHours => 'Daily 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => 'Fast · Stable · Effortless';
|
||||
@override
|
||||
@@ -334,8 +310,6 @@ class StringsEn extends AppText {
|
||||
@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';
|
||||
@@ -349,7 +323,6 @@ class StringsEn extends AppText {
|
||||
String get forgotPw => 'Forgot password?';
|
||||
@override
|
||||
String get tos => 'By continuing you agree to our Terms & Privacy Policy';
|
||||
|
||||
@override
|
||||
String get obSkip => 'Skip';
|
||||
@override
|
||||
@@ -365,12 +338,12 @@ class StringsEn extends AppText {
|
||||
@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.';
|
||||
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
|
||||
@@ -400,15 +373,18 @@ class StringsEn extends AppText {
|
||||
@override
|
||||
String get payNetworkLabel => 'Network';
|
||||
@override
|
||||
String get payExactAmountHint => 'Send the exact amount; activates automatically';
|
||||
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';
|
||||
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";
|
||||
String get openAlipayFailed =>
|
||||
'Couldn\'t open Alipay; check it\'s installed or try again';
|
||||
@override
|
||||
String get awaitingPayment => 'Awaiting payment';
|
||||
@override
|
||||
@@ -433,8 +409,6 @@ class StringsEn extends AppText {
|
||||
String get payUnsupportedRenderType => 'Unsupported, switch payment method';
|
||||
@override
|
||||
String get payNoActiveOrder => 'No active order';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
String get payChannel => 'Payment channel';
|
||||
@override
|
||||
@@ -473,8 +447,6 @@ class StringsEn extends AppText {
|
||||
String get orderRebuy => 'Buy again';
|
||||
@override
|
||||
String get payMethodNezha => 'Nazhapay';
|
||||
|
||||
// ── 邀请 / 通知 / 设置分组 / 订单过期(Spec ②③占位) ──
|
||||
@override
|
||||
String get inviteEntry => 'Invite friends, earn membership';
|
||||
@override
|
||||
@@ -545,4 +517,38 @@ class StringsEn extends AppText {
|
||||
String get promoBadge => 'Limited-time offer';
|
||||
@override
|
||||
String get promoOriginalPrice => 'Was';
|
||||
@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';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user