diff --git a/frontend/admin-app/lib/app/i18n/app_localizations.dart b/frontend/admin-app/lib/app/i18n/app_localizations.dart index 6b764a0..55f4157 100644 --- a/frontend/admin-app/lib/app/i18n/app_localizations.dart +++ b/frontend/admin-app/lib/app/i18n/app_localizations.dart @@ -552,6 +552,37 @@ class AppLocalizations { 'settings_about': '关于企业券信', 'settings_logout': '退出登录', + // ── Hardcoded string fixes ── + 'credit_score_unit': '分', + 'credit_no_factors': '暂无评分因子', + 'credit_no_suggestions': '暂无建议', + 'finance_confirm_withdraw': '确认提现', + 'finance_withdraw_amount': '提现金额', + 'finance_withdraw_submitted': '提现申请已提交', + 'finance_withdraw_failed': '提现失败', + 'finance_no_transactions': '暂无交易记录', + 'finance_today': '今天', + 'finance_yesterday': '昨天', + 'finance_no_reports': '暂无对账报表', + 'redemption_failed': '核销失败', + 'redemption_batch_failed': '批量核销失败', + 'redemption_no_records': '暂无核销记录', + 'redemption_minutes_ago': '分钟前', + 'redemption_hours_ago': '小时前', + 'redemption_days_ago': '天前', + 'store_no_stores': '暂无门店', + 'store_no_employees': '暂无员工', + 'settings_confirm_logout': '确认退出', + 'settings_confirm_logout_desc': '确定要退出登录吗?', + 'settings_tier_benefits': '手续费率 1.2% · 高级数据分析', + 'coupon_status_active': '在售中', + 'coupon_status_pending': '待审核', + 'coupon_status_sold_out': '已售罄', + 'coupon_status_delisted': '已下架', + 'ai_agent_error_reply': '抱歉,暂时无法回复。请稍后重试。', + 'coupon_detail_type_info': '面值', + 'coupon_detail_issue_price_label': '发行价', + // ── AI Suggestion Card ── 'ai_suggestion_label': 'AI 建议', 'ai_suggestion_dismiss': '忽略', @@ -1117,6 +1148,37 @@ class AppLocalizations { 'settings_about': 'About Genex', 'settings_logout': 'Sign Out', + // ── Hardcoded string fixes ── + 'credit_score_unit': 'pts', + 'credit_no_factors': 'No rating factors', + 'credit_no_suggestions': 'No suggestions', + 'finance_confirm_withdraw': 'Confirm Withdrawal', + 'finance_withdraw_amount': 'Withdrawal Amount', + 'finance_withdraw_submitted': 'Withdrawal request submitted', + 'finance_withdraw_failed': 'Withdrawal failed', + 'finance_no_transactions': 'No transactions', + 'finance_today': 'Today', + 'finance_yesterday': 'Yesterday', + 'finance_no_reports': 'No reports', + 'redemption_failed': 'Redemption failed', + 'redemption_batch_failed': 'Batch redemption failed', + 'redemption_no_records': 'No redemption records', + 'redemption_minutes_ago': 'm ago', + 'redemption_hours_ago': 'h ago', + 'redemption_days_ago': 'd ago', + 'store_no_stores': 'No stores', + 'store_no_employees': 'No employees', + 'settings_confirm_logout': 'Confirm Logout', + 'settings_confirm_logout_desc': 'Are you sure you want to sign out?', + 'settings_tier_benefits': 'Fee rate 1.2% · Advanced analytics', + 'coupon_status_active': 'On Sale', + 'coupon_status_pending': 'Pending', + 'coupon_status_sold_out': 'Sold Out', + 'coupon_status_delisted': 'Delisted', + 'ai_agent_error_reply': 'Sorry, unable to respond. Please try again later.', + 'coupon_detail_type_info': 'Face Value', + 'coupon_detail_issue_price_label': 'Issue Price', + // ── AI Suggestion Card ── 'ai_suggestion_label': 'AI Suggestion', 'ai_suggestion_dismiss': 'Dismiss', @@ -1682,6 +1744,37 @@ class AppLocalizations { 'settings_about': 'Genex について', 'settings_logout': 'ログアウト', + // ── Hardcoded string fixes ── + 'credit_score_unit': '点', + 'credit_no_factors': '評価因子なし', + 'credit_no_suggestions': '提案なし', + 'finance_confirm_withdraw': '出金確認', + 'finance_withdraw_amount': '出金金額', + 'finance_withdraw_submitted': '出金申請が送信されました', + 'finance_withdraw_failed': '出金に失敗しました', + 'finance_no_transactions': '取引記録なし', + 'finance_today': '本日', + 'finance_yesterday': '昨日', + 'finance_no_reports': 'レポートなし', + 'redemption_failed': '検証に失敗しました', + 'redemption_batch_failed': '一括検証に失敗しました', + 'redemption_no_records': '検証記録なし', + 'redemption_minutes_ago': '分前', + 'redemption_hours_ago': '時間前', + 'redemption_days_ago': '日前', + 'store_no_stores': '店舗なし', + 'store_no_employees': 'スタッフなし', + 'settings_confirm_logout': 'ログアウト確認', + 'settings_confirm_logout_desc': 'ログアウトしますか?', + 'settings_tier_benefits': '手数料率 1.2% · 高度データ分析', + 'coupon_status_active': '販売中', + 'coupon_status_pending': '審査中', + 'coupon_status_sold_out': '完売', + 'coupon_status_delisted': '非公開', + 'ai_agent_error_reply': '申し訳ございません、現在応答できません。後でもう一度お試しください。', + 'coupon_detail_type_info': '額面', + 'coupon_detail_issue_price_label': '発行価格', + // ── AI Suggestion Card ── 'ai_suggestion_label': 'AI 提案', 'ai_suggestion_dismiss': '無視', diff --git a/frontend/admin-app/lib/features/ai_agent/presentation/pages/ai_agent_page.dart b/frontend/admin-app/lib/features/ai_agent/presentation/pages/ai_agent_page.dart index 5221690..a633a84 100644 --- a/frontend/admin-app/lib/features/ai_agent/presentation/pages/ai_agent_page.dart +++ b/frontend/admin-app/lib/features/ai_agent/presentation/pages/ai_agent_page.dart @@ -253,7 +253,7 @@ class _AiAgentPageState extends State { setState(() { _messages.add(_ChatMessage( isAi: true, - text: '抱歉,暂时无法回复。请稍后重试。', + text: context.t('ai_agent_error_reply'), )); _isSending = false; }); diff --git a/frontend/admin-app/lib/features/coupon_management/presentation/pages/coupon_detail_page.dart b/frontend/admin-app/lib/features/coupon_management/presentation/pages/coupon_detail_page.dart index cb99643..bd1ba67 100644 --- a/frontend/admin-app/lib/features/coupon_management/presentation/pages/coupon_detail_page.dart +++ b/frontend/admin-app/lib/features/coupon_management/presentation/pages/coupon_detail_page.dart @@ -173,7 +173,7 @@ class _IssuerCouponDetailPageState extends State { ), const SizedBox(height: 6), Text( - '${coupon.couponType} · 面值 \$${coupon.faceValue.toStringAsFixed(0)} · 发行价 \$${coupon.currentPrice.toStringAsFixed(2)}', + '${coupon.couponType} · ${context.t('coupon_detail_type_info')} \$${coupon.faceValue.toStringAsFixed(0)} · ${context.t('coupon_detail_issue_price_label')} \$${coupon.currentPrice.toStringAsFixed(2)}', style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.8)), ), const SizedBox(height: 16), diff --git a/frontend/admin-app/lib/features/coupon_management/presentation/pages/coupon_list_page.dart b/frontend/admin-app/lib/features/coupon_management/presentation/pages/coupon_list_page.dart index 128704b..2c0fd3c 100644 --- a/frontend/admin-app/lib/features/coupon_management/presentation/pages/coupon_list_page.dart +++ b/frontend/admin-app/lib/features/coupon_management/presentation/pages/coupon_list_page.dart @@ -323,19 +323,19 @@ class _CouponListPageState extends State { switch (status) { case 'active': color = AppColors.success; - label = '在售中'; + label = context.t('coupon_status_active'); break; case 'pending': color = AppColors.warning; - label = '待审核'; + label = context.t('coupon_status_pending'); break; case 'sold_out': color = AppColors.info; - label = '已售罄'; + label = context.t('coupon_status_sold_out'); break; case 'delisted': color = AppColors.textTertiary; - label = '已下架'; + label = context.t('coupon_status_delisted'); break; default: color = AppColors.textTertiary; diff --git a/frontend/admin-app/lib/features/credit/presentation/pages/credit_page.dart b/frontend/admin-app/lib/features/credit/presentation/pages/credit_page.dart index 28a7537..158e7f3 100644 --- a/frontend/admin-app/lib/features/credit/presentation/pages/credit_page.dart +++ b/frontend/admin-app/lib/features/credit/presentation/pages/credit_page.dart @@ -128,7 +128,7 @@ class _CreditPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text(credit.grade, style: const TextStyle(fontSize: 32, fontWeight: FontWeight.w700, color: Colors.white)), - Text('${credit.score}分', style: const TextStyle(fontSize: 14, color: Colors.white70)), + Text('${credit.score}${context.t('credit_score_unit')}', style: const TextStyle(fontSize: 14, color: Colors.white70)), ], ), ), @@ -171,7 +171,7 @@ class _CreditPageState extends State { children: [ Text(f.name, style: const TextStyle(fontSize: 13)), Text( - '${f.score.toInt()}分 (权重${(f.weight * 100).toInt()}%)', + '${f.score.toInt()}${context.t('credit_score_unit')} (${(f.weight * 100).toInt()}%)', style: const TextStyle(fontSize: 12, color: AppColors.textSecondary), ), ], @@ -193,7 +193,7 @@ class _CreditPageState extends State { if (factors.isEmpty) const Padding( padding: EdgeInsets.all(16), - child: Center(child: Text('暂无评分因子', style: TextStyle(color: AppColors.textTertiary))), + child: Center(child: Text(context.t('credit_no_factors'), style: const TextStyle(color: AppColors.textTertiary))), ), ], ), @@ -285,8 +285,8 @@ class _CreditPageState extends State { color: AppColors.primarySurface, borderRadius: BorderRadius.circular(10), ), - child: const Center( - child: Text('暂无建议', style: TextStyle(color: AppColors.textSecondary)), + child: Center( + child: Text(context.t('credit_no_suggestions'), style: const TextStyle(color: AppColors.textSecondary)), ), ) else diff --git a/frontend/admin-app/lib/features/dashboard/presentation/pages/issuer_dashboard_page.dart b/frontend/admin-app/lib/features/dashboard/presentation/pages/issuer_dashboard_page.dart index b78295d..6615aec 100644 --- a/frontend/admin-app/lib/features/dashboard/presentation/pages/issuer_dashboard_page.dart +++ b/frontend/admin-app/lib/features/dashboard/presentation/pages/issuer_dashboard_page.dart @@ -355,7 +355,7 @@ class _IssuerDashboardPageState extends State { children: [ Text(context.t('dashboard_credit_rating'), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)), Text( - stats != null ? '${context.t('dashboard_credit_gap')} (${stats.creditScore}分)' : context.t('dashboard_credit_gap'), + stats != null ? '${context.t('dashboard_credit_gap')} (${stats.creditScore}${context.t('credit_score_unit')})' : context.t('dashboard_credit_gap'), style: const TextStyle(fontSize: 12, color: AppColors.textTertiary), ), ], diff --git a/frontend/admin-app/lib/features/finance/presentation/pages/finance_page.dart b/frontend/admin-app/lib/features/finance/presentation/pages/finance_page.dart index 09a3f0e..4f24c49 100644 --- a/frontend/admin-app/lib/features/finance/presentation/pages/finance_page.dart +++ b/frontend/admin-app/lib/features/finance/presentation/pages/finance_page.dart @@ -111,8 +111,8 @@ class _OverviewTabState extends State<_OverviewTab> { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( - title: const Text('确认提现'), - content: Text('提现金额: \$${_balance!.withdrawable.toStringAsFixed(2)}'), + title: Text(context.t('finance_confirm_withdraw')), + content: Text('${context.t('finance_withdraw_amount')}: \$${_balance!.withdrawable.toStringAsFixed(2)}'), actions: [ TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text(context.t('cancel'))), ElevatedButton(onPressed: () => Navigator.pop(ctx, true), child: Text(context.t('confirm'))), @@ -126,14 +126,14 @@ class _OverviewTabState extends State<_OverviewTab> { await _financeService.withdraw(_balance!.withdrawable); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('提现申请已提交'), backgroundColor: AppColors.success), + SnackBar(content: Text(context.t('finance_withdraw_submitted')), backgroundColor: AppColors.success), ); _loadData(); } catch (e) { debugPrint('[FinancePage] withdraw error: $e'); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('提现失败: $e'), backgroundColor: AppColors.error), + SnackBar(content: Text('${context.t('finance_withdraw_failed')}: $e'), backgroundColor: AppColors.error), ); } } @@ -397,8 +397,8 @@ class _TransactionDetailTabState extends State<_TransactionDetailTab> { } if (_transactions.isEmpty) { - return const Center( - child: Text('暂无交易记录', style: TextStyle(color: AppColors.textSecondary)), + return Center( + child: Text(context.t('finance_no_transactions'), style: const TextStyle(color: AppColors.textSecondary)), ); } @@ -433,10 +433,10 @@ class _TransactionDetailTabState extends State<_TransactionDetailTab> { final now = DateTime.now(); final isToday = dt.year == now.year && dt.month == now.month && dt.day == now.day; final timeStr = '${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}'; - if (isToday) return '今天 $timeStr'; + if (isToday) return '${context.t('finance_today')} $timeStr'; final yesterday = now.subtract(const Duration(days: 1)); final isYesterday = dt.year == yesterday.year && dt.month == yesterday.month && dt.day == yesterday.day; - if (isYesterday) return '昨天 $timeStr'; + if (isYesterday) return '${context.t('finance_yesterday')} $timeStr'; return '${dt.month}/${dt.day} $timeStr'; } } @@ -527,17 +527,17 @@ class _ReconciliationTabState extends State<_ReconciliationTab> { const SizedBox(height: 20), if (reports.isEmpty) - const Center( + Center( child: Padding( - padding: EdgeInsets.all(32), - child: Text('暂无对账报表', style: TextStyle(color: AppColors.textSecondary)), + padding: const EdgeInsets.all(32), + child: Text(context.t('finance_no_reports'), style: const TextStyle(color: AppColors.textSecondary)), ), ) else ...reports.map((r) { final title = r['title'] ?? ''; final summary = r['summary'] ?? ''; - final status = r['status'] ?? '已生成'; + final status = r['status'] ?? '--'; return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), diff --git a/frontend/admin-app/lib/features/redemption/presentation/pages/redemption_page.dart b/frontend/admin-app/lib/features/redemption/presentation/pages/redemption_page.dart index c663fe9..a05b9c8 100644 --- a/frontend/admin-app/lib/features/redemption/presentation/pages/redemption_page.dart +++ b/frontend/admin-app/lib/features/redemption/presentation/pages/redemption_page.dart @@ -94,7 +94,7 @@ class _ScanRedeemTabState extends State<_ScanRedeemTab> { if (!mounted) return; setState(() => _isRedeeming = false); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('核销失败: $e'), backgroundColor: AppColors.error), + SnackBar(content: Text('${context.t('redemption_failed')}: $e'), backgroundColor: AppColors.error), ); } } @@ -196,7 +196,7 @@ class _ScanRedeemTabState extends State<_ScanRedeemTab> { _loadTodayStats(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('成功: ${result.successCount}, 失败: ${result.failCount}'), + content: Text('${result.successCount} / ${result.failCount} (${context.t('redemption_failed')})'), backgroundColor: result.failCount == 0 ? AppColors.success : AppColors.warning, ), ); @@ -205,7 +205,7 @@ class _ScanRedeemTabState extends State<_ScanRedeemTab> { setLocalState(() => isBatching = false); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('批量核销失败: $e'), backgroundColor: AppColors.error), + SnackBar(content: Text('${context.t('redemption_batch_failed')}: $e'), backgroundColor: AppColors.error), ); } } @@ -413,8 +413,8 @@ class _RedeemHistoryTabState extends State<_RedeemHistoryTab> { } if (_records.isEmpty) { - return const Center( - child: Text('暂无核销记录', style: TextStyle(color: AppColors.textSecondary)), + return Center( + child: Text(context.t('redemption_no_records'), style: const TextStyle(color: AppColors.textSecondary)), ); } @@ -460,9 +460,9 @@ class _RedeemHistoryTabState extends State<_RedeemHistoryTab> { String _formatTime(DateTime dt) { final now = DateTime.now(); final diff = now.difference(dt); - if (diff.inMinutes < 60) return '${diff.inMinutes}分钟前'; - if (diff.inHours < 24) return '${diff.inHours}小时前'; - if (diff.inDays < 7) return '${diff.inDays}天前'; + if (diff.inMinutes < 60) return '${diff.inMinutes}${context.t('redemption_minutes_ago')}'; + if (diff.inHours < 24) return '${diff.inHours}${context.t('redemption_hours_ago')}'; + if (diff.inDays < 7) return '${diff.inDays}${context.t('redemption_days_ago')}'; return '${dt.month}/${dt.day}'; } } diff --git a/frontend/admin-app/lib/features/settings/presentation/pages/settings_page.dart b/frontend/admin-app/lib/features/settings/presentation/pages/settings_page.dart index 46afe23..337301b 100644 --- a/frontend/admin-app/lib/features/settings/presentation/pages/settings_page.dart +++ b/frontend/admin-app/lib/features/settings/presentation/pages/settings_page.dart @@ -60,8 +60,8 @@ class _SettingsPageState extends State { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( - title: const Text('确认退出'), - content: const Text('确定要退出登录吗?'), + title: Text(context.t('settings_confirm_logout')), + content: Text(context.t('settings_confirm_logout_desc')), actions: [ TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text(context.t('cancel'))), ElevatedButton( @@ -272,7 +272,7 @@ class _SettingsPageState extends State { : context.t('settings_gold_issuer'), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.tierGold), ), - const Text('手续费率 1.2% · 高级数据分析', style: TextStyle(fontSize: 12, color: AppColors.textSecondary)), + Text(context.t('settings_tier_benefits'), style: const TextStyle(fontSize: 12, color: AppColors.textSecondary)), ], ), ), diff --git a/frontend/admin-app/lib/features/store_management/presentation/pages/store_management_page.dart b/frontend/admin-app/lib/features/store_management/presentation/pages/store_management_page.dart index 5e92877..50d53f4 100644 --- a/frontend/admin-app/lib/features/store_management/presentation/pages/store_management_page.dart +++ b/frontend/admin-app/lib/features/store_management/presentation/pages/store_management_page.dart @@ -107,8 +107,8 @@ class _StoreListTabState extends State<_StoreListTab> { } if (_stores.isEmpty) { - return const Center( - child: Text('暂无门店', style: TextStyle(color: AppColors.textSecondary)), + return Center( + child: Text(context.t('store_no_stores'), style: const TextStyle(color: AppColors.textSecondary)), ); } @@ -265,8 +265,8 @@ class _EmployeeListTabState extends State<_EmployeeListTab> { } if (_employees.isEmpty) { - return const Center( - child: Text('暂无员工', style: TextStyle(color: AppColors.textSecondary)), + return Center( + child: Text(context.t('store_no_employees'), style: const TextStyle(color: AppColors.textSecondary)), ); }