feat(locale): 实现 App 启动时语言和时区自动设置

- 新增系统语言/时区读取工具函数
- SessionStore 扩展支持时区存储
- 启动流程自动检测并保存系统语言/时区
- 注册时传递语言/时区到后端
- 登录后从服务器同步语言/时区
This commit is contained in:
ZL-Q
2026-04-28 17:18:16 +08:00
parent 14752cdcfa
commit a83001de0d
11 changed files with 402 additions and 14 deletions
+36 -9
View File
@@ -4,7 +4,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import '../core/auth/session_store.dart';
import '../core/localization/system_locale.dart';
import '../core/logging/logger.dart';
import '../core/timezone/system_timezone.dart';
import '../data/network/api_client.dart';
import '../data/storage/local_kv_store.dart';
import '../features/auth/data/apis/auth_api.dart';
@@ -42,6 +44,7 @@ class _EryaoAppState extends State<EryaoApp> {
late final NotificationRepository _notificationRepository;
late final NotificationBloc _notificationBloc;
Locale _locale = const Locale('zh');
String _timezone = 'Asia/Shanghai';
ProfileSettingsV1 _profileSettings = ProfileSettingsV1.defaultsForLocale(
const Locale('zh'),
);
@@ -300,11 +303,14 @@ class _EryaoAppState extends State<EryaoApp> {
if (!mounted) {
return;
}
final serverLanguage = profile.preferences.interfaceLanguage;
final serverLanguage = profile.preferences.language;
final serverTimezone = profile.preferences.timezone;
final serverLocale = localeFromLanguageTag(serverLanguage);
await _sessionStore.saveLocaleTag(serverLanguage);
await _sessionStore.saveTimezone(serverTimezone);
setState(() {
_locale = serverLocale;
_timezone = serverTimezone;
_profileSettings = profile;
_loadedProfileUserEmail = userEmail;
});
@@ -352,8 +358,8 @@ class _EryaoAppState extends State<EryaoApp> {
Future<void> _saveProfileSettings(ProfileSettingsV1 next) async {
try {
final oldLanguage = _profileSettings.preferences.interfaceLanguage;
final newLanguage = next.preferences.interfaceLanguage;
final oldLanguage = _profileSettings.preferences.language;
final newLanguage = next.preferences.language;
final saved = await _profileApi.updateSettings(next);
if (!mounted) {
return;
@@ -383,13 +389,29 @@ class _EryaoAppState extends State<EryaoApp> {
}
Future<void> _bootstrap() async {
final localeTag = await _sessionStore.getLocaleTag();
final locale = localeTag != null
? localeFromLanguageTag(localeTag)
: const Locale('zh');
final savedLocaleTag = await _sessionStore.getLocaleTag();
final Locale locale;
if (savedLocaleTag != null) {
locale = localeFromLanguageTag(savedLocaleTag);
} else {
final systemLocale = getSystemLocale();
locale = resolveSystemLocale(systemLocale) ?? const Locale('zh');
await _sessionStore.saveLocaleTag(languageTagFromLocale(locale));
}
final savedTimezone = await _sessionStore.getTimezone();
final String timezone;
if (savedTimezone != null) {
timezone = savedTimezone;
} else {
timezone = await getSystemTimezone();
await _sessionStore.saveTimezone(timezone);
}
if (mounted) {
setState(() {
_locale = locale;
_timezone = timezone;
_profileSettings = ProfileSettingsV1.defaultsForLocale(locale);
});
}
@@ -406,7 +428,7 @@ class _EryaoAppState extends State<EryaoApp> {
_locale = locale;
_profileSettings = _profileSettings.copyWith(
preferences: _profileSettings.preferences.copyWith(
interfaceLanguage: languageTag,
language: languageTag,
),
);
});
@@ -486,7 +508,12 @@ class _EryaoAppState extends State<EryaoApp> {
onLocaleChanged: (_) {},
onRequestOtp: _authBloc.sendOtp,
onLoginWithOtp: (email, otp) {
return _authBloc.loginWithOtp(email: email, otp: otp);
return _authBloc.loginWithOtp(
email: email,
otp: otp,
language: languageTagFromLocale(_locale),
timezone: _timezone,
);
},
);
}