101 lines
3.7 KiB
Dart
101 lines
3.7 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
|
||
import 'package:maibu_satabot_v2/core/localization/locale_cubit.dart';
|
||
|
||
/// 可选语言项:tag 对应 assets/languages/{tag}.json,nativeName 用该语言本地写法展示。
|
||
class LanguageItem {
|
||
final Locale locale;
|
||
final String nativeName;
|
||
const LanguageItem(this.locale, this.nativeName);
|
||
|
||
String get tag => locale.countryCode == null || locale.countryCode!.isEmpty
|
||
? locale.languageCode
|
||
: '${locale.languageCode}-${locale.countryCode}';
|
||
}
|
||
|
||
/// 全局唯一的“可选语言列表”来源。
|
||
/// 后续接入“接口下载语言包”时,只需在这里扩展(例如把已下载的语言追加进来),
|
||
/// 登录强制弹窗与设置页入口都会自动同步,无需改多处。
|
||
class AppLanguages {
|
||
AppLanguages._();
|
||
static final AppLanguages instance = AppLanguages._();
|
||
|
||
final List<LanguageItem> items = const [
|
||
LanguageItem(Locale('zh', 'CN'), '简体中文'),
|
||
LanguageItem(Locale('en', 'US'), 'English'),
|
||
];
|
||
|
||
String nameOf(Locale locale) {
|
||
final tag = locale.countryCode == null || locale.countryCode!.isEmpty
|
||
? locale.languageCode
|
||
: '${locale.languageCode}-${locale.countryCode}';
|
||
for (final item in items) {
|
||
if (item.tag == tag) return item.nativeName;
|
||
}
|
||
return tag;
|
||
}
|
||
}
|
||
|
||
/// 语言选择弹窗。
|
||
/// [mandatory] = true:登录时强制弹出,拦截系统返回键;可点某个语言即时切换,或点“继续”确认当前默认语言后关闭。
|
||
/// [mandatory] = false:设置页中普通选择,可取消。
|
||
class LanguageSelectionDialog extends StatelessWidget {
|
||
final bool mandatory;
|
||
const LanguageSelectionDialog({super.key, this.mandatory = true});
|
||
|
||
Future<void> _onSelect(BuildContext context, Locale locale) async {
|
||
await context.read<LocaleCubit>().setLocale(locale);
|
||
if (context.mounted) Navigator.of(context).pop();
|
||
}
|
||
|
||
/// “继续”:不改变当前(默认)语言,仅确认已选并关闭,后续启动不再弹。
|
||
Future<void> _onContinue(BuildContext context, Locale current) async {
|
||
await context.read<LocaleCubit>().setLocale(current);
|
||
if (context.mounted) Navigator.of(context).pop();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final current = context.select<LocaleCubit, Locale>(
|
||
(c) => c.state,
|
||
);
|
||
|
||
return PopScope(
|
||
// 强制弹窗时拦截返回键,必须选一个才能继续使用
|
||
canPop: !mandatory,
|
||
child: AlertDialog(
|
||
title: const Text('选择语言 / Select Language'),
|
||
content: SizedBox(
|
||
width: 300,
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
for (final item in AppLanguages.instance.items)
|
||
RadioListTile<String>(
|
||
value: item.tag,
|
||
// 高亮当前语言:按 languageCode 匹配,兼容国家码为空的历史值
|
||
groupValue: current.countryCode == null ||
|
||
current.countryCode!.isEmpty
|
||
? current.languageCode
|
||
: '${current.languageCode}-${current.countryCode}',
|
||
title: Text(item.nativeName),
|
||
onChanged: (_) => _onSelect(context, item.locale),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
actions: [
|
||
// 强制弹窗:提供“继续”出口,保持当前默认语言并确认后关闭;非强制:取消
|
||
TextButton(
|
||
onPressed: () => mandatory
|
||
? _onContinue(context, current)
|
||
: Navigator.of(context).pop(),
|
||
child: Text(mandatory ? '继续 / Continue' : '取消'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|