feat(flutter): localize referral page + fix language auto-detection
- Add 27 new l10n keys (ARB + generated dart) for the referral screen, covering both Enterprise tab and personal circle tab strings - Replace all hardcoded Chinese strings in referral_screen.dart with l10n calls (tab labels, section headers, status labels, rules, etc.) - Fix language auto-detection: default to '' instead of 'en', and return null from localeProvider to follow device locale - Fix 'Refer & Earn' vertical text: wrap trailing with Flexible in _SettingsRow on profile page Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8262d3f8e3
commit
b2594317d7
|
|
@ -114,8 +114,10 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||
icon: Icons.language,
|
||||
iconBg: const Color(0xFF3B82F6),
|
||||
title: l10n.languageLabel,
|
||||
trailing: Text(l10n.languageZh,
|
||||
style: TextStyle(color: subtitleColor, fontSize: 14)),
|
||||
trailing: Text(
|
||||
_languageDisplayLabel(settings.language, l10n),
|
||||
style: TextStyle(color: subtitleColor, fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -350,6 +352,17 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||
);
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
String _languageDisplayLabel(String lang, AppLocalizations l10n) {
|
||||
return switch (lang) {
|
||||
'zh' => l10n.languageZh,
|
||||
'zh_TW' => l10n.languageZhTW,
|
||||
'en' => l10n.languageEn,
|
||||
_ => l10n.languageAuto,
|
||||
};
|
||||
}
|
||||
|
||||
// ── Pickers ───────────────────────────────────────────────────────────────
|
||||
|
||||
void _showThemePicker(ThemeMode current) {
|
||||
|
|
@ -919,7 +932,7 @@ class _SettingsRow extends StatelessWidget {
|
|||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (trailing != null) trailing!,
|
||||
if (trailing != null) Flexible(child: trailing!),
|
||||
if (onTap != null) ...[
|
||||
const SizedBox(width: 4),
|
||||
Icon(Icons.chevron_right,
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ class ReferralScreen extends ConsumerWidget {
|
|||
indicatorColor: AppColors.primary,
|
||||
labelColor: AppColors.primary,
|
||||
unselectedLabelColor: Colors.grey,
|
||||
tabs: const [
|
||||
Tab(text: '企业推荐'),
|
||||
Tab(text: '我的圈子'),
|
||||
tabs: [
|
||||
Tab(text: l10n.referralTabTenant),
|
||||
Tab(text: l10n.referralTabPersonal),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -66,10 +66,11 @@ class _TenantReferralTab extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final infoAsync = ref.watch(referralInfoProvider);
|
||||
return infoAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(child: Text('加载失败: $e')),
|
||||
error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')),
|
||||
data: (info) => ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
children: [
|
||||
|
|
@ -80,14 +81,14 @@ class _TenantReferralTab extends ConsumerWidget {
|
|||
_RewardRulesCard(cardColor: cardColor),
|
||||
const SizedBox(height: 20),
|
||||
_SectionHeader(
|
||||
title: '推荐记录',
|
||||
title: l10n.referralRecordsSection,
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (_) => const _ReferralListPage())),
|
||||
),
|
||||
_ReferralPreviewList(cardColor: cardColor),
|
||||
const SizedBox(height: 20),
|
||||
_SectionHeader(
|
||||
title: '待结算奖励',
|
||||
title: l10n.pendingRewardsSection,
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (_) => const _RewardListPage())),
|
||||
),
|
||||
|
|
@ -109,10 +110,11 @@ class _PersonalCircleTab extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final infoAsync = ref.watch(userReferralInfoProvider);
|
||||
return infoAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(child: Text('加载失败: $e')),
|
||||
error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')),
|
||||
data: (info) => ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
children: [
|
||||
|
|
@ -123,14 +125,14 @@ class _PersonalCircleTab extends ConsumerWidget {
|
|||
_CircleRulesCard(cardColor: cardColor),
|
||||
const SizedBox(height: 20),
|
||||
_SectionHeader(
|
||||
title: '我的圈子成员',
|
||||
title: l10n.myCircleMembersSection,
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (_) => const _CircleListPage())),
|
||||
),
|
||||
_CirclePreviewList(cardColor: cardColor),
|
||||
const SizedBox(height: 20),
|
||||
_SectionHeader(
|
||||
title: '积分记录',
|
||||
title: l10n.pointsHistorySection,
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (_) => const _PointsHistoryPage())),
|
||||
),
|
||||
|
|
@ -152,6 +154,7 @@ class _UserCodeCard extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
return Card(
|
||||
color: cardColor,
|
||||
elevation: 0,
|
||||
|
|
@ -161,9 +164,9 @@ class _UserCodeCard extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'我的个人邀请码',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 13),
|
||||
Text(
|
||||
l10n.myPersonalInviteCode,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 13),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
|
|
@ -181,7 +184,7 @@ class _UserCodeCard extends StatelessWidget {
|
|||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy, color: Color(0xFF7C3AED)),
|
||||
tooltip: '复制邀请码',
|
||||
tooltip: l10n.copyReferralCodeTooltip,
|
||||
onPressed: () => _copy(context, info.code),
|
||||
),
|
||||
],
|
||||
|
|
@ -192,7 +195,7 @@ class _UserCodeCard extends StatelessWidget {
|
|||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
icon: const Icon(Icons.link, size: 18),
|
||||
label: const Text('复制邀请链接'),
|
||||
label: Text(l10n.copyInviteLinkButton),
|
||||
onPressed: () => _copy(context, info.shareUrl),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: const Color(0xFF7C3AED),
|
||||
|
|
@ -206,7 +209,7 @@ class _UserCodeCard extends StatelessWidget {
|
|||
Expanded(
|
||||
child: FilledButton.icon(
|
||||
icon: const Icon(Icons.share, size: 18),
|
||||
label: const Text('分享'),
|
||||
label: Text(l10n.shareButton),
|
||||
onPressed: () => _share(context, info),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF7C3AED),
|
||||
|
|
@ -224,11 +227,12 @@ class _UserCodeCard extends StatelessWidget {
|
|||
}
|
||||
|
||||
void _copy(BuildContext context, String text) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
Clipboard.setData(ClipboardData(text: text));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('已复制到剪贴板'),
|
||||
duration: Duration(seconds: 2)),
|
||||
SnackBar(
|
||||
content: Text(l10n.copiedToClipboard),
|
||||
duration: const Duration(seconds: 2)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -249,6 +253,7 @@ class _PointsBalanceCard extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
return Card(
|
||||
color: cardColor,
|
||||
elevation: 0,
|
||||
|
|
@ -258,34 +263,34 @@ class _PointsBalanceCard extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Row(
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.stars_rounded, color: Color(0xFFF59E0B), size: 20),
|
||||
SizedBox(width: 6),
|
||||
Text('积分余额',
|
||||
const Icon(Icons.stars_rounded, color: Color(0xFFF59E0B), size: 20),
|
||||
const SizedBox(width: 6),
|
||||
Text(l10n.pointsBalanceTitle,
|
||||
style:
|
||||
TextStyle(fontWeight: FontWeight.w600, fontSize: 15)),
|
||||
const TextStyle(fontWeight: FontWeight.w600, fontSize: 15)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
_PointsStatItem(
|
||||
label: '当前余额',
|
||||
label: l10n.currentBalanceLabel,
|
||||
value: '${info.pointsBalance}',
|
||||
unit: 'pts',
|
||||
color: const Color(0xFF7C3AED),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_PointsStatItem(
|
||||
label: '圈子成员',
|
||||
label: l10n.circleMembersCountLabel,
|
||||
value: '${info.circleSize}',
|
||||
unit: '人',
|
||||
color: const Color(0xFF10B981),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_PointsStatItem(
|
||||
label: '累计获得',
|
||||
label: l10n.totalEarnedLabel,
|
||||
value: '${info.totalEarned}',
|
||||
unit: 'pts',
|
||||
color: const Color(0xFF6366F1),
|
||||
|
|
@ -352,6 +357,7 @@ class _CircleRulesCard extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
return Card(
|
||||
color: cardColor,
|
||||
elevation: 0,
|
||||
|
|
@ -361,51 +367,51 @@ class _CircleRulesCard extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Row(
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.people_alt_rounded,
|
||||
const Icon(Icons.people_alt_rounded,
|
||||
color: Color(0xFF7C3AED), size: 20),
|
||||
SizedBox(width: 6),
|
||||
Text('圈子奖励规则',
|
||||
const SizedBox(width: 6),
|
||||
Text(l10n.circleRewardRulesTitle,
|
||||
style:
|
||||
TextStyle(fontWeight: FontWeight.w600, fontSize: 15)),
|
||||
const TextStyle(fontWeight: FontWeight.w600, fontSize: 15)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_RuleItem(
|
||||
icon: Icons.card_giftcard_rounded,
|
||||
color: const Color(0xFF7C3AED),
|
||||
text: '新成员加入你的圈子,你和对方各获 200 积分欢迎礼',
|
||||
text: l10n.circleRule1,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_RuleItem(
|
||||
icon: Icons.star_rounded,
|
||||
color: const Color(0xFF6366F1),
|
||||
text: '圈子成员订阅 Pro 时,你获 1500 pts,对方获 500 pts',
|
||||
text: l10n.circleRule2,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_RuleItem(
|
||||
icon: Icons.star_rounded,
|
||||
color: const Color(0xFF7C3AED),
|
||||
text: '圈子成员订阅 Enterprise 时,你获 5000 pts,对方获 2000 pts',
|
||||
text: l10n.circleRule3,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_RuleItem(
|
||||
icon: Icons.repeat_rounded,
|
||||
color: const Color(0xFF10B981),
|
||||
text: '每月续订时你持续获得付款额 10% 的积分,最长 12 个月',
|
||||
text: l10n.circleRule4,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_RuleItem(
|
||||
icon: Icons.account_tree_rounded,
|
||||
color: const Color(0xFFF59E0B),
|
||||
text: '二级圈子续订,你获 5% 积分,最长 6 个月',
|
||||
text: l10n.circleRule5,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_RuleItem(
|
||||
icon: Icons.redeem_rounded,
|
||||
color: const Color(0xFF10B981),
|
||||
text: '积分可兑换额外使用配额或解锁智能体',
|
||||
text: l10n.circleRule6,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -422,6 +428,7 @@ class _CirclePreviewList extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final async = ref.watch(myCircleProvider);
|
||||
return async.when(
|
||||
loading: () => const SizedBox(
|
||||
|
|
@ -429,7 +436,7 @@ class _CirclePreviewList extends ConsumerWidget {
|
|||
error: (_, __) => const SizedBox.shrink(),
|
||||
data: (result) {
|
||||
if (result.items.isEmpty) {
|
||||
return _EmptyCard(cardColor: cardColor, message: '暂无圈子成员,快去邀请好友吧!');
|
||||
return _EmptyCard(cardColor: cardColor, message: l10n.noCircleMembersMessage);
|
||||
}
|
||||
final preview = result.items.take(3).toList();
|
||||
return Card(
|
||||
|
|
@ -453,16 +460,17 @@ class _CircleMemberTile extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final statusColor = member.isActive
|
||||
? const Color(0xFF10B981)
|
||||
: member.status == 'EXPIRED'
|
||||
? Colors.grey
|
||||
: const Color(0xFFF59E0B);
|
||||
final statusLabel = switch (member.status) {
|
||||
'PENDING' => '待激活',
|
||||
'ACTIVE' => '已激活',
|
||||
'REWARDED' => '已奖励',
|
||||
'EXPIRED' => '已过期',
|
||||
'PENDING' => l10n.pendingPaymentStatus,
|
||||
'ACTIVE' => l10n.activatedStatus,
|
||||
'REWARDED' => l10n.rewardedStatus,
|
||||
'EXPIRED' => l10n.expiredStatus,
|
||||
_ => member.status,
|
||||
};
|
||||
final levelLabel = member.level == 1 ? 'L1' : 'L2';
|
||||
|
|
@ -485,7 +493,7 @@ class _CircleMemberTile extends StatelessWidget {
|
|||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
'加入于 ${_formatDate(member.joinedAt)}',
|
||||
'${l10n.joinedAtLabel} ${_formatDate(member.joinedAt)}',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
trailing: Container(
|
||||
|
|
@ -517,6 +525,7 @@ class _PointsPreviewList extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final async = ref.watch(myPointsProvider);
|
||||
return async.when(
|
||||
loading: () => const SizedBox(
|
||||
|
|
@ -524,7 +533,7 @@ class _PointsPreviewList extends ConsumerWidget {
|
|||
error: (_, __) => const SizedBox.shrink(),
|
||||
data: (result) {
|
||||
if (result.transactions.isEmpty) {
|
||||
return _EmptyCard(cardColor: cardColor, message: '暂无积分记录');
|
||||
return _EmptyCard(cardColor: cardColor, message: l10n.noPointsHistoryMessage);
|
||||
}
|
||||
final preview = result.transactions.take(3).toList();
|
||||
return Card(
|
||||
|
|
@ -589,14 +598,15 @@ class _CircleListPage extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final async = ref.watch(myCircleProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('我的圈子')),
|
||||
appBar: AppBar(title: Text(l10n.referralTabPersonal)),
|
||||
body: async.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(child: Text('加载失败: $e')),
|
||||
error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')),
|
||||
data: (result) => result.items.isEmpty
|
||||
? const Center(child: Text('暂无圈子成员'))
|
||||
? Center(child: Text(l10n.noCircleMembersMessage))
|
||||
: ListView.separated(
|
||||
itemCount: result.items.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
|
|
@ -613,14 +623,15 @@ class _PointsHistoryPage extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final async = ref.watch(myPointsProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('积分记录')),
|
||||
appBar: AppBar(title: Text(l10n.pointsHistorySection)),
|
||||
body: async.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(child: Text('加载失败: $e')),
|
||||
error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')),
|
||||
data: (result) => result.transactions.isEmpty
|
||||
? const Center(child: Text('暂无积分记录'))
|
||||
? Center(child: Text(l10n.noPointsHistoryMessage))
|
||||
: ListView.separated(
|
||||
itemCount: result.transactions.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
|
|
@ -862,25 +873,25 @@ class _RewardRulesCard extends StatelessWidget {
|
|||
_RuleItem(
|
||||
icon: Icons.star_rounded,
|
||||
color: const Color(0xFF6366F1),
|
||||
text: '推荐 Pro 套餐:你获得 \$15 积分,对方获得 \$5 积分',
|
||||
text: l10n.proReferralReward,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_RuleItem(
|
||||
icon: Icons.star_rounded,
|
||||
color: const Color(0xFF7C3AED),
|
||||
text: '推荐 Enterprise 套餐:你获得 \$50 积分,对方获得 \$20 积分',
|
||||
text: l10n.enterpriseReferralReward,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_RuleItem(
|
||||
icon: Icons.repeat_rounded,
|
||||
color: const Color(0xFF10B981),
|
||||
text: '对方续订后,你持续获得每月付款额 10% 的积分,最长 12 个月',
|
||||
text: l10n.renewalBonusReward,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_RuleItem(
|
||||
icon: Icons.account_balance_wallet_outlined,
|
||||
color: const Color(0xFFF59E0B),
|
||||
text: '积分自动抵扣你的下期账单',
|
||||
text: l10n.creditDeductionReward,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -919,13 +930,14 @@ class _SectionHeader extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600, fontSize: 16)),
|
||||
TextButton(onPressed: onTap, child: const Text('查看全部')),
|
||||
TextButton(onPressed: onTap, child: Text(l10n.viewAllButton)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -1132,7 +1144,7 @@ class _ReferralListPage extends ConsumerWidget {
|
|||
appBar: AppBar(title: Text(l10n.referralRecordsSection)),
|
||||
body: async.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(child: Text('加载失败: $e')),
|
||||
error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')),
|
||||
data: (result) => result.items.isEmpty
|
||||
? Center(child: Text(l10n.noReferralsMessage))
|
||||
: ListView.separated(
|
||||
|
|
@ -1150,14 +1162,15 @@ class _RewardListPage extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final async = ref.watch(allRewardsProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('奖励历史')),
|
||||
appBar: AppBar(title: Text(l10n.rewardHistoryPageTitle)),
|
||||
body: async.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(child: Text('加载失败: $e')),
|
||||
error: (e, _) => Center(child: Text('${l10n.loadFailed}: $e')),
|
||||
data: (result) => result.items.isEmpty
|
||||
? const Center(child: Text('暂无奖励记录'))
|
||||
? Center(child: Text(l10n.noRewardsHistoryMessage))
|
||||
: ListView.separated(
|
||||
itemCount: result.items.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class SettingsDatasource {
|
|||
hapticFeedback: _prefs.getBool(_keyHaptic) ?? true,
|
||||
selectedTenantId: _prefs.getString(_keyTenantId),
|
||||
selectedTenantName: _prefs.getString(_keyTenantName),
|
||||
language: _prefs.getString(_keyLanguage) ?? 'en',
|
||||
language: _prefs.getString(_keyLanguage) ?? '',
|
||||
biometricEnabled: _prefs.getBool(_keyBiometric) ?? false,
|
||||
ttsVoice: _prefs.getString(_keyTtsVoice) ?? 'coral',
|
||||
ttsStyle: _prefs.getString(_keyTtsStyle) ?? '',
|
||||
|
|
|
|||
|
|
@ -225,11 +225,13 @@ final notificationsEnabledProvider = Provider<bool>((ref) {
|
|||
return ref.watch(settingsProvider).notificationsEnabled;
|
||||
});
|
||||
|
||||
final localeProvider = Provider<Locale>((ref) {
|
||||
/// Returns null when language is '' (auto/system), letting Flutter pick the device locale.
|
||||
final localeProvider = Provider<Locale?>((ref) {
|
||||
final lang = ref.watch(settingsProvider).language;
|
||||
return switch (lang) {
|
||||
'zh_TW' => const Locale('zh', 'TW'),
|
||||
'zh' => const Locale('zh'),
|
||||
'en' => const Locale('en'),
|
||||
_ => const Locale('zh'),
|
||||
_ => null, // '' or anything else → follow device locale
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -299,6 +299,32 @@
|
|||
"languageZh": "简体中文",
|
||||
"languageZhTW": "繁體中文",
|
||||
"languageEn": "English",
|
||||
"languageAuto": "Auto (System)",
|
||||
"referralTabTenant": "Enterprise",
|
||||
"referralTabPersonal": "My Circle",
|
||||
"loadFailed": "Load failed",
|
||||
"myPersonalInviteCode": "My personal invite code",
|
||||
"pointsBalanceTitle": "Points Balance",
|
||||
"currentBalanceLabel": "Balance",
|
||||
"circleMembersCountLabel": "Members",
|
||||
"totalEarnedLabel": "Total Earned",
|
||||
"circleRewardRulesTitle": "Circle Reward Rules",
|
||||
"circleRule1": "New member joins your circle: both get 200 pts welcome gift",
|
||||
"circleRule2": "Member subscribes Pro: you get 1500 pts, they get 500 pts",
|
||||
"circleRule3": "Member subscribes Enterprise: you get 5000 pts, they get 2000 pts",
|
||||
"circleRule4": "Earn 10% of their monthly payments for up to 12 months",
|
||||
"circleRule5": "L2 circle renewals earn you 5% for up to 6 months",
|
||||
"circleRule6": "Redeem points for usage quota or agent unlocks",
|
||||
"myCircleMembersSection": "My Circle Members",
|
||||
"pointsHistorySection": "Points History",
|
||||
"noCircleMembersMessage": "No circle members yet. Invite friends!",
|
||||
"noPointsHistoryMessage": "No points history",
|
||||
"activatedStatus": "Activated",
|
||||
"joinedAtLabel": "Joined",
|
||||
"viewAllButton": "View all",
|
||||
"rewardHistoryPageTitle": "Reward History",
|
||||
"noRewardsHistoryMessage": "No rewards yet",
|
||||
"creditDeductionReward": "Credits auto-deducted from your next bill",
|
||||
"selectLanguageTitle": "Select Language",
|
||||
"pushNotificationsLabel": "Push notifications",
|
||||
"soundLabel": "Sound",
|
||||
|
|
|
|||
|
|
@ -1503,6 +1503,84 @@ abstract class AppLocalizations {
|
|||
/// **'English'**
|
||||
String get languageEn;
|
||||
|
||||
/// No description provided for @languageAuto.
|
||||
String get languageAuto;
|
||||
|
||||
/// No description provided for @referralTabTenant.
|
||||
String get referralTabTenant;
|
||||
|
||||
/// No description provided for @referralTabPersonal.
|
||||
String get referralTabPersonal;
|
||||
|
||||
/// No description provided for @loadFailed.
|
||||
String get loadFailed;
|
||||
|
||||
/// No description provided for @myPersonalInviteCode.
|
||||
String get myPersonalInviteCode;
|
||||
|
||||
/// No description provided for @pointsBalanceTitle.
|
||||
String get pointsBalanceTitle;
|
||||
|
||||
/// No description provided for @currentBalanceLabel.
|
||||
String get currentBalanceLabel;
|
||||
|
||||
/// No description provided for @circleMembersCountLabel.
|
||||
String get circleMembersCountLabel;
|
||||
|
||||
/// No description provided for @totalEarnedLabel.
|
||||
String get totalEarnedLabel;
|
||||
|
||||
/// No description provided for @circleRewardRulesTitle.
|
||||
String get circleRewardRulesTitle;
|
||||
|
||||
/// No description provided for @circleRule1.
|
||||
String get circleRule1;
|
||||
|
||||
/// No description provided for @circleRule2.
|
||||
String get circleRule2;
|
||||
|
||||
/// No description provided for @circleRule3.
|
||||
String get circleRule3;
|
||||
|
||||
/// No description provided for @circleRule4.
|
||||
String get circleRule4;
|
||||
|
||||
/// No description provided for @circleRule5.
|
||||
String get circleRule5;
|
||||
|
||||
/// No description provided for @circleRule6.
|
||||
String get circleRule6;
|
||||
|
||||
/// No description provided for @myCircleMembersSection.
|
||||
String get myCircleMembersSection;
|
||||
|
||||
/// No description provided for @pointsHistorySection.
|
||||
String get pointsHistorySection;
|
||||
|
||||
/// No description provided for @noCircleMembersMessage.
|
||||
String get noCircleMembersMessage;
|
||||
|
||||
/// No description provided for @noPointsHistoryMessage.
|
||||
String get noPointsHistoryMessage;
|
||||
|
||||
/// No description provided for @activatedStatus.
|
||||
String get activatedStatus;
|
||||
|
||||
/// No description provided for @joinedAtLabel.
|
||||
String get joinedAtLabel;
|
||||
|
||||
/// No description provided for @viewAllButton.
|
||||
String get viewAllButton;
|
||||
|
||||
/// No description provided for @rewardHistoryPageTitle.
|
||||
String get rewardHistoryPageTitle;
|
||||
|
||||
/// No description provided for @noRewardsHistoryMessage.
|
||||
String get noRewardsHistoryMessage;
|
||||
|
||||
/// No description provided for @creditDeductionReward.
|
||||
String get creditDeductionReward;
|
||||
|
||||
/// No description provided for @selectLanguageTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
|
|
|
|||
|
|
@ -755,6 +755,84 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get languageEn => 'English';
|
||||
|
||||
@override
|
||||
String get languageAuto => 'Auto (System)';
|
||||
|
||||
@override
|
||||
String get referralTabTenant => 'Enterprise';
|
||||
|
||||
@override
|
||||
String get referralTabPersonal => 'My Circle';
|
||||
|
||||
@override
|
||||
String get loadFailed => 'Load failed';
|
||||
|
||||
@override
|
||||
String get myPersonalInviteCode => 'My personal invite code';
|
||||
|
||||
@override
|
||||
String get pointsBalanceTitle => 'Points Balance';
|
||||
|
||||
@override
|
||||
String get currentBalanceLabel => 'Balance';
|
||||
|
||||
@override
|
||||
String get circleMembersCountLabel => 'Members';
|
||||
|
||||
@override
|
||||
String get totalEarnedLabel => 'Total Earned';
|
||||
|
||||
@override
|
||||
String get circleRewardRulesTitle => 'Circle Reward Rules';
|
||||
|
||||
@override
|
||||
String get circleRule1 => 'New member joins your circle: both get 200 pts welcome gift';
|
||||
|
||||
@override
|
||||
String get circleRule2 => 'Member subscribes Pro: you get 1500 pts, they get 500 pts';
|
||||
|
||||
@override
|
||||
String get circleRule3 => 'Member subscribes Enterprise: you get 5000 pts, they get 2000 pts';
|
||||
|
||||
@override
|
||||
String get circleRule4 => 'Earn 10% of their monthly payments for up to 12 months';
|
||||
|
||||
@override
|
||||
String get circleRule5 => 'L2 circle renewals earn you 5% for up to 6 months';
|
||||
|
||||
@override
|
||||
String get circleRule6 => 'Redeem points for usage quota or agent unlocks';
|
||||
|
||||
@override
|
||||
String get myCircleMembersSection => 'My Circle Members';
|
||||
|
||||
@override
|
||||
String get pointsHistorySection => 'Points History';
|
||||
|
||||
@override
|
||||
String get noCircleMembersMessage => 'No circle members yet. Invite friends!';
|
||||
|
||||
@override
|
||||
String get noPointsHistoryMessage => 'No points history';
|
||||
|
||||
@override
|
||||
String get activatedStatus => 'Activated';
|
||||
|
||||
@override
|
||||
String get joinedAtLabel => 'Joined';
|
||||
|
||||
@override
|
||||
String get viewAllButton => 'View all';
|
||||
|
||||
@override
|
||||
String get rewardHistoryPageTitle => 'Reward History';
|
||||
|
||||
@override
|
||||
String get noRewardsHistoryMessage => 'No rewards yet';
|
||||
|
||||
@override
|
||||
String get creditDeductionReward => 'Credits auto-deducted from your next bill';
|
||||
|
||||
@override
|
||||
String get selectLanguageTitle => 'Select Language';
|
||||
|
||||
|
|
|
|||
|
|
@ -738,6 +738,84 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get languageEn => 'English';
|
||||
|
||||
@override
|
||||
String get languageAuto => '自动(跟随系统)';
|
||||
|
||||
@override
|
||||
String get referralTabTenant => '企业推荐';
|
||||
|
||||
@override
|
||||
String get referralTabPersonal => '我的圈子';
|
||||
|
||||
@override
|
||||
String get loadFailed => '加载失败';
|
||||
|
||||
@override
|
||||
String get myPersonalInviteCode => '我的个人邀请码';
|
||||
|
||||
@override
|
||||
String get pointsBalanceTitle => '积分余额';
|
||||
|
||||
@override
|
||||
String get currentBalanceLabel => '当前余额';
|
||||
|
||||
@override
|
||||
String get circleMembersCountLabel => '圈子成员';
|
||||
|
||||
@override
|
||||
String get totalEarnedLabel => '累计获得';
|
||||
|
||||
@override
|
||||
String get circleRewardRulesTitle => '圈子奖励规则';
|
||||
|
||||
@override
|
||||
String get circleRule1 => '新成员加入你的圈子,你和对方各获 200 积分欢迎礼';
|
||||
|
||||
@override
|
||||
String get circleRule2 => '圈子成员订阅 Pro 时,你获 1500 pts,对方获 500 pts';
|
||||
|
||||
@override
|
||||
String get circleRule3 => '圈子成员订阅 Enterprise 时,你获 5000 pts,对方获 2000 pts';
|
||||
|
||||
@override
|
||||
String get circleRule4 => '每月续订时你持续获得付款额 10% 的积分,最长 12 个月';
|
||||
|
||||
@override
|
||||
String get circleRule5 => '二级圈子续订,你获 5% 积分,最长 6 个月';
|
||||
|
||||
@override
|
||||
String get circleRule6 => '积分可兑换额外使用配额或解锁智能体';
|
||||
|
||||
@override
|
||||
String get myCircleMembersSection => '我的圈子成员';
|
||||
|
||||
@override
|
||||
String get pointsHistorySection => '积分记录';
|
||||
|
||||
@override
|
||||
String get noCircleMembersMessage => '暂无圈子成员,快去邀请好友吧!';
|
||||
|
||||
@override
|
||||
String get noPointsHistoryMessage => '暂无积分记录';
|
||||
|
||||
@override
|
||||
String get activatedStatus => '已激活';
|
||||
|
||||
@override
|
||||
String get joinedAtLabel => '加入于';
|
||||
|
||||
@override
|
||||
String get viewAllButton => '查看全部';
|
||||
|
||||
@override
|
||||
String get rewardHistoryPageTitle => '奖励历史';
|
||||
|
||||
@override
|
||||
String get noRewardsHistoryMessage => '暂无奖励记录';
|
||||
|
||||
@override
|
||||
String get creditDeductionReward => '积分自动抵扣你的下期账单';
|
||||
|
||||
@override
|
||||
String get selectLanguageTitle => '选择语言';
|
||||
|
||||
|
|
@ -1243,6 +1321,84 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
|
|||
@override
|
||||
String get languageEn => 'English';
|
||||
|
||||
@override
|
||||
String get languageAuto => '自動(跟隨系統)';
|
||||
|
||||
@override
|
||||
String get referralTabTenant => '企業推薦';
|
||||
|
||||
@override
|
||||
String get referralTabPersonal => '我的圈子';
|
||||
|
||||
@override
|
||||
String get loadFailed => '加載失敗';
|
||||
|
||||
@override
|
||||
String get myPersonalInviteCode => '我的個人邀請碼';
|
||||
|
||||
@override
|
||||
String get pointsBalanceTitle => '積分餘額';
|
||||
|
||||
@override
|
||||
String get currentBalanceLabel => '當前餘額';
|
||||
|
||||
@override
|
||||
String get circleMembersCountLabel => '圈子成員';
|
||||
|
||||
@override
|
||||
String get totalEarnedLabel => '累計獲得';
|
||||
|
||||
@override
|
||||
String get circleRewardRulesTitle => '圈子獎勵規則';
|
||||
|
||||
@override
|
||||
String get circleRule1 => '新成員加入你的圈子,你和對方各獲 200 積分歡迎禮';
|
||||
|
||||
@override
|
||||
String get circleRule2 => '圈子成員訂閱 Pro 時,你獲 1500 pts,對方獲 500 pts';
|
||||
|
||||
@override
|
||||
String get circleRule3 => '圈子成員訂閱 Enterprise 時,你獲 5000 pts,對方獲 2000 pts';
|
||||
|
||||
@override
|
||||
String get circleRule4 => '每月續訂時你持續獲得付款額 10% 的積分,最長 12 個月';
|
||||
|
||||
@override
|
||||
String get circleRule5 => '二級圈子續訂,你獲 5% 積分,最長 6 個月';
|
||||
|
||||
@override
|
||||
String get circleRule6 => '積分可兌換額外使用配額或解鎖智能體';
|
||||
|
||||
@override
|
||||
String get myCircleMembersSection => '我的圈子成員';
|
||||
|
||||
@override
|
||||
String get pointsHistorySection => '積分記錄';
|
||||
|
||||
@override
|
||||
String get noCircleMembersMessage => '暫無圈子成員,快去邀請好友吧!';
|
||||
|
||||
@override
|
||||
String get noPointsHistoryMessage => '暫無積分記錄';
|
||||
|
||||
@override
|
||||
String get activatedStatus => '已激活';
|
||||
|
||||
@override
|
||||
String get joinedAtLabel => '加入於';
|
||||
|
||||
@override
|
||||
String get viewAllButton => '查看全部';
|
||||
|
||||
@override
|
||||
String get rewardHistoryPageTitle => '獎勵歷史';
|
||||
|
||||
@override
|
||||
String get noRewardsHistoryMessage => '暫無獎勵記錄';
|
||||
|
||||
@override
|
||||
String get creditDeductionReward => '積分自動抵扣下期賬單';
|
||||
|
||||
@override
|
||||
String get selectLanguageTitle => '選擇語言';
|
||||
|
||||
|
|
|
|||
|
|
@ -299,6 +299,32 @@
|
|||
"languageZh": "简体中文",
|
||||
"languageZhTW": "繁體中文",
|
||||
"languageEn": "English",
|
||||
"languageAuto": "自动(跟随系统)",
|
||||
"referralTabTenant": "企业推荐",
|
||||
"referralTabPersonal": "我的圈子",
|
||||
"loadFailed": "加载失败",
|
||||
"myPersonalInviteCode": "我的个人邀请码",
|
||||
"pointsBalanceTitle": "积分余额",
|
||||
"currentBalanceLabel": "当前余额",
|
||||
"circleMembersCountLabel": "圈子成员",
|
||||
"totalEarnedLabel": "累计获得",
|
||||
"circleRewardRulesTitle": "圈子奖励规则",
|
||||
"circleRule1": "新成员加入你的圈子,你和对方各获 200 积分欢迎礼",
|
||||
"circleRule2": "圈子成员订阅 Pro 时,你获 1500 pts,对方获 500 pts",
|
||||
"circleRule3": "圈子成员订阅 Enterprise 时,你获 5000 pts,对方获 2000 pts",
|
||||
"circleRule4": "每月续订时你持续获得付款额 10% 的积分,最长 12 个月",
|
||||
"circleRule5": "二级圈子续订,你获 5% 积分,最长 6 个月",
|
||||
"circleRule6": "积分可兑换额外使用配额或解锁智能体",
|
||||
"myCircleMembersSection": "我的圈子成员",
|
||||
"pointsHistorySection": "积分记录",
|
||||
"noCircleMembersMessage": "暂无圈子成员,快去邀请好友吧!",
|
||||
"noPointsHistoryMessage": "暂无积分记录",
|
||||
"activatedStatus": "已激活",
|
||||
"joinedAtLabel": "加入于",
|
||||
"viewAllButton": "查看全部",
|
||||
"rewardHistoryPageTitle": "奖励历史",
|
||||
"noRewardsHistoryMessage": "暂无奖励记录",
|
||||
"creditDeductionReward": "积分自动抵扣你的下期账单",
|
||||
"selectLanguageTitle": "选择语言",
|
||||
"pushNotificationsLabel": "推送通知",
|
||||
"soundLabel": "提示音",
|
||||
|
|
|
|||
|
|
@ -44,6 +44,32 @@
|
|||
"languageZh": "簡體中文",
|
||||
"languageZhTW": "繁體中文",
|
||||
"languageEn": "English",
|
||||
"languageAuto": "自動(跟隨系統)",
|
||||
"referralTabTenant": "企業推薦",
|
||||
"referralTabPersonal": "我的圈子",
|
||||
"loadFailed": "加載失敗",
|
||||
"myPersonalInviteCode": "我的個人邀請碼",
|
||||
"pointsBalanceTitle": "積分餘額",
|
||||
"currentBalanceLabel": "當前餘額",
|
||||
"circleMembersCountLabel": "圈子成員",
|
||||
"totalEarnedLabel": "累計獲得",
|
||||
"circleRewardRulesTitle": "圈子獎勵規則",
|
||||
"circleRule1": "新成員加入你的圈子,你和對方各獲 200 積分歡迎禮",
|
||||
"circleRule2": "圈子成員訂閱 Pro 時,你獲 1500 pts,對方獲 500 pts",
|
||||
"circleRule3": "圈子成員訂閱 Enterprise 時,你獲 5000 pts,對方獲 2000 pts",
|
||||
"circleRule4": "每月續訂時你持續獲得付款額 10% 的積分,最長 12 個月",
|
||||
"circleRule5": "二級圈子續訂,你獲 5% 積分,最長 6 個月",
|
||||
"circleRule6": "積分可兌換額外使用配額或解鎖智能體",
|
||||
"myCircleMembersSection": "我的圈子成員",
|
||||
"pointsHistorySection": "積分記錄",
|
||||
"noCircleMembersMessage": "暫無圈子成員,快去邀請好友吧!",
|
||||
"noPointsHistoryMessage": "暫無積分記錄",
|
||||
"activatedStatus": "已激活",
|
||||
"joinedAtLabel": "加入於",
|
||||
"viewAllButton": "查看全部",
|
||||
"rewardHistoryPageTitle": "獎勵歷史",
|
||||
"noRewardsHistoryMessage": "暫無獎勵記錄",
|
||||
"creditDeductionReward": "積分自動抵扣下期賬單",
|
||||
"selectLanguageTitle": "選擇語言",
|
||||
|
||||
"chatStartConversationPrompt": "開始與 我智能體 對話",
|
||||
|
|
|
|||
Loading…
Reference in New Issue