94 lines
3.2 KiB
Dart
94 lines
3.2 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();
|
||
}
|
||
|
||
@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: mandatory
|
||
? null
|
||
: [
|
||
TextButton(
|
||
onPressed: () => Navigator.of(context).pop(),
|
||
child: const Text('取消'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|