feat(client/i18n): 新增 日/韩/俄/西 4 语,默认语种改英文
- AppLang enum 扩为 zh/en/ja/ko/ru/es;新增 strings_{ja,ko,ru,es}.dart
(各 161 条,照 strings_en.dart 结构逐条翻译,无红线词,scan-redline 过)
- app_providers: localeProvider 默认 AppLang.en(国际化默认);appTextProvider
三元改 switch 全覆盖 6 语
- AppLang.nativeLabel 扩展(各语言本地名);settings/account 两处 _LangSwitch
段控改下拉(6 语横排会挤)
- flutter analyze lib 仅剩 2 个既有 withOpacity deprecation info
注:节点/套餐名、错误消息等 ~11 处数据驱动的 zh/en 三元暂回退 en(非崩溃,
后续可扩多语字段)。localeProvider 暂无持久化(沿用原设计,后续可加)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
@@ -11,7 +11,28 @@
|
||||
// 接口形态与 .arb 一致,后续可平滑迁移到官方 l10n 工具链。
|
||||
|
||||
/// 受支持语言。单显——任一时刻只渲染其一。
|
||||
enum AppLang { zh, en }
|
||||
/// zh 中文 · en English · ja 日本語 · ko 한국어 · ru Русский · es Español。
|
||||
enum AppLang { zh, en, ja, ko, ru, es }
|
||||
|
||||
/// 语言的本地名(用于语言切换 UI,各语言用自身文字标注)。
|
||||
extension AppLangLabel on AppLang {
|
||||
String get nativeLabel {
|
||||
switch (this) {
|
||||
case AppLang.zh:
|
||||
return '中文';
|
||||
case AppLang.en:
|
||||
return 'English';
|
||||
case AppLang.ja:
|
||||
return '日本語';
|
||||
case AppLang.ko:
|
||||
return '한국어';
|
||||
case AppLang.ru:
|
||||
return 'Русский';
|
||||
case AppLang.es:
|
||||
return 'Español';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 全部界面文案的抽象契约。zh / en 各实现一份。
|
||||
abstract class AppText {
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
// strings_es.dart — Recursos de texto en español (visualización monolingüe)
|
||||
//
|
||||
// Palabras prohibidas: VPN / traspasar muros / red científica / romper el bloqueo / cruce libre / Go anywhere.
|
||||
// Posicionamiento: aceleración de red / optimización de la experiencia + privacidad.
|
||||
// La numeración de planes sigue design/CLAUDE.md §7.
|
||||
import 'app_text.dart';
|
||||
|
||||
class StringsEs extends AppText {
|
||||
const StringsEs();
|
||||
|
||||
@override
|
||||
AppLang get lang => AppLang.es;
|
||||
|
||||
@override
|
||||
String get brand => 'Pangolin';
|
||||
@override
|
||||
String get online => '● En línea';
|
||||
@override
|
||||
String get offline => '○ Desconectado';
|
||||
@override
|
||||
String get capOff => 'Toca para conectar';
|
||||
@override
|
||||
String get capConnecting => 'Conectando…';
|
||||
@override
|
||||
String get capOn => 'Conectado · Cifrado';
|
||||
@override
|
||||
String get connectNow => 'CONECTAR';
|
||||
@override
|
||||
String get secure => 'SEGURO';
|
||||
|
||||
@override
|
||||
String get tabConnect => 'Conectar';
|
||||
@override
|
||||
String get tabServers => 'Servidores';
|
||||
@override
|
||||
String get tabStats => 'Estadísticas';
|
||||
@override
|
||||
String get tabMe => 'Cuenta';
|
||||
|
||||
@override
|
||||
String get currentNode => 'Nodo actual';
|
||||
@override
|
||||
String get download => 'Bajada';
|
||||
@override
|
||||
String get upload => 'Subida';
|
||||
@override
|
||||
String get latency => 'Ping';
|
||||
|
||||
@override
|
||||
String get quotaToday => 'Restante hoy';
|
||||
@override
|
||||
String get minutes => 'min';
|
||||
@override
|
||||
String get quotaFree => 'Gratis · 10 min/día';
|
||||
@override
|
||||
String get planFreeTag => 'Gratis';
|
||||
@override
|
||||
String get watchAd => 'Mira un anuncio para empezar';
|
||||
@override
|
||||
String get adUnlocked => 'Desbloqueado por hoy';
|
||||
@override
|
||||
String get quotaLeftLabel => 'Restante';
|
||||
@override
|
||||
String get quotaUsedUp => 'Tiempo gratis diario agotado';
|
||||
@override
|
||||
String get watchAdMore => 'Mira un anuncio para más tiempo';
|
||||
@override
|
||||
String get quotaExhaustedNotice => 'Tiempo gratis diario agotado. Mira un anuncio para añadir tiempo o mejora tu plan.';
|
||||
@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.';
|
||||
@override
|
||||
String get gotIt => 'Entendido';
|
||||
|
||||
@override
|
||||
String get chooseNode => 'Elegir servidor';
|
||||
@override
|
||||
String get searchPh => 'Buscar país / ciudad';
|
||||
@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.';
|
||||
@override
|
||||
String get nodeSwitchConfirm => 'Cambiar';
|
||||
@override
|
||||
String get nodeUnhealthySwitched => 'Nodo actual inaccesible: cambiado automáticamente';
|
||||
@override
|
||||
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
|
||||
String get trafficMonth => 'Tráfico';
|
||||
@override
|
||||
String get avgPing => 'Ping medio';
|
||||
@override
|
||||
String get durMonth => 'Tiempo';
|
||||
@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';
|
||||
@override
|
||||
String get periodMonth => 'Este mes';
|
||||
@override
|
||||
String get periodWeek => 'Esta semana';
|
||||
@override
|
||||
String get periodToday => 'Hoy';
|
||||
@override
|
||||
String get statDown => 'Descarga';
|
||||
@override
|
||||
String get statUp => 'Subida';
|
||||
@override
|
||||
String get statDuration => 'Duración';
|
||||
@override
|
||||
String get chartTwoWeeks => 'Últimas 2 semanas';
|
||||
@override
|
||||
String get allDevices => 'Todos los dispositivos';
|
||||
|
||||
@override
|
||||
String get meTitle => 'Cuenta';
|
||||
@override
|
||||
String get proMember => 'Miembro PRO';
|
||||
@override
|
||||
String get freePlanName => 'Gratis';
|
||||
@override
|
||||
String get expires => 'Vence';
|
||||
@override
|
||||
String get upgradeBtn => 'Renovar / Mejorar';
|
||||
@override
|
||||
String get accInfoTitle => 'Información de la cuenta';
|
||||
@override
|
||||
String get accEmail => 'Correo';
|
||||
@override
|
||||
String get accPassword => 'Contraseña';
|
||||
@override
|
||||
String get accChange => 'Cambiar';
|
||||
@override
|
||||
String get deviceLimitTitle => 'Límite de dispositivos alcanzado';
|
||||
@override
|
||||
String get deviceLimitDesc => 'Tu plan permite hasta %s dispositivos. Elimina uno para continuar.';
|
||||
@override
|
||||
String get deviceLimitRemoveOldest => 'Eliminar el menos usado recientemente';
|
||||
@override
|
||||
String get myDevices => 'Mis dispositivos';
|
||||
@override
|
||||
String get devicesSub => 'Hasta 3 dispositivos en PRO';
|
||||
@override
|
||||
String get thisDevice => 'Este dispositivo';
|
||||
@override
|
||||
String get remove => 'Eliminar';
|
||||
@override
|
||||
String get devOnline => 'En línea';
|
||||
@override
|
||||
String get devOffline => 'Desconectado';
|
||||
@override
|
||||
String get devLastOnline => 'Última conexión';
|
||||
@override
|
||||
String get devNeverLogin => 'Nunca ha iniciado sesión';
|
||||
@override
|
||||
String get devForceLogout => 'Forzar cierre de sesión';
|
||||
@override
|
||||
String get devClearLogin => 'Borrar datos de sesión';
|
||||
@override
|
||||
String get devRename => 'Renombrar';
|
||||
@override
|
||||
String get devRenameHint => 'Nombre del dispositivo';
|
||||
@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.';
|
||||
@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.';
|
||||
@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.';
|
||||
@override
|
||||
String get sessionRevokedOk => 'Iniciar sesión de nuevo';
|
||||
@override
|
||||
String get redeemEntry => 'Canjear y comprar';
|
||||
@override
|
||||
String get contactEntry => 'Contáctanos';
|
||||
@override
|
||||
String get signOut => 'Cerrar sesión';
|
||||
@override
|
||||
String get language => 'Idioma';
|
||||
@override
|
||||
String get darkAppearance => 'Apariencia oscura';
|
||||
@override
|
||||
String get stateOn => 'Activado';
|
||||
@override
|
||||
String get followLight => 'Desactivado';
|
||||
@override
|
||||
String get protocol => 'Protocolo';
|
||||
@override
|
||||
String get settingsTitle => 'Ajustes';
|
||||
@override
|
||||
String get autostart => 'Iniciar al arrancar';
|
||||
@override
|
||||
String get autostartSub => 'Ejecutar automáticamente al iniciar el sistema';
|
||||
@override
|
||||
String get autoConnect => 'Conexión automática';
|
||||
@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';
|
||||
@override
|
||||
String get killSwitch => 'Kill Switch';
|
||||
@override
|
||||
String get killSwitchSub => 'Bloquea el tráfico si se cae el enlace';
|
||||
@override
|
||||
String get checkUpdate => 'Buscar actualizaciones';
|
||||
@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.';
|
||||
@override
|
||||
String get updateLater => 'Más tarde';
|
||||
@override
|
||||
String get updateDownload => 'Descargar actualización';
|
||||
@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';
|
||||
|
||||
@override
|
||||
String get choosePlan => 'Elegir plan';
|
||||
@override
|
||||
String get freePlan => 'Gratis';
|
||||
@override
|
||||
String get proPlan => 'Pro';
|
||||
@override
|
||||
String get teamPlan => 'Equipo';
|
||||
@override
|
||||
String get perMonth => '/mes';
|
||||
@override
|
||||
String get current => 'Actual';
|
||||
@override
|
||||
String get upgrade => 'Mejorar';
|
||||
@override
|
||||
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
|
||||
String get redeemCodeTitle => 'Canjear un código';
|
||||
@override
|
||||
String get redeemPh => 'Introduce el código de activación';
|
||||
@override
|
||||
String get redeemBtn => 'Canjear';
|
||||
@override
|
||||
String get redeemOk => 'Activado · PRO desbloqueado';
|
||||
@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:';
|
||||
@override
|
||||
String get chStore => 'Tienda autoservicio';
|
||||
@override
|
||||
String get chEmail => 'Soporte por correo';
|
||||
|
||||
@override
|
||||
String get contactTitle => 'Contáctanos';
|
||||
@override
|
||||
String get contactIntro =>
|
||||
'¿Necesitas ayuda? Escríbenos por cualquiera de los canales de abajo: solemos responder en minutos.';
|
||||
@override
|
||||
String get contactEmail => 'Soporte por correo';
|
||||
@override
|
||||
String get contactStore => 'Tienda autoservicio';
|
||||
@override
|
||||
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
|
||||
String get tabLogin => 'Iniciar sesión';
|
||||
@override
|
||||
String get tabRegister => 'Registrarse';
|
||||
@override
|
||||
String get emailLabel => 'Correo';
|
||||
@override
|
||||
String get emailPh => 'tu@correo.com';
|
||||
@override
|
||||
String get pwLabel => 'Contraseña';
|
||||
@override
|
||||
String get pwPh => 'Introduce la contraseña';
|
||||
@override
|
||||
String get setPwPh => 'Crea una contraseña (para acceso multidispositivo)';
|
||||
@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';
|
||||
@override
|
||||
String get doLogin => 'Iniciar sesión';
|
||||
@override
|
||||
String get doNext => 'Siguiente';
|
||||
@override
|
||||
String get doCreate => 'Crear cuenta';
|
||||
@override
|
||||
String get forgotPw => '¿Olvidaste la contraseña?';
|
||||
@override
|
||||
String get tos => 'Al continuar aceptas nuestros Términos y Política de privacidad';
|
||||
|
||||
@override
|
||||
String get obSkip => 'Omitir';
|
||||
@override
|
||||
String get obNext => 'Siguiente';
|
||||
@override
|
||||
String get obAllow => 'Permitir y continuar';
|
||||
@override
|
||||
String get obStart => 'Empezar';
|
||||
@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.';
|
||||
@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.';
|
||||
@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.';
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
// strings_ja.dart — 日本語の文言リソース(単一言語表示)
|
||||
//
|
||||
// 禁止ワード: VPN / 翻墙 / 科学上网 / 突破封锁 / 自由穿越 / Go anywhere。
|
||||
// ポジショニング: ネットワーク高速化 / 体験最適化 + プライバシー。
|
||||
// プラン番号は design/CLAUDE.md §7 に準拠。
|
||||
import 'app_text.dart';
|
||||
|
||||
class StringsJa extends AppText {
|
||||
const StringsJa();
|
||||
|
||||
@override
|
||||
AppLang get lang => AppLang.ja;
|
||||
|
||||
@override
|
||||
String get brand => 'Pangolin';
|
||||
@override
|
||||
String get online => '● オンライン';
|
||||
@override
|
||||
String get offline => '○ オフライン';
|
||||
@override
|
||||
String get capOff => 'タップして接続';
|
||||
@override
|
||||
String get capConnecting => '接続中…';
|
||||
@override
|
||||
String get capOn => '接続済み · 暗号化';
|
||||
@override
|
||||
String get connectNow => '接続';
|
||||
@override
|
||||
String get secure => 'セキュア';
|
||||
|
||||
@override
|
||||
String get tabConnect => '接続';
|
||||
@override
|
||||
String get tabServers => 'サーバー';
|
||||
@override
|
||||
String get tabStats => '統計';
|
||||
@override
|
||||
String get tabMe => 'アカウント';
|
||||
|
||||
@override
|
||||
String get currentNode => '現在のノード';
|
||||
@override
|
||||
String get download => '下り';
|
||||
@override
|
||||
String get upload => '上り';
|
||||
@override
|
||||
String get latency => 'Ping';
|
||||
|
||||
@override
|
||||
String get quotaToday => '本日の残り';
|
||||
@override
|
||||
String get minutes => '分';
|
||||
@override
|
||||
String get quotaFree => '無料 · 1日10分';
|
||||
@override
|
||||
String get planFreeTag => '無料';
|
||||
@override
|
||||
String get watchAd => '広告を見て開始';
|
||||
@override
|
||||
String get adUnlocked => '本日は利用可能';
|
||||
@override
|
||||
String get quotaLeftLabel => '残り';
|
||||
@override
|
||||
String get quotaUsedUp => '本日の無料時間を使い切りました';
|
||||
@override
|
||||
String get watchAdMore => '広告を見て時間を追加';
|
||||
@override
|
||||
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 => 'モバイルアプリで広告を見ると時間を追加できます。無制限に使うにはアップグレードしてください。';
|
||||
@override
|
||||
String get gotIt => '了解';
|
||||
|
||||
@override
|
||||
String get chooseNode => 'サーバーを選択';
|
||||
@override
|
||||
String get searchPh => '国・都市を検索';
|
||||
@override
|
||||
String get smartSelect => 'スマート選択';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '$name に切り替えますか?';
|
||||
@override
|
||||
String get nodeSwitchBody => '現在の接続を切断し、このノードに再接続します。';
|
||||
@override
|
||||
String get nodeSwitchConfirm => '切り替える';
|
||||
@override
|
||||
String get nodeUnhealthySwitched => '現在のノードに接続できないため、自動的に切り替えました';
|
||||
@override
|
||||
String get nodeUnhealthyError => '現在のノードに接続できません。再接続するかノードを切り替えてください';
|
||||
@override
|
||||
String get nodeReconnecting => 'ネットワークが不安定です。再接続中…';
|
||||
@override
|
||||
String get smartSub => 'お使いのネットワークに最適なノードを選びます';
|
||||
@override
|
||||
String get recommended => 'おすすめ';
|
||||
|
||||
@override
|
||||
String get statsTitle => '統計';
|
||||
@override
|
||||
String get trafficMonth => '通信量';
|
||||
@override
|
||||
String get avgPing => '平均Ping';
|
||||
@override
|
||||
String get durMonth => '接続時間';
|
||||
@override
|
||||
String get weekTraffic => '今週 (GB)';
|
||||
@override
|
||||
List<String> get days7 => const ['月', '火', '水', '木', '金', '土', '日'];
|
||||
@override
|
||||
String get byDevice => 'デバイス別';
|
||||
@override
|
||||
String get noDeviceUsage => 'デバイスの利用履歴はまだありません';
|
||||
@override
|
||||
String get periodMonth => '今月';
|
||||
@override
|
||||
String get periodWeek => '今週';
|
||||
@override
|
||||
String get periodToday => '今日';
|
||||
@override
|
||||
String get statDown => 'ダウンロード';
|
||||
@override
|
||||
String get statUp => 'アップロード';
|
||||
@override
|
||||
String get statDuration => '接続時間';
|
||||
@override
|
||||
String get chartTwoWeeks => '過去2週間';
|
||||
@override
|
||||
String get allDevices => 'すべてのデバイス';
|
||||
|
||||
@override
|
||||
String get meTitle => 'アカウント';
|
||||
@override
|
||||
String get proMember => 'PRO会員';
|
||||
@override
|
||||
String get freePlanName => '無料';
|
||||
@override
|
||||
String get expires => '有効期限';
|
||||
@override
|
||||
String get upgradeBtn => '更新 / アップグレード';
|
||||
@override
|
||||
String get accInfoTitle => 'アカウント情報';
|
||||
@override
|
||||
String get accEmail => 'メールアドレス';
|
||||
@override
|
||||
String get accPassword => 'パスワード';
|
||||
@override
|
||||
String get accChange => '変更';
|
||||
@override
|
||||
String get deviceLimitTitle => 'デバイス数の上限に達しました';
|
||||
@override
|
||||
String get deviceLimitDesc => '現在のプランでは最大 %s 台まで利用できます。続けるには1台を削除してください。';
|
||||
@override
|
||||
String get deviceLimitRemoveOldest => '最後に使用したのが最も古いデバイスを削除';
|
||||
@override
|
||||
String get myDevices => 'マイデバイス';
|
||||
@override
|
||||
String get devicesSub => 'PROでは最大3台まで';
|
||||
@override
|
||||
String get thisDevice => 'このデバイス';
|
||||
@override
|
||||
String get remove => '削除';
|
||||
@override
|
||||
String get devOnline => 'オンライン';
|
||||
@override
|
||||
String get devOffline => 'オフライン';
|
||||
@override
|
||||
String get devLastOnline => '最終オンライン';
|
||||
@override
|
||||
String get devNeverLogin => 'ログイン履歴なし';
|
||||
@override
|
||||
String get devForceLogout => '強制ログアウト';
|
||||
@override
|
||||
String get devClearLogin => 'ログイン情報を消去';
|
||||
@override
|
||||
String get devRename => '名前を変更';
|
||||
@override
|
||||
String get devRenameHint => 'デバイス名';
|
||||
@override
|
||||
String get devSave => '保存';
|
||||
@override
|
||||
String get devForceLogoutConfirm => 'このデバイスのログインセッションを無効にします。オフラインになり、再度サインインが必要になります。デバイスはリストに残ります。';
|
||||
@override
|
||||
String get devClearLoginConfirm => 'このデバイスをリストから完全に削除し、ログインセッションとデータ通信の認証情報を無効にします。再び利用するには、もう一度サインインが必要です。';
|
||||
@override
|
||||
String get devCancel => 'キャンセル';
|
||||
@override
|
||||
String get sessionRevokedTitle => 'このデバイスはサインアウトされました';
|
||||
@override
|
||||
String get sessionRevokedBody => '別のデバイスから、このデバイスがサインアウトされました。もう一度サインインしてください。';
|
||||
@override
|
||||
String get sessionRevokedOk => '再度サインイン';
|
||||
@override
|
||||
String get redeemEntry => 'コード引き換え・購入';
|
||||
@override
|
||||
String get contactEntry => 'お問い合わせ';
|
||||
@override
|
||||
String get signOut => 'サインアウト';
|
||||
@override
|
||||
String get language => '言語';
|
||||
@override
|
||||
String get darkAppearance => 'ダークモード';
|
||||
@override
|
||||
String get stateOn => 'オン';
|
||||
@override
|
||||
String get followLight => 'オフ';
|
||||
@override
|
||||
String get protocol => 'プロトコル';
|
||||
@override
|
||||
String get settingsTitle => '設定';
|
||||
@override
|
||||
String get autostart => 'スタートアップ時に起動';
|
||||
@override
|
||||
String get autostartSub => 'システム起動時に自動的に実行';
|
||||
@override
|
||||
String get autoConnect => '自動接続';
|
||||
@override
|
||||
String get autoConnectSub => '起動時に自動的に接続';
|
||||
@override
|
||||
String autoConnectingTo(String name) => '$name に自動接続中…';
|
||||
@override
|
||||
String get smartRoute => 'スマートルーティング';
|
||||
@override
|
||||
String get smartRouteSub => '海外は高速化、国内は直接接続';
|
||||
@override
|
||||
String get killSwitch => 'キルスイッチ';
|
||||
@override
|
||||
String get killSwitchSub => '接続が切れたら通信を遮断';
|
||||
@override
|
||||
String get checkUpdate => 'アップデートを確認';
|
||||
@override
|
||||
String get webUserCenter => 'ユーザーセンター (Web)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => '新しいバージョン v$version が利用可能です';
|
||||
@override
|
||||
String get updateNotesFallback => 'このアップデートには不具合の修正と安定性の向上が含まれています。';
|
||||
@override
|
||||
String get updateLater => '後で';
|
||||
@override
|
||||
String get updateDownload => 'アップデートをダウンロード';
|
||||
@override
|
||||
String get updateUpToDate => '最新の状態です';
|
||||
@override
|
||||
String get updateCheckFailed => 'アップデートの確認に失敗しました。後でもう一度お試しください';
|
||||
|
||||
@override
|
||||
String get choosePlan => 'プランを選択';
|
||||
@override
|
||||
String get freePlan => '無料';
|
||||
@override
|
||||
String get proPlan => 'Pro';
|
||||
@override
|
||||
String get teamPlan => 'Team';
|
||||
@override
|
||||
String get perMonth => '/月';
|
||||
@override
|
||||
String get current => '現在';
|
||||
@override
|
||||
String get upgrade => 'アップグレード';
|
||||
@override
|
||||
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
|
||||
String get redeemCodeTitle => 'コードを引き換える';
|
||||
@override
|
||||
String get redeemPh => 'アクティベーションコードを入力';
|
||||
@override
|
||||
String get redeemBtn => '引き換える';
|
||||
@override
|
||||
String get redeemOk => '認証完了 · PROが利用可能に';
|
||||
@override
|
||||
String get buyTitle => '購入方法';
|
||||
@override
|
||||
String get buySub => 'アプリ内決済はご利用いただけません。以下からコードを入手してください:';
|
||||
@override
|
||||
String get chStore => 'セルフサービスストア';
|
||||
@override
|
||||
String get chEmail => 'メールサポート';
|
||||
|
||||
@override
|
||||
String get contactTitle => 'お問い合わせ';
|
||||
@override
|
||||
String get contactIntro =>
|
||||
'お困りですか?以下のいずれかの方法でお問い合わせください。通常は数分以内に返信します。';
|
||||
@override
|
||||
String get contactEmail => 'メールサポート';
|
||||
@override
|
||||
String get contactStore => 'セルフサービスストア';
|
||||
@override
|
||||
String get contactHoursTitle => 'サポート時間';
|
||||
@override
|
||||
String get contactHours => '毎日 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => '高速 · 安定 · かんたん';
|
||||
@override
|
||||
String get tabLogin => 'ログイン';
|
||||
@override
|
||||
String get tabRegister => '新規登録';
|
||||
@override
|
||||
String get emailLabel => 'メールアドレス';
|
||||
@override
|
||||
String get emailPh => 'your@email.com';
|
||||
@override
|
||||
String get pwLabel => 'パスワード';
|
||||
@override
|
||||
String get pwPh => 'パスワードを入力';
|
||||
@override
|
||||
String get setPwPh => 'パスワードを設定(複数デバイスでのログイン用)';
|
||||
@override
|
||||
String get codeLabel => '確認コード';
|
||||
@override
|
||||
String codeSentTo(String email) => '$email に確認コードを送信しました';
|
||||
@override
|
||||
String get sendCode => 'コードを送信';
|
||||
@override
|
||||
String get resend => '再送信';
|
||||
@override
|
||||
String get doLogin => 'ログイン';
|
||||
@override
|
||||
String get doNext => '次へ';
|
||||
@override
|
||||
String get doCreate => 'アカウントを作成';
|
||||
@override
|
||||
String get forgotPw => 'パスワードをお忘れですか?';
|
||||
@override
|
||||
String get tos => '続行すると、利用規約とプライバシーポリシーに同意したことになります';
|
||||
|
||||
@override
|
||||
String get obSkip => 'スキップ';
|
||||
@override
|
||||
String get obNext => '次へ';
|
||||
@override
|
||||
String get obAllow => '許可して続行';
|
||||
@override
|
||||
String get obStart => '始める';
|
||||
@override
|
||||
String get ob1Title => '世界中に接続';
|
||||
@override
|
||||
String get ob1Sub => '80以上の拠点、最速ルートを自動選択、抜群の安定性。';
|
||||
@override
|
||||
String get ob2Title => 'ネットワーク設定を許可';
|
||||
@override
|
||||
String get ob2Sub => '暗号化トンネルを構築するため、システムがネットワークプロファイルの追加を求めます。';
|
||||
@override
|
||||
String get ob3Title => '設計段階からプライバシー重視';
|
||||
@override
|
||||
String get ob3Sub => 'エンドツーエンドで暗号化。閲覧ログは一切保持しません。';
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
// strings_ko.dart — 한국어 문구 리소스 (단일 언어 표시)
|
||||
//
|
||||
// 금지어: VPN / 翻墙 / 科学上网 / 突破封锁 / 自由穿越 / Go anywhere.
|
||||
// 포지셔닝: 네트워크 가속 / 경험 최적화 + 프라이버시.
|
||||
// 요금제 번호는 design/CLAUDE.md §7 을 따름.
|
||||
import 'app_text.dart';
|
||||
|
||||
class StringsKo extends AppText {
|
||||
const StringsKo();
|
||||
|
||||
@override
|
||||
AppLang get lang => AppLang.ko;
|
||||
|
||||
@override
|
||||
String get brand => 'Pangolin';
|
||||
@override
|
||||
String get online => '● 온라인';
|
||||
@override
|
||||
String get offline => '○ 오프라인';
|
||||
@override
|
||||
String get capOff => '탭하여 연결';
|
||||
@override
|
||||
String get capConnecting => '연결 중…';
|
||||
@override
|
||||
String get capOn => '연결됨 · 암호화';
|
||||
@override
|
||||
String get connectNow => '연결';
|
||||
@override
|
||||
String get secure => '보안';
|
||||
|
||||
@override
|
||||
String get tabConnect => '연결';
|
||||
@override
|
||||
String get tabServers => '서버';
|
||||
@override
|
||||
String get tabStats => '통계';
|
||||
@override
|
||||
String get tabMe => '계정';
|
||||
|
||||
@override
|
||||
String get currentNode => '현재 노드';
|
||||
@override
|
||||
String get download => '다운';
|
||||
@override
|
||||
String get upload => '업';
|
||||
@override
|
||||
String get latency => '지연';
|
||||
|
||||
@override
|
||||
String get quotaToday => '오늘 남은 시간';
|
||||
@override
|
||||
String get minutes => '분';
|
||||
@override
|
||||
String get quotaFree => '무료 · 하루 10분';
|
||||
@override
|
||||
String get planFreeTag => '무료';
|
||||
@override
|
||||
String get watchAd => '광고 시청 후 시작';
|
||||
@override
|
||||
String get adUnlocked => '오늘 사용 가능';
|
||||
@override
|
||||
String get quotaLeftLabel => '남음';
|
||||
@override
|
||||
String get quotaUsedUp => '오늘 무료 시간을 모두 사용함';
|
||||
@override
|
||||
String get watchAdMore => '광고 시청으로 시간 추가';
|
||||
@override
|
||||
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 => '모바일 앱에서 광고를 시청해 시간을 추가하거나, 업그레이드하여 무제한으로 이용하세요.';
|
||||
@override
|
||||
String get gotIt => '확인';
|
||||
|
||||
@override
|
||||
String get chooseNode => '서버 선택';
|
||||
@override
|
||||
String get searchPh => '국가 / 도시 검색';
|
||||
@override
|
||||
String get smartSelect => '스마트 선택';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => '$name(으)로 전환할까요?';
|
||||
@override
|
||||
String get nodeSwitchBody => '현재 연결을 끊고 이 노드로 다시 연결합니다.';
|
||||
@override
|
||||
String get nodeSwitchConfirm => '전환';
|
||||
@override
|
||||
String get nodeUnhealthySwitched => '현재 노드에 연결할 수 없어 자동으로 전환했습니다';
|
||||
@override
|
||||
String get nodeUnhealthyError => '현재 노드에 연결할 수 없습니다 — 재연결하거나 노드를 전환하세요';
|
||||
@override
|
||||
String get nodeReconnecting => '네트워크가 불안정하여 다시 연결 중…';
|
||||
@override
|
||||
String get smartSub => '네트워크에 가장 적합한 노드를 선택합니다';
|
||||
@override
|
||||
String get recommended => '추천';
|
||||
|
||||
@override
|
||||
String get statsTitle => '통계';
|
||||
@override
|
||||
String get trafficMonth => '트래픽';
|
||||
@override
|
||||
String get avgPing => '평균 지연';
|
||||
@override
|
||||
String get durMonth => '시간';
|
||||
@override
|
||||
String get weekTraffic => '이번 주 (GB)';
|
||||
@override
|
||||
List<String> get days7 => const ['월', '화', '수', '목', '금', '토', '일'];
|
||||
@override
|
||||
String get byDevice => '기기별';
|
||||
@override
|
||||
String get noDeviceUsage => '아직 기기 사용 기록이 없습니다';
|
||||
@override
|
||||
String get periodMonth => '이번 달';
|
||||
@override
|
||||
String get periodWeek => '이번 주';
|
||||
@override
|
||||
String get periodToday => '오늘';
|
||||
@override
|
||||
String get statDown => '다운로드';
|
||||
@override
|
||||
String get statUp => '업로드';
|
||||
@override
|
||||
String get statDuration => '사용 시간';
|
||||
@override
|
||||
String get chartTwoWeeks => '최근 2주';
|
||||
@override
|
||||
String get allDevices => '모든 기기';
|
||||
|
||||
@override
|
||||
String get meTitle => '계정';
|
||||
@override
|
||||
String get proMember => 'PRO 회원';
|
||||
@override
|
||||
String get freePlanName => '무료';
|
||||
@override
|
||||
String get expires => '만료일';
|
||||
@override
|
||||
String get upgradeBtn => '갱신 / 업그레이드';
|
||||
@override
|
||||
String get accInfoTitle => '계정 정보';
|
||||
@override
|
||||
String get accEmail => '이메일';
|
||||
@override
|
||||
String get accPassword => '비밀번호';
|
||||
@override
|
||||
String get accChange => '변경';
|
||||
@override
|
||||
String get deviceLimitTitle => '기기 한도에 도달함';
|
||||
@override
|
||||
String get deviceLimitDesc => '현재 요금제는 최대 %s대의 기기를 허용합니다. 계속하려면 한 대를 삭제하세요.';
|
||||
@override
|
||||
String get deviceLimitRemoveOldest => '가장 오래 사용하지 않은 기기 삭제';
|
||||
@override
|
||||
String get myDevices => '내 기기';
|
||||
@override
|
||||
String get devicesSub => 'PRO는 최대 3대';
|
||||
@override
|
||||
String get thisDevice => '이 기기';
|
||||
@override
|
||||
String get remove => '삭제';
|
||||
@override
|
||||
String get devOnline => '온라인';
|
||||
@override
|
||||
String get devOffline => '오프라인';
|
||||
@override
|
||||
String get devLastOnline => '마지막 접속';
|
||||
@override
|
||||
String get devNeverLogin => '로그인한 적 없음';
|
||||
@override
|
||||
String get devForceLogout => '강제 로그아웃';
|
||||
@override
|
||||
String get devClearLogin => '로그인 정보 삭제';
|
||||
@override
|
||||
String get devRename => '이름 변경';
|
||||
@override
|
||||
String get devRenameHint => '기기 이름';
|
||||
@override
|
||||
String get devSave => '저장';
|
||||
@override
|
||||
String get devForceLogoutConfirm => '이 기기의 로그인 세션을 무효화합니다 — 오프라인으로 강제 종료되며 다시 로그인해야 합니다. 기기는 목록에 남아 있습니다.';
|
||||
@override
|
||||
String get devClearLoginConfirm => '이 기기를 목록에서 완전히 삭제하고 로그인 세션과 데이터 전송 자격 증명을 무효화합니다. 다시 사용하려면 재로그인해야 합니다.';
|
||||
@override
|
||||
String get devCancel => '취소';
|
||||
@override
|
||||
String get sessionRevokedTitle => '이 기기에서 로그아웃되었습니다';
|
||||
@override
|
||||
String get sessionRevokedBody => '다른 기기에서 이 기기를 로그아웃했습니다. 다시 로그인하세요.';
|
||||
@override
|
||||
String get sessionRevokedOk => '다시 로그인';
|
||||
@override
|
||||
String get redeemEntry => '코드 등록 및 구매';
|
||||
@override
|
||||
String get contactEntry => '문의하기';
|
||||
@override
|
||||
String get signOut => '로그아웃';
|
||||
@override
|
||||
String get language => '언어';
|
||||
@override
|
||||
String get darkAppearance => '다크 모드';
|
||||
@override
|
||||
String get stateOn => '켜짐';
|
||||
@override
|
||||
String get followLight => '꺼짐';
|
||||
@override
|
||||
String get protocol => '프로토콜';
|
||||
@override
|
||||
String get settingsTitle => '설정';
|
||||
@override
|
||||
String get autostart => '시작 시 실행';
|
||||
@override
|
||||
String get autostartSub => '시스템 시작 시 자동으로 실행';
|
||||
@override
|
||||
String get autoConnect => '자동 연결';
|
||||
@override
|
||||
String get autoConnectSub => '실행 시 자동으로 연결';
|
||||
@override
|
||||
String autoConnectingTo(String name) => '$name(으)로 자동 연결 중…';
|
||||
@override
|
||||
String get smartRoute => '스마트 라우팅';
|
||||
@override
|
||||
String get smartRouteSub => '해외는 가속, 국내는 직접 연결';
|
||||
@override
|
||||
String get killSwitch => 'Kill Switch';
|
||||
@override
|
||||
String get killSwitchSub => '연결이 끊기면 트래픽 차단';
|
||||
@override
|
||||
String get checkUpdate => '업데이트 확인';
|
||||
@override
|
||||
String get webUserCenter => '사용자 센터 (웹)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => '새 버전 v$version 사용 가능';
|
||||
@override
|
||||
String get updateNotesFallback => '이번 업데이트에는 버그 수정과 안정성 개선이 포함되어 있습니다.';
|
||||
@override
|
||||
String get updateLater => '나중에';
|
||||
@override
|
||||
String get updateDownload => '업데이트 다운로드';
|
||||
@override
|
||||
String get updateUpToDate => '최신 버전입니다';
|
||||
@override
|
||||
String get updateCheckFailed => '업데이트 확인에 실패했습니다. 잠시 후 다시 시도하세요';
|
||||
|
||||
@override
|
||||
String get choosePlan => '요금제 선택';
|
||||
@override
|
||||
String get freePlan => '무료';
|
||||
@override
|
||||
String get proPlan => 'Pro';
|
||||
@override
|
||||
String get teamPlan => 'Team';
|
||||
@override
|
||||
String get perMonth => '/월';
|
||||
@override
|
||||
String get current => '현재';
|
||||
@override
|
||||
String get upgrade => '업그레이드';
|
||||
@override
|
||||
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
|
||||
String get redeemCodeTitle => '코드 등록';
|
||||
@override
|
||||
String get redeemPh => '활성화 코드 입력';
|
||||
@override
|
||||
String get redeemBtn => '등록';
|
||||
@override
|
||||
String get redeemOk => '활성화됨 · PRO 잠금 해제';
|
||||
@override
|
||||
String get buyTitle => '구매처';
|
||||
@override
|
||||
String get buySub => '앱 내 결제는 지원되지 않습니다. 다음 경로로 코드를 받으세요:';
|
||||
@override
|
||||
String get chStore => '셀프 스토어';
|
||||
@override
|
||||
String get chEmail => '이메일 지원';
|
||||
|
||||
@override
|
||||
String get contactTitle => '문의하기';
|
||||
@override
|
||||
String get contactIntro =>
|
||||
'도움이 필요하신가요? 아래 채널로 문의하세요 — 보통 몇 분 안에 답변드립니다.';
|
||||
@override
|
||||
String get contactEmail => '이메일 지원';
|
||||
@override
|
||||
String get contactStore => '셀프 스토어';
|
||||
@override
|
||||
String get contactHoursTitle => '지원 시간';
|
||||
@override
|
||||
String get contactHours => '매일 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => '빠르게 · 안정적으로 · 손쉽게';
|
||||
@override
|
||||
String get tabLogin => '로그인';
|
||||
@override
|
||||
String get tabRegister => '회원가입';
|
||||
@override
|
||||
String get emailLabel => '이메일';
|
||||
@override
|
||||
String get emailPh => 'your@email.com';
|
||||
@override
|
||||
String get pwLabel => '비밀번호';
|
||||
@override
|
||||
String get pwPh => '비밀번호 입력';
|
||||
@override
|
||||
String get setPwPh => '비밀번호 설정 (다중 기기 로그인용)';
|
||||
@override
|
||||
String get codeLabel => '인증 코드';
|
||||
@override
|
||||
String codeSentTo(String email) => '$email(으)로 코드를 보냈습니다';
|
||||
@override
|
||||
String get sendCode => '코드 전송';
|
||||
@override
|
||||
String get resend => '재전송';
|
||||
@override
|
||||
String get doLogin => '로그인';
|
||||
@override
|
||||
String get doNext => '다음';
|
||||
@override
|
||||
String get doCreate => '계정 만들기';
|
||||
@override
|
||||
String get forgotPw => '비밀번호를 잊으셨나요?';
|
||||
@override
|
||||
String get tos => '계속하면 이용약관 및 개인정보 처리방침에 동의하는 것입니다';
|
||||
|
||||
@override
|
||||
String get obSkip => '건너뛰기';
|
||||
@override
|
||||
String get obNext => '다음';
|
||||
@override
|
||||
String get obAllow => '허용하고 계속';
|
||||
@override
|
||||
String get obStart => '시작하기';
|
||||
@override
|
||||
String get ob1Title => '전 세계로 연결';
|
||||
@override
|
||||
String get ob1Sub => '80개 이상의 지역, 자동 최적 경로, 안정적인 연결.';
|
||||
@override
|
||||
String get ob2Title => '네트워크 설정 허용';
|
||||
@override
|
||||
String get ob2Sub => '암호화된 터널을 구성하기 위해 시스템이 네트워크 프로필 추가를 요청합니다.';
|
||||
@override
|
||||
String get ob3Title => '설계부터 프라이버시';
|
||||
@override
|
||||
String get ob3Sub => '종단 간 암호화. 브라우징 기록을 전혀 저장하지 않습니다.';
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
// strings_ru.dart — русские текстовые ресурсы (отображение на одном языке)
|
||||
//
|
||||
// Запрещённые слова: VPN /翻墙 / 科学上网 / 突破封锁 / 自由穿越 / Go anywhere / ВПН.
|
||||
// Позиционирование: ускорение сети / оптимизация опыта + приватность.
|
||||
// Номера тарифов соответствуют design/CLAUDE.md §7.
|
||||
import 'app_text.dart';
|
||||
|
||||
class StringsRu extends AppText {
|
||||
const StringsRu();
|
||||
|
||||
@override
|
||||
AppLang get lang => AppLang.ru;
|
||||
|
||||
@override
|
||||
String get brand => 'Pangolin';
|
||||
@override
|
||||
String get online => '● В сети';
|
||||
@override
|
||||
String get offline => '○ Не в сети';
|
||||
@override
|
||||
String get capOff => 'Нажмите для подключения';
|
||||
@override
|
||||
String get capConnecting => 'Подключение…';
|
||||
@override
|
||||
String get capOn => 'Подключено · Шифрование';
|
||||
@override
|
||||
String get connectNow => 'ПОДКЛЮЧИТЬ';
|
||||
@override
|
||||
String get secure => 'ЗАЩИЩЕНО';
|
||||
|
||||
@override
|
||||
String get tabConnect => 'Подключение';
|
||||
@override
|
||||
String get tabServers => 'Серверы';
|
||||
@override
|
||||
String get tabStats => 'Статистика';
|
||||
@override
|
||||
String get tabMe => 'Аккаунт';
|
||||
|
||||
@override
|
||||
String get currentNode => 'Текущий узел';
|
||||
@override
|
||||
String get download => 'Приём';
|
||||
@override
|
||||
String get upload => 'Отдача';
|
||||
@override
|
||||
String get latency => 'Пинг';
|
||||
|
||||
@override
|
||||
String get quotaToday => 'Осталось сегодня';
|
||||
@override
|
||||
String get minutes => 'мин';
|
||||
@override
|
||||
String get quotaFree => 'Бесплатно · 10 мин/день';
|
||||
@override
|
||||
String get planFreeTag => 'Бесплатно';
|
||||
@override
|
||||
String get watchAd => 'Смотрите рекламу, чтобы начать';
|
||||
@override
|
||||
String get adUnlocked => 'Открыто на сегодня';
|
||||
@override
|
||||
String get quotaLeftLabel => 'Осталось';
|
||||
@override
|
||||
String get quotaUsedUp => 'Дневной бесплатный лимит исчерпан';
|
||||
@override
|
||||
String get watchAdMore => 'Смотрите рекламу, чтобы добавить время';
|
||||
@override
|
||||
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 => 'Смотрите рекламу в мобильном приложении, чтобы добавить время, или оформите подписку для безлимитного доступа.';
|
||||
@override
|
||||
String get gotIt => 'Понятно';
|
||||
|
||||
@override
|
||||
String get chooseNode => 'Выбор сервера';
|
||||
@override
|
||||
String get searchPh => 'Поиск страны / города';
|
||||
@override
|
||||
String get smartSelect => 'Умный выбор';
|
||||
@override
|
||||
String nodeSwitchTitle(String name) => 'Переключиться на $name?';
|
||||
@override
|
||||
String get nodeSwitchBody => 'Текущее соединение будет разорвано и восстановлено через этот узел.';
|
||||
@override
|
||||
String get nodeSwitchConfirm => 'Переключить';
|
||||
@override
|
||||
String get nodeUnhealthySwitched => 'Текущий узел недоступен — переключение выполнено автоматически';
|
||||
@override
|
||||
String get nodeUnhealthyError => 'Текущий узел недоступен — переподключитесь или смените узел';
|
||||
@override
|
||||
String get nodeReconnecting => 'Сеть нестабильна, переподключение…';
|
||||
@override
|
||||
String get smartSub => 'Подбирает лучший узел для вашей сети';
|
||||
@override
|
||||
String get recommended => 'Рекомендуется';
|
||||
|
||||
@override
|
||||
String get statsTitle => 'Статистика';
|
||||
@override
|
||||
String get trafficMonth => 'Трафик';
|
||||
@override
|
||||
String get avgPing => 'Средний пинг';
|
||||
@override
|
||||
String get durMonth => 'Время';
|
||||
@override
|
||||
String get weekTraffic => 'На этой неделе (ГБ)';
|
||||
@override
|
||||
List<String> get days7 => const ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'];
|
||||
@override
|
||||
String get byDevice => 'По устройствам';
|
||||
@override
|
||||
String get noDeviceUsage => 'Пока нет данных по устройствам';
|
||||
@override
|
||||
String get periodMonth => 'Этот месяц';
|
||||
@override
|
||||
String get periodWeek => 'Эта неделя';
|
||||
@override
|
||||
String get periodToday => 'Сегодня';
|
||||
@override
|
||||
String get statDown => 'Приём';
|
||||
@override
|
||||
String get statUp => 'Отдача';
|
||||
@override
|
||||
String get statDuration => 'Длительность';
|
||||
@override
|
||||
String get chartTwoWeeks => 'Последние 2 недели';
|
||||
@override
|
||||
String get allDevices => 'Все устройства';
|
||||
|
||||
@override
|
||||
String get meTitle => 'Аккаунт';
|
||||
@override
|
||||
String get proMember => 'PRO-подписка';
|
||||
@override
|
||||
String get freePlanName => 'Бесплатный';
|
||||
@override
|
||||
String get expires => 'Действует до';
|
||||
@override
|
||||
String get upgradeBtn => 'Продлить / Улучшить';
|
||||
@override
|
||||
String get accInfoTitle => 'Данные аккаунта';
|
||||
@override
|
||||
String get accEmail => 'Эл. почта';
|
||||
@override
|
||||
String get accPassword => 'Пароль';
|
||||
@override
|
||||
String get accChange => 'Изменить';
|
||||
@override
|
||||
String get deviceLimitTitle => 'Достигнут лимит устройств';
|
||||
@override
|
||||
String get deviceLimitDesc => 'Ваш тариф допускает до %s устройств. Удалите одно, чтобы продолжить.';
|
||||
@override
|
||||
String get deviceLimitRemoveOldest => 'Удалить давно неиспользуемое';
|
||||
@override
|
||||
String get myDevices => 'Мои устройства';
|
||||
@override
|
||||
String get devicesSub => 'До 3 устройств на PRO';
|
||||
@override
|
||||
String get thisDevice => 'Это устройство';
|
||||
@override
|
||||
String get remove => 'Удалить';
|
||||
@override
|
||||
String get devOnline => 'В сети';
|
||||
@override
|
||||
String get devOffline => 'Не в сети';
|
||||
@override
|
||||
String get devLastOnline => 'Был(а) в сети';
|
||||
@override
|
||||
String get devNeverLogin => 'Ни разу не входил(а)';
|
||||
@override
|
||||
String get devForceLogout => 'Принудительный выход';
|
||||
@override
|
||||
String get devClearLogin => 'Очистить данные входа';
|
||||
@override
|
||||
String get devRename => 'Переименовать';
|
||||
@override
|
||||
String get devRenameHint => 'Имя устройства';
|
||||
@override
|
||||
String get devSave => 'Сохранить';
|
||||
@override
|
||||
String get devForceLogoutConfirm => 'Это завершит сеанс входа устройства — оно будет отключено и потребует повторного входа. Устройство останется в списке.';
|
||||
@override
|
||||
String get devClearLoginConfirm => 'Это полностью удалит устройство из списка и отзовёт его сеанс входа и учётные данные передачи данных. Чтобы снова использовать устройство, нужно войти заново.';
|
||||
@override
|
||||
String get devCancel => 'Отмена';
|
||||
@override
|
||||
String get sessionRevokedTitle => 'Это устройство было отключено';
|
||||
@override
|
||||
String get sessionRevokedBody => 'Вход с этого устройства завершён с другого устройства вашего аккаунта. Пожалуйста, войдите снова.';
|
||||
@override
|
||||
String get sessionRevokedOk => 'Войти снова';
|
||||
@override
|
||||
String get redeemEntry => 'Активация и покупка';
|
||||
@override
|
||||
String get contactEntry => 'Связаться с нами';
|
||||
@override
|
||||
String get signOut => 'Выйти';
|
||||
@override
|
||||
String get language => 'Язык';
|
||||
@override
|
||||
String get darkAppearance => 'Тёмная тема';
|
||||
@override
|
||||
String get stateOn => 'Вкл.';
|
||||
@override
|
||||
String get followLight => 'Выкл.';
|
||||
@override
|
||||
String get protocol => 'Протокол';
|
||||
@override
|
||||
String get settingsTitle => 'Настройки';
|
||||
@override
|
||||
String get autostart => 'Запуск при старте системы';
|
||||
@override
|
||||
String get autostartSub => 'Запускать автоматически при включении системы';
|
||||
@override
|
||||
String get autoConnect => 'Автоподключение';
|
||||
@override
|
||||
String get autoConnectSub => 'Подключаться автоматически при запуске';
|
||||
@override
|
||||
String autoConnectingTo(String name) => 'Автоподключение к $name…';
|
||||
@override
|
||||
String get smartRoute => 'Умная маршрутизация';
|
||||
@override
|
||||
String get smartRouteSub => 'Ускорение для зарубежных ресурсов, прямой доступ для локальных';
|
||||
@override
|
||||
String get killSwitch => 'Kill Switch';
|
||||
@override
|
||||
String get killSwitchSub => 'Блокировать трафик при разрыве соединения';
|
||||
@override
|
||||
String get checkUpdate => 'Проверить обновления';
|
||||
@override
|
||||
String get webUserCenter => 'Личный кабинет (веб)';
|
||||
@override
|
||||
String updateAvailableTitle(String version) => 'Доступна новая версия v$version';
|
||||
@override
|
||||
String get updateNotesFallback => 'Это обновление включает исправления и улучшения стабильности.';
|
||||
@override
|
||||
String get updateLater => 'Позже';
|
||||
@override
|
||||
String get updateDownload => 'Скачать обновление';
|
||||
@override
|
||||
String get updateUpToDate => 'У вас установлена последняя версия';
|
||||
@override
|
||||
String get updateCheckFailed => 'Не удалось проверить обновления, попробуйте позже';
|
||||
|
||||
@override
|
||||
String get choosePlan => 'Выбор тарифа';
|
||||
@override
|
||||
String get freePlan => 'Бесплатный';
|
||||
@override
|
||||
String get proPlan => 'Pro';
|
||||
@override
|
||||
String get teamPlan => 'Команда';
|
||||
@override
|
||||
String get perMonth => '/мес';
|
||||
@override
|
||||
String get current => 'Текущий';
|
||||
@override
|
||||
String get upgrade => 'Улучшить';
|
||||
@override
|
||||
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
|
||||
String get redeemCodeTitle => 'Активировать код';
|
||||
@override
|
||||
String get redeemPh => 'Введите код активации';
|
||||
@override
|
||||
String get redeemBtn => 'Активировать';
|
||||
@override
|
||||
String get redeemOk => 'Активировано · PRO открыт';
|
||||
@override
|
||||
String get buyTitle => 'Где купить';
|
||||
@override
|
||||
String get buySub => 'Оплата внутри приложения недоступна. Получить код можно через:';
|
||||
@override
|
||||
String get chStore => 'Магазин самообслуживания';
|
||||
@override
|
||||
String get chEmail => 'Поддержка по эл. почте';
|
||||
|
||||
@override
|
||||
String get contactTitle => 'Связаться с нами';
|
||||
@override
|
||||
String get contactIntro =>
|
||||
'Нужна помощь? Свяжитесь с нами любым способом ниже — обычно отвечаем за считанные минуты.';
|
||||
@override
|
||||
String get contactEmail => 'Поддержка по эл. почте';
|
||||
@override
|
||||
String get contactStore => 'Магазин самообслуживания';
|
||||
@override
|
||||
String get contactHoursTitle => 'Часы поддержки';
|
||||
@override
|
||||
String get contactHours => 'Ежедневно 9:00 – 24:00 (GMT+8)';
|
||||
|
||||
@override
|
||||
String get authTagline => 'Быстро · Стабильно · Без усилий';
|
||||
@override
|
||||
String get tabLogin => 'Вход';
|
||||
@override
|
||||
String get tabRegister => 'Регистрация';
|
||||
@override
|
||||
String get emailLabel => 'Эл. почта';
|
||||
@override
|
||||
String get emailPh => 'your@email.com';
|
||||
@override
|
||||
String get pwLabel => 'Пароль';
|
||||
@override
|
||||
String get pwPh => 'Введите пароль';
|
||||
@override
|
||||
String get setPwPh => 'Задайте пароль (для входа с нескольких устройств)';
|
||||
@override
|
||||
String get codeLabel => 'Код подтверждения';
|
||||
@override
|
||||
String codeSentTo(String email) => 'Код отправлен на $email';
|
||||
@override
|
||||
String get sendCode => 'Отправить код';
|
||||
@override
|
||||
String get resend => 'Отправить снова';
|
||||
@override
|
||||
String get doLogin => 'Войти';
|
||||
@override
|
||||
String get doNext => 'Далее';
|
||||
@override
|
||||
String get doCreate => 'Создать аккаунт';
|
||||
@override
|
||||
String get forgotPw => 'Забыли пароль?';
|
||||
@override
|
||||
String get tos => 'Продолжая, вы принимаете наши Условия и Политику конфиденциальности';
|
||||
|
||||
@override
|
||||
String get obSkip => 'Пропустить';
|
||||
@override
|
||||
String get obNext => 'Далее';
|
||||
@override
|
||||
String get obAllow => 'Разрешить и продолжить';
|
||||
@override
|
||||
String get obStart => 'Начать';
|
||||
@override
|
||||
String get ob1Title => 'Подключение по всему миру';
|
||||
@override
|
||||
String get ob1Sub => '80+ локаций, автоматический выбор самого быстрого маршрута, надёжная стабильность.';
|
||||
@override
|
||||
String get ob2Title => 'Разрешите настройку сети';
|
||||
@override
|
||||
String get ob2Sub => 'Система запросит добавление сетевого профиля для создания зашифрованного туннеля.';
|
||||
@override
|
||||
String get ob3Title => 'Приватность по умолчанию';
|
||||
@override
|
||||
String get ob3Sub => 'Сквозное шифрование. Мы не храним журналы просмотров.';
|
||||
}
|
||||
@@ -351,31 +351,37 @@ class _LangSwitch extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = context.pangolin;
|
||||
Widget seg(AppLang v, String label) {
|
||||
final active = lang == v;
|
||||
return GestureDetector(
|
||||
onTap: () => onChange(v),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: active ? c.accent : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(PangolinRadius.full),
|
||||
// 6 种语言用下拉(段控横排会挤)。
|
||||
return PopupMenuButton<AppLang>(
|
||||
initialValue: lang,
|
||||
onSelected: onChange,
|
||||
color: c.surface,
|
||||
elevation: 8,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(PangolinRadius.md)),
|
||||
itemBuilder: (_) => [
|
||||
for (final l in AppLang.values)
|
||||
PopupMenuItem<AppLang>(
|
||||
value: l,
|
||||
height: 42,
|
||||
child: Text(l.nativeLabel,
|
||||
style: PangolinText.caption.copyWith(
|
||||
color: l == lang ? c.accent : c.fg1,
|
||||
fontWeight: l == lang ? FontWeight.w700 : FontWeight.w500,
|
||||
fontSize: 13)),
|
||||
),
|
||||
child: Text(label,
|
||||
style: PangolinText.caption.copyWith(
|
||||
color: active ? c.fgOnAccent : c.fg3, fontWeight: FontWeight.w700, fontSize: 12.5)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(3),
|
||||
decoration: BoxDecoration(color: c.bgSubtle, borderRadius: BorderRadius.circular(PangolinRadius.full)),
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
seg(AppLang.zh, '中文'),
|
||||
const SizedBox(width: 2),
|
||||
seg(AppLang.en, 'EN'),
|
||||
]),
|
||||
],
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 6),
|
||||
decoration:
|
||||
BoxDecoration(color: c.bgSubtle, borderRadius: BorderRadius.circular(PangolinRadius.full)),
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
Text(lang.nativeLabel,
|
||||
style: PangolinText.caption
|
||||
.copyWith(color: c.fg2, fontWeight: FontWeight.w700, fontSize: 12.5)),
|
||||
const SizedBox(width: 4),
|
||||
Icon(PangolinIcons.chevronDown, size: 14, color: c.fg3),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,27 +148,37 @@ class _LangSwitch extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = context.pangolin;
|
||||
Widget seg(AppLang v, String label) {
|
||||
final on = lang == v;
|
||||
return GestureDetector(
|
||||
onTap: () => onPick(v),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: on ? c.accent : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(PangolinRadius.full),
|
||||
// 6 种语言用下拉(段控横排会挤)。当前语言以本地名 + 下拉箭头展示。
|
||||
return PopupMenuButton<AppLang>(
|
||||
initialValue: lang,
|
||||
onSelected: onPick,
|
||||
color: c.surface,
|
||||
elevation: 8,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(PangolinRadius.md)),
|
||||
itemBuilder: (_) => [
|
||||
for (final l in AppLang.values)
|
||||
PopupMenuItem<AppLang>(
|
||||
value: l,
|
||||
height: 42,
|
||||
child: Text(l.nativeLabel,
|
||||
style: PangolinText.caption.copyWith(
|
||||
color: l == lang ? c.accent : c.fg1,
|
||||
fontWeight: l == lang ? FontWeight.w700 : FontWeight.w500,
|
||||
fontSize: 13)),
|
||||
),
|
||||
child: Text(label,
|
||||
style: PangolinText.caption.copyWith(
|
||||
color: on ? c.fgOnAccent : c.fg3, fontWeight: FontWeight.w700, fontSize: 12)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(3),
|
||||
decoration: BoxDecoration(color: c.bgSubtle, borderRadius: BorderRadius.circular(PangolinRadius.full)),
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [seg(AppLang.zh, '中文'), seg(AppLang.en, 'EN')]),
|
||||
],
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration:
|
||||
BoxDecoration(color: c.bgSubtle, borderRadius: BorderRadius.circular(PangolinRadius.full)),
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
Text(lang.nativeLabel,
|
||||
style: PangolinText.caption
|
||||
.copyWith(color: c.fg2, fontWeight: FontWeight.w700, fontSize: 12)),
|
||||
const SizedBox(width: 4),
|
||||
Icon(PangolinIcons.chevronDown, size: 14, color: c.fg3),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,33 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../l10n/app_text.dart';
|
||||
import '../l10n/strings_en.dart';
|
||||
import '../l10n/strings_es.dart';
|
||||
import '../l10n/strings_ja.dart';
|
||||
import '../l10n/strings_ko.dart';
|
||||
import '../l10n/strings_ru.dart';
|
||||
import '../l10n/strings_zh.dart';
|
||||
import 'account_providers.dart';
|
||||
import 'auth_provider.dart';
|
||||
|
||||
/// 当前语言(单显)。设置/账户页段控切换。
|
||||
final localeProvider = StateProvider<AppLang>((ref) => AppLang.zh);
|
||||
/// 当前语言(单显)。设置/账户页切换。默认英文(国际化默认语种)。
|
||||
final localeProvider = StateProvider<AppLang>((ref) => AppLang.en);
|
||||
|
||||
/// 由语言派生的文案资源——UI 一律通过它取文案,不写死字面量。
|
||||
final appTextProvider = Provider<AppText>((ref) {
|
||||
final lang = ref.watch(localeProvider);
|
||||
return lang == AppLang.zh ? const StringsZh() : const StringsEn();
|
||||
switch (ref.watch(localeProvider)) {
|
||||
case AppLang.zh:
|
||||
return const StringsZh();
|
||||
case AppLang.en:
|
||||
return const StringsEn();
|
||||
case AppLang.ja:
|
||||
return const StringsJa();
|
||||
case AppLang.ko:
|
||||
return const StringsKo();
|
||||
case AppLang.ru:
|
||||
return const StringsRu();
|
||||
case AppLang.es:
|
||||
return const StringsEs();
|
||||
}
|
||||
});
|
||||
|
||||
/// 主题模式。默认跟随系统;设置页可显式切深色。
|
||||
|
||||
Reference in New Issue
Block a user