feat(client/i18n): B1 硬编码文案 6 语化 + B2 语言持久化

B2 持久化:localeProvider 由 StateProvider 改 StateNotifier(LocaleNotifier),
默认英文,选择存 shared_preferences(key pg_lang),重启保留;新增 appTextFor(lang)
helper 供非 Consumer 场景复用。两处切换 .state= 改 .set()。

B1 硬编码 UI 文案 6 语化(AppLangMisc 扩展,exhaustive switch 不会漏):
- 不可用 / 流媒体优化 / 节点未就绪 / 连接失败 / 加载失败 / 暂无设备 / 改邮箱
- 相对时间(刚刚/X分钟前/…)relativeTime(Duration)
- 修 auth_screen 错误消息 bug:原 _errorZh 永远显示中文(连英文用户都中招)→
  按当前语言取 messageZh/En(非中文显英文)

仍回退英文(服务端数据,需服务端多语字段):节点/套餐名(nameZh/nameEn)、
API 错误消息(messageZh/En)。flutter analyze 仅 2 个既有 withOpacity info。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-07 14:32:13 +08:00
parent f9a87a9993
commit 617b43083d
10 changed files with 239 additions and 35 deletions
+4 -3
View File
@@ -203,7 +203,8 @@ class ConnectionController extends StateNotifier<ConnectionState> {
state = const ConnectionState(phase: VpnPhase.connecting);
final node = _ref.read(effectiveNodeProvider);
final zh = _ref.read(localeProvider) == AppLang.zh;
final lang = _ref.read(localeProvider);
final zh = lang == AppLang.zh; // 仅用于服务端 e.messageZh/En 的二选一(下方)
logLine('Connect', '_connect node=${node.code} uuid=${node.uuid.isEmpty ? "EMPTY" : "ok"} '
'selected=${_ref.read(selectedNodeCodeProvider)} nodes=${(_ref.read(nodesProvider).valueOrNull ?? const []).length}');
@@ -212,7 +213,7 @@ class ConnectionController extends StateNotifier<ConnectionState> {
if (mounted) {
state = ConnectionState(
phase: VpnPhase.off,
error: zh ? '节点尚未就绪,请稍候重试' : 'Nodes not ready, please retry',
error: lang.nodesNotReady,
);
}
return;
@@ -244,7 +245,7 @@ class ConnectionController extends StateNotifier<ConnectionState> {
if (mounted) {
state = ConnectionState(
phase: VpnPhase.off,
error: zh ? '连接失败,请重试' : 'Connection failed, please retry',
error: lang.connectFailed,
);
}
}