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';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// strings_es.dart — Recursos de texto en español (visualización monolingüe)
|
||||
// strings_es.dart — Español文案(自动生成 · 勿手改)
|
||||
//
|
||||
// Palabras prohibidas: términos de evasión de censura. Lista en design/CLAUDE.md §1 regla 13.
|
||||
// Posicionamiento: aceleración de red / optimización de la experiencia + privacidad.
|
||||
// La numeración de planes sigue 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 StringsEs extends AppText {
|
||||
@@ -27,7 +26,6 @@ class StringsEs extends AppText {
|
||||
String get connectNow => 'CONECTAR';
|
||||
@override
|
||||
String get secure => 'SEGURO';
|
||||
|
||||
@override
|
||||
String get tabConnect => 'Conectar';
|
||||
@override
|
||||
@@ -36,7 +34,6 @@ class StringsEs extends AppText {
|
||||
String get tabStats => 'Estadísticas';
|
||||
@override
|
||||
String get tabMe => 'Cuenta';
|
||||
|
||||
@override
|
||||
String get currentNode => 'Nodo actual';
|
||||
@override
|
||||
@@ -45,7 +42,6 @@ class StringsEs extends AppText {
|
||||
String get upload => 'Subida';
|
||||
@override
|
||||
String get latency => 'Ping';
|
||||
|
||||
@override
|
||||
String get quotaToday => 'Restante hoy';
|
||||
@override
|
||||
@@ -65,20 +61,19 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get watchAdMore => 'Mira un anuncio para más tiempo';
|
||||
@override
|
||||
String get quotaExhaustedNotice => 'Tiempo gratis diario agotado. Mejora tu plan para seguir conectado.';
|
||||
String get quotaExhaustedNotice =>
|
||||
'Tiempo gratis diario agotado. Mejora tu plan para seguir conectado.';
|
||||
@override
|
||||
String get adPlaying => 'Reproduciendo anuncio…';
|
||||
@override
|
||||
String adRewarded(int minutes) => '+$minutes min añadidos';
|
||||
@override
|
||||
String get adFailed => 'No se pudo añadir tiempo, inténtalo de nuevo';
|
||||
@override
|
||||
String get quotaDesktopTitle => 'Tiempo gratis diario agotado';
|
||||
@override
|
||||
String get quotaDesktopBody => 'Mira anuncios en la app móvil para añadir tiempo, o mejora tu plan para acceso ilimitado.';
|
||||
String get quotaDesktopBody =>
|
||||
'Mira anuncios en la app móvil para añadir tiempo, o mejora tu plan para acceso ilimitado.';
|
||||
@override
|
||||
String get gotIt => 'Entendido';
|
||||
|
||||
@override
|
||||
String get chooseNode => 'Elegir servidor';
|
||||
@override
|
||||
@@ -86,22 +81,22 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get smartSelect => 'Selección inteligente';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '¿Cambiar a $name?';
|
||||
@override
|
||||
String get nodeSwitchBody => 'Esto desconecta el enlace actual y reconecta a este nodo.';
|
||||
String get nodeSwitchBody =>
|
||||
'Esto desconecta el enlace actual y reconecta a este nodo.';
|
||||
@override
|
||||
String get nodeSwitchConfirm => 'Cambiar';
|
||||
@override
|
||||
String get nodeUnhealthySwitched => 'Nodo actual inaccesible: cambiado automáticamente';
|
||||
String get nodeUnhealthySwitched =>
|
||||
'Nodo actual inaccesible: cambiado automáticamente';
|
||||
@override
|
||||
String get nodeUnhealthyError => 'El nodo actual está inaccesible: reconecta o cambia de nodo';
|
||||
String get nodeUnhealthyError =>
|
||||
'El nodo actual está inaccesible: reconecta o cambia de nodo';
|
||||
@override
|
||||
String get nodeReconnecting => 'Red inestable, reconectando…';
|
||||
@override
|
||||
String get smartSub => 'Elige el mejor nodo para tu red';
|
||||
@override
|
||||
String get recommended => 'Recomendado';
|
||||
|
||||
@override
|
||||
String get statsTitle => 'Estadísticas';
|
||||
@override
|
||||
@@ -113,8 +108,6 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get weekTraffic => 'Esta semana (GB)';
|
||||
@override
|
||||
List<String> get days7 => const ['L', 'M', 'X', 'J', 'V', 'S', 'D'];
|
||||
@override
|
||||
String get byDevice => 'Por dispositivo';
|
||||
@override
|
||||
String get noDeviceUsage => 'Aún no hay uso por dispositivo';
|
||||
@@ -134,7 +127,6 @@ class StringsEs extends AppText {
|
||||
String get chartTwoWeeks => 'Últimas 2 semanas';
|
||||
@override
|
||||
String get allDevices => 'Todos los dispositivos';
|
||||
|
||||
@override
|
||||
String get meTitle => 'Cuenta';
|
||||
@override
|
||||
@@ -156,7 +148,8 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get deviceLimitTitle => 'Límite de dispositivos alcanzado';
|
||||
@override
|
||||
String get deviceLimitDesc => 'Tu plan permite hasta %s dispositivos. Elimina uno para continuar.';
|
||||
String get deviceLimitDesc =>
|
||||
'Tu plan permite hasta %s dispositivos. Elimina uno para continuar.';
|
||||
@override
|
||||
String get deviceLimitRemoveOldest => 'Eliminar el menos usado recientemente';
|
||||
@override
|
||||
@@ -186,15 +179,18 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get devSave => 'Guardar';
|
||||
@override
|
||||
String get devForceLogoutConfirm => 'Esto revoca la sesión del dispositivo: será desconectado y deberá iniciar sesión de nuevo. El dispositivo permanece en la lista.';
|
||||
String get devForceLogoutConfirm =>
|
||||
'Esto revoca la sesión del dispositivo: será desconectado y deberá iniciar sesión de nuevo. El dispositivo permanece en la lista.';
|
||||
@override
|
||||
String get devClearLoginConfirm => 'Esto elimina el dispositivo de la lista por completo y revoca su sesión y su credencial del plano de datos. El dispositivo deberá iniciar sesión de nuevo para poder usarse.';
|
||||
String get devClearLoginConfirm =>
|
||||
'Esto elimina el dispositivo de la lista por completo y revoca su sesión y su credencial del plano de datos. El dispositivo deberá iniciar sesión de nuevo para poder usarse.';
|
||||
@override
|
||||
String get devCancel => 'Cancelar';
|
||||
@override
|
||||
String get sessionRevokedTitle => 'Se cerró la sesión de este dispositivo';
|
||||
@override
|
||||
String get sessionRevokedBody => 'Tu cuenta cerró la sesión de este dispositivo desde otro dispositivo. Inicia sesión de nuevo.';
|
||||
String get sessionRevokedBody =>
|
||||
'Tu cuenta cerró la sesión de este dispositivo desde otro dispositivo. Inicia sesión de nuevo.';
|
||||
@override
|
||||
String get sessionRevokedOk => 'Iniciar sesión de nuevo';
|
||||
@override
|
||||
@@ -224,8 +220,6 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get autoConnectSub => 'Conectar automáticamente al abrir';
|
||||
@override
|
||||
String autoConnectingTo(String name) => 'Conectando automáticamente a $name…';
|
||||
@override
|
||||
String get smartRoute => 'Enrutamiento inteligente';
|
||||
@override
|
||||
String get smartRouteSub => 'Acelera en el extranjero, directo en casa';
|
||||
@@ -238,9 +232,8 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get webUserCenter => 'Centro de usuario (Web)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => 'Nueva versión v$version disponible';
|
||||
@override
|
||||
String get updateNotesFallback => 'Esta actualización incluye correcciones y mejoras de estabilidad.';
|
||||
String get updateNotesFallback =>
|
||||
'Esta actualización incluye correcciones y mejoras de estabilidad.';
|
||||
@override
|
||||
String get updateLater => 'Más tarde';
|
||||
@override
|
||||
@@ -248,8 +241,8 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get updateUpToDate => 'Ya tienes la última versión';
|
||||
@override
|
||||
String get updateCheckFailed => 'Falló la búsqueda de actualizaciones, inténtalo más tarde';
|
||||
|
||||
String get updateCheckFailed =>
|
||||
'Falló la búsqueda de actualizaciones, inténtalo más tarde';
|
||||
@override
|
||||
String get freePlan => 'Gratis';
|
||||
@override
|
||||
@@ -266,20 +259,6 @@ class StringsEs extends AppText {
|
||||
String get choose => 'Elegir';
|
||||
@override
|
||||
String get mostPopular => 'Popular';
|
||||
@override
|
||||
List<String> 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',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro =>
|
||||
const ['Más de 80 ubicaciones', 'Ilimitado · rápido', '5 dispositivos', 'Optimizado para streaming'];
|
||||
@override
|
||||
List<String> get featsTeam =>
|
||||
const ['Todo lo de Pro', '10 plazas', 'Facturación centralizada', 'Soporte prioritario'];
|
||||
|
||||
@override
|
||||
String get redeemTitle => 'Canjear y comprar';
|
||||
@override
|
||||
@@ -293,12 +272,12 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get buyTitle => 'Dónde comprar';
|
||||
@override
|
||||
String get buySub => 'El pago dentro de la app no está disponible. Consigue un código mediante:';
|
||||
String get buySub =>
|
||||
'El pago dentro de la app no está disponible. Consigue un código mediante:';
|
||||
@override
|
||||
String get chStore => 'Tienda autoservicio';
|
||||
@override
|
||||
String get chEmail => 'Soporte por correo';
|
||||
|
||||
@override
|
||||
String get contactTitle => 'Contáctanos';
|
||||
@override
|
||||
@@ -314,7 +293,6 @@ class StringsEs extends AppText {
|
||||
String get contactHoursTitle => 'Horario de soporte';
|
||||
@override
|
||||
String get contactHours => 'Todos los días 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => 'Rápido · Estable · Sin complicaciones';
|
||||
@override
|
||||
@@ -334,8 +312,6 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get codeLabel => 'Código de verificación';
|
||||
@override
|
||||
String codeSentTo(String email) => 'Código enviado a $email';
|
||||
@override
|
||||
String get sendCode => 'Enviar código';
|
||||
@override
|
||||
String get resend => 'Reenviar';
|
||||
@@ -348,8 +324,8 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get forgotPw => '¿Olvidaste la contraseña?';
|
||||
@override
|
||||
String get tos => 'Al continuar aceptas nuestros Términos y Política de privacidad';
|
||||
|
||||
String get tos =>
|
||||
'Al continuar aceptas nuestros Términos y Política de privacidad';
|
||||
@override
|
||||
String get obSkip => 'Omitir';
|
||||
@override
|
||||
@@ -361,17 +337,18 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get ob1Title => 'Conéctate en todo el mundo';
|
||||
@override
|
||||
String get ob1Sub => 'Más de 80 ubicaciones, enrutamiento al más rápido automático, sólido como una roca.';
|
||||
String get ob1Sub =>
|
||||
'Más de 80 ubicaciones, enrutamiento al más rápido automático, sólido como una roca.';
|
||||
@override
|
||||
String get ob2Title => 'Permite la configuración de red';
|
||||
@override
|
||||
String get ob2Sub => 'El sistema te pedirá añadir un perfil de red para crear el túnel cifrado.';
|
||||
String get ob2Sub =>
|
||||
'El sistema te pedirá añadir un perfil de red para crear el túnel cifrado.';
|
||||
@override
|
||||
String get ob3Title => 'Privado por diseño';
|
||||
@override
|
||||
String get ob3Sub => 'Cifrado de extremo a extremo. No guardamos ningún registro de navegación.';
|
||||
|
||||
// ── 购买 / 支付 ──
|
||||
String get ob3Sub =>
|
||||
'Cifrado de extremo a extremo. No guardamos ningún registro de navegación.';
|
||||
@override
|
||||
String get purchaseTitle => 'Comprar plan';
|
||||
@override
|
||||
@@ -401,15 +378,18 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get payNetworkLabel => 'Red';
|
||||
@override
|
||||
String get payExactAmountHint => 'Envía el importe exacto; se activa automáticamente';
|
||||
String get payExactAmountHint =>
|
||||
'Envía el importe exacto; se activa automáticamente';
|
||||
@override
|
||||
String get copied => 'Copiado';
|
||||
@override
|
||||
String get openAlipay => 'Pagar con Alipay';
|
||||
@override
|
||||
String get openAlipayHint => 'Vuelve tras pagar; esta página se actualiza automáticamente';
|
||||
String get openAlipayHint =>
|
||||
'Vuelve tras pagar; esta página se actualiza automáticamente';
|
||||
@override
|
||||
String get openAlipayFailed => 'No se pudo abrir Alipay; comprueba que esté instalado o inténtalo de nuevo';
|
||||
String get openAlipayFailed =>
|
||||
'No se pudo abrir Alipay; comprueba que esté instalado o inténtalo de nuevo';
|
||||
@override
|
||||
String get awaitingPayment => 'Esperando el pago';
|
||||
@override
|
||||
@@ -429,13 +409,13 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get qrNotSupported => 'Copia el contenido y ábrelo en Alipay';
|
||||
@override
|
||||
String get orderCreateFailed => 'Error al crear el pedido, inténtalo de nuevo más tarde';
|
||||
String get orderCreateFailed =>
|
||||
'Error al crear el pedido, inténtalo de nuevo más tarde';
|
||||
@override
|
||||
String get payUnsupportedRenderType => 'No compatible, cambia el método de pago';
|
||||
String get payUnsupportedRenderType =>
|
||||
'No compatible, cambia el método de pago';
|
||||
@override
|
||||
String get payNoActiveOrder => 'No hay pedidos activos';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
String get payChannel => 'Canal de pago';
|
||||
@override
|
||||
@@ -474,8 +454,6 @@ class StringsEs extends AppText {
|
||||
String get orderRebuy => 'Comprar de nuevo';
|
||||
@override
|
||||
String get payMethodNezha => 'Nazhapay';
|
||||
|
||||
// ── 邀请 / 通知 / 设置分组 / 订单过期(Spec ②③占位) ──
|
||||
@override
|
||||
String get inviteEntry => 'Invita a amigos y gana membresía';
|
||||
@override
|
||||
@@ -495,7 +473,8 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get inviteRewardRegister => 'Registro de amigo · +3 días cada uno';
|
||||
@override
|
||||
String get inviteRewardFirstBuy => 'Primera compra del amigo · +7 días cada uno';
|
||||
String get inviteRewardFirstBuy =>
|
||||
'Primera compra del amigo · +7 días cada uno';
|
||||
@override
|
||||
String get inviteRewardJoinTg => 'Unirse al canal de TG · +3 días';
|
||||
@override
|
||||
@@ -546,4 +525,38 @@ class StringsEs extends AppText {
|
||||
String get promoBadge => 'Oferta especial';
|
||||
@override
|
||||
String get promoOriginalPrice => 'Antes';
|
||||
@override
|
||||
List<String> get days7 => const ['L', 'M', 'X', 'J', 'V', 'S', 'D'];
|
||||
@override
|
||||
List<String> 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',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro => const [
|
||||
'Más de 80 ubicaciones',
|
||||
'Ilimitado · rápido',
|
||||
'5 dispositivos',
|
||||
'Optimizado para streaming',
|
||||
];
|
||||
@override
|
||||
List<String> get featsTeam => const [
|
||||
'Todo lo de Pro',
|
||||
'10 plazas',
|
||||
'Facturación centralizada',
|
||||
'Soporte prioritario',
|
||||
];
|
||||
@override
|
||||
String adRewarded(int minutes) => '+$minutes min añadidos';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '¿Cambiar a $name?';
|
||||
@override
|
||||
String autoConnectingTo(String name) => 'Conectando automáticamente a $name…';
|
||||
@override
|
||||
String updateAvailableTitle(String version) =>
|
||||
'Nueva versión v$version disponible';
|
||||
@override
|
||||
String codeSentTo(String email) => 'Código enviado a $email';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// strings_ja.dart — 日本語の文言リソース(単一言語表示)
|
||||
// strings_ja.dart — 日本語文案(自动生成 · 勿手改)
|
||||
//
|
||||
// 禁止ワード: 検閲回避・センシティブな表現。一覧は design/CLAUDE.md §1 ルール13を参照。
|
||||
// ポジショニング: ネットワーク高速化 / 体験最適化 + プライバシー。
|
||||
// プラン番号は 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 StringsJa extends AppText {
|
||||
@@ -27,7 +26,6 @@ class StringsJa extends AppText {
|
||||
String get connectNow => '接続';
|
||||
@override
|
||||
String get secure => 'セキュア';
|
||||
|
||||
@override
|
||||
String get tabConnect => '接続';
|
||||
@override
|
||||
@@ -36,7 +34,6 @@ class StringsJa extends AppText {
|
||||
String get tabStats => '統計';
|
||||
@override
|
||||
String get tabMe => 'アカウント';
|
||||
|
||||
@override
|
||||
String get currentNode => '現在のノード';
|
||||
@override
|
||||
@@ -45,7 +42,6 @@ class StringsJa extends AppText {
|
||||
String get upload => '上り';
|
||||
@override
|
||||
String get latency => 'Ping';
|
||||
|
||||
@override
|
||||
String get quotaToday => '本日の残り';
|
||||
@override
|
||||
@@ -69,16 +65,14 @@ class StringsJa extends AppText {
|
||||
@override
|
||||
String get adPlaying => '広告を再生中…';
|
||||
@override
|
||||
String adRewarded(int minutes) => '+$minutes 分を追加しました';
|
||||
@override
|
||||
String get adFailed => '時間の追加に失敗しました。もう一度お試しください';
|
||||
@override
|
||||
String get quotaDesktopTitle => '本日の無料時間を使い切りました';
|
||||
@override
|
||||
String get quotaDesktopBody => 'モバイルアプリで広告を見ると時間を追加できます。無制限に使うにはアップグレードしてください。';
|
||||
String get quotaDesktopBody =>
|
||||
'モバイルアプリで広告を見ると時間を追加できます。無制限に使うにはアップグレードしてください。';
|
||||
@override
|
||||
String get gotIt => '了解';
|
||||
|
||||
@override
|
||||
String get chooseNode => 'サーバーを選択';
|
||||
@override
|
||||
@@ -86,8 +80,6 @@ class StringsJa extends AppText {
|
||||
@override
|
||||
String get smartSelect => 'スマート選択';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '$name に切り替えますか?';
|
||||
@override
|
||||
String get nodeSwitchBody => '現在の接続を切断し、このノードに再接続します。';
|
||||
@override
|
||||
String get nodeSwitchConfirm => '切り替える';
|
||||
@@ -101,7 +93,6 @@ class StringsJa extends AppText {
|
||||
String get smartSub => 'お使いのネットワークに最適なノードを選びます';
|
||||
@override
|
||||
String get recommended => 'おすすめ';
|
||||
|
||||
@override
|
||||
String get statsTitle => '統計';
|
||||
@override
|
||||
@@ -113,8 +104,6 @@ class StringsJa extends AppText {
|
||||
@override
|
||||
String get weekTraffic => '今週 (GB)';
|
||||
@override
|
||||
List<String> get days7 => const ['月', '火', '水', '木', '金', '土', '日'];
|
||||
@override
|
||||
String get byDevice => 'デバイス別';
|
||||
@override
|
||||
String get noDeviceUsage => 'デバイスの利用履歴はまだありません';
|
||||
@@ -134,7 +123,6 @@ class StringsJa extends AppText {
|
||||
String get chartTwoWeeks => '過去2週間';
|
||||
@override
|
||||
String get allDevices => 'すべてのデバイス';
|
||||
|
||||
@override
|
||||
String get meTitle => 'アカウント';
|
||||
@override
|
||||
@@ -186,15 +174,18 @@ class StringsJa extends AppText {
|
||||
@override
|
||||
String get devSave => '保存';
|
||||
@override
|
||||
String get devForceLogoutConfirm => 'このデバイスのログインセッションを無効にします。オフラインになり、再度サインインが必要になります。デバイスはリストに残ります。';
|
||||
String get devForceLogoutConfirm =>
|
||||
'このデバイスのログインセッションを無効にします。オフラインになり、再度サインインが必要になります。デバイスはリストに残ります。';
|
||||
@override
|
||||
String get devClearLoginConfirm => 'このデバイスをリストから完全に削除し、ログインセッションとデータ通信の認証情報を無効にします。再び利用するには、もう一度サインインが必要です。';
|
||||
String get devClearLoginConfirm =>
|
||||
'このデバイスをリストから完全に削除し、ログインセッションとデータ通信の認証情報を無効にします。再び利用するには、もう一度サインインが必要です。';
|
||||
@override
|
||||
String get devCancel => 'キャンセル';
|
||||
@override
|
||||
String get sessionRevokedTitle => 'このデバイスはサインアウトされました';
|
||||
@override
|
||||
String get sessionRevokedBody => '別のデバイスから、このデバイスがサインアウトされました。もう一度サインインしてください。';
|
||||
String get sessionRevokedBody =>
|
||||
'別のデバイスから、このデバイスがサインアウトされました。もう一度サインインしてください。';
|
||||
@override
|
||||
String get sessionRevokedOk => '再度サインイン';
|
||||
@override
|
||||
@@ -224,8 +215,6 @@ class StringsJa extends AppText {
|
||||
@override
|
||||
String get autoConnectSub => '起動時に自動的に接続';
|
||||
@override
|
||||
String autoConnectingTo(String name) => '$name に自動接続中…';
|
||||
@override
|
||||
String get smartRoute => 'スマートルーティング';
|
||||
@override
|
||||
String get smartRouteSub => '海外は高速化、国内は直接接続';
|
||||
@@ -238,8 +227,6 @@ class StringsJa extends AppText {
|
||||
@override
|
||||
String get webUserCenter => 'ユーザーセンター (Web)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => '新しいバージョン v$version が利用可能です';
|
||||
@override
|
||||
String get updateNotesFallback => 'このアップデートには不具合の修正と安定性の向上が含まれています。';
|
||||
@override
|
||||
String get updateLater => '後で';
|
||||
@@ -249,7 +236,6 @@ class StringsJa extends AppText {
|
||||
String get updateUpToDate => '最新の状態です';
|
||||
@override
|
||||
String get updateCheckFailed => 'アップデートの確認に失敗しました。後でもう一度お試しください';
|
||||
|
||||
@override
|
||||
String get freePlan => '無料';
|
||||
@override
|
||||
@@ -266,20 +252,6 @@ class StringsJa extends AppText {
|
||||
String get choose => '選択';
|
||||
@override
|
||||
String get mostPopular => '人気';
|
||||
@override
|
||||
List<String> get featsFree => const [
|
||||
'基本ノード1つのみ',
|
||||
'1日10分',
|
||||
'広告を見て開始',
|
||||
'登録で7日間無料トライアル',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro =>
|
||||
const ['80以上の拠点', '無制限 · 高速', '5台のデバイス', 'ストリーミング最適化'];
|
||||
@override
|
||||
List<String> get featsTeam =>
|
||||
const ['Proのすべての機能', '10ライセンス', '一括請求', '優先サポート'];
|
||||
|
||||
@override
|
||||
String get redeemTitle => 'コード引き換え・購入';
|
||||
@override
|
||||
@@ -298,12 +270,10 @@ class StringsJa extends AppText {
|
||||
String get chStore => 'セルフサービスストア';
|
||||
@override
|
||||
String get chEmail => 'メールサポート';
|
||||
|
||||
@override
|
||||
String get contactTitle => 'お問い合わせ';
|
||||
@override
|
||||
String get contactIntro =>
|
||||
'お困りですか?以下のいずれかの方法でお問い合わせください。通常は数分以内に返信します。';
|
||||
String get contactIntro => 'お困りですか?以下のいずれかの方法でお問い合わせください。通常は数分以内に返信します。';
|
||||
@override
|
||||
String get contactEmail => 'メールサポート';
|
||||
@override
|
||||
@@ -314,7 +284,6 @@ class StringsJa extends AppText {
|
||||
String get contactHoursTitle => 'サポート時間';
|
||||
@override
|
||||
String get contactHours => '毎日 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => '高速 · 安定 · かんたん';
|
||||
@override
|
||||
@@ -334,8 +303,6 @@ class StringsJa extends AppText {
|
||||
@override
|
||||
String get codeLabel => '確認コード';
|
||||
@override
|
||||
String codeSentTo(String email) => '$email に確認コードを送信しました';
|
||||
@override
|
||||
String get sendCode => 'コードを送信';
|
||||
@override
|
||||
String get resend => '再送信';
|
||||
@@ -349,7 +316,6 @@ class StringsJa extends AppText {
|
||||
String get forgotPw => 'パスワードをお忘れですか?';
|
||||
@override
|
||||
String get tos => '続行すると、利用規約とプライバシーポリシーに同意したことになります';
|
||||
|
||||
@override
|
||||
String get obSkip => 'スキップ';
|
||||
@override
|
||||
@@ -370,8 +336,6 @@ class StringsJa extends AppText {
|
||||
String get ob3Title => '設計段階からプライバシー重視';
|
||||
@override
|
||||
String get ob3Sub => 'エンドツーエンドで暗号化。閲覧ログは一切保持しません。';
|
||||
|
||||
// ── 购买 / 支付 ──
|
||||
@override
|
||||
String get purchaseTitle => 'プランを購入';
|
||||
@override
|
||||
@@ -434,8 +398,6 @@ class StringsJa extends AppText {
|
||||
String get payUnsupportedRenderType => '未対応です。支払い方法を変更してください';
|
||||
@override
|
||||
String get payNoActiveOrder => '進行中の注文はありません';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
String get payChannel => '支払いチャネル';
|
||||
@override
|
||||
@@ -474,8 +436,6 @@ class StringsJa extends AppText {
|
||||
String get orderRebuy => '再購入';
|
||||
@override
|
||||
String get payMethodNezha => 'Nazhapay';
|
||||
|
||||
// ── 邀请 / 通知 / 设置分组 / 订单过期(Spec ②③占位) ──
|
||||
@override
|
||||
String get inviteEntry => '友達を招待してメンバーシップを獲得';
|
||||
@override
|
||||
@@ -546,4 +506,37 @@ class StringsJa extends AppText {
|
||||
String get promoBadge => '期間限定オファー';
|
||||
@override
|
||||
String get promoOriginalPrice => '通常価格';
|
||||
@override
|
||||
List<String> get days7 => const ['月', '火', '水', '木', '金', '土', '日'];
|
||||
@override
|
||||
List<String> get featsFree => const [
|
||||
'基本ノード1つのみ',
|
||||
'1日10分',
|
||||
'広告を見て開始',
|
||||
'登録で7日間無料トライアル',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro => const [
|
||||
'80以上の拠点',
|
||||
'無制限 · 高速',
|
||||
'5台のデバイス',
|
||||
'ストリーミング最適化',
|
||||
];
|
||||
@override
|
||||
List<String> get featsTeam => const [
|
||||
'Proのすべての機能',
|
||||
'10ライセンス',
|
||||
'一括請求',
|
||||
'優先サポート',
|
||||
];
|
||||
@override
|
||||
String adRewarded(int minutes) => '+$minutes 分を追加しました';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '$name に切り替えますか?';
|
||||
@override
|
||||
String autoConnectingTo(String name) => '$name に自動接続中…';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => '新しいバージョン v$version が利用可能です';
|
||||
@override
|
||||
String codeSentTo(String email) => '$email に確認コードを送信しました';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// strings_ko.dart — 한국어 문구 리소스 (단일 언어 표시)
|
||||
// strings_ko.dart — 한국어文案(自动生成 · 勿手改)
|
||||
//
|
||||
// 금지어: 검열 우회·민감 표현. 목록은 design/CLAUDE.md §1 규칙 13 참조.
|
||||
// 포지셔닝: 네트워크 가속 / 경험 최적화 + 프라이버시.
|
||||
// 요금제 번호는 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 StringsKo extends AppText {
|
||||
@@ -27,7 +26,6 @@ class StringsKo extends AppText {
|
||||
String get connectNow => '연결';
|
||||
@override
|
||||
String get secure => '보안';
|
||||
|
||||
@override
|
||||
String get tabConnect => '연결';
|
||||
@override
|
||||
@@ -36,7 +34,6 @@ class StringsKo extends AppText {
|
||||
String get tabStats => '통계';
|
||||
@override
|
||||
String get tabMe => '계정';
|
||||
|
||||
@override
|
||||
String get currentNode => '현재 노드';
|
||||
@override
|
||||
@@ -45,7 +42,6 @@ class StringsKo extends AppText {
|
||||
String get upload => '업';
|
||||
@override
|
||||
String get latency => '지연';
|
||||
|
||||
@override
|
||||
String get quotaToday => '오늘 남은 시간';
|
||||
@override
|
||||
@@ -65,20 +61,19 @@ class StringsKo extends AppText {
|
||||
@override
|
||||
String get watchAdMore => '광고 시청으로 시간 추가';
|
||||
@override
|
||||
String get quotaExhaustedNotice => '오늘 무료 시간을 모두 사용했습니다. 업그레이드하면 계속 연결할 수 있습니다.';
|
||||
String get quotaExhaustedNotice =>
|
||||
'오늘 무료 시간을 모두 사용했습니다. 업그레이드하면 계속 연결할 수 있습니다.';
|
||||
@override
|
||||
String get adPlaying => '광고 재생 중…';
|
||||
@override
|
||||
String adRewarded(int minutes) => '+$minutes분 추가됨';
|
||||
@override
|
||||
String get adFailed => '시간 추가에 실패했습니다. 다시 시도하세요';
|
||||
@override
|
||||
String get quotaDesktopTitle => '오늘 무료 시간을 모두 사용함';
|
||||
@override
|
||||
String get quotaDesktopBody => '모바일 앱에서 광고를 시청해 시간을 추가하거나, 업그레이드하여 무제한으로 이용하세요.';
|
||||
String get quotaDesktopBody =>
|
||||
'모바일 앱에서 광고를 시청해 시간을 추가하거나, 업그레이드하여 무제한으로 이용하세요.';
|
||||
@override
|
||||
String get gotIt => '확인';
|
||||
|
||||
@override
|
||||
String get chooseNode => '서버 선택';
|
||||
@override
|
||||
@@ -86,8 +81,6 @@ class StringsKo extends AppText {
|
||||
@override
|
||||
String get smartSelect => '스마트 선택';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '$name(으)로 전환할까요?';
|
||||
@override
|
||||
String get nodeSwitchBody => '현재 연결을 끊고 이 노드로 다시 연결합니다.';
|
||||
@override
|
||||
String get nodeSwitchConfirm => '전환';
|
||||
@@ -101,7 +94,6 @@ class StringsKo extends AppText {
|
||||
String get smartSub => '네트워크에 가장 적합한 노드를 선택합니다';
|
||||
@override
|
||||
String get recommended => '추천';
|
||||
|
||||
@override
|
||||
String get statsTitle => '통계';
|
||||
@override
|
||||
@@ -113,8 +105,6 @@ class StringsKo extends AppText {
|
||||
@override
|
||||
String get weekTraffic => '이번 주 (GB)';
|
||||
@override
|
||||
List<String> get days7 => const ['월', '화', '수', '목', '금', '토', '일'];
|
||||
@override
|
||||
String get byDevice => '기기별';
|
||||
@override
|
||||
String get noDeviceUsage => '아직 기기 사용 기록이 없습니다';
|
||||
@@ -134,7 +124,6 @@ class StringsKo extends AppText {
|
||||
String get chartTwoWeeks => '최근 2주';
|
||||
@override
|
||||
String get allDevices => '모든 기기';
|
||||
|
||||
@override
|
||||
String get meTitle => '계정';
|
||||
@override
|
||||
@@ -186,9 +175,11 @@ class StringsKo extends AppText {
|
||||
@override
|
||||
String get devSave => '저장';
|
||||
@override
|
||||
String get devForceLogoutConfirm => '이 기기의 로그인 세션을 무효화합니다 — 오프라인으로 강제 종료되며 다시 로그인해야 합니다. 기기는 목록에 남아 있습니다.';
|
||||
String get devForceLogoutConfirm =>
|
||||
'이 기기의 로그인 세션을 무효화합니다 — 오프라인으로 강제 종료되며 다시 로그인해야 합니다. 기기는 목록에 남아 있습니다.';
|
||||
@override
|
||||
String get devClearLoginConfirm => '이 기기를 목록에서 완전히 삭제하고 로그인 세션과 데이터 전송 자격 증명을 무효화합니다. 다시 사용하려면 재로그인해야 합니다.';
|
||||
String get devClearLoginConfirm =>
|
||||
'이 기기를 목록에서 완전히 삭제하고 로그인 세션과 데이터 전송 자격 증명을 무효화합니다. 다시 사용하려면 재로그인해야 합니다.';
|
||||
@override
|
||||
String get devCancel => '취소';
|
||||
@override
|
||||
@@ -224,8 +215,6 @@ class StringsKo extends AppText {
|
||||
@override
|
||||
String get autoConnectSub => '실행 시 자동으로 연결';
|
||||
@override
|
||||
String autoConnectingTo(String name) => '$name(으)로 자동 연결 중…';
|
||||
@override
|
||||
String get smartRoute => '스마트 라우팅';
|
||||
@override
|
||||
String get smartRouteSub => '해외는 가속, 국내는 직접 연결';
|
||||
@@ -238,8 +227,6 @@ class StringsKo extends AppText {
|
||||
@override
|
||||
String get webUserCenter => '사용자 센터 (웹)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => '새 버전 v$version 사용 가능';
|
||||
@override
|
||||
String get updateNotesFallback => '이번 업데이트에는 버그 수정과 안정성 개선이 포함되어 있습니다.';
|
||||
@override
|
||||
String get updateLater => '나중에';
|
||||
@@ -249,7 +236,6 @@ class StringsKo extends AppText {
|
||||
String get updateUpToDate => '최신 버전입니다';
|
||||
@override
|
||||
String get updateCheckFailed => '업데이트 확인에 실패했습니다. 잠시 후 다시 시도하세요';
|
||||
|
||||
@override
|
||||
String get freePlan => '무료';
|
||||
@override
|
||||
@@ -266,20 +252,6 @@ class StringsKo extends AppText {
|
||||
String get choose => '선택';
|
||||
@override
|
||||
String get mostPopular => '인기';
|
||||
@override
|
||||
List<String> get featsFree => const [
|
||||
'기본 노드 1개',
|
||||
'하루 10분',
|
||||
'광고 시청 후 시작',
|
||||
'가입 시 7일 무료 체험',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro =>
|
||||
const ['80개 이상 지역', '무제한 · 빠른 속도', '기기 5대', '스트리밍 최적화'];
|
||||
@override
|
||||
List<String> get featsTeam =>
|
||||
const ['Pro의 모든 기능', '10인 좌석', '통합 결제', '우선 지원'];
|
||||
|
||||
@override
|
||||
String get redeemTitle => '코드 등록 및 구매';
|
||||
@override
|
||||
@@ -298,12 +270,10 @@ class StringsKo extends AppText {
|
||||
String get chStore => '셀프 스토어';
|
||||
@override
|
||||
String get chEmail => '이메일 지원';
|
||||
|
||||
@override
|
||||
String get contactTitle => '문의하기';
|
||||
@override
|
||||
String get contactIntro =>
|
||||
'도움이 필요하신가요? 아래 채널로 문의하세요 — 보통 몇 분 안에 답변드립니다.';
|
||||
String get contactIntro => '도움이 필요하신가요? 아래 채널로 문의하세요 — 보통 몇 분 안에 답변드립니다.';
|
||||
@override
|
||||
String get contactEmail => '이메일 지원';
|
||||
@override
|
||||
@@ -314,7 +284,6 @@ class StringsKo extends AppText {
|
||||
String get contactHoursTitle => '지원 시간';
|
||||
@override
|
||||
String get contactHours => '매일 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => '빠르게 · 안정적으로 · 손쉽게';
|
||||
@override
|
||||
@@ -334,8 +303,6 @@ class StringsKo extends AppText {
|
||||
@override
|
||||
String get codeLabel => '인증 코드';
|
||||
@override
|
||||
String codeSentTo(String email) => '$email(으)로 코드를 보냈습니다';
|
||||
@override
|
||||
String get sendCode => '코드 전송';
|
||||
@override
|
||||
String get resend => '재전송';
|
||||
@@ -349,7 +316,6 @@ class StringsKo extends AppText {
|
||||
String get forgotPw => '비밀번호를 잊으셨나요?';
|
||||
@override
|
||||
String get tos => '계속하면 이용약관 및 개인정보 처리방침에 동의하는 것입니다';
|
||||
|
||||
@override
|
||||
String get obSkip => '건너뛰기';
|
||||
@override
|
||||
@@ -370,8 +336,6 @@ class StringsKo extends AppText {
|
||||
String get ob3Title => '설계부터 프라이버시';
|
||||
@override
|
||||
String get ob3Sub => '종단 간 암호화. 브라우징 기록을 전혀 저장하지 않습니다.';
|
||||
|
||||
// ── 购买 / 支付 ──
|
||||
@override
|
||||
String get purchaseTitle => '요금제 구매';
|
||||
@override
|
||||
@@ -434,8 +398,6 @@ class StringsKo extends AppText {
|
||||
String get payUnsupportedRenderType => '지원되지 않습니다. 결제 방법을 변경하세요';
|
||||
@override
|
||||
String get payNoActiveOrder => '진행 중인 주문이 없습니다';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
String get payChannel => '결제 채널';
|
||||
@override
|
||||
@@ -474,8 +436,6 @@ class StringsKo extends AppText {
|
||||
String get orderRebuy => '다시 구매';
|
||||
@override
|
||||
String get payMethodNezha => 'Nazhapay';
|
||||
|
||||
// ── 邀请 / 通知 / 设置分组 / 订单过期(Spec ②③占位) ──
|
||||
@override
|
||||
String get inviteEntry => '친구 초대하고 멤버십 받기';
|
||||
@override
|
||||
@@ -546,4 +506,37 @@ class StringsKo extends AppText {
|
||||
String get promoBadge => '한정 특가';
|
||||
@override
|
||||
String get promoOriginalPrice => '정가';
|
||||
@override
|
||||
List<String> get days7 => const ['월', '화', '수', '목', '금', '토', '일'];
|
||||
@override
|
||||
List<String> get featsFree => const [
|
||||
'기본 노드 1개',
|
||||
'하루 10분',
|
||||
'광고 시청 후 시작',
|
||||
'가입 시 7일 무료 체험',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro => const [
|
||||
'80개 이상 지역',
|
||||
'무제한 · 빠른 속도',
|
||||
'기기 5대',
|
||||
'스트리밍 최적화',
|
||||
];
|
||||
@override
|
||||
List<String> get featsTeam => const [
|
||||
'Pro의 모든 기능',
|
||||
'10인 좌석',
|
||||
'통합 결제',
|
||||
'우선 지원',
|
||||
];
|
||||
@override
|
||||
String adRewarded(int minutes) => '+$minutes분 추가됨';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '$name(으)로 전환할까요?';
|
||||
@override
|
||||
String autoConnectingTo(String name) => '$name(으)로 자동 연결 중…';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => '새 버전 v$version 사용 가능';
|
||||
@override
|
||||
String codeSentTo(String email) => '$email(으)로 코드를 보냈습니다';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// strings_ru.dart — русские текстовые ресурсы (отображение на одном языке)
|
||||
// strings_ru.dart — Русский文案(自动生成 · 勿手改)
|
||||
//
|
||||
// Запрещённые слова: термины обхода цензуры. Список см. design/CLAUDE.md §1 правило 13.
|
||||
// Позиционирование: ускорение сети / оптимизация опыта + приватность.
|
||||
// Номера тарифов соответствуют 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 StringsRu extends AppText {
|
||||
@@ -27,7 +26,6 @@ class StringsRu extends AppText {
|
||||
String get connectNow => 'ПОДКЛЮЧИТЬ';
|
||||
@override
|
||||
String get secure => 'ЗАЩИЩЕНО';
|
||||
|
||||
@override
|
||||
String get tabConnect => 'Подключение';
|
||||
@override
|
||||
@@ -36,7 +34,6 @@ class StringsRu extends AppText {
|
||||
String get tabStats => 'Статистика';
|
||||
@override
|
||||
String get tabMe => 'Аккаунт';
|
||||
|
||||
@override
|
||||
String get currentNode => 'Текущий узел';
|
||||
@override
|
||||
@@ -45,7 +42,6 @@ class StringsRu extends AppText {
|
||||
String get upload => 'Отдача';
|
||||
@override
|
||||
String get latency => 'Пинг';
|
||||
|
||||
@override
|
||||
String get quotaToday => 'Осталось сегодня';
|
||||
@override
|
||||
@@ -65,20 +61,19 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get watchAdMore => 'Смотрите рекламу, чтобы добавить время';
|
||||
@override
|
||||
String get quotaExhaustedNotice => 'Дневной бесплатный лимит исчерпан. Оформите подписку, чтобы продолжить подключение.';
|
||||
String get quotaExhaustedNotice =>
|
||||
'Дневной бесплатный лимит исчерпан. Оформите подписку, чтобы продолжить подключение.';
|
||||
@override
|
||||
String get adPlaying => 'Реклама воспроизводится…';
|
||||
@override
|
||||
String adRewarded(int minutes) => '+$minutes мин добавлено';
|
||||
@override
|
||||
String get adFailed => 'Не удалось добавить время, попробуйте ещё раз';
|
||||
@override
|
||||
String get quotaDesktopTitle => 'Дневной бесплатный лимит исчерпан';
|
||||
@override
|
||||
String get quotaDesktopBody => 'Смотрите рекламу в мобильном приложении, чтобы добавить время, или оформите подписку для безлимитного доступа.';
|
||||
String get quotaDesktopBody =>
|
||||
'Смотрите рекламу в мобильном приложении, чтобы добавить время, или оформите подписку для безлимитного доступа.';
|
||||
@override
|
||||
String get gotIt => 'Понятно';
|
||||
|
||||
@override
|
||||
String get chooseNode => 'Выбор сервера';
|
||||
@override
|
||||
@@ -86,22 +81,22 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get smartSelect => 'Умный выбор';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => 'Переключиться на $name?';
|
||||
@override
|
||||
String get nodeSwitchBody => 'Текущее соединение будет разорвано и восстановлено через этот узел.';
|
||||
String get nodeSwitchBody =>
|
||||
'Текущее соединение будет разорвано и восстановлено через этот узел.';
|
||||
@override
|
||||
String get nodeSwitchConfirm => 'Переключить';
|
||||
@override
|
||||
String get nodeUnhealthySwitched => 'Текущий узел недоступен — переключение выполнено автоматически';
|
||||
String get nodeUnhealthySwitched =>
|
||||
'Текущий узел недоступен — переключение выполнено автоматически';
|
||||
@override
|
||||
String get nodeUnhealthyError => 'Текущий узел недоступен — переподключитесь или смените узел';
|
||||
String get nodeUnhealthyError =>
|
||||
'Текущий узел недоступен — переподключитесь или смените узел';
|
||||
@override
|
||||
String get nodeReconnecting => 'Сеть нестабильна, переподключение…';
|
||||
@override
|
||||
String get smartSub => 'Подбирает лучший узел для вашей сети';
|
||||
@override
|
||||
String get recommended => 'Рекомендуется';
|
||||
|
||||
@override
|
||||
String get statsTitle => 'Статистика';
|
||||
@override
|
||||
@@ -113,8 +108,6 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get weekTraffic => 'На этой неделе (ГБ)';
|
||||
@override
|
||||
List<String> get days7 => const ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'];
|
||||
@override
|
||||
String get byDevice => 'По устройствам';
|
||||
@override
|
||||
String get noDeviceUsage => 'Пока нет данных по устройствам';
|
||||
@@ -134,7 +127,6 @@ class StringsRu extends AppText {
|
||||
String get chartTwoWeeks => 'Последние 2 недели';
|
||||
@override
|
||||
String get allDevices => 'Все устройства';
|
||||
|
||||
@override
|
||||
String get meTitle => 'Аккаунт';
|
||||
@override
|
||||
@@ -156,7 +148,8 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get deviceLimitTitle => 'Достигнут лимит устройств';
|
||||
@override
|
||||
String get deviceLimitDesc => 'Ваш тариф допускает до %s устройств. Удалите одно, чтобы продолжить.';
|
||||
String get deviceLimitDesc =>
|
||||
'Ваш тариф допускает до %s устройств. Удалите одно, чтобы продолжить.';
|
||||
@override
|
||||
String get deviceLimitRemoveOldest => 'Удалить давно неиспользуемое';
|
||||
@override
|
||||
@@ -186,15 +179,18 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get devSave => 'Сохранить';
|
||||
@override
|
||||
String get devForceLogoutConfirm => 'Это завершит сеанс входа устройства — оно будет отключено и потребует повторного входа. Устройство останется в списке.';
|
||||
String get devForceLogoutConfirm =>
|
||||
'Это завершит сеанс входа устройства — оно будет отключено и потребует повторного входа. Устройство останется в списке.';
|
||||
@override
|
||||
String get devClearLoginConfirm => 'Это полностью удалит устройство из списка и отзовёт его сеанс входа и учётные данные передачи данных. Чтобы снова использовать устройство, нужно войти заново.';
|
||||
String get devClearLoginConfirm =>
|
||||
'Это полностью удалит устройство из списка и отзовёт его сеанс входа и учётные данные передачи данных. Чтобы снова использовать устройство, нужно войти заново.';
|
||||
@override
|
||||
String get devCancel => 'Отмена';
|
||||
@override
|
||||
String get sessionRevokedTitle => 'Это устройство было отключено';
|
||||
@override
|
||||
String get sessionRevokedBody => 'Вход с этого устройства завершён с другого устройства вашего аккаунта. Пожалуйста, войдите снова.';
|
||||
String get sessionRevokedBody =>
|
||||
'Вход с этого устройства завершён с другого устройства вашего аккаунта. Пожалуйста, войдите снова.';
|
||||
@override
|
||||
String get sessionRevokedOk => 'Войти снова';
|
||||
@override
|
||||
@@ -224,11 +220,10 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get autoConnectSub => 'Подключаться автоматически при запуске';
|
||||
@override
|
||||
String autoConnectingTo(String name) => 'Автоподключение к $name…';
|
||||
@override
|
||||
String get smartRoute => 'Умная маршрутизация';
|
||||
@override
|
||||
String get smartRouteSub => 'Ускорение для зарубежных ресурсов, прямой доступ для локальных';
|
||||
String get smartRouteSub =>
|
||||
'Ускорение для зарубежных ресурсов, прямой доступ для локальных';
|
||||
@override
|
||||
String get killSwitch => 'Kill Switch';
|
||||
@override
|
||||
@@ -238,9 +233,8 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get webUserCenter => 'Личный кабинет (веб)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => 'Доступна новая версия v$version';
|
||||
@override
|
||||
String get updateNotesFallback => 'Это обновление включает исправления и улучшения стабильности.';
|
||||
String get updateNotesFallback =>
|
||||
'Это обновление включает исправления и улучшения стабильности.';
|
||||
@override
|
||||
String get updateLater => 'Позже';
|
||||
@override
|
||||
@@ -248,8 +242,8 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get updateUpToDate => 'У вас установлена последняя версия';
|
||||
@override
|
||||
String get updateCheckFailed => 'Не удалось проверить обновления, попробуйте позже';
|
||||
|
||||
String get updateCheckFailed =>
|
||||
'Не удалось проверить обновления, попробуйте позже';
|
||||
@override
|
||||
String get freePlan => 'Бесплатный';
|
||||
@override
|
||||
@@ -266,20 +260,6 @@ class StringsRu extends AppText {
|
||||
String get choose => 'Выбрать';
|
||||
@override
|
||||
String get mostPopular => 'Популярный';
|
||||
@override
|
||||
List<String> get featsFree => const [
|
||||
'Только 1 базовый узел',
|
||||
'10 минут в день',
|
||||
'Смотрите рекламу, чтобы начать',
|
||||
'7 дней бесплатно при регистрации',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro =>
|
||||
const ['80+ локаций', 'Безлимитно · быстро', '5 устройств', 'Оптимизация для стриминга'];
|
||||
@override
|
||||
List<String> get featsTeam =>
|
||||
const ['Всё из Pro', '10 мест', 'Единый счёт', 'Приоритетная поддержка'];
|
||||
|
||||
@override
|
||||
String get redeemTitle => 'Активация и покупка';
|
||||
@override
|
||||
@@ -293,12 +273,12 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get buyTitle => 'Где купить';
|
||||
@override
|
||||
String get buySub => 'Оплата внутри приложения недоступна. Получить код можно через:';
|
||||
String get buySub =>
|
||||
'Оплата внутри приложения недоступна. Получить код можно через:';
|
||||
@override
|
||||
String get chStore => 'Магазин самообслуживания';
|
||||
@override
|
||||
String get chEmail => 'Поддержка по эл. почте';
|
||||
|
||||
@override
|
||||
String get contactTitle => 'Связаться с нами';
|
||||
@override
|
||||
@@ -314,7 +294,6 @@ class StringsRu extends AppText {
|
||||
String get contactHoursTitle => 'Часы поддержки';
|
||||
@override
|
||||
String get contactHours => 'Ежедневно 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => 'Быстро · Стабильно · Без усилий';
|
||||
@override
|
||||
@@ -334,8 +313,6 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get codeLabel => 'Код подтверждения';
|
||||
@override
|
||||
String codeSentTo(String email) => 'Код отправлен на $email';
|
||||
@override
|
||||
String get sendCode => 'Отправить код';
|
||||
@override
|
||||
String get resend => 'Отправить снова';
|
||||
@@ -348,8 +325,8 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get forgotPw => 'Забыли пароль?';
|
||||
@override
|
||||
String get tos => 'Продолжая, вы принимаете наши Условия и Политику конфиденциальности';
|
||||
|
||||
String get tos =>
|
||||
'Продолжая, вы принимаете наши Условия и Политику конфиденциальности';
|
||||
@override
|
||||
String get obSkip => 'Пропустить';
|
||||
@override
|
||||
@@ -361,17 +338,17 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get ob1Title => 'Подключение по всему миру';
|
||||
@override
|
||||
String get ob1Sub => '80+ локаций, автоматический выбор самого быстрого маршрута, надёжная стабильность.';
|
||||
String get ob1Sub =>
|
||||
'80+ локаций, автоматический выбор самого быстрого маршрута, надёжная стабильность.';
|
||||
@override
|
||||
String get ob2Title => 'Разрешите настройку сети';
|
||||
@override
|
||||
String get ob2Sub => 'Система запросит добавление сетевого профиля для создания зашифрованного туннеля.';
|
||||
String get ob2Sub =>
|
||||
'Система запросит добавление сетевого профиля для создания зашифрованного туннеля.';
|
||||
@override
|
||||
String get ob3Title => 'Приватность по умолчанию';
|
||||
@override
|
||||
String get ob3Sub => 'Сквозное шифрование. Мы не храним журналы просмотров.';
|
||||
|
||||
// ── 购买 / 支付 ──
|
||||
@override
|
||||
String get purchaseTitle => 'Покупка тарифа';
|
||||
@override
|
||||
@@ -401,15 +378,18 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get payNetworkLabel => 'Сеть';
|
||||
@override
|
||||
String get payExactAmountHint => 'Отправьте точную сумму — активация произойдёт автоматически';
|
||||
String get payExactAmountHint =>
|
||||
'Отправьте точную сумму — активация произойдёт автоматически';
|
||||
@override
|
||||
String get copied => 'Скопировано';
|
||||
@override
|
||||
String get openAlipay => 'Оплатить через Alipay';
|
||||
@override
|
||||
String get openAlipayHint => 'Вернитесь после оплаты — страница обновится автоматически';
|
||||
String get openAlipayHint =>
|
||||
'Вернитесь после оплаты — страница обновится автоматически';
|
||||
@override
|
||||
String get openAlipayFailed => 'Не удалось открыть Alipay. Проверьте, установлен ли он, или повторите';
|
||||
String get openAlipayFailed =>
|
||||
'Не удалось открыть Alipay. Проверьте, установлен ли он, или повторите';
|
||||
@override
|
||||
String get awaitingPayment => 'Ожидание оплаты';
|
||||
@override
|
||||
@@ -429,13 +409,13 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get qrNotSupported => 'Скопируйте и откройте в Alipay';
|
||||
@override
|
||||
String get orderCreateFailed => 'Не удалось создать заказ, повторите попытку позже';
|
||||
String get orderCreateFailed =>
|
||||
'Не удалось создать заказ, повторите попытку позже';
|
||||
@override
|
||||
String get payUnsupportedRenderType => 'Не поддерживается, смените способ оплаты';
|
||||
String get payUnsupportedRenderType =>
|
||||
'Не поддерживается, смените способ оплаты';
|
||||
@override
|
||||
String get payNoActiveOrder => 'Нет активных заказов';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
String get payChannel => 'Способ оплаты';
|
||||
@override
|
||||
@@ -474,12 +454,11 @@ class StringsRu extends AppText {
|
||||
String get orderRebuy => 'Купить снова';
|
||||
@override
|
||||
String get payMethodNezha => 'Nazhapay';
|
||||
|
||||
// ── 邀请 / 通知 / 设置分组 / 订单过期(Spec ②③占位) ──
|
||||
@override
|
||||
String get inviteEntry => 'Приглашайте друзей и получайте членство';
|
||||
@override
|
||||
String get inviteEntrySub => 'Друг регистрируется · оба получают дни подписки';
|
||||
String get inviteEntrySub =>
|
||||
'Друг регистрируется · оба получают дни подписки';
|
||||
@override
|
||||
String get inviteTitle => 'Пригласить друзей';
|
||||
@override
|
||||
@@ -546,4 +525,38 @@ class StringsRu extends AppText {
|
||||
String get promoBadge => 'Спецпредложение';
|
||||
@override
|
||||
String get promoOriginalPrice => 'Было';
|
||||
@override
|
||||
List<String> get days7 => const ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'];
|
||||
@override
|
||||
List<String> get featsFree => const [
|
||||
'Только 1 базовый узел',
|
||||
'10 минут в день',
|
||||
'Смотрите рекламу, чтобы начать',
|
||||
'7 дней бесплатно при регистрации',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro => const [
|
||||
'80+ локаций',
|
||||
'Безлимитно · быстро',
|
||||
'5 устройств',
|
||||
'Оптимизация для стриминга',
|
||||
];
|
||||
@override
|
||||
List<String> get featsTeam => const [
|
||||
'Всё из Pro',
|
||||
'10 мест',
|
||||
'Единый счёт',
|
||||
'Приоритетная поддержка',
|
||||
];
|
||||
@override
|
||||
String adRewarded(int minutes) => '+$minutes мин добавлено';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => 'Переключиться на $name?';
|
||||
@override
|
||||
String autoConnectingTo(String name) => 'Автоподключение к $name…';
|
||||
@override
|
||||
String updateAvailableTitle(String version) =>
|
||||
'Доступна новая версия v$version';
|
||||
@override
|
||||
String codeSentTo(String email) => 'Код отправлен на $email';
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// strings_zh.dart — 中文文案资源(单显)
|
||||
// strings_zh.dart — 中文文案(自动生成 · 勿手改)
|
||||
//
|
||||
// 红线词禁用:审查规避/敏感定位词,清单见 design/CLAUDE.md §1 铁律 13。
|
||||
// 脱敏口径:网络加速 / 体验优化 + 隐私保护。套餐数字以 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 StringsZh extends AppText {
|
||||
@@ -26,7 +26,6 @@ class StringsZh extends AppText {
|
||||
String get connectNow => '点击连接';
|
||||
@override
|
||||
String get secure => '已加密';
|
||||
|
||||
@override
|
||||
String get tabConnect => '连接';
|
||||
@override
|
||||
@@ -35,7 +34,6 @@ class StringsZh extends AppText {
|
||||
String get tabStats => '统计';
|
||||
@override
|
||||
String get tabMe => '我的';
|
||||
|
||||
@override
|
||||
String get currentNode => '当前节点';
|
||||
@override
|
||||
@@ -44,7 +42,6 @@ class StringsZh extends AppText {
|
||||
String get upload => '上传';
|
||||
@override
|
||||
String get latency => '延迟';
|
||||
|
||||
@override
|
||||
String get quotaToday => '今日剩余';
|
||||
@override
|
||||
@@ -68,8 +65,6 @@ class StringsZh extends AppText {
|
||||
@override
|
||||
String get adPlaying => '广告播放中…';
|
||||
@override
|
||||
String adRewarded(int minutes) => '已加 $minutes 分钟';
|
||||
@override
|
||||
String get adFailed => '加时失败,请重试';
|
||||
@override
|
||||
String get quotaDesktopTitle => '今日免费时长已用完';
|
||||
@@ -77,7 +72,6 @@ class StringsZh extends AppText {
|
||||
String get quotaDesktopBody => '前往移动端 App 看广告加时,或升级会员畅享无限时长。';
|
||||
@override
|
||||
String get gotIt => '知道了';
|
||||
|
||||
@override
|
||||
String get chooseNode => '选择节点';
|
||||
@override
|
||||
@@ -85,8 +79,6 @@ class StringsZh extends AppText {
|
||||
@override
|
||||
String get smartSelect => '智能选择';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '切换到「$name」?';
|
||||
@override
|
||||
String get nodeSwitchBody => '当前连接将断开并重连到该节点。';
|
||||
@override
|
||||
String get nodeSwitchConfirm => '切换';
|
||||
@@ -100,7 +92,6 @@ class StringsZh extends AppText {
|
||||
String get smartSub => '根据当前网络环境,自动选择最优节点';
|
||||
@override
|
||||
String get recommended => '推荐';
|
||||
|
||||
@override
|
||||
String get statsTitle => '使用统计';
|
||||
@override
|
||||
@@ -112,8 +103,6 @@ class StringsZh extends AppText {
|
||||
@override
|
||||
String get weekTraffic => '本周流量 (GB)';
|
||||
@override
|
||||
List<String> get days7 => const ['一', '二', '三', '四', '五', '六', '日'];
|
||||
@override
|
||||
String get byDevice => '设备明细';
|
||||
@override
|
||||
String get noDeviceUsage => '暂无设备用量';
|
||||
@@ -133,7 +122,6 @@ class StringsZh extends AppText {
|
||||
String get chartTwoWeeks => '最近两周流量';
|
||||
@override
|
||||
String get allDevices => '全部设备';
|
||||
|
||||
@override
|
||||
String get meTitle => '我的';
|
||||
@override
|
||||
@@ -187,7 +175,8 @@ class StringsZh extends AppText {
|
||||
@override
|
||||
String get devForceLogoutConfirm => '将吊销该设备的登录会话,它会被踢下线、需重新登录。设备仍保留在列表中。';
|
||||
@override
|
||||
String get devClearLoginConfirm => '将从设备列表彻底移除此设备,并吊销其登录会话与数据面凭证。该设备需重新登录才能再次使用。';
|
||||
String get devClearLoginConfirm =>
|
||||
'将从设备列表彻底移除此设备,并吊销其登录会话与数据面凭证。该设备需重新登录才能再次使用。';
|
||||
@override
|
||||
String get devCancel => '取消';
|
||||
@override
|
||||
@@ -223,8 +212,6 @@ class StringsZh extends AppText {
|
||||
@override
|
||||
String get autoConnectSub => '启动时自动连接节点';
|
||||
@override
|
||||
String autoConnectingTo(String name) => '正在自动连接到 $name…';
|
||||
@override
|
||||
String get smartRoute => '智能分流';
|
||||
@override
|
||||
String get smartRouteSub => '海外应用走加速,国内直连';
|
||||
@@ -237,8 +224,6 @@ class StringsZh extends AppText {
|
||||
@override
|
||||
String get webUserCenter => '用户中心(网页)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => '发现新版本 v$version';
|
||||
@override
|
||||
String get updateNotesFallback => '本次更新修复了一些问题并提升了稳定性,建议尽快更新。';
|
||||
@override
|
||||
String get updateLater => '稍后';
|
||||
@@ -248,7 +233,6 @@ class StringsZh extends AppText {
|
||||
String get updateUpToDate => '已是最新版本';
|
||||
@override
|
||||
String get updateCheckFailed => '检查更新失败,请稍后重试';
|
||||
|
||||
@override
|
||||
String get freePlan => '免费版';
|
||||
@override
|
||||
@@ -265,16 +249,6 @@ class StringsZh extends AppText {
|
||||
String get choose => '选择';
|
||||
@override
|
||||
String get mostPopular => '最受欢迎';
|
||||
@override
|
||||
List<String> get featsFree =>
|
||||
const ['仅 1 个基础节点', '每日 10 分钟时长', '使用前观看广告', '注册享 7 天免费试用'];
|
||||
@override
|
||||
List<String> get featsPro =>
|
||||
const ['80+ 全球节点', '无限流量 · 极速', '5 台设备同时在线', '流媒体优化'];
|
||||
@override
|
||||
List<String> get featsTeam =>
|
||||
const ['专业版全部功能', '10 个成员席位', '集中计费与管理', '优先客服'];
|
||||
|
||||
@override
|
||||
String get redeemTitle => '兑换 & 购买';
|
||||
@override
|
||||
@@ -293,7 +267,6 @@ class StringsZh extends AppText {
|
||||
String get chStore => '自助发卡商店';
|
||||
@override
|
||||
String get chEmail => '邮箱客服';
|
||||
|
||||
@override
|
||||
String get contactTitle => '联系我们';
|
||||
@override
|
||||
@@ -308,7 +281,6 @@ class StringsZh extends AppText {
|
||||
String get contactHoursTitle => '服务时间';
|
||||
@override
|
||||
String get contactHours => '每日 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => '极速 · 稳定 · 省心';
|
||||
@override
|
||||
@@ -328,8 +300,6 @@ class StringsZh extends AppText {
|
||||
@override
|
||||
String get codeLabel => '邮箱验证码';
|
||||
@override
|
||||
String codeSentTo(String email) => '验证码已发送至 $email';
|
||||
@override
|
||||
String get sendCode => '发送验证码';
|
||||
@override
|
||||
String get resend => '重新发送';
|
||||
@@ -343,7 +313,6 @@ class StringsZh extends AppText {
|
||||
String get forgotPw => '忘记密码?';
|
||||
@override
|
||||
String get tos => '继续即代表同意《服务条款》与《隐私政策》';
|
||||
|
||||
@override
|
||||
String get obSkip => '跳过';
|
||||
@override
|
||||
@@ -364,7 +333,6 @@ class StringsZh extends AppText {
|
||||
String get ob3Title => '安全 · 无日志';
|
||||
@override
|
||||
String get ob3Sub => '端到端加密,我们不记录你的任何浏览数据。';
|
||||
|
||||
@override
|
||||
String get purchaseTitle => '购买套餐';
|
||||
@override
|
||||
@@ -427,8 +395,6 @@ class StringsZh extends AppText {
|
||||
String get payUnsupportedRenderType => '暂不支持,请换支付方式';
|
||||
@override
|
||||
String get payNoActiveOrder => '暂无进行中的订单';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
String get payChannel => '支付渠道';
|
||||
@override
|
||||
@@ -467,8 +433,6 @@ class StringsZh extends AppText {
|
||||
String get orderRebuy => '重新购买';
|
||||
@override
|
||||
String get payMethodNezha => '哪吒支付';
|
||||
|
||||
// ── 邀请 / 通知 / 设置分组 / 订单过期(Spec ②③占位) ──
|
||||
@override
|
||||
String get inviteEntry => '邀请好友得会员';
|
||||
@override
|
||||
@@ -539,4 +503,37 @@ class StringsZh extends AppText {
|
||||
String get promoBadge => '限时优惠';
|
||||
@override
|
||||
String get promoOriginalPrice => '原价';
|
||||
@override
|
||||
List<String> get days7 => const ['一', '二', '三', '四', '五', '六', '日'];
|
||||
@override
|
||||
List<String> get featsFree => const [
|
||||
'仅 1 个基础节点',
|
||||
'每日 10 分钟时长',
|
||||
'使用前观看广告',
|
||||
'注册享 7 天免费试用',
|
||||
];
|
||||
@override
|
||||
List<String> get featsPro => const [
|
||||
'80+ 全球节点',
|
||||
'无限流量 · 极速',
|
||||
'5 台设备同时在线',
|
||||
'流媒体优化',
|
||||
];
|
||||
@override
|
||||
List<String> get featsTeam => const [
|
||||
'专业版全部功能',
|
||||
'10 个成员席位',
|
||||
'集中计费与管理',
|
||||
'优先客服',
|
||||
];
|
||||
@override
|
||||
String adRewarded(int minutes) => '已加 $minutes 分钟';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '切换到「$name」?';
|
||||
@override
|
||||
String autoConnectingTo(String name) => '正在自动连接到 $name…';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => '发现新版本 v$version';
|
||||
@override
|
||||
String codeSentTo(String email) => '验证码已发送至 $email';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env node
|
||||
// gen_l10n_dart.mjs — l10n 单源 codegen
|
||||
//
|
||||
// 真源:design/i18n/strings.json(getters + lists + methods × 6 语)。
|
||||
// 生成:client/lib/l10n/strings_{zh,en,ja,ko,ru,es}.dart(concrete AppText 实现)。
|
||||
// 手写保留:client/lib/l10n/app_text.dart(enum AppLang / abstract AppText 声明 /
|
||||
// AppLangLabel / AppLangMisc 逻辑)。Dart 编译器强制 concrete⊆abstract 兜底漂移。
|
||||
//
|
||||
// 用法:node design/codegen/gen_l10n_dart.mjs
|
||||
// 生成后自动 `dart format`,保证 CI「重生成零 diff」闸稳定(漂移闸 = 重生成 + git diff)。
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import url from 'node:url';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
|
||||
const ROOT = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '..', '..');
|
||||
const SRC = path.join(ROOT, 'design/i18n/strings.json');
|
||||
const OUT_DIR = path.join(ROOT, 'client/lib/l10n');
|
||||
|
||||
const LANGS = ['zh', 'en', 'ja', 'ko', 'ru', 'es'];
|
||||
const CLASS = { zh: 'StringsZh', en: 'StringsEn', ja: 'StringsJa', ko: 'StringsKo', ru: 'StringsRu', es: 'StringsEs' };
|
||||
const HEADER_NOTE = { zh: '中文', en: 'English', ja: '日本語', ko: '한국어', ru: 'Русский', es: 'Español' };
|
||||
|
||||
// 字面量转义(getter / list 元素):无插值,$ 一律转义为字面量。
|
||||
function litStr(s) {
|
||||
return s.replace(/\\/g, '\\\\').replace(/\$/g, '\\$').replace(/'/g, "\\'").replace(/\n/g, '\\n');
|
||||
}
|
||||
// 模板转义(method):保留 $var 插值,仅转义反斜杠/引号/换行。
|
||||
function tplStr(s) {
|
||||
return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n');
|
||||
}
|
||||
|
||||
const data = JSON.parse(fs.readFileSync(SRC, 'utf8'));
|
||||
const { getters, lists, methods } = data;
|
||||
|
||||
function genFile(lang) {
|
||||
const L = [];
|
||||
L.push(`// strings_${lang}.dart — ${HEADER_NOTE[lang]}文案(自动生成 · 勿手改)`);
|
||||
L.push('//');
|
||||
L.push('// GENERATED by design/codegen/gen_l10n_dart.mjs from design/i18n/strings.json.');
|
||||
L.push('// 改文案:改 strings.json 后重跑 codegen;新增 key 需同步 app_text.dart 抽象声明。');
|
||||
L.push(`import 'app_text.dart';`);
|
||||
L.push('');
|
||||
L.push(`class ${CLASS[lang]} extends AppText {`);
|
||||
L.push(` const ${CLASS[lang]}();`);
|
||||
L.push('');
|
||||
L.push(' @override');
|
||||
L.push(` AppLang get lang => AppLang.${lang};`);
|
||||
L.push('');
|
||||
for (const [k, m] of Object.entries(getters)) {
|
||||
L.push(' @override');
|
||||
L.push(` String get ${k} => '${litStr(m[lang])}';`);
|
||||
}
|
||||
for (const [k, m] of Object.entries(lists)) {
|
||||
const items = m[lang].map((x) => `'${litStr(x)}'`).join(', ');
|
||||
L.push(' @override');
|
||||
L.push(` List<String> get ${k} => const [${items}];`);
|
||||
}
|
||||
for (const [k, m] of Object.entries(methods)) {
|
||||
L.push(' @override');
|
||||
L.push(` String ${k}(${m.args}) => '${tplStr(m.tpl[lang])}';`);
|
||||
}
|
||||
L.push('}');
|
||||
L.push('');
|
||||
return L.join('\n');
|
||||
}
|
||||
|
||||
const written = [];
|
||||
for (const lang of LANGS) {
|
||||
const out = path.join(OUT_DIR, `strings_${lang}.dart`);
|
||||
fs.writeFileSync(out, genFile(lang));
|
||||
written.push(out);
|
||||
}
|
||||
|
||||
// dart format 定型,保证零 diff 闸稳定
|
||||
try {
|
||||
execFileSync('dart', ['format', ...written], { cwd: ROOT, stdio: 'inherit' });
|
||||
} catch (e) {
|
||||
console.warn('⚠ dart format 失败(dart 不在 PATH?),已生成未格式化文件:', e.message);
|
||||
}
|
||||
const g = Object.keys(getters).length, l = Object.keys(lists).length, me = Object.keys(methods).length;
|
||||
console.log(`✓ 生成 ${written.length} 份:getters=${g} lists=${l} methods=${me} × ${LANGS.length} 语`);
|
||||
@@ -1,55 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
# 从 6 份手写 strings_*.dart 抽取 l10n 到单源 JSON。只读,不改 Dart。
|
||||
import re, json, sys, os
|
||||
# 完整抽取 l10n → 单源 JSON:getters(String get) + lists(List<String> get) + methods(带参)。
|
||||
# 只读 Dart,不改。与已提交 strings.json 的 getters/methods 校验无回归,新增 lists 段。
|
||||
import re, json, os
|
||||
|
||||
BASE = "/Users/wangjia/code/pangolin/.claude/worktrees/configurable-proxy/client/lib/l10n"
|
||||
OUT = "/Users/wangjia/code/pangolin/.claude/worktrees/configurable-proxy/design/i18n/strings.json"
|
||||
LANGS = ["zh", "en", "ja", "ko", "ru", "es"]
|
||||
|
||||
# Dart 字符串字面量:单引号或双引号(各自允许 \ 转义);getter 与带参方法
|
||||
STR = r"""(?:'((?:[^'\\]|\\.)*)'|"((?:[^"\\]|\\.)*)")"""
|
||||
STR_RE = re.compile(STR)
|
||||
GET_RE = re.compile(r"String\s+get\s+(\w+)\s*=>\s*" + STR + r"\s*;")
|
||||
MET_RE = re.compile(r"String\s+(\w+)\(([^)]*)\)\s*=>\s*" + STR + r"\s*;")
|
||||
# List<String> get name => const [ ... ]; —— 跨行,惰性到第一个 '];'
|
||||
LIST_RE = re.compile(r"List<String>\s+get\s+(\w+)\s*=>\s*const\s*\[(.*?)\]\s*;", re.DOTALL)
|
||||
|
||||
|
||||
def unescape(s):
|
||||
return s.replace("\\'", "'").replace('\\"', '"').replace('\\\\', '\\').replace('\\n', '\n').replace('\\$', '$')
|
||||
return (s.replace("\\'", "'").replace('\\"', '"')
|
||||
.replace('\\n', '\n').replace('\\$', '$').replace('\\\\', '\\'))
|
||||
|
||||
getters = {} # key -> {lang: val}
|
||||
methods = {} # key -> {args, tpl:{lang:val}}
|
||||
counts = {}
|
||||
|
||||
getters, lists, methods, counts = {}, {}, {}, {}
|
||||
for lang in LANGS:
|
||||
path = os.path.join(BASE, f"strings_{lang}.dart")
|
||||
src = open(path, encoding="utf-8").read()
|
||||
g = 0
|
||||
for m in GET_RE.finditer(src):
|
||||
k = m.group(1); v = unescape(m.group(2) if m.group(2) is not None else m.group(3))
|
||||
getters.setdefault(k, {})[lang] = v
|
||||
g += 1
|
||||
mm = 0
|
||||
for m in MET_RE.finditer(src):
|
||||
k = m.group(1); args = m.group(2).strip()
|
||||
v = unescape(m.group(3) if m.group(3) is not None else m.group(4))
|
||||
src = open(os.path.join(BASE, f"strings_{lang}.dart"), encoding="utf-8").read()
|
||||
g = m = ln = 0
|
||||
for mt in GET_RE.finditer(src):
|
||||
k = mt.group(1)
|
||||
v = unescape(mt.group(2) if mt.group(2) is not None else mt.group(3))
|
||||
getters.setdefault(k, {})[lang] = v; g += 1
|
||||
for mt in MET_RE.finditer(src):
|
||||
k = mt.group(1); args = mt.group(2).strip()
|
||||
v = unescape(mt.group(3) if mt.group(3) is not None else mt.group(4))
|
||||
methods.setdefault(k, {"args": args, "tpl": {}})
|
||||
methods[k]["tpl"][lang] = v
|
||||
mm += 1
|
||||
counts[lang] = {"getters": g, "methods": mm}
|
||||
methods[k]["tpl"][lang] = v; m += 1
|
||||
for mt in LIST_RE.finditer(src):
|
||||
k = mt.group(1); body = mt.group(2)
|
||||
items = [unescape(a if a is not None else b) for a, b in STR_RE.findall(body)]
|
||||
lists.setdefault(k, {})[lang] = items; ln += 1
|
||||
counts[lang] = {"getters": g, "methods": m, "lists": ln}
|
||||
|
||||
# 完整性校验:每个 key 六语齐全
|
||||
# 完整性:每 key 六语齐全
|
||||
missing = []
|
||||
for k, d in getters.items():
|
||||
for l in LANGS:
|
||||
if l not in d: missing.append(f"getter {k}:{l}")
|
||||
for k, d in lists.items():
|
||||
for l in LANGS:
|
||||
if l not in d: missing.append(f"list {k}:{l}")
|
||||
for k, d in methods.items():
|
||||
for l in LANGS:
|
||||
if l not in d["tpl"]: missing.append(f"method {k}:{l}")
|
||||
|
||||
out = {"_note": "l10n 单源(唯一真相源)。codegen 生成 client/lib/l10n/strings_*.dart + app_text.dart + 原型读同源。勿手改生成物。",
|
||||
"getters": getters, "methods": methods}
|
||||
os.makedirs("/Users/wangjia/code/pangolin/.claude/worktrees/configurable-proxy/design/i18n", exist_ok=True)
|
||||
outpath = "/Users/wangjia/code/pangolin/.claude/worktrees/configurable-proxy/design/i18n/strings.json"
|
||||
json.dump(out, open(outpath, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
|
||||
# 与已提交 JSON 的 getters/methods 校验无回归
|
||||
old = json.load(open(OUT, encoding="utf-8")) if os.path.exists(OUT) else {}
|
||||
regress = []
|
||||
if old:
|
||||
if set(old.get("getters", {})) != set(getters):
|
||||
regress.append("getter 键集变化")
|
||||
for k, d in old.get("getters", {}).items():
|
||||
if getters.get(k) != d: regress.append(f"getter 值变化:{k}")
|
||||
if set(old.get("methods", {})) != set(methods):
|
||||
regress.append("method 键集变化")
|
||||
|
||||
print("每语言抽取数:", counts)
|
||||
print(f"合并后:getters={len(getters)} methods={len(methods)}")
|
||||
print(f"缺失(应为0):{len(missing)}", missing[:5])
|
||||
print("输出:", outpath)
|
||||
out = {"_note": "l10n 单源(唯一真相源)。design/codegen/gen_l10n_dart.mjs 生成 client/lib/l10n/strings_*.dart;"
|
||||
"原型 serve.mjs 读同源。勿手改生成物;abstract 声明由 app_text.dart 手写(编译器强制 concrete⊆abstract)。",
|
||||
"getters": getters, "lists": lists, "methods": methods}
|
||||
json.dump(out, open(OUT, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
|
||||
|
||||
print("每语言:", counts)
|
||||
print(f"合并:getters={len(getters)} lists={len(lists)} methods={len(methods)}")
|
||||
print(f"缺失(应0):{len(missing)}", missing[:5])
|
||||
print(f"回归(应0):{len(regress)}", regress[:5])
|
||||
print("lists 键:", list(lists))
|
||||
print("输出:", OUT)
|
||||
|
||||
+173
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"_note": "l10n 单源(唯一真相源)。codegen 生成 client/lib/l10n/strings_*.dart + app_text.dart + 原型读同源。勿手改生成物。",
|
||||
"_note": "l10n 单源(唯一真相源)。design/codegen/gen_l10n_dart.mjs 生成 client/lib/l10n/strings_*.dart;原型 serve.mjs 读同源。勿手改生成物;abstract 声明由 app_text.dart 手写(编译器强制 concrete⊆abstract)。",
|
||||
"getters": {
|
||||
"brand": {
|
||||
"zh": "穿山甲",
|
||||
@@ -1970,6 +1970,178 @@
|
||||
"es": "Antes"
|
||||
}
|
||||
},
|
||||
"lists": {
|
||||
"days7": {
|
||||
"zh": [
|
||||
"一",
|
||||
"二",
|
||||
"三",
|
||||
"四",
|
||||
"五",
|
||||
"六",
|
||||
"日"
|
||||
],
|
||||
"en": [
|
||||
"M",
|
||||
"T",
|
||||
"W",
|
||||
"T",
|
||||
"F",
|
||||
"S",
|
||||
"S"
|
||||
],
|
||||
"ja": [
|
||||
"月",
|
||||
"火",
|
||||
"水",
|
||||
"木",
|
||||
"金",
|
||||
"土",
|
||||
"日"
|
||||
],
|
||||
"ko": [
|
||||
"월",
|
||||
"화",
|
||||
"수",
|
||||
"목",
|
||||
"금",
|
||||
"토",
|
||||
"일"
|
||||
],
|
||||
"ru": [
|
||||
"Пн",
|
||||
"Вт",
|
||||
"Ср",
|
||||
"Чт",
|
||||
"Пт",
|
||||
"Сб",
|
||||
"Вс"
|
||||
],
|
||||
"es": [
|
||||
"L",
|
||||
"M",
|
||||
"X",
|
||||
"J",
|
||||
"V",
|
||||
"S",
|
||||
"D"
|
||||
]
|
||||
},
|
||||
"featsFree": {
|
||||
"zh": [
|
||||
"仅 1 个基础节点",
|
||||
"每日 10 分钟时长",
|
||||
"使用前观看广告",
|
||||
"注册享 7 天免费试用"
|
||||
],
|
||||
"en": [
|
||||
"1 basic node only",
|
||||
"10 min per day",
|
||||
"Watch an ad to start",
|
||||
"7-day free trial on sign-up"
|
||||
],
|
||||
"ja": [
|
||||
"基本ノード1つのみ",
|
||||
"1日10分",
|
||||
"広告を見て開始",
|
||||
"登録で7日間無料トライアル"
|
||||
],
|
||||
"ko": [
|
||||
"기본 노드 1개",
|
||||
"하루 10분",
|
||||
"광고 시청 후 시작",
|
||||
"가입 시 7일 무료 체험"
|
||||
],
|
||||
"ru": [
|
||||
"Только 1 базовый узел",
|
||||
"10 минут в день",
|
||||
"Смотрите рекламу, чтобы начать",
|
||||
"7 дней бесплатно при регистрации"
|
||||
],
|
||||
"es": [
|
||||
"Solo 1 nodo básico",
|
||||
"10 min al día",
|
||||
"Mira un anuncio para empezar",
|
||||
"Prueba gratis de 7 días al registrarte"
|
||||
]
|
||||
},
|
||||
"featsPro": {
|
||||
"zh": [
|
||||
"80+ 全球节点",
|
||||
"无限流量 · 极速",
|
||||
"5 台设备同时在线",
|
||||
"流媒体优化"
|
||||
],
|
||||
"en": [
|
||||
"80+ locations",
|
||||
"Unlimited · fast",
|
||||
"5 devices",
|
||||
"Streaming optimized"
|
||||
],
|
||||
"ja": [
|
||||
"80以上の拠点",
|
||||
"無制限 · 高速",
|
||||
"5台のデバイス",
|
||||
"ストリーミング最適化"
|
||||
],
|
||||
"ko": [
|
||||
"80개 이상 지역",
|
||||
"무제한 · 빠른 속도",
|
||||
"기기 5대",
|
||||
"스트리밍 최적화"
|
||||
],
|
||||
"ru": [
|
||||
"80+ локаций",
|
||||
"Безлимитно · быстро",
|
||||
"5 устройств",
|
||||
"Оптимизация для стриминга"
|
||||
],
|
||||
"es": [
|
||||
"Más de 80 ubicaciones",
|
||||
"Ilimitado · rápido",
|
||||
"5 dispositivos",
|
||||
"Optimizado para streaming"
|
||||
]
|
||||
},
|
||||
"featsTeam": {
|
||||
"zh": [
|
||||
"专业版全部功能",
|
||||
"10 个成员席位",
|
||||
"集中计费与管理",
|
||||
"优先客服"
|
||||
],
|
||||
"en": [
|
||||
"Everything in Pro",
|
||||
"10 seats",
|
||||
"Central billing",
|
||||
"Priority support"
|
||||
],
|
||||
"ja": [
|
||||
"Proのすべての機能",
|
||||
"10ライセンス",
|
||||
"一括請求",
|
||||
"優先サポート"
|
||||
],
|
||||
"ko": [
|
||||
"Pro의 모든 기능",
|
||||
"10인 좌석",
|
||||
"통합 결제",
|
||||
"우선 지원"
|
||||
],
|
||||
"ru": [
|
||||
"Всё из Pro",
|
||||
"10 мест",
|
||||
"Единый счёт",
|
||||
"Приоритетная поддержка"
|
||||
],
|
||||
"es": [
|
||||
"Todo lo de Pro",
|
||||
"10 plazas",
|
||||
"Facturación centralizada",
|
||||
"Soporte prioritario"
|
||||
]
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"adRewarded": {
|
||||
"args": "int minutes",
|
||||
|
||||
Reference in New Issue
Block a user