118 lines
3.1 KiB
Dart
118 lines
3.1 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
|
||
/// 全局语言状态管理
|
||
///
|
||
/// userLocale = null 时跟随系统语言
|
||
/// 用户在设置页选择后,持久化到 SharedPreferences,下次启动自动恢复
|
||
class LocaleManager {
|
||
static final ValueNotifier<Locale?> userLocale = ValueNotifier(null);
|
||
|
||
static const _prefKey = 'user_locale';
|
||
|
||
static const supportedLocales = [
|
||
Locale('zh', 'CN'),
|
||
Locale('zh', 'TW'),
|
||
Locale('en'),
|
||
Locale('ja'),
|
||
];
|
||
|
||
/// 启动时调用,从 SharedPreferences 恢复用户选择的语言
|
||
static Future<void> init() async {
|
||
final prefs = await SharedPreferences.getInstance();
|
||
final saved = prefs.getString(_prefKey);
|
||
if (saved != null) {
|
||
final locale = _parseLocale(saved);
|
||
if (locale != null) {
|
||
userLocale.value = locale;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 设置用户语言并持久化(传 null 表示恢复跟随系统)
|
||
static Future<void> setLocale(Locale? locale) async {
|
||
userLocale.value = locale;
|
||
final prefs = await SharedPreferences.getInstance();
|
||
if (locale == null) {
|
||
await prefs.remove(_prefKey);
|
||
} else {
|
||
await prefs.setString(_prefKey, _serializeLocale(locale));
|
||
}
|
||
}
|
||
|
||
/// 根据系统 locale 解析最佳匹配
|
||
static Locale resolve(List<Locale>? systemLocales, Iterable<Locale> supported) {
|
||
if (systemLocales == null || systemLocales.isEmpty) {
|
||
return const Locale('zh', 'CN');
|
||
}
|
||
final system = systemLocales.first;
|
||
|
||
// 精确匹配
|
||
for (final s in supported) {
|
||
if (s.languageCode == system.languageCode &&
|
||
s.countryCode == system.countryCode) {
|
||
return s;
|
||
}
|
||
}
|
||
|
||
// 语言码匹配
|
||
for (final s in supported) {
|
||
if (s.languageCode == system.languageCode) {
|
||
return s;
|
||
}
|
||
}
|
||
|
||
// 默认中文
|
||
return const Locale('zh', 'CN');
|
||
}
|
||
|
||
/// 根据语言获取默认货币
|
||
static String defaultCurrencyForLocale(Locale locale) {
|
||
switch (locale.languageCode) {
|
||
case 'zh':
|
||
return locale.countryCode == 'TW' ? 'TWD' : 'CNY';
|
||
case 'ja':
|
||
return 'JPY';
|
||
case 'en':
|
||
default:
|
||
return 'USD';
|
||
}
|
||
}
|
||
|
||
/// Locale 转显示名称
|
||
static String localeDisplayName(Locale locale) {
|
||
final key = '${locale.languageCode}_${locale.countryCode ?? ''}';
|
||
switch (key) {
|
||
case 'zh_CN':
|
||
return '简体中文';
|
||
case 'zh_TW':
|
||
return '繁體中文';
|
||
case 'en_':
|
||
case 'en_US':
|
||
return 'English';
|
||
case 'ja_':
|
||
case 'ja_JP':
|
||
return '日本語';
|
||
default:
|
||
return locale.toString();
|
||
}
|
||
}
|
||
|
||
static String _serializeLocale(Locale locale) {
|
||
if (locale.countryCode != null && locale.countryCode!.isNotEmpty) {
|
||
return '${locale.languageCode}_${locale.countryCode}';
|
||
}
|
||
return locale.languageCode;
|
||
}
|
||
|
||
static Locale? _parseLocale(String str) {
|
||
final parts = str.split('_');
|
||
if (parts.length == 2) {
|
||
return Locale(parts[0], parts[1]);
|
||
} else if (parts.length == 1 && parts[0].isNotEmpty) {
|
||
return Locale(parts[0]);
|
||
}
|
||
return null;
|
||
}
|
||
}
|