From 5bc1cbe4d85a867be73f312235a3f11e55c7b8f4 Mon Sep 17 00:00:00 2001 From: hailin Date: Thu, 12 Feb 2026 00:16:00 -0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=85=A8=E9=87=8F?= =?UTF-8?q?=E5=9B=BD=E9=99=85=E5=8C=96(i18n)=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=AE=80=E4=B8=AD/=E7=B9=81=E4=B8=AD/=E8=8B=B1=E6=96=87/?= =?UTF-8?q?=E6=97=A5=E6=96=87=E5=9B=9B=E8=AF=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概述 实现 Genex Mobile 全量国际化支持,覆盖全部 UI 字符串。 支持语言:简体中文(zh_CN)、繁体中文(zh_TW)、英文(en)、日文(ja)。 共计 720+ 翻译键值对,涉及 51 个文件。 ## 新增文件 - lib/app/i18n/strings/zh_cn.dart — 简体中文翻译(基础语言) - lib/app/i18n/strings/zh_tw.dart — 繁体中文翻译 - lib/app/i18n/strings/en.dart — 英文翻译 - lib/app/i18n/strings/ja.dart — 日文翻译 - lib/app/i18n/locale_manager.dart — 全局语言/货币状态管理器 ## i18n 基础架构 - AppLocalizations: 基于 Map 的翻译查找 - AppLocalizationsDelegate: Flutter 本地化委托集成 - context.t('key') 扩展方法:便捷取用翻译文本 - 回退链:缺失 key → zh_CN → key 本身 - LocaleManager: ValueNotifier 响应式语言切换 - 货币绑定:根据 locale 自动匹配货币符号(CNY/TWD/USD/JPY) ## 页面级国际化(46 个文件) ### 认证模块 (auth) - welcome_page, login_page, register_page, forgot_password_page ### 券模块 (coupons) - home_page, market_page, search_page, coupon_detail_page - my_coupons_page, my_coupon_detail_page, wallet_coupons_page - order_confirm_page, payment_page, payment_success_page - redeem_qr_page, receive_coupon_sheet ### 交易模块 (trading) - trading_page, trading_detail_page, sell_order_page, transfer_page ### 钱包模块 (wallet) - wallet_page, deposit_page, withdraw_page, transaction_records_page ### 用户模块 (profile) - profile_page, settings_page, kyc_page - payment_management_page, pro_mode_page ### 消息模块 (message) - message_page, message_detail_page ### 商户模块 (merchant) - merchant_home_page, merchant_ai_assistant_page ### 发行方模块 (issuer) - issuer_main_page ### AI 模块 (ai_agent) - agent_chat_page, ai_fab ### 公共组件 (shared/widgets) - coupon_card, price_tag, status_tag, empty_state - ai_confirm_dialog, kyc_badge ### 应用层 (app) - main.dart (本地化委托/Locale解析配置) - main_shell.dart, app_localizations.dart, pubspec.yaml ## 技术处理 - Widget getter → method(BuildContext): 需要 context 的属性转为方法 - const 默认参数 → nullable: 无法在 const 中使用 context.t(),改为可空参数在 build() 中解析 - Mock 数据保留中文:示例/演示数据将来自 API,无需国际化 - 语言选择器原生显示:中文/English/日本語 等按各语言原生名称展示 Co-Authored-By: Claude Opus 4.6 --- .../lib/app/i18n/app_localizations.dart | 342 ++------ .../lib/app/i18n/locale_manager.dart | 74 ++ .../genex-mobile/lib/app/i18n/strings/en.dart | 736 ++++++++++++++++++ .../genex-mobile/lib/app/i18n/strings/ja.dart | 736 ++++++++++++++++++ .../lib/app/i18n/strings/zh_cn.dart | 736 ++++++++++++++++++ .../lib/app/i18n/strings/zh_tw.dart | 736 ++++++++++++++++++ frontend/genex-mobile/lib/app/main_shell.dart | 9 +- .../presentation/pages/agent_chat_page.dart | 36 +- .../ai_agent/presentation/widgets/ai_fab.dart | 19 +- .../pages/forgot_password_page.dart | 45 +- .../auth/presentation/pages/login_page.dart | 39 +- .../presentation/pages/register_page.dart | 43 +- .../auth/presentation/pages/welcome_page.dart | 15 +- .../pages/coupon_detail_page.dart | 77 +- .../coupons/presentation/pages/home_page.dart | 49 +- .../presentation/pages/market_page.dart | 62 +- .../pages/my_coupon_detail_page.dart | 39 +- .../presentation/pages/my_coupons_page.dart | 33 +- .../pages/order_confirm_page.dart | 29 +- .../presentation/pages/payment_page.dart | 24 +- .../pages/payment_success_page.dart | 17 +- .../presentation/pages/redeem_qr_page.dart | 11 +- .../presentation/pages/search_page.dart | 19 +- .../pages/wallet_coupons_page.dart | 39 +- .../widgets/receive_coupon_sheet.dart | 13 +- .../presentation/pages/issuer_main_page.dart | 131 ++-- .../pages/merchant_ai_assistant_page.dart | 119 +-- .../pages/merchant_home_page.dart | 71 +- .../pages/message_detail_page.dart | 40 +- .../presentation/pages/message_page.dart | 15 +- .../profile/presentation/pages/kyc_page.dart | 39 +- .../pages/payment_management_page.dart | 25 +- .../presentation/pages/pro_mode_page.dart | 66 +- .../presentation/pages/profile_page.dart | 47 +- .../presentation/pages/settings_page.dart | 151 ++-- .../presentation/pages/sell_order_page.dart | 31 +- .../pages/trading_detail_page.dart | 55 +- .../presentation/pages/trading_page.dart | 17 +- .../presentation/pages/transfer_page.dart | 98 +-- .../presentation/pages/deposit_page.dart | 15 +- .../pages/transaction_records_page.dart | 35 +- .../presentation/pages/wallet_page.dart | 37 +- .../presentation/pages/withdraw_page.dart | 23 +- frontend/genex-mobile/lib/main.dart | 49 +- .../lib/shared/widgets/ai_confirm_dialog.dart | 29 +- .../lib/shared/widgets/coupon_card.dart | 31 +- .../lib/shared/widgets/empty_state.dart | 40 +- .../lib/shared/widgets/kyc_badge.dart | 3 +- .../lib/shared/widgets/price_tag.dart | 9 +- .../lib/shared/widgets/status_tag.dart | 19 +- frontend/genex-mobile/pubspec.yaml | 3 + 51 files changed, 4036 insertions(+), 1140 deletions(-) create mode 100644 frontend/genex-mobile/lib/app/i18n/locale_manager.dart create mode 100644 frontend/genex-mobile/lib/app/i18n/strings/en.dart create mode 100644 frontend/genex-mobile/lib/app/i18n/strings/ja.dart create mode 100644 frontend/genex-mobile/lib/app/i18n/strings/zh_cn.dart create mode 100644 frontend/genex-mobile/lib/app/i18n/strings/zh_tw.dart diff --git a/frontend/genex-mobile/lib/app/i18n/app_localizations.dart b/frontend/genex-mobile/lib/app/i18n/app_localizations.dart index 20f07c6..f1c8e7d 100644 --- a/frontend/genex-mobile/lib/app/i18n/app_localizations.dart +++ b/frontend/genex-mobile/lib/app/i18n/app_localizations.dart @@ -1,302 +1,62 @@ -/// Genex Mobile App - i18n 多语言支持 +import 'package:flutter/material.dart'; +import 'strings/zh_cn.dart'; +import 'strings/zh_tw.dart'; +import 'strings/en.dart'; +import 'strings/ja.dart'; + +/// Genex 多语言支持 /// -/// 支持语言: zh-CN (默认), en-US, ja-JP -/// 使用方式: AppLocalizations.of(context).translate('key') - +/// 使用方式: context.t('key') 或 AppLocalizations.of(context).get('key') +/// 支持语言: zh_CN(默认), zh_TW, en, ja class AppLocalizations { - final String locale; + final Locale locale; + late final Map _strings; - AppLocalizations(this.locale); + AppLocalizations(this.locale) { + final key = locale.countryCode != null && locale.countryCode!.isNotEmpty + ? '${locale.languageCode}_${locale.countryCode}' + : locale.languageCode; - static AppLocalizations of(dynamic context) { - // In production, obtain from InheritedWidget / Provider - return AppLocalizations('zh-CN'); + _strings = _allStrings[key] ?? _allStrings[locale.languageCode] ?? zhCN; } - String translate(String key) { - return _localizedValues[locale]?[key] ?? - _localizedValues['zh-CN']?[key] ?? - key; + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations)!; } - // Shorthand - String t(String key) => translate(key); + /// 获取翻译字符串,找不到时 fallback 到中文,再找不到返回 key + String get(String key) => _strings[key] ?? zhCN[key] ?? key; - static const supportedLocales = ['zh-CN', 'en-US', 'ja-JP']; - - static const Map> _localizedValues = { - 'zh-CN': _zhCN, - 'en-US': _enUS, - 'ja-JP': _jaJP, - }; - - static const Map _zhCN = { - // Common - 'app_name': 'Genex', - 'confirm': '确认', - 'cancel': '取消', - 'save': '保存', - 'delete': '删除', - 'edit': '编辑', - 'search': '搜索', - 'loading': '加载中...', - 'retry': '重试', - 'done': '完成', - 'next': '下一步', - 'back': '返回', - 'close': '关闭', - 'more': '更多', - 'all': '全部', - - // Tabs - 'tab_home': '首页', - 'tab_market': '市场', - 'tab_wallet': '钱包', - 'tab_profile': '我的', - - // Home - 'home_greeting': '你好', - 'home_search_hint': '搜索券、品牌...', - 'home_recommended': 'AI推荐', - 'home_hot': '热门券', - 'home_new': '新上架', - 'home_categories': '分类浏览', - - // Coupon - 'coupon_buy': '购买', - 'coupon_sell': '出售', - 'coupon_transfer': '转赠', - 'coupon_use': '使用', - 'coupon_detail': '券详情', - 'coupon_face_value': '面值', - 'coupon_price': '价格', - 'coupon_discount': '折扣', - 'coupon_valid_until': '有效期至', - 'coupon_brand': '品牌', - 'coupon_category': '类别', - 'coupon_my_coupons': '我的券', - 'coupon_available': '可用', - 'coupon_used': '已使用', - 'coupon_expired': '已过期', - - // Trading - 'trade_buy_order': '买单', - 'trade_sell_order': '卖单', - 'trade_price_input': '输入价格', - 'trade_quantity': '数量', - 'trade_total': '合计', - 'trade_history': '交易记录', - 'trade_pending': '待成交', - 'trade_completed': '已完成', - - // Wallet - 'wallet_balance': '余额', - 'wallet_deposit': '充值', - 'wallet_withdraw': '提现', - 'wallet_transactions': '交易记录', - - // Profile - 'profile_settings': '设置', - 'profile_kyc': '身份认证', - 'profile_kyc_l0': '未认证', - 'profile_kyc_l1': 'L1 基础认证', - 'profile_kyc_l2': 'L2 身份认证', - 'profile_kyc_l3': 'L3 高级认证', - 'profile_language': '语言', - 'profile_currency': '货币', - 'profile_help': '帮助中心', - 'profile_about': '关于', - 'profile_logout': '退出登录', - 'profile_pro_mode': '高级模式', - - // Payment - 'payment_method': '支付方式', - 'payment_confirm': '确认支付', - 'payment_success': '支付成功', - - // AI - 'ai_assistant': 'AI助手', - 'ai_ask': '问我任何问题...', - 'ai_suggestion': 'AI建议', - }; - - static const Map _enUS = { - // Common - 'app_name': 'Genex', - 'confirm': 'Confirm', - 'cancel': 'Cancel', - 'save': 'Save', - 'delete': 'Delete', - 'edit': 'Edit', - 'search': 'Search', - 'loading': 'Loading...', - 'retry': 'Retry', - 'done': 'Done', - 'next': 'Next', - 'back': 'Back', - 'close': 'Close', - 'more': 'More', - 'all': 'All', - - // Tabs - 'tab_home': 'Home', - 'tab_market': 'Market', - 'tab_wallet': 'Wallet', - 'tab_profile': 'Profile', - - // Home - 'home_greeting': 'Hello', - 'home_search_hint': 'Search coupons, brands...', - 'home_recommended': 'AI Picks', - 'home_hot': 'Trending', - 'home_new': 'New Arrivals', - 'home_categories': 'Categories', - - // Coupon - 'coupon_buy': 'Buy', - 'coupon_sell': 'Sell', - 'coupon_transfer': 'Gift', - 'coupon_use': 'Redeem', - 'coupon_detail': 'Coupon Details', - 'coupon_face_value': 'Face Value', - 'coupon_price': 'Price', - 'coupon_discount': 'Discount', - 'coupon_valid_until': 'Valid Until', - 'coupon_brand': 'Brand', - 'coupon_category': 'Category', - 'coupon_my_coupons': 'My Coupons', - 'coupon_available': 'Available', - 'coupon_used': 'Used', - 'coupon_expired': 'Expired', - - // Trading - 'trade_buy_order': 'Buy Order', - 'trade_sell_order': 'Sell Order', - 'trade_price_input': 'Enter Price', - 'trade_quantity': 'Quantity', - 'trade_total': 'Total', - 'trade_history': 'Trade History', - 'trade_pending': 'Pending', - 'trade_completed': 'Completed', - - // Wallet - 'wallet_balance': 'Balance', - 'wallet_deposit': 'Deposit', - 'wallet_withdraw': 'Withdraw', - 'wallet_transactions': 'Transactions', - - // Profile - 'profile_settings': 'Settings', - 'profile_kyc': 'Verification', - 'profile_kyc_l0': 'Unverified', - 'profile_kyc_l1': 'L1 Basic', - 'profile_kyc_l2': 'L2 Identity', - 'profile_kyc_l3': 'L3 Advanced', - 'profile_language': 'Language', - 'profile_currency': 'Currency', - 'profile_help': 'Help Center', - 'profile_about': 'About', - 'profile_logout': 'Log Out', - 'profile_pro_mode': 'Pro Mode', - - // Payment - 'payment_method': 'Payment Method', - 'payment_confirm': 'Confirm Payment', - 'payment_success': 'Payment Successful', - - // AI - 'ai_assistant': 'AI Assistant', - 'ai_ask': 'Ask me anything...', - 'ai_suggestion': 'AI Suggestion', - }; - - static const Map _jaJP = { - // Common - 'app_name': 'Genex', - 'confirm': '確認', - 'cancel': 'キャンセル', - 'save': '保存', - 'delete': '削除', - 'edit': '編集', - 'search': '検索', - 'loading': '読み込み中...', - 'retry': 'リトライ', - 'done': '完了', - 'next': '次へ', - 'back': '戻る', - 'close': '閉じる', - 'more': 'もっと見る', - 'all': 'すべて', - - // Tabs - 'tab_home': 'ホーム', - 'tab_market': 'マーケット', - 'tab_wallet': 'ウォレット', - 'tab_profile': 'マイページ', - - // Home - 'home_greeting': 'こんにちは', - 'home_search_hint': 'クーポン、ブランドを検索...', - 'home_recommended': 'AIおすすめ', - 'home_hot': '人気', - 'home_new': '新着', - 'home_categories': 'カテゴリー', - - // Coupon - 'coupon_buy': '購入', - 'coupon_sell': '売却', - 'coupon_transfer': '贈与', - 'coupon_use': '使用', - 'coupon_detail': 'クーポン詳細', - 'coupon_face_value': '額面', - 'coupon_price': '価格', - 'coupon_discount': '割引', - 'coupon_valid_until': '有効期限', - 'coupon_brand': 'ブランド', - 'coupon_category': 'カテゴリー', - 'coupon_my_coupons': 'マイクーポン', - 'coupon_available': '利用可能', - 'coupon_used': '使用済み', - 'coupon_expired': '期限切れ', - - // Trading - 'trade_buy_order': '買い注文', - 'trade_sell_order': '売り注文', - 'trade_price_input': '価格を入力', - 'trade_quantity': '数量', - 'trade_total': '合計', - 'trade_history': '取引履歴', - 'trade_pending': '未約定', - 'trade_completed': '約定済み', - - // Wallet - 'wallet_balance': '残高', - 'wallet_deposit': '入金', - 'wallet_withdraw': '出金', - 'wallet_transactions': '取引履歴', - - // Profile - 'profile_settings': '設定', - 'profile_kyc': '本人確認', - 'profile_kyc_l0': '未確認', - 'profile_kyc_l1': 'L1 基本認証', - 'profile_kyc_l2': 'L2 身分認証', - 'profile_kyc_l3': 'L3 高度認証', - 'profile_language': '言語', - 'profile_currency': '通貨', - 'profile_help': 'ヘルプ', - 'profile_about': 'アプリについて', - 'profile_logout': 'ログアウト', - 'profile_pro_mode': 'プロモード', - - // Payment - 'payment_method': '支払い方法', - 'payment_confirm': '支払いを確認', - 'payment_success': '支払い完了', - - // AI - 'ai_assistant': 'AIアシスタント', - 'ai_ask': '何でも聞いてください...', - 'ai_suggestion': 'AIの提案', + static const Map> _allStrings = { + 'zh': zhCN, + 'zh_CN': zhCN, + 'zh_TW': zhTW, + 'en': en, + 'ja': ja, }; } + +/// LocalizationsDelegate +class AppLocalizationsDelegate extends LocalizationsDelegate { + const AppLocalizationsDelegate(); + + @override + bool isSupported(Locale locale) { + return ['zh', 'en', 'ja'].contains(locale.languageCode); + } + + @override + Future load(Locale locale) async { + return AppLocalizations(locale); + } + + @override + bool shouldReload(covariant LocalizationsDelegate old) => + false; +} + +/// BuildContext 扩展,快捷访问翻译 +extension AppLocalizationsExtension on BuildContext { + /// 快捷翻译: context.t('key') + String t(String key) => AppLocalizations.of(this).get(key); +} diff --git a/frontend/genex-mobile/lib/app/i18n/locale_manager.dart b/frontend/genex-mobile/lib/app/i18n/locale_manager.dart new file mode 100644 index 0000000..c75ea98 --- /dev/null +++ b/frontend/genex-mobile/lib/app/i18n/locale_manager.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; + +/// 全局语言状态管理 +/// +/// userLocale = null 时跟随系统语言 +/// 用户在设置页选择后,设置为具体 Locale +class LocaleManager { + static final ValueNotifier userLocale = ValueNotifier(null); + + static const supportedLocales = [ + Locale('zh', 'CN'), + Locale('zh', 'TW'), + Locale('en'), + Locale('ja'), + ]; + + /// 根据系统 locale 解析最佳匹配 + static Locale resolve(List? systemLocales, Iterable 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(); + } + } +} diff --git a/frontend/genex-mobile/lib/app/i18n/strings/en.dart b/frontend/genex-mobile/lib/app/i18n/strings/en.dart new file mode 100644 index 0000000..1ef93c3 --- /dev/null +++ b/frontend/genex-mobile/lib/app/i18n/strings/en.dart @@ -0,0 +1,736 @@ +const Map en = { + // ============ Common ============ + 'common.confirm': 'Confirm', + 'common.cancel': 'Cancel', + 'common.save': 'Save', + 'common.delete': 'Delete', + 'common.edit': 'Edit', + 'common.search': 'Search', + 'common.loading': 'Loading...', + 'common.retry': 'Retry', + 'common.done': 'Done', + 'common.next': 'Next', + 'common.back': 'Back', + 'common.close': 'Close', + 'common.more': 'More', + 'common.all': 'All', + 'common.filter': 'Filter', + 'common.sort': 'Sort', + 'common.copy': 'Copy', + 'common.copied': 'Copied to clipboard', + 'common.today': 'Today', + 'common.thisWeek': 'This Week', + 'common.thisMonth': 'This Month', + + // ============ Navigation ============ + 'nav.home': 'Home', + 'nav.trading': 'Trade', + 'nav.messages': 'Messages', + 'nav.profile': 'Profile', + + // ============ Welcome / Auth ============ + 'welcome.slogan': 'Make Every Coupon Count', + 'welcome.phoneRegister': 'Phone Sign Up', + 'welcome.emailRegister': 'Email Sign Up', + 'welcome.otherLogin': 'Other Login Methods', + 'welcome.hasAccount': 'Already have an account?', + 'welcome.login': 'Log In', + 'welcome.agreement': 'By signing up you agree to Terms of Service and Privacy Policy', + + 'login.title': 'Welcome Back', + 'login.subtitle': 'Log in to manage your coupon assets', + 'login.passwordTab': 'Password', + 'login.codeTab': 'Verification Code', + 'login.phoneOrEmail': 'Phone or Email', + 'login.password': 'Password', + 'login.forgotPassword': 'Forgot password?', + 'login.submit': 'Log In', + 'login.phone': 'Phone', + 'login.verifyCode': 'Code', + 'login.getCode': 'Get Code', + + 'register.title': 'Create Account', + 'register.emailSubtitle': 'Sign up with your email', + 'register.phoneSubtitle': 'Sign up with your phone number', + 'register.email': 'Email', + 'register.phone': 'Phone', + 'register.emailHint': 'Enter your email', + 'register.phoneHint': 'Enter your phone number', + 'register.code': 'Code', + 'register.codeHint': 'Enter 6-digit code', + 'register.getCode': 'Get Code', + 'register.setPassword': 'Set Password', + 'register.passwordHint': '8-20 chars, letters and numbers', + 'register.agreement': 'I have read and agree to', + 'register.userAgreement': 'Terms of Service', + 'register.privacyPolicy': 'Privacy Policy', + 'register.submit': 'Sign Up', + 'register.stepVerify': 'Verify', + 'register.stepPassword': 'Password', + 'register.stepDone': 'Done', + 'register.rule8chars': '8+ chars', + 'register.ruleLetter': 'Letters', + 'register.ruleNumber': 'Numbers', + + 'forgot.title': 'Reset Password', + 'forgot.inputAccount': 'Enter phone or email', + 'forgot.sendHint': 'We\'ll send you a verification code', + 'forgot.accountHint': 'Phone / Email', + 'forgot.getCode': 'Get Code', + 'forgot.inputCode': 'Enter Code', + 'forgot.codeSentTo': 'Code sent to', + 'forgot.codeHint': '6-digit code', + 'forgot.resend': 'Resend', + 'forgot.next': 'Next', + 'forgot.setNewPassword': 'Set New Password', + 'forgot.newPasswordHint': 'Enter new password (8+ chars)', + 'forgot.newPassword': 'New Password', + 'forgot.confirmPassword': 'Confirm Password', + 'forgot.confirmChange': 'Confirm', + 'forgot.success': 'Password Changed', + 'forgot.successHint': 'Please log in with your new password', + 'forgot.backToLogin': 'Back to Login', + + // ============ Home ============ + 'home.searchHint': 'Search coupons, brands, categories...', + 'home.position': 'Holdings', + 'home.hold': 'Hold', + 'home.couponUnit': ' coupons', + 'home.totalValue': 'Value', + 'home.receive': 'Receive', + 'home.transfer': 'Gift', + 'home.sell': 'Sell', + 'home.redeem': 'Redeem', + 'home.flashSale': 'Flash Sale', + 'home.newRelease': 'New', + 'home.discountRank': 'Top Deals', + 'home.expiringSoon': 'Expiring', + 'home.priceCompare': 'Compare', + 'home.resaleMarket': 'Resale', + 'home.hotTrades': 'Hot Trades', + 'home.viewAll': 'All', + 'home.aiRecommend': 'AI Picks', + 'home.aiRecommendDesc': 'Found 3 great deals based on your preferences', + 'home.featuredCoupons': 'Featured Coupons', + 'home.viewAllCoupons': 'View All', + + // ============ Market ============ + 'market.title': 'Trade', + 'market.primary': 'Primary Market', + 'market.secondary': 'Secondary Market', + 'market.dining': 'Dining', + 'market.shopping': 'Shopping', + 'market.entertainment': 'Entertainment', + 'market.travel': 'Travel', + 'market.lifestyle': 'Lifestyle', + 'market.sports': 'Sports', + 'market.discountRate': 'Discount', + 'market.priceUp': 'Price ↑', + 'market.priceDown': 'Price ↓', + 'market.expiryDate': 'Expiry', + 'market.issuePrice': 'Issue Price', + 'market.faceValue': 'Face Value', + 'market.discount': 'Discount', + 'market.totalSupply': 'Supply', + 'market.salesProgress': 'Sales Progress', + 'market.upcoming': 'Upcoming', + 'market.subscribing': 'Live', + 'market.ended': 'Ended', + 'market.timeToStart': 'Starts in', + 'market.couponBrand': 'Coupon / Brand', + 'market.latestPrice': 'Price', + 'market.change24h': '24h Change', + 'market.discountSuffix': ' off', + + // ============ Trading Detail ============ + 'trading.depth': 'Order Book', + 'trading.askSide': 'Ask', + 'trading.bidSide': 'Bid', + 'trading.quantitySheets': 'Qty (pcs)', + 'trading.placeOrder': 'Place Order', + 'trading.buy': 'Buy', + 'trading.sell': 'Sell', + 'trading.limitOrder': 'Limit', + 'trading.marketOrder': 'Market', + 'trading.price': 'Price', + 'trading.quantity': 'Qty', + 'trading.available': 'Available', + 'trading.currentOrders': 'Open Orders', + 'trading.historyOrders': 'History', + 'trading.cancelOrder': 'Cancel', + 'trading.high24h': '24h High', + 'trading.low24h': '24h Low', + 'trading.open': 'Open', + 'trading.vol24h': '24h Vol', + 'trading.sheetsUnit': 'pcs', + 'trading.discountLabel': 'Discount', + + // ============ Transfer ============ + 'transfer.title': 'Gift Transfer', + 'transfer.scanTransfer': 'Scan to Transfer', + 'transfer.scanDesc': 'Scan recipient\'s code\nInstant delivery', + 'transfer.inputTransfer': 'Enter ID/Email/Phone', + 'transfer.inputDesc': 'Enter recipient info\nSecure transfer', + 'transfer.recentRecipients': 'Recent Recipients', + 'transfer.manage': 'Manage', + 'transfer.noRecent': 'No recent transfers\nCompleted transfers will appear here', + 'transfer.expired': 'Expired', + 'transfer.refresh': 'Refresh', + 'transfer.transferHistory': 'Transfer History', + 'transfer.viewAll': 'View All', + 'transfer.lastTransfer': 'Last Transfer', + 'transfer.inputRecipient': 'Enter Recipient', + 'transfer.recipientIdHint': 'ID / Email / Phone', + 'transfer.paste': 'Paste', + 'transfer.pasteFromClipboard': 'Paste from clipboard', + 'transfer.searchRecipient': 'Search', + 'transfer.selectCoupon': 'Select Coupon to Transfer', + 'transfer.noCoupons': 'No coupons available', + 'transfer.confirmTransfer': 'Confirm Transfer', + 'transfer.transferTo': 'Transfer to', + 'transfer.couponInfo': 'Coupon', + 'transfer.confirmBtn': 'Confirm Transfer', + 'transfer.outgoing': 'Sent', + 'transfer.incoming': 'Received', + + // ============ Wallet Coupons ============ + 'walletCoupons.title': 'My Holdings', + 'walletCoupons.receiveTip': 'Receive Coupon', + 'walletCoupons.usable': 'Active', + 'walletCoupons.pendingRedeem': 'Pending', + 'walletCoupons.expired': 'Expired', + 'walletCoupons.holdCount': 'Coupons Held', + 'walletCoupons.totalFaceValue': 'Total Value', + 'walletCoupons.saved': 'Saved', + 'walletCoupons.faceValue': 'Value', + 'walletCoupons.expiredText': 'Expired', + 'walletCoupons.expiringToday': 'Expires today', + 'walletCoupons.daysToExpiry': 'd left', + 'walletCoupons.expiryFormat': 'Expires', + + // ============ Coupon Detail ============ + 'couponDetail.title': 'Coupon Details', + 'couponDetail.saveBadge': 'Save', + 'couponDetail.faceValue': 'Face Value', + 'couponDetail.validUntil': 'Valid Until', + 'couponDetail.type': 'Type', + 'couponDetail.issuer': 'Issuer', + 'couponDetail.consumeCoupon': 'Voucher', + 'couponDetail.usageNote': 'Usage Notes', + 'couponDetail.allStores': 'Valid at all nationwide stores', + 'couponDetail.canTransfer': 'Can be gifted to others', + 'couponDetail.useAnytime': 'Use anytime before expiry', + 'couponDetail.noStack': 'Cannot be combined', + 'couponDetail.noCash': 'Not redeemable for cash', + 'couponDetail.stores': 'Stores', + 'couponDetail.storeCount': '12,800+ Stores Nationwide', + 'couponDetail.storeDesc': 'Valid at all official stores nationwide', + 'couponDetail.priceTrend': 'Price Trend', + 'couponDetail.last30Days': 'Last 30 Days', + 'couponDetail.highest': 'High', + 'couponDetail.lowest': 'Low', + 'couponDetail.average': 'Avg', + 'couponDetail.tradeHistory': 'Trade History', + 'couponDetail.nearbyStores': 'Nearby Stores', + 'couponDetail.distance': 'Distance', + 'couponDetail.open': 'Open', + 'couponDetail.similar': 'Similar Coupons', + 'couponDetail.favorite': 'Favorite', + 'couponDetail.buyNow': 'Buy Now', + + // ============ My Coupon Detail ============ + 'myCoupon.title': 'My Coupon', + 'myCoupon.active': 'Active', + 'myCoupon.showQrHint': 'Show this QR code to the merchant to redeem', + 'myCoupon.switchBarcode': 'Switch to Barcode', + 'myCoupon.faceValue': 'Face Value', + 'myCoupon.purchasePrice': 'Purchase Price', + 'myCoupon.validUntil': 'Valid Until', + 'myCoupon.orderNo': 'Order No.', + 'myCoupon.resellCount': 'Resale Remaining', + 'myCoupon.transfer': 'Gift', + 'myCoupon.sell': 'Sell', + 'myCoupon.usageNote': 'Usage Notes', + 'myCoupon.useInStore': 'Valid at all nationwide stores', + 'myCoupon.useInTime': 'Use before expiry date', + 'myCoupon.onePerVisit': 'One coupon per visit', + 'myCoupon.noCash': 'Not redeemable for cash', + 'myCoupon.extractToWallet': 'Withdraw to External Wallet', + 'myCoupon.requireKycL2': 'Requires KYC L2+', + 'myCoupon.viewTrades': 'View Trade Records', + 'myCoupon.help': 'Help', + + // ============ Order Confirm ============ + 'orderConfirm.title': 'Confirm Order', + 'orderConfirm.quantity': 'Quantity', + 'orderConfirm.paymentMethod': 'Payment Method', + 'orderConfirm.bankCard': 'Bank/Credit Card', + 'orderConfirm.priceDetail': 'Price Details', + 'orderConfirm.buyingNote': 'You are purchasing a voucher for consumption', + 'orderConfirm.total': 'Total', + 'orderConfirm.confirmPay': 'Confirm Payment', + 'orderConfirm.unitPrice': 'Unit Price', + 'orderConfirm.count': 'Qty', + 'orderConfirm.saveBadge': 'Savings vs Face Value', + 'orderConfirm.biometricHint': 'Verify with fingerprint or face to complete payment', + 'orderConfirm.usePasswordPay': 'Use Password Instead', + + // ============ Payment ============ + 'payment.title': 'Select Payment Method', + 'payment.addNew': 'Add New Payment Method', + 'payment.confirmPay': 'Confirm Payment', + 'payment.bankTransfer': 'Bank Transfer', + + 'paymentSuccess.title': 'Payment Successful', + 'paymentSuccess.hint': 'Coupon added to your holdings', + 'paymentSuccess.couponName': 'Coupon', + 'paymentSuccess.payAmount': 'Amount Paid', + 'paymentSuccess.orderNo': 'Order No.', + 'paymentSuccess.payTime': 'Payment Time', + 'paymentSuccess.viewMyCoupon': 'View My Coupons', + 'paymentSuccess.continueBrowse': 'Continue Browsing', + + // ============ Search ============ + 'search.hint': 'Search coupons, brands, categories...', + 'search.cancel': 'Cancel', + 'search.hotSearch': 'Trending Searches', + 'search.history': 'Search History', + 'search.clear': 'Clear', + 'search.diningCoupon': 'Dining', + 'search.discountCoupon': 'Discount', + 'search.travel': 'Travel', + + // ============ Redeem ============ + 'redeem.title': 'Show Code', + 'redeem.faceValue': 'Face Value', + 'redeem.validTime': 'Valid Time', + 'redeem.refresh': 'Refresh Code', + 'redeem.showHint': 'Show this code to merchant. Screen brightness auto-maximized.', + + // ============ Sell Order ============ + 'sellOrder.title': 'List for Sale', + 'sellOrder.faceValue': 'Face Value', + 'sellOrder.credit': 'Rating', + 'sellOrder.setPrice': 'Set Price', + 'sellOrder.price': 'Price', + 'sellOrder.aiSuggest': 'AI Suggested Price', + 'sellOrder.bestDealRate': 'Best chance of selling', + 'sellOrder.discountRate': 'Discount', + 'sellOrder.platformFee': 'Platform Fee (1.5%)', + 'sellOrder.estimatedReceive': 'Est. Receive', + 'sellOrder.marketAvg': 'Market Average', + 'sellOrder.recent24hTrades': '24h Trades', + 'sellOrder.tradesUnit': ' trades', + 'sellOrder.confirmList': 'Confirm Listing', + 'sellOrder.success': 'Listed Successfully', + 'sellOrder.successHint': 'Your coupon is now on the market. It will be auto-matched when a buyer places an order.', + 'sellOrder.ok': 'OK', + + // ============ Trading Page ============ + 'tradingPage.title': 'My Trades', + 'tradingPage.pendingOrders': 'Open Orders', + 'tradingPage.tradeRecords': 'Trade History', + 'tradingPage.listPrice': 'List Price', + 'tradingPage.listTime': 'Listed', + 'tradingPage.cancelOrder': 'Cancel', + + // ============ Wallet ============ + 'wallet.myBalance': 'My Balance', + 'wallet.totalBalance': 'Total Balance', + 'wallet.withdrawable': 'Withdrawable', + 'wallet.frozen': 'Frozen', + 'wallet.deposit': 'Deposit', + 'wallet.withdraw': 'Withdraw', + 'wallet.records': 'Records', + + 'deposit.title': 'Deposit', + 'deposit.currentBalance': 'Current Balance', + 'deposit.amount': 'Amount', + 'deposit.custom': 'Custom Amount', + 'deposit.paymentMethod': 'Payment Method', + 'deposit.submit': 'Deposit', + + 'withdraw.title': 'Withdraw', + 'withdraw.availableBalance': 'Available Balance', + 'withdraw.amount': 'Amount', + 'withdraw.all': 'All', + 'withdraw.to': 'Withdraw to', + 'withdraw.savingsAccount': 'Savings Account', + 'withdraw.fee': 'Fee (0.5%)', + 'withdraw.actualReceive': 'You\'ll Receive', + 'withdraw.estimateTime': 'Estimated 1-2 business days', + 'withdraw.submit': 'Confirm Withdrawal', + + 'txRecords.title': 'Transaction Records', + 'txRecords.buy': 'Buy', + 'txRecords.sell': 'Sell', + 'txRecords.transfer': 'Gift', + 'txRecords.noRecords': 'No records', + 'txRecords.orderNo': 'Order No.', + 'txRecords.transferTo': 'Gifted to', + + // ============ Profile ============ + 'profile.favorites': 'Favorites', + 'profile.orders': 'Orders', + 'profile.coupons': 'Coupons', + 'profile.wallet': 'Wallet', + 'profile.myTrades': 'My Trades', + 'profile.walletBalance': 'Balance', + 'profile.paymentManage': 'Payment', + 'profile.kyc': 'Verification', + 'profile.proMode': 'Pro Mode', + 'profile.settings': 'Settings', + 'profile.helpCenter': 'Help', + 'profile.issuerPortal': 'Issuer Portal', + 'profile.merchantPortal': 'Merchant Portal', + + // ============ Settings ============ + 'settings.title': 'Settings', + 'settings.accountSecurity': 'Account & Security', + 'settings.phone': 'Phone', + 'settings.email': 'Email', + 'settings.changePassword': 'Change Password', + 'settings.identity': 'Verification', + 'settings.paymentManage': 'Payment Management', + 'settings.paymentMethod': 'Payment Methods', + 'settings.bankAccount': 'Bank Account', + 'settings.paymentPassword': 'Payment Password', + 'settings.notifications': 'Notifications', + 'settings.tradeNotify': 'Trade Alerts', + 'settings.expiryRemind': 'Expiry Reminders', + 'settings.marketChange': 'Market Updates', + 'settings.marketingPush': 'Promotions', + 'settings.general': 'General', + 'settings.language': 'Language', + 'settings.currency': 'Currency', + 'settings.clearCache': 'Clear Cache', + 'settings.about': 'About', + 'settings.version': 'Version', + 'settings.userAgreement': 'Terms of Service', + 'settings.privacyPolicy': 'Privacy Policy', + 'settings.helpCenter': 'Help Center', + 'settings.logout': 'Log Out', + 'settings.selectCurrency': 'Select Currency', + 'settings.currencyNote': 'This affects currency display in all trading pages', + 'settings.selectLanguage': 'Select Language', + 'settings.currencySymbol': 'Symbol', + + // ============ KYC ============ + 'kyc.title': 'Verification', + 'kyc.currentLevel': 'Current Level', + 'kyc.l1Title': 'L1 Basic', + 'kyc.l1Desc': 'Phone + Email verification', + 'kyc.l1Limit': 'Daily limit \$500', + 'kyc.l1Feature': 'Buy coupons, redeem at stores', + 'kyc.l2Title': 'L2 Identity', + 'kyc.l2Desc': 'ID / Passport verification', + 'kyc.l2Limit': 'Daily limit \$5,000', + 'kyc.l2Feature': 'Unlock trading & P2P transfers', + 'kyc.l3Title': 'L3 Advanced', + 'kyc.l3Desc': 'Video review + Proof of address', + 'kyc.l3Limit': 'No limit', + 'kyc.l3Feature': 'Unlimited trading & withdrawals', + 'kyc.completed': 'Completed', + 'kyc.goVerify': 'Verify', + + // ============ Payment Management ============ + 'payManage.title': 'Payment Management', + 'payManage.myCards': 'My Cards', + 'payManage.addCard': 'Add New Card', + 'payManage.bankAccount': 'Bank Account (for withdrawals)', + 'payManage.paymentSecurity': 'Payment Security', + 'payManage.paymentPassword': 'Payment Password', + 'payManage.passwordSet': 'Set', + 'payManage.biometricPay': 'Biometric Payment', + 'payManage.biometricEnabled': 'Enabled', + 'payManage.noPasswordPay': 'Quick Pay', + 'payManage.noPasswordLimit': 'Up to \$10/txn', + + // ============ AI Chat ============ + 'aiChat.title': 'AI Assistant', + 'aiChat.greeting': 'Hi! I\'m Genex AI Assistant. I can help you find great deals, compare prices, and recommend combos. Try asking:', + 'aiChat.suggest1': 'Recommend coupons for me', + 'aiChat.suggest2': 'Is Starbucks coupon worth buying?', + 'aiChat.suggest3': 'Help me compare prices', + 'aiChat.suggest4': 'My coupon is expiring soon', + 'aiChat.inputHint': 'Ask me anything about coupons...', + 'aiChat.confirmAction': 'Confirm', + 'aiChat.riskLow': 'Low Risk', + 'aiChat.riskNormal': 'Confirm', + 'aiChat.riskHigh': 'High Risk', + + // ============ Messages ============ + 'message.title': 'Messages', + 'message.detailTitle': 'Message Details', + 'message.tradeNotify': 'Trade Alert', + 'message.expiryRemind': 'Expiry Reminder', + 'message.systemNotify': 'System Notice', + 'message.promoNotify': 'Promotion', + 'message.tradeSuccess': 'Trade Successful', + 'message.couponName': 'Coupon', + 'message.faceValue': 'Face Value', + 'message.payAmount': 'Amount Paid', + 'message.orderNo': 'Order No.', + 'message.payMethod': 'Payment Method', + 'message.viewCouponDetail': 'View Coupon Details', + + // ============ Status Tags ============ + 'status.active': 'Active', + 'status.pending': 'Pending', + 'status.expired': 'Expired', + 'status.used': 'Used', + 'status.processing': 'Processing', + 'status.completed': 'Completed', + 'status.cancelled': 'Cancelled', + 'status.refunding': 'Refunding', + 'status.onSale': 'On Sale', + + // ============ Empty States ============ + 'empty.noCoupons': 'No Coupons Yet', + 'empty.noCouponsHint': 'Browse the market for great deals', + 'empty.browse': 'Browse', + 'empty.noTrades': 'No Trades Yet', + 'empty.noTradesHint': 'Your trade records will appear here', + 'empty.noResults': 'No Results Found', + 'empty.noResultsHint': 'Try a different keyword', + 'empty.noMessages': 'No Messages', + 'empty.noMessagesHint': 'Trade alerts and notifications will appear here', + 'empty.networkError': 'Network Error', + 'empty.networkErrorHint': 'Please check your connection and try again', + + // ============ Receive Coupon ============ + 'receiveCoupon.title': 'Receive Coupon', + 'receiveCoupon.hint': 'Show the QR code or ID below to the sender. They can scan or enter the ID to transfer a coupon to you.', + 'receiveCoupon.id': 'Receive ID', + 'receiveCoupon.idCopied': 'Receive ID copied to clipboard', + 'receiveCoupon.note': 'Received coupons will be automatically added to your holdings.', + + // ============ Issuer ============ + 'issuer.title': 'Issuer Portal', + 'issuer.verified': 'Verified Issuer', + 'issuer.overview': 'Overview', + 'issuer.issue': 'Issue', + 'issuer.redeem': 'Redeem', + 'issuer.finance': 'Finance', + 'issuer.totalIssued': 'Total Issued', + 'issuer.totalSold': 'Sold', + 'issuer.totalRedeemed': 'Redeemed', + 'issuer.redeemRate': 'Redeem Rate', + 'issuer.quickActions': 'Quick Actions', + 'issuer.createCoupon': 'Create Coupon', + 'issuer.storeManage': 'Stores', + 'issuer.salesAnalysis': 'Analytics', + 'issuer.statement': 'Statements', + 'issuer.myCoupons': 'My Coupons', + 'issuer.listed': 'Listed', + 'issuer.underReview': 'Under Review', + 'issuer.soldOut': 'Sold Out', + 'issuer.issuedSlash': 'Issued', + 'issuer.sold': 'Sold', + 'issuer.issueCenter': 'Issue Center', + 'issuer.selectTemplate': 'Select Template', + 'issuer.voucherType': 'Voucher', + 'issuer.discountType': 'Discount', + 'issuer.giftCardType': 'Gift Card', + 'issuer.storedValueType': 'Stored Value', + 'issuer.couponManage': 'Manage', + 'issuer.viewAll': 'View All', + 'issuer.couponEvents': 'Events', + 'issuer.createNew': 'Create New', + 'issuer.redeemManage': 'Redemption', + 'issuer.redeemTrend': 'Redemption Trends', + 'issuer.allStores': 'All Stores', + 'issuer.employees': ' staff', + 'issuer.financeManage': 'Finance', + 'issuer.totalSales': 'Total Sales', + 'issuer.settled': 'Settled', + 'issuer.pendingSettle': 'Pending', + 'issuer.breakage': 'Breakage', + 'issuer.withdrawBtn': 'Withdraw', + 'issuer.reportBtn': 'Reports', + 'issuer.settleDetail': 'Settlement Details', + 'issuer.creditLevel': 'Credit Rating', + 'issuer.issueQuota': 'Issue Quota', + 'issuer.usedQuota': 'Used Quota', + 'issuer.dataCenter': 'Data Center', + 'issuer.issueSalesRate': 'Issue/Sales/Redeem Rate', + 'issuer.userProfile': 'User Insights', + 'issuer.userProfileDesc': 'Buyer demographics', + 'issuer.creditDetail': 'Credit Details', + 'issuer.creditDetailDesc': 'Score and improvement tips', + 'issuer.quotaChange': 'Quota Changes', + 'issuer.quotaChangeDesc': 'Historical quota adjustments', + 'issuer.companyInfo': 'Company Info', + 'issuer.companyInfoDesc': 'License / Contacts', + 'issuer.settingsItem': 'Settings', + 'issuer.settingsItemDesc': 'Notifications / Security', + 'issuer.helpItem': 'Help Center', + 'issuer.helpItemDesc': 'FAQ & Support', + + // ============ Merchant ============ + 'merchant.today': 'Today', + 'merchant.onlineMode': 'Online Mode', + 'merchant.offlineMode': 'Offline Mode', + 'merchant.pendingSync': 'Pending Sync', + 'merchant.syncUnit': ' txns', + 'merchant.scanHint': 'Align coupon QR code in the frame', + 'merchant.flashlight': 'Flashlight', + 'merchant.manualInput': 'Manual Entry', + 'merchant.redeemRecords': 'Records', + 'merchant.storeData': 'Store Data', + 'merchant.inputCode': 'Enter Coupon Code', + 'merchant.inputCodeHint': 'Enter code', + 'merchant.query': 'Look Up', + 'merchant.userNickname': 'User', + 'merchant.consumer': 'Consumer', + 'merchant.couponName': 'Coupon', + 'merchant.faceValue': 'Value', + 'merchant.validUntil': 'Valid Until', + 'merchant.useCondition': 'Conditions', + 'merchant.noMinSpend': 'No minimum spend', + 'merchant.confirmRedeem': 'Confirm Redemption', + 'merchant.redeemSuccess': 'Redeemed', + 'merchant.continueRedeem': 'Continue', + 'merchant.synced': 'Synced', + 'merchant.pendingSyncLabel': 'Pending', + 'merchant.redeemOperator': 'Operator', + 'merchant.todayRedeem': 'Today', + 'merchant.redeemAmount': 'Amount', + 'merchant.weekTrend': 'Weekly Trend', + 'merchant.operatorRank': 'Operator Ranking', + + // ============ Profile Page (additional) ============ + 'profile.account': 'Account', + 'profile.trade': 'Trades', + 'profile.holdCoupons': 'Held', + 'profile.saved': 'Saved', + 'profile.credit': 'Credit', + 'profile.creditScore': 'Credit Score', + 'profile.myFavorites': 'My Favorites', + 'profile.securitySettings': 'Security', + 'profile.advancedSettings': 'Advanced', + 'profile.aboutGenex': 'About Genex', + 'profile.simplifiedChinese': 'Simplified Chinese', + + // ============ Pro Mode ============ + 'proMode.toggleDesc': 'View on-chain info and connect external wallets', + 'proMode.requireKycL2': 'Requires KYC L2 or above', + 'proMode.connected': 'Connected', + 'proMode.disconnect': 'Disconnect', + 'proMode.connectWallet': 'Connect External Wallet', + 'proMode.walletDesc': 'After connecting, you can withdraw platform assets to your own address', + 'proMode.showChainAddress': 'Show Chain Address', + 'proMode.showChainAddressDesc': 'Display contract address in coupon details', + 'proMode.showTxHash': 'Show Tx Hash', + 'proMode.showTxHashDesc': 'Display on-chain hash in transaction records', + 'proMode.txExplorer': 'Transaction Explorer', + 'proMode.txBuyExample': 'Buy Starbucks \$25 Gift Card', + 'proMode.txSellExample': 'Sell Amazon \$100 Coupon', + 'proMode.confirmed': 'Confirmed', + 'proMode.confirming': 'Confirming', + 'proMode.viewAllTx': 'View All On-Chain Transactions', + 'proMode.chainAssets': 'On-Chain Assets', + 'proMode.custodialWallet': 'Custodial Wallet', + 'proMode.externalWallet': 'External Wallet (MetaMask)', + 'proMode.couponCount5': '5 coupons', + 'proMode.couponCount0': '0 coupons', + 'proMode.extractToWallet': 'Extract to External Wallet', + 'proMode.tradeTrack': 'Trading Track', + 'proMode.utilityTrackDesc': 'Coupon validity ≤12 months, no securities license needed', + 'proMode.securitiesTrackDesc': 'Long-term investment coupons (coming soon)', + 'proMode.mvpNote': 'Current MVP only supports Utility Track', + 'proMode.comingSoon': 'Coming Soon', + 'proMode.whatIsTitle': 'What is Pro Mode?', + 'proMode.whatIsDesc': 'Pro Mode is for users with blockchain experience. After enabling, you can:\n' + '• Connect external wallets (MetaMask, etc.)\n' + '• View on-chain addresses and transaction hashes\n' + '• Extract assets to your own wallet\n' + '• View underlying on-chain data\n\n' + 'KYC L2 verification is required to enable.', + + // ============ Wallet Page (additional) ============ + 'wallet.buyIn': 'Buy', + 'wallet.sellOut': 'Sell', + 'wallet.giftTransfer': 'Gift', + 'wallet.redeemUse': 'Redeem', + + // ============ AI Fab ============ + 'aiFab.greeting': 'Hi! I\'m Genex AI Assistant. I can help you manage coupon assets, find deals, and analyze prices. How can I help?', + 'aiFab.inputHint': 'Type a message...', + 'aiFab.suggest1': 'Find high-discount coupons', + 'aiFab.suggest2': 'Are my coupons expiring soon?', + 'aiFab.suggest3': 'Recommend today\'s best deals', + 'aiFab.suggest4': 'Analyze my coupon portfolio', + + // ============ KYC Badge ============ + 'kyc.badgeLabel': 'Verified', + + // ============ Message Tabs ============ + 'message.markAllRead': 'Mark All Read', + 'message.tabTrade': 'Trades', + 'message.tabExpiry': 'Expiry', + 'message.tabAnnouncement': 'Notices', + + // ============ Transfer additions ============ + 'transfer.contactEmail': 'Email', + 'transfer.contactPhone': 'Phone', + 'transfer.yesterday': 'Yesterday', + 'transfer.daysAgo': 'd ago', + 'transfer.weeksAgo': 'w ago', + 'transfer.monthsAgo': 'mo ago', + + // ============ Merchant AI ============ + 'merchantAi.redeemAssist': 'Redeem Assist', + 'merchantAi.trafficForecast': 'Traffic Forecast', + 'merchantAi.anomalyAlert': 'Anomaly Alerts', + 'merchantAi.verifyAuth': 'Verify Coupon', + 'merchantAi.checkStatus': 'Check Status', + 'merchantAi.batchRedeem': 'Batch Redeem', + 'merchantAi.feedback': 'Feedback', + 'merchantAi.quickActions': 'AI Quick Actions', + 'merchantAi.redeemTips': 'Redemption Tips', + 'merchantAi.todayHotRedeem': 'Today\'s Top Redemptions', + 'merchantAi.countUnit': ' txns', + 'merchantAi.aiMarketing': 'AI Marketing Suggestions', + 'merchantAi.crossSellTitle': 'Cross-sell Recommendation', + 'merchantAi.crossSellDesc': 'Customers buying coffee coupons also show interest in bakery coupons. Consider offering combos.', + 'merchantAi.weekendPromoTitle': 'Weekend Promo Suggestion', + 'merchantAi.weekendPromoDesc': 'Historical data shows +30% redemptions on Saturdays. Consider launching a weekend flash deal.', + 'merchantAi.todayForecast': 'Today\'s Traffic Forecast', + 'merchantAi.expectedRedeem': 'Expected Redemptions', + 'merchantAi.peakHours': 'Peak Hours', + 'merchantAi.expectedRevenue': 'Expected Revenue', + 'merchantAi.trafficInsight': '+12% vs. last week. Consider adding 1 more cashier during lunch.', + 'merchantAi.hourlyForecast': 'Hourly Forecast', + 'merchantAi.weeklyForecast': 'Weekly Forecast', + 'merchantAi.staffSuggestion': 'Staff Scheduling', + 'merchantAi.pendingCount': 'Pending', + 'merchantAi.resolvedToday': 'Resolved Today', + 'merchantAi.riskIndex': 'Risk Index', + 'merchantAi.riskLow': 'Low', + 'merchantAi.activeAlerts': 'Active Alerts', + 'merchantAi.highFreqRedeem': 'High-frequency Redemption', + 'merchantAi.suspectFakeCode': 'Suspected Fake Code', + 'merchantAi.suspiciousPatterns': 'Suspicious Patterns', + 'merchantAi.consecutiveRedeem': 'Consecutive Redemptions by Same User', + 'merchantAi.offHoursRedeem': 'Off-hours Redemption Attempts', + 'merchantAi.expiredRedeemAttempt': 'Expired Coupon Redemption Attempts', + 'merchantAi.statusAbnormal': 'Abnormal', + 'merchantAi.statusWarning': 'Warning', + 'merchantAi.statusNormal': 'Normal', + 'merchantAi.expiredBlock': 'Expired Coupon Blocked', + 'merchantAi.duplicateBlock': 'Duplicate Redemption Blocked', + 'merchantAi.wrongStoreAlert': 'Wrong Store Alert', + 'merchantAi.insufficientBalance': 'Insufficient Balance', + 'merchantAi.systemRetry': 'System Timeout Retry', + 'merchantAi.monday': 'Mon', + 'merchantAi.tuesday': 'Tue', + 'merchantAi.wednesday': 'Wed', + 'merchantAi.thursday': 'Thu', + 'merchantAi.friday': 'Fri', + 'merchantAi.saturday': 'Sat', + 'merchantAi.sunday': 'Sun', + + // ============ Issuer additions ============ + 'issuer.unlisted': 'Unlisted', +}; diff --git a/frontend/genex-mobile/lib/app/i18n/strings/ja.dart b/frontend/genex-mobile/lib/app/i18n/strings/ja.dart new file mode 100644 index 0000000..b3f32f3 --- /dev/null +++ b/frontend/genex-mobile/lib/app/i18n/strings/ja.dart @@ -0,0 +1,736 @@ +const Map ja = { + // ============ Common ============ + 'common.confirm': '確認', + 'common.cancel': 'キャンセル', + 'common.save': '保存', + 'common.delete': '削除', + 'common.edit': '編集', + 'common.search': '検索', + 'common.loading': '読み込み中...', + 'common.retry': '再試行', + 'common.done': '完了', + 'common.next': '次へ', + 'common.back': '戻る', + 'common.close': '閉じる', + 'common.more': 'もっと見る', + 'common.all': 'すべて', + 'common.filter': 'フィルター', + 'common.sort': '並び替え', + 'common.copy': 'コピー', + 'common.copied': 'クリップボードにコピーしました', + 'common.today': '今日', + 'common.thisWeek': '今週', + 'common.thisMonth': '今月', + + // ============ Navigation ============ + 'nav.home': 'ホーム', + 'nav.trading': '取引', + 'nav.messages': 'メッセージ', + 'nav.profile': 'マイページ', + + // ============ Welcome / Auth ============ + 'welcome.slogan': 'すべてのクーポンに価値を', + 'welcome.phoneRegister': '電話番号で登録', + 'welcome.emailRegister': 'メールで登録', + 'welcome.otherLogin': '他の方法でログイン', + 'welcome.hasAccount': 'アカウントをお持ちですか?', + 'welcome.login': 'ログイン', + 'welcome.agreement': '登録することで「利用規約」と「プライバシーポリシー」に同意したものとみなされます', + + 'login.title': 'おかえりなさい', + 'login.subtitle': 'Genex にログインしてクーポン資産を管理', + 'login.passwordTab': 'パスワードでログイン', + 'login.codeTab': '認証コードでログイン', + 'login.phoneOrEmail': '電話番号またはメール', + 'login.password': 'パスワード', + 'login.forgotPassword': 'パスワードを忘れた場合', + 'login.submit': 'ログイン', + 'login.phone': '電話番号', + 'login.verifyCode': '認証コード', + 'login.getCode': '認証コードを取得', + + 'register.title': 'アカウント作成', + 'register.emailSubtitle': 'メールアドレスで Genex アカウントを登録', + 'register.phoneSubtitle': '電話番号で Genex アカウントを登録', + 'register.email': 'メールアドレス', + 'register.phone': '電話番号', + 'register.emailHint': 'メールアドレスを入力', + 'register.phoneHint': '電話番号を入力', + 'register.code': '認証コード', + 'register.codeHint': '6桁の認証コードを入力', + 'register.getCode': '認証コードを取得', + 'register.setPassword': 'パスワードを設定', + 'register.passwordHint': '8〜20文字、英字と数字を含む', + 'register.agreement': '以下に同意します', + 'register.userAgreement': '「利用規約」', + 'register.privacyPolicy': '「プライバシーポリシー」', + 'register.submit': '登録', + 'register.stepVerify': '認証', + 'register.stepPassword': 'パスワード設定', + 'register.stepDone': '完了', + 'register.rule8chars': '8文字以上', + 'register.ruleLetter': '英字を含む', + 'register.ruleNumber': '数字を含む', + + 'forgot.title': 'パスワード再設定', + 'forgot.inputAccount': '電話番号またはメールを入力', + 'forgot.sendHint': '認証コードをお送りします', + 'forgot.accountHint': '電話番号 / メールアドレス', + 'forgot.getCode': '認証コードを取得', + 'forgot.inputCode': '認証コードを入力', + 'forgot.codeSentTo': '認証コードの送信先', + 'forgot.codeHint': '6桁の認証コード', + 'forgot.resend': '再送信', + 'forgot.next': '次へ', + 'forgot.setNewPassword': '新しいパスワードを設定', + 'forgot.newPasswordHint': '新しいパスワードを入力(8文字以上)', + 'forgot.newPassword': '新しいパスワード', + 'forgot.confirmPassword': '新しいパスワードを確認', + 'forgot.confirmChange': '変更を確認', + 'forgot.success': 'パスワードの変更が完了しました', + 'forgot.successHint': '新しいパスワードでログインしてください', + 'forgot.backToLogin': 'ログインに戻る', + + // ============ Home ============ + 'home.searchHint': 'クーポン、ブランド、カテゴリを検索...', + 'home.position': 'ポジション', + 'home.hold': '保有', + 'home.couponUnit': '枚', + 'home.totalValue': '総額', + 'home.receive': '受取', + 'home.transfer': '譲渡', + 'home.sell': '売却', + 'home.redeem': '利用', + 'home.flashSale': 'タイムセール', + 'home.newRelease': '新着クーポン', + 'home.discountRank': '割引ランキング', + 'home.expiringSoon': 'まもなく期限切れ', + 'home.priceCompare': '価格比較', + 'home.resaleMarket': 'リセール市場', + 'home.hotTrades': '人気取引', + 'home.viewAll': 'すべて', + 'home.aiRecommend': 'AI おすすめ', + 'home.aiRecommendDesc': 'あなたの好みに基づき、コスパの高いクーポンを3枚発見しました', + 'home.featuredCoupons': '厳選クーポン', + 'home.viewAllCoupons': 'すべて見る', + + // ============ Market ============ + 'market.title': '取引', + 'market.primary': 'プライマリー市場', + 'market.secondary': 'セカンダリー市場', + 'market.dining': 'グルメ', + 'market.shopping': 'ショッピング', + 'market.entertainment': 'エンタメ', + 'market.travel': 'トラベル', + 'market.lifestyle': 'ライフスタイル', + 'market.sports': 'スポーツ', + 'market.discountRate': '割引率', + 'market.priceUp': '価格↑', + 'market.priceDown': '価格↓', + 'market.expiryDate': '有効期限', + 'market.issuePrice': '発行価格', + 'market.faceValue': '額面', + 'market.discount': '割引', + 'market.totalSupply': '発行数量', + 'market.salesProgress': '販売進捗', + 'market.upcoming': '開始予定', + 'market.subscribing': '申込受付中', + 'market.ended': '終了', + 'market.timeToStart': '開始まで', + 'market.couponBrand': 'クーポン名/ブランド', + 'market.latestPrice': '最新価格', + 'market.change24h': '24h騰落', + 'market.discountSuffix': '引', + + // ============ Trading Detail ============ + 'trading.depth': '取引板', + 'trading.askSide': '売り板', + 'trading.bidSide': '買い板', + 'trading.quantitySheets': '数量(枚)', + 'trading.placeOrder': '注文', + 'trading.buy': '購入', + 'trading.sell': '売却', + 'trading.limitOrder': '指値注文', + 'trading.marketOrder': '成行注文', + 'trading.price': '価格', + 'trading.quantity': '数量', + 'trading.available': '利用可能', + 'trading.currentOrders': '未約定注文', + 'trading.historyOrders': '注文履歴', + 'trading.cancelOrder': '取消', + 'trading.high24h': '24h高値', + 'trading.low24h': '24h安値', + 'trading.open': '始値', + 'trading.vol24h': '24h出来高', + 'trading.sheetsUnit': '枚', + 'trading.discountLabel': '割引', + + // ============ Transfer ============ + 'transfer.title': '譲渡', + 'transfer.scanTransfer': 'QRコードで譲渡', + 'transfer.scanDesc': '相手の受取コードをスキャン\n即時反映', + 'transfer.inputTransfer': 'ID/メール/電話番号を入力', + 'transfer.inputDesc': '相手の情報を入力\n安全に譲渡', + 'transfer.recentRecipients': '最近の譲渡先', + 'transfer.manage': '管理', + 'transfer.noRecent': '最近の譲渡記録はありません\n譲渡を完了するとここに表示されます', + 'transfer.expired': '期限切れ', + 'transfer.refresh': '更新', + 'transfer.transferHistory': '譲渡履歴', + 'transfer.viewAll': 'すべて見る', + 'transfer.lastTransfer': '最近の譲渡', + 'transfer.inputRecipient': '受取人を入力', + 'transfer.recipientIdHint': 'ID / メール / 電話番号', + 'transfer.paste': '貼り付け', + 'transfer.pasteFromClipboard': 'クリップボードから貼り付け', + 'transfer.searchRecipient': '検索', + 'transfer.selectCoupon': '譲渡するクーポンを選択', + 'transfer.noCoupons': '譲渡可能なクーポンはありません', + 'transfer.confirmTransfer': '譲渡を確認', + 'transfer.transferTo': '譲渡先', + 'transfer.couponInfo': '譲渡するクーポン', + 'transfer.confirmBtn': '譲渡を確認', + 'transfer.outgoing': '送信', + 'transfer.incoming': '受取', + + // ============ Wallet Coupons ============ + 'walletCoupons.title': 'マイポジション', + 'walletCoupons.receiveTip': 'クーポンを受取', + 'walletCoupons.usable': '利用可能', + 'walletCoupons.pendingRedeem': '利用待ち', + 'walletCoupons.expired': '期限切れ', + 'walletCoupons.holdCount': '保有枚数', + 'walletCoupons.totalFaceValue': '合計額面', + 'walletCoupons.saved': '節約額', + 'walletCoupons.faceValue': '額面', + 'walletCoupons.expiredText': '期限切れ', + 'walletCoupons.expiringToday': '本日期限', + 'walletCoupons.daysToExpiry': '日後に期限切れ', + 'walletCoupons.expiryFormat': '期限', + + // ============ Coupon Detail ============ + 'couponDetail.title': 'クーポン詳細', + 'couponDetail.saveBadge': '額面よりお得', + 'couponDetail.faceValue': '額面', + 'couponDetail.validUntil': '有効期限', + 'couponDetail.type': 'タイプ', + 'couponDetail.issuer': '発行元', + 'couponDetail.consumeCoupon': '消費クーポン', + 'couponDetail.usageNote': '利用案内', + 'couponDetail.allStores': '全国の店舗で利用可能', + 'couponDetail.canTransfer': '友達に譲渡可能', + 'couponDetail.useAnytime': '有効期間内いつでも利用可能', + 'couponDetail.noStack': '併用不可', + 'couponDetail.noCash': '現金との交換不可', + 'couponDetail.stores': '利用可能店舗', + 'couponDetail.storeCount': '全国 12,800+ 店舗', + 'couponDetail.storeDesc': '全国のすべての直営店舗で利用可能', + 'couponDetail.priceTrend': '価格推移', + 'couponDetail.last30Days': '過去30日間', + 'couponDetail.highest': '最高値', + 'couponDetail.lowest': '最安値', + 'couponDetail.average': '平均価格', + 'couponDetail.tradeHistory': '取引履歴', + 'couponDetail.nearbyStores': '近くの利用可能店舗', + 'couponDetail.distance': '距離', + 'couponDetail.open': '営業中', + 'couponDetail.similar': '類似クーポン', + 'couponDetail.favorite': 'お気に入り', + 'couponDetail.buyNow': '今すぐ購入', + + // ============ My Coupon Detail ============ + 'myCoupon.title': 'クーポン詳細', + 'myCoupon.active': '利用可能', + 'myCoupon.showQrHint': 'このQRコードを店舗スタッフに提示してスキャンしてもらってください', + 'myCoupon.switchBarcode': 'バーコードに切替', + 'myCoupon.faceValue': '額面', + 'myCoupon.purchasePrice': '購入価格', + 'myCoupon.validUntil': '有効期限', + 'myCoupon.orderNo': '注文番号', + 'myCoupon.resellCount': '残り転売回数', + 'myCoupon.transfer': '譲渡', + 'myCoupon.sell': '売却', + 'myCoupon.usageNote': '利用案内', + 'myCoupon.useInStore': '全国の店舗で利用可能', + 'myCoupon.useInTime': '有効期限内にご利用ください', + 'myCoupon.onePerVisit': '1回のお会計につき1枚のみ利用可能', + 'myCoupon.noCash': '現金との交換不可', + 'myCoupon.extractToWallet': '外部ウォレットに引き出す', + 'myCoupon.requireKycL2': 'KYC L2以上の認証が必要', + 'myCoupon.viewTrades': '取引履歴を見る', + 'myCoupon.help': '利用ヘルプ', + + // ============ Order Confirm ============ + 'orderConfirm.title': '注文確認', + 'orderConfirm.quantity': '購入数量', + 'orderConfirm.paymentMethod': '支払い方法', + 'orderConfirm.bankCard': '銀行カード/クレジットカード', + 'orderConfirm.priceDetail': '価格明細', + 'orderConfirm.buyingNote': '消費用クーポンを購入します', + 'orderConfirm.total': '合計', + 'orderConfirm.confirmPay': '支払いを確認', + 'orderConfirm.unitPrice': '単価', + 'orderConfirm.count': '数量', + 'orderConfirm.saveBadge': '額面よりお得', + 'orderConfirm.biometricHint': '支払いを完了するには指紋または顔認証を行ってください', + 'orderConfirm.usePasswordPay': 'パスワードで支払う', + + // ============ Payment ============ + 'payment.title': '支払い方法を選択', + 'payment.addNew': '新しい支払い方法を追加', + 'payment.confirmPay': '支払いを確認', + 'payment.bankTransfer': '銀行振込', + + 'paymentSuccess.title': '支払い完了', + 'paymentSuccess.hint': 'クーポンが届きました。「マイクーポン」で確認できます', + 'paymentSuccess.couponName': 'クーポン名', + 'paymentSuccess.payAmount': '支払い金額', + 'paymentSuccess.orderNo': '注文番号', + 'paymentSuccess.payTime': '支払い日時', + 'paymentSuccess.viewMyCoupon': 'マイクーポンを見る', + 'paymentSuccess.continueBrowse': '買い物を続ける', + + // ============ Search ============ + 'search.hint': 'クーポン、ブランド、カテゴリを検索...', + 'search.cancel': 'キャンセル', + 'search.hotSearch': '人気の検索', + 'search.history': '検索履歴', + 'search.clear': 'クリア', + 'search.diningCoupon': 'グルメクーポン', + 'search.discountCoupon': '割引クーポン', + 'search.travel': '旅行', + + // ============ Redeem ============ + 'redeem.title': 'クーポンコードを提示', + 'redeem.faceValue': '額面', + 'redeem.validTime': '有効期間', + 'redeem.refresh': 'コードを更新', + 'redeem.showHint': 'このコードを店舗スタッフに提示してスキャンしてもらってください。画面の明るさは自動的に最大に調整されました', + + // ============ Sell Order ============ + 'sellOrder.title': '売り注文を出す', + 'sellOrder.faceValue': '額面', + 'sellOrder.credit': '信用', + 'sellOrder.setPrice': '販売価格を設定', + 'sellOrder.price': '販売価格', + 'sellOrder.aiSuggest': 'AI推奨価格', + 'sellOrder.bestDealRate': 'この価格が最も成約率が高いです', + 'sellOrder.discountRate': '割引率', + 'sellOrder.platformFee': 'プラットフォーム手数料 (1.5%)', + 'sellOrder.estimatedReceive': '受取見込額', + 'sellOrder.marketAvg': '現在の市場平均価格', + 'sellOrder.recent24hTrades': '直近24時間の取引', + 'sellOrder.tradesUnit': '件', + 'sellOrder.confirmList': '出品を確認', + 'sellOrder.success': '出品完了', + 'sellOrder.successHint': 'クーポンが市場に出品されました。買い手が注文すると自動的に成約します。', + 'sellOrder.ok': 'OK', + + // ============ Trading Page (My Trades) ============ + 'tradingPage.title': 'マイ取引', + 'tradingPage.pendingOrders': '出品中の注文', + 'tradingPage.tradeRecords': '取引履歴', + 'tradingPage.listPrice': '出品価格', + 'tradingPage.listTime': '出品日時', + 'tradingPage.cancelOrder': '注文取消', + + // ============ Wallet ============ + 'wallet.myBalance': '残高', + 'wallet.totalBalance': '合計残高', + 'wallet.withdrawable': '出金可能', + 'wallet.frozen': '凍結中', + 'wallet.deposit': '入金', + 'wallet.withdraw': '出金', + 'wallet.records': '取引履歴', + + 'deposit.title': '入金', + 'deposit.currentBalance': '現在の残高', + 'deposit.amount': '入金額', + 'deposit.custom': 'カスタム金額', + 'deposit.paymentMethod': '支払い方法', + 'deposit.submit': '入金', + + 'withdraw.title': '出金', + 'withdraw.availableBalance': '出金可能残高', + 'withdraw.amount': '出金額', + 'withdraw.all': '全額', + 'withdraw.to': '出金先', + 'withdraw.savingsAccount': '普通預金口座', + 'withdraw.fee': '手数料 (0.5%)', + 'withdraw.actualReceive': '実際の受取額', + 'withdraw.estimateTime': '1〜2営業日で入金予定', + 'withdraw.submit': '出金を確認', + + 'txRecords.title': '取引履歴', + 'txRecords.buy': '購入', + 'txRecords.sell': '売却', + 'txRecords.transfer': '譲渡', + 'txRecords.noRecords': '記録がありません', + 'txRecords.orderNo': '注文番号', + 'txRecords.transferTo': '譲渡先', + + // ============ Profile ============ + 'profile.favorites': 'お気に入り', + 'profile.orders': '注文', + 'profile.coupons': 'クーポン', + 'profile.wallet': 'ウォレット', + 'profile.myTrades': 'マイ取引', + 'profile.walletBalance': 'ウォレット残高', + 'profile.paymentManage': '支払い管理', + 'profile.kyc': '本人確認', + 'profile.proMode': 'プロモード', + 'profile.settings': '設定', + 'profile.helpCenter': 'ヘルプセンター', + 'profile.issuerPortal': '発行者ポータル', + 'profile.merchantPortal': '加盟店ポータル', + + // ============ Settings ============ + 'settings.title': '設定', + 'settings.accountSecurity': 'アカウントとセキュリティ', + 'settings.phone': '電話番号', + 'settings.email': 'メール', + 'settings.changePassword': 'パスワード変更', + 'settings.identity': '本人確認', + 'settings.paymentManage': '支払い管理', + 'settings.paymentMethod': '支払い方法', + 'settings.bankAccount': '銀行口座', + 'settings.paymentPassword': '支払いパスワード', + 'settings.notifications': '通知設定', + 'settings.tradeNotify': '取引通知', + 'settings.expiryRemind': '期限切れリマインダー', + 'settings.marketChange': '相場変動', + 'settings.marketingPush': 'プロモーション通知', + 'settings.general': '一般', + 'settings.language': '言語', + 'settings.currency': '通貨', + 'settings.clearCache': 'キャッシュをクリア', + 'settings.about': 'アプリについて', + 'settings.version': 'バージョン', + 'settings.userAgreement': '利用規約', + 'settings.privacyPolicy': 'プライバシーポリシー', + 'settings.helpCenter': 'ヘルプセンター', + 'settings.logout': 'ログアウト', + 'settings.selectCurrency': '表示通貨を選択', + 'settings.currencyNote': 'この設定は取引ページの価格表示通貨に影響します', + 'settings.selectLanguage': '言語を選択', + 'settings.currencySymbol': '記号', + + // ============ KYC ============ + 'kyc.title': '本人確認', + 'kyc.currentLevel': '現在の認証レベル', + 'kyc.l1Title': 'L1 基本認証', + 'kyc.l1Desc': '電話番号 + メール認証', + 'kyc.l1Limit': '1日の購入上限 \$500', + 'kyc.l1Feature': 'クーポンの購入・利用が可能', + 'kyc.l2Title': 'L2 本人認証', + 'kyc.l2Desc': '身分証/パスポート認証', + 'kyc.l2Limit': '1日の購入上限 \$5,000', + 'kyc.l2Feature': 'セカンダリー市場取引・P2P譲渡を解放', + 'kyc.l3Title': 'L3 上級認証', + 'kyc.l3Desc': 'ビデオ面談 + 住所証明', + 'kyc.l3Limit': '上限なし', + 'kyc.l3Feature': '大口取引・出金制限なし', + 'kyc.completed': '完了', + 'kyc.goVerify': '認証する', + + // ============ Payment Management ============ + 'payManage.title': '支払い管理', + 'payManage.myCards': '登録カード', + 'payManage.addCard': '新しいカードを追加', + 'payManage.bankAccount': '銀行口座(出金用)', + 'payManage.paymentSecurity': '支払いセキュリティ', + 'payManage.paymentPassword': '支払いパスワード', + 'payManage.passwordSet': '設定済み', + 'payManage.biometricPay': '指紋/顔認証支払い', + 'payManage.biometricEnabled': '有効', + 'payManage.noPasswordPay': 'パスワード不要の支払い', + 'payManage.noPasswordLimit': '1回あたり\$10以下', + + // ============ AI Chat ============ + 'aiChat.title': 'AI アシスタント', + 'aiChat.greeting': 'こんにちは!Genex AI アシスタントです。お得なクーポンの発見、価格比較、おすすめの組み合わせをお手伝いします。こんな質問をどうぞ:', + 'aiChat.suggest1': 'おすすめのクーポンを教えて', + 'aiChat.suggest2': 'Starbucksのクーポンは買い得?', + 'aiChat.suggest3': '価格比較をして', + 'aiChat.suggest4': 'クーポンがもうすぐ期限切れ、どうすればいい?', + 'aiChat.inputHint': 'クーポンについて何でも聞いてください...', + 'aiChat.confirmAction': '実行を確認', + 'aiChat.riskLow': '低リスク', + 'aiChat.riskNormal': '要確認', + 'aiChat.riskHigh': '高リスク', + + // ============ Messages ============ + 'message.title': 'メッセージ', + 'message.detailTitle': 'メッセージ詳細', + 'message.tradeNotify': '取引通知', + 'message.expiryRemind': '期限切れリマインダー', + 'message.systemNotify': 'システム通知', + 'message.promoNotify': 'キャンペーン情報', + 'message.tradeSuccess': '取引完了通知', + 'message.couponName': 'クーポン名', + 'message.faceValue': '額面', + 'message.payAmount': '支払い金額', + 'message.orderNo': '注文番号', + 'message.payMethod': '支払い方法', + 'message.viewCouponDetail': 'クーポン詳細を見る', + + // ============ Status Tags ============ + 'status.active': '利用可能', + 'status.pending': '利用待ち', + 'status.expired': '期限切れ', + 'status.used': '使用済み', + 'status.processing': '処理中', + 'status.completed': '完了', + 'status.cancelled': 'キャンセル済み', + 'status.refunding': '返金処理中', + 'status.onSale': '出品中', + + // ============ Empty States ============ + 'empty.noCoupons': 'クーポンがありません', + 'empty.noCouponsHint': 'マーケットでお得なクーポンを探してみましょう', + 'empty.browse': '見てみる', + 'empty.noTrades': '取引履歴がありません', + 'empty.noTradesHint': '最初の取引を完了するとここに表示されます', + 'empty.noResults': '結果が見つかりません', + 'empty.noResultsHint': '別のキーワードで試してみてください', + 'empty.noMessages': 'メッセージはありません', + 'empty.noMessagesHint': '取引通知やシステムのお知らせがここに表示されます', + 'empty.networkError': 'ネットワーク接続に失敗しました', + 'empty.networkErrorHint': 'ネットワーク設定を確認してから再試行してください', + + // ============ Receive Coupon ============ + 'receiveCoupon.title': 'クーポンを受取', + 'receiveCoupon.hint': '下のQRコードまたは受取IDを相手に見せてください。相手がスキャンまたはIDを入力することで、クーポンがあなたのウォレットに届きます。', + 'receiveCoupon.id': '受取ID', + 'receiveCoupon.idCopied': '受取IDをクリップボードにコピーしました', + 'receiveCoupon.note': '受取ったクーポンは自動的にウォレットに保存されます。ホーム画面のウォレットから確認・管理できます。', + + // ============ Issuer ============ + 'issuer.title': '発行者管理', + 'issuer.verified': '認証済み発行者', + 'issuer.overview': '概要', + 'issuer.issue': '発行', + 'issuer.redeem': '利用処理', + 'issuer.finance': '財務', + 'issuer.totalIssued': '総発行数', + 'issuer.totalSold': '販売済み', + 'issuer.totalRedeemed': '利用処理済み', + 'issuer.redeemRate': '利用率', + 'issuer.quickActions': 'クイック操作', + 'issuer.createCoupon': 'クーポン作成', + 'issuer.storeManage': '店舗管理', + 'issuer.salesAnalysis': '売上分析', + 'issuer.statement': '精算書', + 'issuer.myCoupons': 'マイクーポン', + 'issuer.listed': '掲載中', + 'issuer.underReview': '審査中', + 'issuer.soldOut': '完売', + 'issuer.issuedSlash': '発行', + 'issuer.sold': '販売済み', + 'issuer.issueCenter': '発行センター', + 'issuer.selectTemplate': 'クーポンテンプレートを選択', + 'issuer.voucherType': '割引券', + 'issuer.discountType': '値引きクーポン', + 'issuer.giftCardType': 'ギフトカード', + 'issuer.storedValueType': 'プリペイドクーポン', + 'issuer.couponManage': 'クーポン管理', + 'issuer.viewAll': 'すべて見る', + 'issuer.couponEvents': 'クーポンイベント', + 'issuer.createNew': '新規クーポン作成', + 'issuer.redeemManage': '利用処理管理', + 'issuer.redeemTrend': '利用処理トレンド', + 'issuer.allStores': '全店舗', + 'issuer.employees': '名のスタッフ', + 'issuer.financeManage': '財務管理', + 'issuer.totalSales': '総売上額', + 'issuer.settled': '入金済み', + 'issuer.pendingSettle': '精算待ち', + 'issuer.breakage': 'Breakage', + 'issuer.withdrawBtn': '出金', + 'issuer.reportBtn': '精算レポート', + 'issuer.settleDetail': '精算明細', + 'issuer.creditLevel': '信用等級', + 'issuer.issueQuota': '発行枠', + 'issuer.usedQuota': '使用済み枠', + 'issuer.dataCenter': 'データセンター', + 'issuer.issueSalesRate': '発行数/販売数/利用率', + 'issuer.userProfile': 'ユーザープロフィール', + 'issuer.userProfileDesc': '購入ユーザーの分布分析', + 'issuer.creditDetail': '信用詳細', + 'issuer.creditDetailDesc': 'スコア詳細と改善提案', + 'issuer.quotaChange': '枠の変動', + 'issuer.quotaChangeDesc': '過去の枠調整履歴', + 'issuer.companyInfo': '企業情報', + 'issuer.companyInfoDesc': '営業許可証/担当者', + 'issuer.settingsItem': '設定', + 'issuer.settingsItemDesc': '通知/セキュリティ/言語', + 'issuer.helpItem': 'ヘルプセンター', + 'issuer.helpItemDesc': 'よくある質問とサポート', + + // ============ Merchant ============ + 'merchant.today': '今日', + 'merchant.onlineMode': 'オンラインモード', + 'merchant.offlineMode': 'オフラインモード', + 'merchant.pendingSync': '同期待ち', + 'merchant.syncUnit': '件', + 'merchant.scanHint': 'クーポンのQRコードをスキャン枠に合わせてください', + 'merchant.flashlight': 'ライト', + 'merchant.manualInput': '手動入力', + 'merchant.redeemRecords': '利用処理履歴', + 'merchant.storeData': '店舗データ', + 'merchant.inputCode': 'クーポンコードを手動入力', + 'merchant.inputCodeHint': 'クーポンコードを入力', + 'merchant.query': '照会', + 'merchant.userNickname': 'ユーザー名', + 'merchant.consumer': '消費者', + 'merchant.couponName': 'クーポン名', + 'merchant.faceValue': '額面', + 'merchant.validUntil': '有効期限', + 'merchant.useCondition': '利用条件', + 'merchant.noMinSpend': '最低利用金額なし', + 'merchant.confirmRedeem': '利用処理を確認', + 'merchant.redeemSuccess': '利用処理完了', + 'merchant.continueRedeem': '続けて処理', + 'merchant.synced': '同期済み', + 'merchant.pendingSyncLabel': '同期待ち', + 'merchant.redeemOperator': '処理担当者', + 'merchant.todayRedeem': '本日の利用処理', + 'merchant.redeemAmount': '利用処理金額', + 'merchant.weekTrend': '今週のトレンド', + 'merchant.operatorRank': '担当者ランキング', + + // ============ Profile Page (additional) ============ + 'profile.account': 'アカウント', + 'profile.trade': '取引', + 'profile.holdCoupons': '保有', + 'profile.saved': '節約', + 'profile.credit': '信用', + 'profile.creditScore': '信用スコア', + 'profile.myFavorites': 'お気に入り', + 'profile.securitySettings': 'セキュリティ設定', + 'profile.advancedSettings': '詳細設定', + 'profile.aboutGenex': 'Genex について', + 'profile.simplifiedChinese': '簡体中国語', + + // ============ Pro Mode ============ + 'proMode.toggleDesc': 'オンチェーン情報の閲覧と外部ウォレット接続が可能', + 'proMode.requireKycL2': 'KYC L2以上の認証が必要', + 'proMode.connected': '接続済み', + 'proMode.disconnect': '切断', + 'proMode.connectWallet': '外部ウォレットを接続', + 'proMode.walletDesc': '外部ウォレット接続後、プラットフォーム資産を自分のアドレスに引き出せます', + 'proMode.showChainAddress': 'チェーンアドレスを表示', + 'proMode.showChainAddressDesc': 'クーポン詳細にコントラクトアドレスを表示', + 'proMode.showTxHash': '取引ハッシュを表示', + 'proMode.showTxHashDesc': '取引記録にオンチェーンハッシュを表示', + 'proMode.txExplorer': 'トランザクションエクスプローラー', + 'proMode.txBuyExample': 'スターバックス \$25 ギフトカード購入', + 'proMode.txSellExample': 'Amazon \$100 クーポン売却', + 'proMode.confirmed': '確認済み', + 'proMode.confirming': '確認中', + 'proMode.viewAllTx': 'すべてのオンチェーン取引を表示', + 'proMode.chainAssets': 'オンチェーン資産', + 'proMode.custodialWallet': 'プラットフォーム管理ウォレット', + 'proMode.externalWallet': '外部ウォレット (MetaMask)', + 'proMode.couponCount5': '5 枚のクーポン', + 'proMode.couponCount0': '0 枚のクーポン', + 'proMode.extractToWallet': '外部ウォレットに引き出し', + 'proMode.tradeTrack': '取引トラック', + 'proMode.utilityTrackDesc': 'クーポン有効期限≤12ヶ月、証券ライセンス不要', + 'proMode.securitiesTrackDesc': '長期投資型クーポン商品(近日公開)', + 'proMode.mvpNote': '現在のMVP版はUtility Trackのみ対応', + 'proMode.comingSoon': '近日公開', + 'proMode.whatIsTitle': 'プロモードとは?', + 'proMode.whatIsDesc': 'プロモードはブロックチェーン経験のあるユーザー向けです。有効化後:\n' + '• 外部ウォレット接続(MetaMask等)\n' + '• オンチェーンアドレスと取引ハッシュの閲覧\n' + '• 資産を自分のウォレットに引き出し\n' + '• 基盤となるオンチェーンデータの閲覧\n\n' + 'KYC L2認証完了後に有効化できます。', + + // ============ Wallet Page (additional) ============ + 'wallet.buyIn': '購入', + 'wallet.sellOut': '売却', + 'wallet.giftTransfer': '譲渡', + 'wallet.redeemUse': '利用', + + // ============ AI Fab ============ + 'aiFab.greeting': 'こんにちは!Genex AI アシスタントです。クーポン資産の管理、お得情報の検索、価格分析をお手伝いします。何かお手伝いできることはありますか?', + 'aiFab.inputHint': 'メッセージを入力...', + 'aiFab.suggest1': '割引率の高いクーポンを探して', + 'aiFab.suggest2': '期限切れ間近のクーポンはある?', + 'aiFab.suggest3': '今日のおすすめクーポンは?', + 'aiFab.suggest4': 'クーポン資産を分析して', + + // ============ KYC Badge ============ + 'kyc.badgeLabel': '認証済', + + // ============ Message Tabs ============ + 'message.markAllRead': 'すべて既読', + 'message.tabTrade': '取引', + 'message.tabExpiry': '期限', + 'message.tabAnnouncement': 'お知らせ', + + // ============ Transfer additions ============ + 'transfer.contactEmail': 'メール', + 'transfer.contactPhone': '電話', + 'transfer.yesterday': '昨日', + 'transfer.daysAgo': '日前', + 'transfer.weeksAgo': '週間前', + 'transfer.monthsAgo': 'ヶ月前', + + // ============ Merchant AI ============ + 'merchantAi.redeemAssist': '利用処理アシスト', + 'merchantAi.trafficForecast': '客数予測', + 'merchantAi.anomalyAlert': '異常アラート', + 'merchantAi.verifyAuth': 'クーポン検証', + 'merchantAi.checkStatus': 'ステータス確認', + 'merchantAi.batchRedeem': '一括処理', + 'merchantAi.feedback': 'フィードバック', + 'merchantAi.quickActions': 'AI クイック操作', + 'merchantAi.redeemTips': '利用処理のヒント', + 'merchantAi.todayHotRedeem': '本日の人気利用処理', + 'merchantAi.countUnit': '件', + 'merchantAi.aiMarketing': 'AI マーケティング提案', + 'merchantAi.crossSellTitle': 'クロスセル提案', + 'merchantAi.crossSellDesc': 'コーヒークーポン購入者はベーカリークーポンにも関心があります。セット販売をお勧めします', + 'merchantAi.weekendPromoTitle': '週末プロモーション提案', + 'merchantAi.weekendPromoDesc': '過去のデータでは土曜日の利用処理が+30%。週末限定セールの実施をお勧めします', + 'merchantAi.todayForecast': '本日の客数予測', + 'merchantAi.expectedRedeem': '予想利用処理数', + 'merchantAi.peakHours': 'ピーク時間帯', + 'merchantAi.expectedRevenue': '予想収益', + 'merchantAi.trafficInsight': '先週同期比+12%。昼のピーク時にレジ担当を1名追加することをお勧めします', + 'merchantAi.hourlyForecast': '時間帯別予測', + 'merchantAi.weeklyForecast': '今週の予測', + 'merchantAi.staffSuggestion': 'シフト提案', + 'merchantAi.pendingCount': '未処理', + 'merchantAi.resolvedToday': '本日処理済み', + 'merchantAi.riskIndex': 'リスク指数', + 'merchantAi.riskLow': '低', + 'merchantAi.activeAlerts': 'アクティブアラート', + 'merchantAi.highFreqRedeem': '高頻度利用処理検知', + 'merchantAi.suspectFakeCode': '偽造コード疑い', + 'merchantAi.suspiciousPatterns': '不審なパターン検知', + 'merchantAi.consecutiveRedeem': '同一ユーザーの連続利用処理', + 'merchantAi.offHoursRedeem': '営業時間外の利用処理試行', + 'merchantAi.expiredRedeemAttempt': '期限切れクーポンの利用処理試行', + 'merchantAi.statusAbnormal': '異常', + 'merchantAi.statusWarning': '注意', + 'merchantAi.statusNormal': '正常', + 'merchantAi.expiredBlock': '期限切れクーポンブロック', + 'merchantAi.duplicateBlock': '重複利用処理ブロック', + 'merchantAi.wrongStoreAlert': '対象外店舗アラート', + 'merchantAi.insufficientBalance': '残高不足', + 'merchantAi.systemRetry': 'システムタイムアウト再試行', + 'merchantAi.monday': '月', + 'merchantAi.tuesday': '火', + 'merchantAi.wednesday': '水', + 'merchantAi.thursday': '木', + 'merchantAi.friday': '金', + 'merchantAi.saturday': '土', + 'merchantAi.sunday': '日', + + // ============ Issuer additions ============ + 'issuer.unlisted': '非掲載', +}; diff --git a/frontend/genex-mobile/lib/app/i18n/strings/zh_cn.dart b/frontend/genex-mobile/lib/app/i18n/strings/zh_cn.dart new file mode 100644 index 0000000..46113c6 --- /dev/null +++ b/frontend/genex-mobile/lib/app/i18n/strings/zh_cn.dart @@ -0,0 +1,736 @@ +const Map zhCN = { + // ============ Common ============ + 'common.confirm': '确认', + 'common.cancel': '取消', + 'common.save': '保存', + 'common.delete': '删除', + 'common.edit': '编辑', + 'common.search': '搜索', + 'common.loading': '加载中...', + 'common.retry': '重试', + 'common.done': '完成', + 'common.next': '下一步', + 'common.back': '返回', + 'common.close': '关闭', + 'common.more': '更多', + 'common.all': '全部', + 'common.filter': '筛选', + 'common.sort': '排序', + 'common.copy': '复制', + 'common.copied': '已复制到剪贴板', + 'common.today': '今日', + 'common.thisWeek': '本周', + 'common.thisMonth': '本月', + + // ============ Navigation ============ + 'nav.home': '首页', + 'nav.trading': '交易', + 'nav.messages': '消息', + 'nav.profile': '我的', + + // ============ Welcome / Auth ============ + 'welcome.slogan': '让每一张券都有价值', + 'welcome.phoneRegister': '手机号注册', + 'welcome.emailRegister': '邮箱注册', + 'welcome.otherLogin': '其他方式登录', + 'welcome.hasAccount': '已有账号?', + 'welcome.login': '登录', + 'welcome.agreement': '注册即表示同意《用户协议》和《隐私政策》', + + 'login.title': '欢迎回来', + 'login.subtitle': '登录 Genex 管理你的券资产', + 'login.passwordTab': '密码登录', + 'login.codeTab': '验证码登录', + 'login.phoneOrEmail': '手机号或邮箱', + 'login.password': '密码', + 'login.forgotPassword': '忘记密码?', + 'login.submit': '登录', + 'login.phone': '手机号', + 'login.verifyCode': '验证码', + 'login.getCode': '获取验证码', + + 'register.title': '创建账号', + 'register.emailSubtitle': '使用邮箱注册 Genex 账号', + 'register.phoneSubtitle': '使用手机号注册 Genex 账号', + 'register.email': '邮箱地址', + 'register.phone': '手机号', + 'register.emailHint': '请输入邮箱地址', + 'register.phoneHint': '请输入手机号', + 'register.code': '验证码', + 'register.codeHint': '请输入6位验证码', + 'register.getCode': '获取验证码', + 'register.setPassword': '设置密码', + 'register.passwordHint': '8-20位,含字母和数字', + 'register.agreement': '我已阅读并同意', + 'register.userAgreement': '《用户协议》', + 'register.privacyPolicy': '《隐私政策》', + 'register.submit': '注册', + 'register.stepVerify': '验证', + 'register.stepPassword': '设密码', + 'register.stepDone': '完成', + 'register.rule8chars': '8位以上', + 'register.ruleLetter': '含字母', + 'register.ruleNumber': '含数字', + + 'forgot.title': '找回密码', + 'forgot.inputAccount': '输入手机号或邮箱', + 'forgot.sendHint': '我们将向您发送验证码', + 'forgot.accountHint': '手机号 / 邮箱地址', + 'forgot.getCode': '获取验证码', + 'forgot.inputCode': '输入验证码', + 'forgot.codeSentTo': '验证码已发送至', + 'forgot.codeHint': '6位验证码', + 'forgot.resend': '重新发送', + 'forgot.next': '下一步', + 'forgot.setNewPassword': '设置新密码', + 'forgot.newPasswordHint': '请输入新密码(8位以上)', + 'forgot.newPassword': '新密码', + 'forgot.confirmPassword': '确认新密码', + 'forgot.confirmChange': '确认修改', + 'forgot.success': '密码修改成功', + 'forgot.successHint': '请使用新密码登录', + 'forgot.backToLogin': '返回登录', + + // ============ Home ============ + 'home.searchHint': '搜索券、品牌、分类...', + 'home.position': '持仓', + 'home.hold': '持有', + 'home.couponUnit': '张券', + 'home.totalValue': '总值', + 'home.receive': '接收', + 'home.transfer': '转赠', + 'home.sell': '出售', + 'home.redeem': '核销', + 'home.flashSale': '限时抢购', + 'home.newRelease': '新券首发', + 'home.discountRank': '折扣排行', + 'home.expiringSoon': '即将到期', + 'home.priceCompare': '比价', + 'home.resaleMarket': '转让市场', + 'home.hotTrades': '热门交易', + 'home.viewAll': '全部', + 'home.aiRecommend': 'AI 推荐', + 'home.aiRecommendDesc': '根据你的偏好,发现了3张高性价比券', + 'home.featuredCoupons': '精选好券', + 'home.viewAllCoupons': '查看全部', + + // ============ Market ============ + 'market.title': '交易', + 'market.primary': '一级市场', + 'market.secondary': '二级市场', + 'market.dining': '餐饮', + 'market.shopping': '购物', + 'market.entertainment': '娱乐', + 'market.travel': '出行', + 'market.lifestyle': '生活', + 'market.sports': '运动', + 'market.discountRate': '折扣率', + 'market.priceUp': '价格↑', + 'market.priceDown': '价格↓', + 'market.expiryDate': '到期时间', + 'market.issuePrice': '发行价', + 'market.faceValue': '面值', + 'market.discount': '折扣', + 'market.totalSupply': '发行量', + 'market.salesProgress': '销售进度', + 'market.upcoming': '即将开始', + 'market.subscribing': '申购中', + 'market.ended': '已结束', + 'market.timeToStart': '距开始', + 'market.couponBrand': '券名/品牌', + 'market.latestPrice': '最新价', + 'market.change24h': '24h涨跌', + 'market.discountSuffix': '折', + + // ============ Trading Detail ============ + 'trading.depth': '交易深度', + 'trading.askSide': '卖盘', + 'trading.bidSide': '买盘', + 'trading.quantitySheets': '数量(张)', + 'trading.placeOrder': '下单', + 'trading.buy': '买入', + 'trading.sell': '卖出', + 'trading.limitOrder': '限价单', + 'trading.marketOrder': '市价单', + 'trading.price': '价格', + 'trading.quantity': '数量', + 'trading.available': '可用', + 'trading.currentOrders': '当前委托', + 'trading.historyOrders': '历史委托', + 'trading.cancelOrder': '撤销', + 'trading.high24h': '24h高', + 'trading.low24h': '24h低', + 'trading.open': '开盘', + 'trading.vol24h': '24h量', + 'trading.sheetsUnit': '张', + 'trading.discountLabel': '折扣', + + // ============ Transfer ============ + 'transfer.title': '转赠', + 'transfer.scanTransfer': '扫码转赠', + 'transfer.scanDesc': '扫描对方接收码\n即时到账', + 'transfer.inputTransfer': '输入ID/邮箱/手机', + 'transfer.inputDesc': '输入对方信息\n安全转赠', + 'transfer.recentRecipients': '最近转赠', + 'transfer.manage': '管理', + 'transfer.noRecent': '暂无最近转赠记录\n完成一次转赠后会显示在这里', + 'transfer.expired': '已过期', + 'transfer.refresh': '刷新', + 'transfer.transferHistory': '转赠记录', + 'transfer.viewAll': '查看全部', + 'transfer.lastTransfer': '最近转赠', + 'transfer.inputRecipient': '输入收件人', + 'transfer.recipientIdHint': 'ID / 邮箱 / 手机号', + 'transfer.paste': '粘贴', + 'transfer.pasteFromClipboard': '从剪贴板粘贴', + 'transfer.searchRecipient': '搜索', + 'transfer.selectCoupon': '选择转赠的券', + 'transfer.noCoupons': '暂无可转赠的券', + 'transfer.confirmTransfer': '确认转赠', + 'transfer.transferTo': '转赠给', + 'transfer.couponInfo': '转赠的券', + 'transfer.confirmBtn': '确认转赠', + 'transfer.outgoing': '转出', + 'transfer.incoming': '收到', + + // ============ Wallet Coupons ============ + 'walletCoupons.title': '我的持仓', + 'walletCoupons.receiveTip': '接收券', + 'walletCoupons.usable': '可使用', + 'walletCoupons.pendingRedeem': '待核销', + 'walletCoupons.expired': '已过期', + 'walletCoupons.holdCount': '持有券数', + 'walletCoupons.totalFaceValue': '总面值', + 'walletCoupons.saved': '已节省', + 'walletCoupons.faceValue': '面值', + 'walletCoupons.expiredText': '已过期', + 'walletCoupons.expiringToday': '今天到期', + 'walletCoupons.daysToExpiry': '天后到期', + 'walletCoupons.expiryFormat': '到期', + + // ============ Coupon Detail ============ + 'couponDetail.title': '券详情', + 'couponDetail.saveBadge': '比面值节省', + 'couponDetail.faceValue': '面值', + 'couponDetail.validUntil': '有效期', + 'couponDetail.type': '类型', + 'couponDetail.issuer': '发行方', + 'couponDetail.consumeCoupon': '消费券', + 'couponDetail.usageNote': '使用说明', + 'couponDetail.allStores': '全国门店通用', + 'couponDetail.canTransfer': '可转赠给好友', + 'couponDetail.useAnytime': '有效期内随时使用', + 'couponDetail.noStack': '不可叠加使用', + 'couponDetail.noCash': '不可兑换现金', + 'couponDetail.stores': '使用门店', + 'couponDetail.storeCount': '全国 12,800+ 门店', + 'couponDetail.storeDesc': '支持全国所有直营门店使用', + 'couponDetail.priceTrend': '价格走势', + 'couponDetail.last30Days': '近30天', + 'couponDetail.highest': '最高', + 'couponDetail.lowest': '最低', + 'couponDetail.average': '均价', + 'couponDetail.tradeHistory': '历史成交', + 'couponDetail.nearbyStores': '附近可用门店', + 'couponDetail.distance': '距离', + 'couponDetail.open': '营业中', + 'couponDetail.similar': '同类券推荐', + 'couponDetail.favorite': '收藏', + 'couponDetail.buyNow': '立即购买', + + // ============ My Coupon Detail ============ + 'myCoupon.title': '券详情', + 'myCoupon.active': '可使用', + 'myCoupon.showQrHint': '出示此二维码给商户扫描核销', + 'myCoupon.switchBarcode': '切换条形码', + 'myCoupon.faceValue': '面值', + 'myCoupon.purchasePrice': '购买价格', + 'myCoupon.validUntil': '有效期', + 'myCoupon.orderNo': '订单号', + 'myCoupon.resellCount': '剩余可转售次数', + 'myCoupon.transfer': '转赠', + 'myCoupon.sell': '出售', + 'myCoupon.usageNote': '使用说明', + 'myCoupon.useInStore': '全国门店通用', + 'myCoupon.useInTime': '请在有效期内使用', + 'myCoupon.onePerVisit': '每次消费仅可使用一张', + 'myCoupon.noCash': '不可兑换现金', + 'myCoupon.extractToWallet': '提取到外部钱包', + 'myCoupon.requireKycL2': '需KYC L2+认证', + 'myCoupon.viewTrades': '查看交易记录', + 'myCoupon.help': '使用帮助', + + // ============ Order Confirm ============ + 'orderConfirm.title': '确认订单', + 'orderConfirm.quantity': '购买数量', + 'orderConfirm.paymentMethod': '支付方式', + 'orderConfirm.bankCard': '银行卡/信用卡', + 'orderConfirm.priceDetail': '价格明细', + 'orderConfirm.buyingNote': '您正在购买消费券用于消费', + 'orderConfirm.total': '合计', + 'orderConfirm.confirmPay': '确认支付', + 'orderConfirm.unitPrice': '单价', + 'orderConfirm.count': '数量', + 'orderConfirm.saveBadge': '比面值节省', + 'orderConfirm.biometricHint': '请验证指纹或面容以完成支付', + 'orderConfirm.usePasswordPay': '使用密码支付', + + // ============ Payment ============ + 'payment.title': '选择支付方式', + 'payment.addNew': '添加新支付方式', + 'payment.confirmPay': '确认支付', + 'payment.bankTransfer': '银行转账', + + 'paymentSuccess.title': '支付成功', + 'paymentSuccess.hint': '券已到账,可在「我的券」中查看', + 'paymentSuccess.couponName': '券名称', + 'paymentSuccess.payAmount': '支付金额', + 'paymentSuccess.orderNo': '订单号', + 'paymentSuccess.payTime': '支付时间', + 'paymentSuccess.viewMyCoupon': '查看我的券', + 'paymentSuccess.continueBrowse': '继续逛', + + // ============ Search ============ + 'search.hint': '搜索券、品牌、分类...', + 'search.cancel': '取消', + 'search.hotSearch': '热门搜索', + 'search.history': '搜索历史', + 'search.clear': '清空', + 'search.diningCoupon': '餐饮券', + 'search.discountCoupon': '折扣券', + 'search.travel': '旅游', + + // ============ Redeem ============ + 'redeem.title': '出示券码', + 'redeem.faceValue': '面值', + 'redeem.validTime': '有效时间', + 'redeem.refresh': '刷新券码', + 'redeem.showHint': '请将此码出示给商户扫描,屏幕已自动调至最高亮度', + + // ============ Sell Order ============ + 'sellOrder.title': '挂单出售', + 'sellOrder.faceValue': '面值', + 'sellOrder.credit': '信用', + 'sellOrder.setPrice': '设定售价', + 'sellOrder.price': '售价', + 'sellOrder.aiSuggest': 'AI建议售价', + 'sellOrder.bestDealRate': '此价格成交概率最高', + 'sellOrder.discountRate': '折扣率', + 'sellOrder.platformFee': '平台手续费 (1.5%)', + 'sellOrder.estimatedReceive': '预计到账', + 'sellOrder.marketAvg': '当前市场均价', + 'sellOrder.recent24hTrades': '最近24小时成交', + 'sellOrder.tradesUnit': '笔', + 'sellOrder.confirmList': '确认挂单', + 'sellOrder.success': '挂单成功', + 'sellOrder.successHint': '您的券已挂到市场,当有买家下单时将自动成交。', + 'sellOrder.ok': '确定', + + // ============ Trading Page (My Trades) ============ + 'tradingPage.title': '我的交易', + 'tradingPage.pendingOrders': '我的挂单', + 'tradingPage.tradeRecords': '交易记录', + 'tradingPage.listPrice': '挂单价', + 'tradingPage.listTime': '挂单时间', + 'tradingPage.cancelOrder': '撤单', + + // ============ Wallet ============ + 'wallet.myBalance': '我的余额', + 'wallet.totalBalance': '总余额', + 'wallet.withdrawable': '可提现', + 'wallet.frozen': '冻结中', + 'wallet.deposit': '充值', + 'wallet.withdraw': '提现', + 'wallet.records': '交易记录', + + 'deposit.title': '充值', + 'deposit.currentBalance': '当前余额', + 'deposit.amount': '充值金额', + 'deposit.custom': '自定义金额', + 'deposit.paymentMethod': '支付方式', + 'deposit.submit': '充值', + + 'withdraw.title': '提现', + 'withdraw.availableBalance': '可提现余额', + 'withdraw.amount': '提现金额', + 'withdraw.all': '全部', + 'withdraw.to': '提现到', + 'withdraw.savingsAccount': '储蓄账户', + 'withdraw.fee': '手续费 (0.5%)', + 'withdraw.actualReceive': '实际到账', + 'withdraw.estimateTime': '预计 1-2 个工作日到账', + 'withdraw.submit': '确认提现', + + 'txRecords.title': '交易记录', + 'txRecords.buy': '购买', + 'txRecords.sell': '出售', + 'txRecords.transfer': '转赠', + 'txRecords.noRecords': '暂无记录', + 'txRecords.orderNo': '订单号', + 'txRecords.transferTo': '转赠给', + + // ============ Profile ============ + 'profile.favorites': '收藏', + 'profile.orders': '订单', + 'profile.coupons': '券', + 'profile.wallet': '钱包', + 'profile.myTrades': '我的交易', + 'profile.walletBalance': '钱包余额', + 'profile.paymentManage': '支付管理', + 'profile.kyc': '身份认证', + 'profile.proMode': '高级模式', + 'profile.settings': '设置', + 'profile.helpCenter': '帮助中心', + 'profile.issuerPortal': '发行方入口', + 'profile.merchantPortal': '商户入口', + + // ============ Settings ============ + 'settings.title': '设置', + 'settings.accountSecurity': '账号与安全', + 'settings.phone': '手机号', + 'settings.email': '邮箱', + 'settings.changePassword': '修改密码', + 'settings.identity': '身份认证', + 'settings.paymentManage': '支付管理', + 'settings.paymentMethod': '支付方式', + 'settings.bankAccount': '银行账户', + 'settings.paymentPassword': '支付密码', + 'settings.notifications': '通知设置', + 'settings.tradeNotify': '交易通知', + 'settings.expiryRemind': '到期提醒', + 'settings.marketChange': '行情变动', + 'settings.marketingPush': '营销推送', + 'settings.general': '通用', + 'settings.language': '语言', + 'settings.currency': '货币', + 'settings.clearCache': '清除缓存', + 'settings.about': '关于', + 'settings.version': '版本', + 'settings.userAgreement': '用户协议', + 'settings.privacyPolicy': '隐私政策', + 'settings.helpCenter': '帮助中心', + 'settings.logout': '退出登录', + 'settings.selectCurrency': '选择计价货币', + 'settings.currencyNote': '此设置影响交易页面中所有价格的计价货币显示', + 'settings.selectLanguage': '选择语言', + 'settings.currencySymbol': '符号', + + // ============ KYC ============ + 'kyc.title': '身份认证', + 'kyc.currentLevel': '当前认证等级', + 'kyc.l1Title': 'L1 基础认证', + 'kyc.l1Desc': '手机号 + 邮箱验证', + 'kyc.l1Limit': '每日购买限额 \$500', + 'kyc.l1Feature': '可购买券、出示核销', + 'kyc.l2Title': 'L2 身份认证', + 'kyc.l2Desc': '身份证/护照验证', + 'kyc.l2Limit': '每日购买限额 \$5,000', + 'kyc.l2Feature': '解锁二级市场交易、P2P转赠', + 'kyc.l3Title': 'L3 高级认证', + 'kyc.l3Desc': '视频面审 + 地址证明', + 'kyc.l3Limit': '无限额', + 'kyc.l3Feature': '解锁大额交易、提现无限制', + 'kyc.completed': '已完成', + 'kyc.goVerify': '去认证', + + // ============ Payment Management ============ + 'payManage.title': '支付管理', + 'payManage.myCards': '我的银行卡', + 'payManage.addCard': '添加新银行卡', + 'payManage.bankAccount': '银行账户(提现用)', + 'payManage.paymentSecurity': '支付安全', + 'payManage.paymentPassword': '支付密码', + 'payManage.passwordSet': '已设置', + 'payManage.biometricPay': '指纹/面容支付', + 'payManage.biometricEnabled': '已开启', + 'payManage.noPasswordPay': '免密支付', + 'payManage.noPasswordLimit': '单笔≤\$10', + + // ============ AI Chat ============ + 'aiChat.title': 'AI 助手', + 'aiChat.greeting': '你好!我是 Genex AI 助手,可以帮你发现高性价比好券、比价分析、组合推荐。试试问我:', + 'aiChat.suggest1': '推荐适合我的券', + 'aiChat.suggest2': '星巴克券值不值得买?', + 'aiChat.suggest3': '帮我做比价分析', + 'aiChat.suggest4': '我的券快到期了怎么办?', + 'aiChat.inputHint': '问我任何关于券的问题...', + 'aiChat.confirmAction': '确认执行', + 'aiChat.riskLow': '低风险', + 'aiChat.riskNormal': '需确认', + 'aiChat.riskHigh': '高风险', + + // ============ Messages ============ + 'message.title': '消息', + 'message.detailTitle': '消息详情', + 'message.tradeNotify': '交易通知', + 'message.expiryRemind': '到期提醒', + 'message.systemNotify': '系统通知', + 'message.promoNotify': '活动推送', + 'message.tradeSuccess': '交易成功通知', + 'message.couponName': '券名称', + 'message.faceValue': '面值', + 'message.payAmount': '支付金额', + 'message.orderNo': '订单号', + 'message.payMethod': '支付方式', + 'message.viewCouponDetail': '查看券详情', + + // ============ Status Tags ============ + 'status.active': '可使用', + 'status.pending': '待核销', + 'status.expired': '已过期', + 'status.used': '已使用', + 'status.processing': '处理中', + 'status.completed': '已完成', + 'status.cancelled': '已取消', + 'status.refunding': '退款中', + 'status.onSale': '出售中', + + // ============ Empty States ============ + 'empty.noCoupons': '还没有券', + 'empty.noCouponsHint': '去市场看看有什么好券吧', + 'empty.browse': '去逛逛', + 'empty.noTrades': '暂无交易记录', + 'empty.noTradesHint': '完成首笔交易后这里会显示记录', + 'empty.noResults': '没有找到结果', + 'empty.noResultsHint': '换个关键词试试', + 'empty.noMessages': '暂无消息', + 'empty.noMessagesHint': '交易通知和系统公告会显示在这里', + 'empty.networkError': '网络连接失败', + 'empty.networkErrorHint': '请检查网络设置后重试', + + // ============ Receive Coupon ============ + 'receiveCoupon.title': '接收券', + 'receiveCoupon.hint': '向他人展示下方二维码或接收ID,对方可通过扫码或输入ID将券转赠到你的钱包。', + 'receiveCoupon.id': '接收ID', + 'receiveCoupon.idCopied': '接收ID已复制到剪贴板', + 'receiveCoupon.note': '接收的券将自动存入你的钱包,可在首页钱包中查看和管理。', + + // ============ Issuer ============ + 'issuer.title': '发行方管理', + 'issuer.verified': '已认证发行方', + 'issuer.overview': '总览', + 'issuer.issue': '发券', + 'issuer.redeem': '核销', + 'issuer.finance': '财务', + 'issuer.totalIssued': '发行总量', + 'issuer.totalSold': '已售出', + 'issuer.totalRedeemed': '已核销', + 'issuer.redeemRate': '核销率', + 'issuer.quickActions': '快捷操作', + 'issuer.createCoupon': '创建券', + 'issuer.storeManage': '门店管理', + 'issuer.salesAnalysis': '销售分析', + 'issuer.statement': '对账单', + 'issuer.myCoupons': '我的券', + 'issuer.listed': '已上架', + 'issuer.underReview': '审核中', + 'issuer.soldOut': '已售罄', + 'issuer.issuedSlash': '发行', + 'issuer.sold': '已售', + 'issuer.issueCenter': '发券中心', + 'issuer.selectTemplate': '选择券模板', + 'issuer.voucherType': '满减券', + 'issuer.discountType': '折扣券', + 'issuer.giftCardType': '礼品卡', + 'issuer.storedValueType': '储值券', + 'issuer.couponManage': '券管理', + 'issuer.viewAll': '查看全部', + 'issuer.couponEvents': '券活动', + 'issuer.createNew': '创建新券', + 'issuer.redeemManage': '核销管理', + 'issuer.redeemTrend': '核销趋势', + 'issuer.allStores': '全部门店', + 'issuer.employees': '名员工', + 'issuer.financeManage': '财务管理', + 'issuer.totalSales': '总销售额', + 'issuer.settled': '已到账', + 'issuer.pendingSettle': '待结算', + 'issuer.breakage': 'Breakage', + 'issuer.withdrawBtn': '提现', + 'issuer.reportBtn': '对账报表', + 'issuer.settleDetail': '结算明细', + 'issuer.creditLevel': '信用等级', + 'issuer.issueQuota': '发行额度', + 'issuer.usedQuota': '已用额度', + 'issuer.dataCenter': '数据中心', + 'issuer.issueSalesRate': '发行量/销量/兑付率', + 'issuer.userProfile': '用户画像', + 'issuer.userProfileDesc': '购买用户分布分析', + 'issuer.creditDetail': '信用详情', + 'issuer.creditDetailDesc': '评分详情与提升建议', + 'issuer.quotaChange': '额度变动', + 'issuer.quotaChangeDesc': '历史额度调整记录', + 'issuer.companyInfo': '企业信息', + 'issuer.companyInfoDesc': '营业执照/联系人', + 'issuer.settingsItem': '设置', + 'issuer.settingsItemDesc': '通知/安全/语言', + 'issuer.helpItem': '帮助中心', + 'issuer.helpItemDesc': '常见问题与客服', + + // ============ Merchant ============ + 'merchant.today': '今日', + 'merchant.onlineMode': '在线模式', + 'merchant.offlineMode': '离线模式', + 'merchant.pendingSync': '待同步', + 'merchant.syncUnit': '笔', + 'merchant.scanHint': '将券二维码对准扫描框', + 'merchant.flashlight': '手电筒', + 'merchant.manualInput': '手动输码', + 'merchant.redeemRecords': '核销记录', + 'merchant.storeData': '门店数据', + 'merchant.inputCode': '手动输入券码', + 'merchant.inputCodeHint': '请输入券码', + 'merchant.query': '查询', + 'merchant.userNickname': '用户昵称', + 'merchant.consumer': '消费者', + 'merchant.couponName': '券名称', + 'merchant.faceValue': '面值', + 'merchant.validUntil': '有效期', + 'merchant.useCondition': '使用条件', + 'merchant.noMinSpend': '无最低消费', + 'merchant.confirmRedeem': '确认核销', + 'merchant.redeemSuccess': '核销成功', + 'merchant.continueRedeem': '继续核销', + 'merchant.synced': '已同步', + 'merchant.pendingSyncLabel': '待同步', + 'merchant.redeemOperator': '核销员', + 'merchant.todayRedeem': '今日核销', + 'merchant.redeemAmount': '核销金额', + 'merchant.weekTrend': '本周趋势', + 'merchant.operatorRank': '核销员排行', + + // ============ Profile Page (additional) ============ + 'profile.account': '账户', + 'profile.trade': '交易', + 'profile.holdCoupons': '持券', + 'profile.saved': '节省', + 'profile.credit': '信用', + 'profile.creditScore': '信用积分', + 'profile.myFavorites': '我的收藏', + 'profile.securitySettings': '安全设置', + 'profile.advancedSettings': '高级设置', + 'profile.aboutGenex': '关于 Genex', + 'profile.simplifiedChinese': '简体中文', + + // ============ Pro Mode ============ + 'proMode.toggleDesc': '开启后可查看链上信息和连接外部钱包', + 'proMode.requireKycL2': '需要 KYC L2 及以上认证', + 'proMode.connected': '已连接', + 'proMode.disconnect': '断开', + 'proMode.connectWallet': '连接外部钱包', + 'proMode.walletDesc': '连接外部钱包后可将平台资产提取至自有地址', + 'proMode.showChainAddress': '显示链上地址', + 'proMode.showChainAddressDesc': '在券详情中展示合约地址', + 'proMode.showTxHash': '显示交易Hash', + 'proMode.showTxHashDesc': '在交易记录中展示链上Hash', + 'proMode.txExplorer': '交易浏览器', + 'proMode.txBuyExample': '购买 星巴克 \$25 礼品卡', + 'proMode.txSellExample': '出售 Amazon \$100 券', + 'proMode.confirmed': '已确认', + 'proMode.confirming': '确认中', + 'proMode.viewAllTx': '查看全部链上交易', + 'proMode.chainAssets': '链上资产', + 'proMode.custodialWallet': '平台托管钱包', + 'proMode.externalWallet': '外部钱包 (MetaMask)', + 'proMode.couponCount5': '5 张券', + 'proMode.couponCount0': '0 张券', + 'proMode.extractToWallet': '提取至外部钱包', + 'proMode.tradeTrack': '交易轨道', + 'proMode.utilityTrackDesc': '券有效期≤12个月,无需证券牌照', + 'proMode.securitiesTrackDesc': '长期投资型券产品(即将推出)', + 'proMode.mvpNote': '当前MVP版本仅支持Utility Track', + 'proMode.comingSoon': '敬请期待', + 'proMode.whatIsTitle': '什么是高级模式?', + 'proMode.whatIsDesc': '高级模式面向有区块链经验的用户,开启后可以:\n' + '• 连接外部钱包(MetaMask等)\n' + '• 查看链上地址和交易Hash\n' + '• 将资产提取至自有钱包\n' + '• 查看底层链上数据\n\n' + '需要完成 KYC L2 认证后方可开启。', + + // ============ Wallet Page (additional) ============ + 'wallet.buyIn': '买入', + 'wallet.sellOut': '卖出', + 'wallet.giftTransfer': '转赠', + 'wallet.redeemUse': '核销', + + // ============ AI Fab ============ + 'aiFab.greeting': '你好!我是 Genex AI 助手,可以帮你管理券资产、查找优惠、分析价格。有什么需要帮助的吗?', + 'aiFab.inputHint': '输入消息...', + 'aiFab.suggest1': '帮我找高折扣券', + 'aiFab.suggest2': '我的券快到期了吗?', + 'aiFab.suggest3': '推荐今日好券', + 'aiFab.suggest4': '分析我的券资产', + + // ============ KYC Badge ============ + 'kyc.badgeLabel': '认证', + + // ============ Message Tabs ============ + 'message.markAllRead': '全部已读', + 'message.tabTrade': '交易', + 'message.tabExpiry': '到期', + 'message.tabAnnouncement': '公告', + + // ============ Transfer additions ============ + 'transfer.contactEmail': '邮箱', + 'transfer.contactPhone': '手机', + 'transfer.yesterday': '昨天', + 'transfer.daysAgo': '天前', + 'transfer.weeksAgo': '周前', + 'transfer.monthsAgo': '月前', + + // ============ Merchant AI ============ + 'merchantAi.redeemAssist': '核销辅助', + 'merchantAi.trafficForecast': '客流预测', + 'merchantAi.anomalyAlert': '异常预警', + 'merchantAi.verifyAuth': '验券真伪', + 'merchantAi.checkStatus': '查券状态', + 'merchantAi.batchRedeem': '批量核销', + 'merchantAi.feedback': '问题反馈', + 'merchantAi.quickActions': 'AI 快捷操作', + 'merchantAi.redeemTips': '核销提示', + 'merchantAi.todayHotRedeem': '今日热门核销', + 'merchantAi.countUnit': '笔', + 'merchantAi.aiMarketing': 'AI 营销建议', + 'merchantAi.crossSellTitle': '推荐搭配销售', + 'merchantAi.crossSellDesc': '购买咖啡券的顾客同时对糕点券感兴趣,建议推荐组合', + 'merchantAi.weekendPromoTitle': '周末促销建议', + 'merchantAi.weekendPromoDesc': '历史数据显示周六核销量+30%,建议推出周末限时活动', + 'merchantAi.todayForecast': '今日客流预测', + 'merchantAi.expectedRedeem': '预计核销', + 'merchantAi.peakHours': '高峰时段', + 'merchantAi.expectedRevenue': '预计收入', + 'merchantAi.trafficInsight': '较上周同期增长12%,建议午间增加1名收银员', + 'merchantAi.hourlyForecast': '分时段预测', + 'merchantAi.weeklyForecast': '本周预测', + 'merchantAi.staffSuggestion': '排班建议', + 'merchantAi.pendingCount': '待处理', + 'merchantAi.resolvedToday': '今日已处理', + 'merchantAi.riskIndex': '风险指数', + 'merchantAi.riskLow': '低', + 'merchantAi.activeAlerts': '活跃预警', + 'merchantAi.highFreqRedeem': '高频核销检测', + 'merchantAi.suspectFakeCode': '疑似伪造券码', + 'merchantAi.suspiciousPatterns': '可疑模式检测', + 'merchantAi.consecutiveRedeem': '同一用户连续核销', + 'merchantAi.offHoursRedeem': '非营业时间核销尝试', + 'merchantAi.expiredRedeemAttempt': '过期券核销尝试', + 'merchantAi.statusAbnormal': '异常', + 'merchantAi.statusWarning': '注意', + 'merchantAi.statusNormal': '正常', + 'merchantAi.expiredBlock': '过期券核销拦截', + 'merchantAi.duplicateBlock': '重复核销拦截', + 'merchantAi.wrongStoreAlert': '非本店券提醒', + 'merchantAi.insufficientBalance': '余额不足核销', + 'merchantAi.systemRetry': '系统超时重试', + 'merchantAi.monday': '周一', + 'merchantAi.tuesday': '周二', + 'merchantAi.wednesday': '周三', + 'merchantAi.thursday': '周四', + 'merchantAi.friday': '周五', + 'merchantAi.saturday': '周六', + 'merchantAi.sunday': '周日', + + // ============ Issuer additions ============ + 'issuer.unlisted': '已下架', +}; diff --git a/frontend/genex-mobile/lib/app/i18n/strings/zh_tw.dart b/frontend/genex-mobile/lib/app/i18n/strings/zh_tw.dart new file mode 100644 index 0000000..9f0d8ee --- /dev/null +++ b/frontend/genex-mobile/lib/app/i18n/strings/zh_tw.dart @@ -0,0 +1,736 @@ +const Map zhTW = { + // ============ Common ============ + 'common.confirm': '確認', + 'common.cancel': '取消', + 'common.save': '儲存', + 'common.delete': '刪除', + 'common.edit': '編輯', + 'common.search': '搜尋', + 'common.loading': '載入中...', + 'common.retry': '重試', + 'common.done': '完成', + 'common.next': '下一步', + 'common.back': '返回', + 'common.close': '關閉', + 'common.more': '更多', + 'common.all': '全部', + 'common.filter': '篩選', + 'common.sort': '排序', + 'common.copy': '複製', + 'common.copied': '已複製到剪貼簿', + 'common.today': '今日', + 'common.thisWeek': '本週', + 'common.thisMonth': '本月', + + // ============ Navigation ============ + 'nav.home': '首頁', + 'nav.trading': '交易', + 'nav.messages': '訊息', + 'nav.profile': '我的', + + // ============ Welcome / Auth ============ + 'welcome.slogan': '讓每一張券都有價值', + 'welcome.phoneRegister': '手機號註冊', + 'welcome.emailRegister': '信箱註冊', + 'welcome.otherLogin': '其他方式登入', + 'welcome.hasAccount': '已有帳號?', + 'welcome.login': '登入', + 'welcome.agreement': '註冊即表示同意《使用者協議》和《隱私權政策》', + + 'login.title': '歡迎回來', + 'login.subtitle': '登入 Genex 管理你的券資產', + 'login.passwordTab': '密碼登入', + 'login.codeTab': '驗證碼登入', + 'login.phoneOrEmail': '手機號或信箱', + 'login.password': '密碼', + 'login.forgotPassword': '忘記密碼?', + 'login.submit': '登入', + 'login.phone': '手機號', + 'login.verifyCode': '驗證碼', + 'login.getCode': '取得驗證碼', + + 'register.title': '建立帳號', + 'register.emailSubtitle': '使用信箱註冊 Genex 帳號', + 'register.phoneSubtitle': '使用手機號註冊 Genex 帳號', + 'register.email': '信箱地址', + 'register.phone': '手機號', + 'register.emailHint': '請輸入信箱地址', + 'register.phoneHint': '請輸入手機號', + 'register.code': '驗證碼', + 'register.codeHint': '請輸入6位驗證碼', + 'register.getCode': '取得驗證碼', + 'register.setPassword': '設定密碼', + 'register.passwordHint': '8-20位,含字母和數字', + 'register.agreement': '我已閱讀並同意', + 'register.userAgreement': '《使用者協議》', + 'register.privacyPolicy': '《隱私權政策》', + 'register.submit': '註冊', + 'register.stepVerify': '驗證', + 'register.stepPassword': '設密碼', + 'register.stepDone': '完成', + 'register.rule8chars': '8位以上', + 'register.ruleLetter': '含字母', + 'register.ruleNumber': '含數字', + + 'forgot.title': '找回密碼', + 'forgot.inputAccount': '輸入手機號或信箱', + 'forgot.sendHint': '我們將向您發送驗證碼', + 'forgot.accountHint': '手機號 / 信箱地址', + 'forgot.getCode': '取得驗證碼', + 'forgot.inputCode': '輸入驗證碼', + 'forgot.codeSentTo': '驗證碼已發送至', + 'forgot.codeHint': '6位驗證碼', + 'forgot.resend': '重新發送', + 'forgot.next': '下一步', + 'forgot.setNewPassword': '設定新密碼', + 'forgot.newPasswordHint': '請輸入新密碼(8位以上)', + 'forgot.newPassword': '新密碼', + 'forgot.confirmPassword': '確認新密碼', + 'forgot.confirmChange': '確認修改', + 'forgot.success': '密碼修改成功', + 'forgot.successHint': '請使用新密碼登入', + 'forgot.backToLogin': '返回登入', + + // ============ Home ============ + 'home.searchHint': '搜尋券、品牌、分類...', + 'home.position': '持倉', + 'home.hold': '持有', + 'home.couponUnit': '張券', + 'home.totalValue': '總值', + 'home.receive': '接收', + 'home.transfer': '轉贈', + 'home.sell': '出售', + 'home.redeem': '核銷', + 'home.flashSale': '限時搶購', + 'home.newRelease': '新券首發', + 'home.discountRank': '折扣排行', + 'home.expiringSoon': '即將到期', + 'home.priceCompare': '比價', + 'home.resaleMarket': '轉讓市場', + 'home.hotTrades': '熱門交易', + 'home.viewAll': '全部', + 'home.aiRecommend': 'AI 推薦', + 'home.aiRecommendDesc': '根據你的偏好,發現了3張高性價比券', + 'home.featuredCoupons': '精選好券', + 'home.viewAllCoupons': '查看全部', + + // ============ Market ============ + 'market.title': '交易', + 'market.primary': '一級市場', + 'market.secondary': '二級市場', + 'market.dining': '餐飲', + 'market.shopping': '購物', + 'market.entertainment': '娛樂', + 'market.travel': '出行', + 'market.lifestyle': '生活', + 'market.sports': '運動', + 'market.discountRate': '折扣率', + 'market.priceUp': '價格↑', + 'market.priceDown': '價格↓', + 'market.expiryDate': '到期時間', + 'market.issuePrice': '發行價', + 'market.faceValue': '面值', + 'market.discount': '折扣', + 'market.totalSupply': '發行量', + 'market.salesProgress': '銷售進度', + 'market.upcoming': '即將開始', + 'market.subscribing': '申購中', + 'market.ended': '已結束', + 'market.timeToStart': '距開始', + 'market.couponBrand': '券名/品牌', + 'market.latestPrice': '最新價', + 'market.change24h': '24h漲跌', + 'market.discountSuffix': '折', + + // ============ Trading Detail ============ + 'trading.depth': '交易深度', + 'trading.askSide': '賣盤', + 'trading.bidSide': '買盤', + 'trading.quantitySheets': '數量(張)', + 'trading.placeOrder': '下單', + 'trading.buy': '買入', + 'trading.sell': '賣出', + 'trading.limitOrder': '限價單', + 'trading.marketOrder': '市價單', + 'trading.price': '價格', + 'trading.quantity': '數量', + 'trading.available': '可用', + 'trading.currentOrders': '當前委託', + 'trading.historyOrders': '歷史委託', + 'trading.cancelOrder': '撤銷', + 'trading.high24h': '24h高', + 'trading.low24h': '24h低', + 'trading.open': '開盤', + 'trading.vol24h': '24h量', + 'trading.sheetsUnit': '張', + 'trading.discountLabel': '折扣', + + // ============ Transfer ============ + 'transfer.title': '轉贈', + 'transfer.scanTransfer': '掃碼轉贈', + 'transfer.scanDesc': '掃描對方接收碼\n即時到帳', + 'transfer.inputTransfer': '輸入ID/信箱/手機', + 'transfer.inputDesc': '輸入對方資訊\n安全轉贈', + 'transfer.recentRecipients': '最近轉贈', + 'transfer.manage': '管理', + 'transfer.noRecent': '暫無最近轉贈紀錄\n完成一次轉贈後會顯示在這裡', + 'transfer.expired': '已過期', + 'transfer.refresh': '重新整理', + 'transfer.transferHistory': '轉贈紀錄', + 'transfer.viewAll': '查看全部', + 'transfer.lastTransfer': '最近轉贈', + 'transfer.inputRecipient': '輸入收件人', + 'transfer.recipientIdHint': 'ID / 信箱 / 手機號', + 'transfer.paste': '貼上', + 'transfer.pasteFromClipboard': '從剪貼簿貼上', + 'transfer.searchRecipient': '搜尋', + 'transfer.selectCoupon': '選擇轉贈的券', + 'transfer.noCoupons': '暫無可轉贈的券', + 'transfer.confirmTransfer': '確認轉贈', + 'transfer.transferTo': '轉贈給', + 'transfer.couponInfo': '轉贈的券', + 'transfer.confirmBtn': '確認轉贈', + 'transfer.outgoing': '轉出', + 'transfer.incoming': '收到', + + // ============ Wallet Coupons ============ + 'walletCoupons.title': '我的持倉', + 'walletCoupons.receiveTip': '接收券', + 'walletCoupons.usable': '可使用', + 'walletCoupons.pendingRedeem': '待核銷', + 'walletCoupons.expired': '已過期', + 'walletCoupons.holdCount': '持有券數', + 'walletCoupons.totalFaceValue': '總面值', + 'walletCoupons.saved': '已節省', + 'walletCoupons.faceValue': '面值', + 'walletCoupons.expiredText': '已過期', + 'walletCoupons.expiringToday': '今天到期', + 'walletCoupons.daysToExpiry': '天後到期', + 'walletCoupons.expiryFormat': '到期', + + // ============ Coupon Detail ============ + 'couponDetail.title': '券詳情', + 'couponDetail.saveBadge': '比面值節省', + 'couponDetail.faceValue': '面值', + 'couponDetail.validUntil': '有效期', + 'couponDetail.type': '類型', + 'couponDetail.issuer': '發行方', + 'couponDetail.consumeCoupon': '消費券', + 'couponDetail.usageNote': '使用說明', + 'couponDetail.allStores': '全國門市通用', + 'couponDetail.canTransfer': '可轉贈給好友', + 'couponDetail.useAnytime': '有效期內隨時使用', + 'couponDetail.noStack': '不可疊加使用', + 'couponDetail.noCash': '不可兌換現金', + 'couponDetail.stores': '使用門市', + 'couponDetail.storeCount': '全國 12,800+ 門市', + 'couponDetail.storeDesc': '支援全國所有直營門市使用', + 'couponDetail.priceTrend': '價格走勢', + 'couponDetail.last30Days': '近30天', + 'couponDetail.highest': '最高', + 'couponDetail.lowest': '最低', + 'couponDetail.average': '均價', + 'couponDetail.tradeHistory': '歷史成交', + 'couponDetail.nearbyStores': '附近可用門市', + 'couponDetail.distance': '距離', + 'couponDetail.open': '營業中', + 'couponDetail.similar': '同類券推薦', + 'couponDetail.favorite': '收藏', + 'couponDetail.buyNow': '立即購買', + + // ============ My Coupon Detail ============ + 'myCoupon.title': '券詳情', + 'myCoupon.active': '可使用', + 'myCoupon.showQrHint': '出示此二維碼給商戶掃描核銷', + 'myCoupon.switchBarcode': '切換條碼', + 'myCoupon.faceValue': '面值', + 'myCoupon.purchasePrice': '購買價格', + 'myCoupon.validUntil': '有效期', + 'myCoupon.orderNo': '訂單號', + 'myCoupon.resellCount': '剩餘可轉售次數', + 'myCoupon.transfer': '轉贈', + 'myCoupon.sell': '出售', + 'myCoupon.usageNote': '使用說明', + 'myCoupon.useInStore': '全國門市通用', + 'myCoupon.useInTime': '請在有效期內使用', + 'myCoupon.onePerVisit': '每次消費僅可使用一張', + 'myCoupon.noCash': '不可兌換現金', + 'myCoupon.extractToWallet': '提取到外部錢包', + 'myCoupon.requireKycL2': '需KYC L2+認證', + 'myCoupon.viewTrades': '查看交易紀錄', + 'myCoupon.help': '使用幫助', + + // ============ Order Confirm ============ + 'orderConfirm.title': '確認訂單', + 'orderConfirm.quantity': '購買數量', + 'orderConfirm.paymentMethod': '支付方式', + 'orderConfirm.bankCard': '銀行卡/信用卡', + 'orderConfirm.priceDetail': '價格明細', + 'orderConfirm.buyingNote': '您正在購買消費券用於消費', + 'orderConfirm.total': '合計', + 'orderConfirm.confirmPay': '確認支付', + 'orderConfirm.unitPrice': '單價', + 'orderConfirm.count': '數量', + 'orderConfirm.saveBadge': '比面值節省', + 'orderConfirm.biometricHint': '請驗證指紋或臉部辨識以完成支付', + 'orderConfirm.usePasswordPay': '使用密碼支付', + + // ============ Payment ============ + 'payment.title': '選擇支付方式', + 'payment.addNew': '新增支付方式', + 'payment.confirmPay': '確認支付', + 'payment.bankTransfer': '銀行轉帳', + + 'paymentSuccess.title': '支付成功', + 'paymentSuccess.hint': '券已到帳,可在「我的券」中查看', + 'paymentSuccess.couponName': '券名稱', + 'paymentSuccess.payAmount': '支付金額', + 'paymentSuccess.orderNo': '訂單號', + 'paymentSuccess.payTime': '支付時間', + 'paymentSuccess.viewMyCoupon': '查看我的券', + 'paymentSuccess.continueBrowse': '繼續逛', + + // ============ Search ============ + 'search.hint': '搜尋券、品牌、分類...', + 'search.cancel': '取消', + 'search.hotSearch': '熱門搜尋', + 'search.history': '搜尋歷史', + 'search.clear': '清空', + 'search.diningCoupon': '餐飲券', + 'search.discountCoupon': '折扣券', + 'search.travel': '旅遊', + + // ============ Redeem ============ + 'redeem.title': '出示券碼', + 'redeem.faceValue': '面值', + 'redeem.validTime': '有效時間', + 'redeem.refresh': '重新整理券碼', + 'redeem.showHint': '請將此碼出示給商戶掃描,螢幕已自動調至最高亮度', + + // ============ Sell Order ============ + 'sellOrder.title': '掛單出售', + 'sellOrder.faceValue': '面值', + 'sellOrder.credit': '信用', + 'sellOrder.setPrice': '設定售價', + 'sellOrder.price': '售價', + 'sellOrder.aiSuggest': 'AI建議售價', + 'sellOrder.bestDealRate': '此價格成交機率最高', + 'sellOrder.discountRate': '折扣率', + 'sellOrder.platformFee': '平台手續費 (1.5%)', + 'sellOrder.estimatedReceive': '預計到帳', + 'sellOrder.marketAvg': '目前市場均價', + 'sellOrder.recent24hTrades': '最近24小時成交', + 'sellOrder.tradesUnit': '筆', + 'sellOrder.confirmList': '確認掛單', + 'sellOrder.success': '掛單成功', + 'sellOrder.successHint': '您的券已掛到市場,當有買家下單時將自動成交。', + 'sellOrder.ok': '確定', + + // ============ Trading Page (My Trades) ============ + 'tradingPage.title': '我的交易', + 'tradingPage.pendingOrders': '我的掛單', + 'tradingPage.tradeRecords': '交易紀錄', + 'tradingPage.listPrice': '掛單價', + 'tradingPage.listTime': '掛單時間', + 'tradingPage.cancelOrder': '撤單', + + // ============ Wallet ============ + 'wallet.myBalance': '我的餘額', + 'wallet.totalBalance': '總餘額', + 'wallet.withdrawable': '可提現', + 'wallet.frozen': '凍結中', + 'wallet.deposit': '儲值', + 'wallet.withdraw': '提現', + 'wallet.records': '交易紀錄', + + 'deposit.title': '儲值', + 'deposit.currentBalance': '目前餘額', + 'deposit.amount': '儲值金額', + 'deposit.custom': '自訂金額', + 'deposit.paymentMethod': '支付方式', + 'deposit.submit': '儲值', + + 'withdraw.title': '提現', + 'withdraw.availableBalance': '可提現餘額', + 'withdraw.amount': '提現金額', + 'withdraw.all': '全部', + 'withdraw.to': '提現到', + 'withdraw.savingsAccount': '儲蓄帳戶', + 'withdraw.fee': '手續費 (0.5%)', + 'withdraw.actualReceive': '實際到帳', + 'withdraw.estimateTime': '預計 1-2 個工作日到帳', + 'withdraw.submit': '確認提現', + + 'txRecords.title': '交易紀錄', + 'txRecords.buy': '購買', + 'txRecords.sell': '出售', + 'txRecords.transfer': '轉贈', + 'txRecords.noRecords': '暫無紀錄', + 'txRecords.orderNo': '訂單號', + 'txRecords.transferTo': '轉贈給', + + // ============ Profile ============ + 'profile.favorites': '收藏', + 'profile.orders': '訂單', + 'profile.coupons': '券', + 'profile.wallet': '錢包', + 'profile.myTrades': '我的交易', + 'profile.walletBalance': '錢包餘額', + 'profile.paymentManage': '支付管理', + 'profile.kyc': '身分認證', + 'profile.proMode': '進階模式', + 'profile.settings': '設定', + 'profile.helpCenter': '幫助中心', + 'profile.issuerPortal': '發行方入口', + 'profile.merchantPortal': '商戶入口', + + // ============ Settings ============ + 'settings.title': '設定', + 'settings.accountSecurity': '帳號與安全', + 'settings.phone': '手機號', + 'settings.email': '信箱', + 'settings.changePassword': '修改密碼', + 'settings.identity': '身分認證', + 'settings.paymentManage': '支付管理', + 'settings.paymentMethod': '支付方式', + 'settings.bankAccount': '銀行帳戶', + 'settings.paymentPassword': '支付密碼', + 'settings.notifications': '通知設定', + 'settings.tradeNotify': '交易通知', + 'settings.expiryRemind': '到期提醒', + 'settings.marketChange': '行情變動', + 'settings.marketingPush': '行銷推播', + 'settings.general': '一般', + 'settings.language': '語言', + 'settings.currency': '貨幣', + 'settings.clearCache': '清除快取', + 'settings.about': '關於', + 'settings.version': '版本', + 'settings.userAgreement': '使用者協議', + 'settings.privacyPolicy': '隱私權政策', + 'settings.helpCenter': '幫助中心', + 'settings.logout': '登出', + 'settings.selectCurrency': '選擇計價貨幣', + 'settings.currencyNote': '此設定影響交易頁面中所有價格的計價貨幣顯示', + 'settings.selectLanguage': '選擇語言', + 'settings.currencySymbol': '符號', + + // ============ KYC ============ + 'kyc.title': '身分認證', + 'kyc.currentLevel': '目前認證等級', + 'kyc.l1Title': 'L1 基礎認證', + 'kyc.l1Desc': '手機號 + 信箱驗證', + 'kyc.l1Limit': '每日購買限額 \$500', + 'kyc.l1Feature': '可購買券、出示核銷', + 'kyc.l2Title': 'L2 身分認證', + 'kyc.l2Desc': '身分證/護照驗證', + 'kyc.l2Limit': '每日購買限額 \$5,000', + 'kyc.l2Feature': '解鎖二級市場交易、P2P轉贈', + 'kyc.l3Title': 'L3 進階認證', + 'kyc.l3Desc': '視訊面審 + 地址證明', + 'kyc.l3Limit': '無限額', + 'kyc.l3Feature': '解鎖大額交易、提現無限制', + 'kyc.completed': '已完成', + 'kyc.goVerify': '去認證', + + // ============ Payment Management ============ + 'payManage.title': '支付管理', + 'payManage.myCards': '我的銀行卡', + 'payManage.addCard': '新增銀行卡', + 'payManage.bankAccount': '銀行帳戶(提現用)', + 'payManage.paymentSecurity': '支付安全', + 'payManage.paymentPassword': '支付密碼', + 'payManage.passwordSet': '已設定', + 'payManage.biometricPay': '指紋/臉部辨識支付', + 'payManage.biometricEnabled': '已開啟', + 'payManage.noPasswordPay': '免密支付', + 'payManage.noPasswordLimit': '單筆≤\$10', + + // ============ AI Chat ============ + 'aiChat.title': 'AI 助手', + 'aiChat.greeting': '你好!我是 Genex AI 助手,可以幫你發現高性價比好券、比價分析、組合推薦。試試問我:', + 'aiChat.suggest1': '推薦適合我的券', + 'aiChat.suggest2': 'Starbucks 券值不值得買?', + 'aiChat.suggest3': '幫我做比價分析', + 'aiChat.suggest4': '我的券快到期了怎麼辦?', + 'aiChat.inputHint': '問我任何關於券的問題...', + 'aiChat.confirmAction': '確認執行', + 'aiChat.riskLow': '低風險', + 'aiChat.riskNormal': '需確認', + 'aiChat.riskHigh': '高風險', + + // ============ Messages ============ + 'message.title': '訊息', + 'message.detailTitle': '訊息詳情', + 'message.tradeNotify': '交易通知', + 'message.expiryRemind': '到期提醒', + 'message.systemNotify': '系統通知', + 'message.promoNotify': '活動推播', + 'message.tradeSuccess': '交易成功通知', + 'message.couponName': '券名稱', + 'message.faceValue': '面值', + 'message.payAmount': '支付金額', + 'message.orderNo': '訂單號', + 'message.payMethod': '支付方式', + 'message.viewCouponDetail': '查看券詳情', + + // ============ Status Tags ============ + 'status.active': '可使用', + 'status.pending': '待核銷', + 'status.expired': '已過期', + 'status.used': '已使用', + 'status.processing': '處理中', + 'status.completed': '已完成', + 'status.cancelled': '已取消', + 'status.refunding': '退款中', + 'status.onSale': '出售中', + + // ============ Empty States ============ + 'empty.noCoupons': '還沒有券', + 'empty.noCouponsHint': '去市場看看有什麼好券吧', + 'empty.browse': '去逛逛', + 'empty.noTrades': '暫無交易紀錄', + 'empty.noTradesHint': '完成首筆交易後這裡會顯示紀錄', + 'empty.noResults': '沒有找到結果', + 'empty.noResultsHint': '換個關鍵字試試', + 'empty.noMessages': '暫無訊息', + 'empty.noMessagesHint': '交易通知和系統公告會顯示在這裡', + 'empty.networkError': '網路連線失敗', + 'empty.networkErrorHint': '請檢查網路設定後重試', + + // ============ Receive Coupon ============ + 'receiveCoupon.title': '接收券', + 'receiveCoupon.hint': '向他人展示下方二維碼或接收ID,對方可透過掃碼或輸入ID將券轉贈到你的錢包。', + 'receiveCoupon.id': '接收ID', + 'receiveCoupon.idCopied': '接收ID已複製到剪貼簿', + 'receiveCoupon.note': '接收的券將自動存入你的錢包,可在首頁錢包中查看和管理。', + + // ============ Issuer ============ + 'issuer.title': '發行方管理', + 'issuer.verified': '已認證發行方', + 'issuer.overview': '總覽', + 'issuer.issue': '發券', + 'issuer.redeem': '核銷', + 'issuer.finance': '財務', + 'issuer.totalIssued': '發行總量', + 'issuer.totalSold': '已售出', + 'issuer.totalRedeemed': '已核銷', + 'issuer.redeemRate': '核銷率', + 'issuer.quickActions': '快捷操作', + 'issuer.createCoupon': '建立券', + 'issuer.storeManage': '門市管理', + 'issuer.salesAnalysis': '銷售分析', + 'issuer.statement': '對帳單', + 'issuer.myCoupons': '我的券', + 'issuer.listed': '已上架', + 'issuer.underReview': '審核中', + 'issuer.soldOut': '已售罄', + 'issuer.issuedSlash': '發行', + 'issuer.sold': '已售', + 'issuer.issueCenter': '發券中心', + 'issuer.selectTemplate': '選擇券範本', + 'issuer.voucherType': '滿減券', + 'issuer.discountType': '折扣券', + 'issuer.giftCardType': '禮品卡', + 'issuer.storedValueType': '儲值券', + 'issuer.couponManage': '券管理', + 'issuer.viewAll': '查看全部', + 'issuer.couponEvents': '券活動', + 'issuer.createNew': '建立新券', + 'issuer.redeemManage': '核銷管理', + 'issuer.redeemTrend': '核銷趨勢', + 'issuer.allStores': '全部門市', + 'issuer.employees': '名員工', + 'issuer.financeManage': '財務管理', + 'issuer.totalSales': '總銷售額', + 'issuer.settled': '已到帳', + 'issuer.pendingSettle': '待結算', + 'issuer.breakage': 'Breakage', + 'issuer.withdrawBtn': '提現', + 'issuer.reportBtn': '對帳報表', + 'issuer.settleDetail': '結算明細', + 'issuer.creditLevel': '信用等級', + 'issuer.issueQuota': '發行額度', + 'issuer.usedQuota': '已用額度', + 'issuer.dataCenter': '數據中心', + 'issuer.issueSalesRate': '發行量/銷量/兌付率', + 'issuer.userProfile': '使用者畫像', + 'issuer.userProfileDesc': '購買使用者分佈分析', + 'issuer.creditDetail': '信用詳情', + 'issuer.creditDetailDesc': '評分詳情與提升建議', + 'issuer.quotaChange': '額度變動', + 'issuer.quotaChangeDesc': '歷史額度調整紀錄', + 'issuer.companyInfo': '企業資訊', + 'issuer.companyInfoDesc': '營業執照/聯絡人', + 'issuer.settingsItem': '設定', + 'issuer.settingsItemDesc': '通知/安全/語言', + 'issuer.helpItem': '幫助中心', + 'issuer.helpItemDesc': '常見問題與客服', + + // ============ Merchant ============ + 'merchant.today': '今日', + 'merchant.onlineMode': '線上模式', + 'merchant.offlineMode': '離線模式', + 'merchant.pendingSync': '待同步', + 'merchant.syncUnit': '筆', + 'merchant.scanHint': '將券二維碼對準掃描框', + 'merchant.flashlight': '手電筒', + 'merchant.manualInput': '手動輸碼', + 'merchant.redeemRecords': '核銷紀錄', + 'merchant.storeData': '門市數據', + 'merchant.inputCode': '手動輸入券碼', + 'merchant.inputCodeHint': '請輸入券碼', + 'merchant.query': '查詢', + 'merchant.userNickname': '使用者暱稱', + 'merchant.consumer': '消費者', + 'merchant.couponName': '券名稱', + 'merchant.faceValue': '面值', + 'merchant.validUntil': '有效期', + 'merchant.useCondition': '使用條件', + 'merchant.noMinSpend': '無最低消費', + 'merchant.confirmRedeem': '確認核銷', + 'merchant.redeemSuccess': '核銷成功', + 'merchant.continueRedeem': '繼續核銷', + 'merchant.synced': '已同步', + 'merchant.pendingSyncLabel': '待同步', + 'merchant.redeemOperator': '核銷員', + 'merchant.todayRedeem': '今日核銷', + 'merchant.redeemAmount': '核銷金額', + 'merchant.weekTrend': '本週趨勢', + 'merchant.operatorRank': '核銷員排行', + + // ============ Profile Page (additional) ============ + 'profile.account': '帳戶', + 'profile.trade': '交易', + 'profile.holdCoupons': '持券', + 'profile.saved': '節省', + 'profile.credit': '信用', + 'profile.creditScore': '信用積分', + 'profile.myFavorites': '我的收藏', + 'profile.securitySettings': '安全設定', + 'profile.advancedSettings': '進階設定', + 'profile.aboutGenex': '關於 Genex', + 'profile.simplifiedChinese': '簡體中文', + + // ============ Pro Mode ============ + 'proMode.toggleDesc': '開啟後可查看鏈上資訊和連接外部錢包', + 'proMode.requireKycL2': '需要 KYC L2 及以上認證', + 'proMode.connected': '已連接', + 'proMode.disconnect': '斷開', + 'proMode.connectWallet': '連接外部錢包', + 'proMode.walletDesc': '連接外部錢包後可將平台資產提取至自有地址', + 'proMode.showChainAddress': '顯示鏈上地址', + 'proMode.showChainAddressDesc': '在券詳情中展示合約地址', + 'proMode.showTxHash': '顯示交易Hash', + 'proMode.showTxHashDesc': '在交易記錄中展示鏈上Hash', + 'proMode.txExplorer': '交易瀏覽器', + 'proMode.txBuyExample': '購買 星巴克 \$25 禮品卡', + 'proMode.txSellExample': '出售 Amazon \$100 券', + 'proMode.confirmed': '已確認', + 'proMode.confirming': '確認中', + 'proMode.viewAllTx': '查看全部鏈上交易', + 'proMode.chainAssets': '鏈上資產', + 'proMode.custodialWallet': '平台託管錢包', + 'proMode.externalWallet': '外部錢包 (MetaMask)', + 'proMode.couponCount5': '5 張券', + 'proMode.couponCount0': '0 張券', + 'proMode.extractToWallet': '提取至外部錢包', + 'proMode.tradeTrack': '交易軌道', + 'proMode.utilityTrackDesc': '券有效期≤12個月,無需證券牌照', + 'proMode.securitiesTrackDesc': '長期投資型券產品(即將推出)', + 'proMode.mvpNote': '當前MVP版本僅支持Utility Track', + 'proMode.comingSoon': '敬請期待', + 'proMode.whatIsTitle': '什麼是高級模式?', + 'proMode.whatIsDesc': '高級模式面向有區塊鏈經驗的使用者,開啟後可以:\n' + '• 連接外部錢包(MetaMask等)\n' + '• 查看鏈上地址和交易Hash\n' + '• 將資產提取至自有錢包\n' + '• 查看底層鏈上數據\n\n' + '需要完成 KYC L2 認證後方可開啟。', + + // ============ Wallet Page (additional) ============ + 'wallet.buyIn': '買入', + 'wallet.sellOut': '賣出', + 'wallet.giftTransfer': '轉贈', + 'wallet.redeemUse': '核銷', + + // ============ AI Fab ============ + 'aiFab.greeting': '你好!我是 Genex AI 助手,可以幫你管理券資產、查找優惠、分析價格。有什麼需要幫助的嗎?', + 'aiFab.inputHint': '輸入訊息...', + 'aiFab.suggest1': '幫我找高折扣券', + 'aiFab.suggest2': '我的券快到期了嗎?', + 'aiFab.suggest3': '推薦今日好券', + 'aiFab.suggest4': '分析我的券資產', + + // ============ KYC Badge ============ + 'kyc.badgeLabel': '認證', + + // ============ Message Tabs ============ + 'message.markAllRead': '全部已讀', + 'message.tabTrade': '交易', + 'message.tabExpiry': '到期', + 'message.tabAnnouncement': '公告', + + // ============ Transfer additions ============ + 'transfer.contactEmail': '信箱', + 'transfer.contactPhone': '手機', + 'transfer.yesterday': '昨天', + 'transfer.daysAgo': '天前', + 'transfer.weeksAgo': '週前', + 'transfer.monthsAgo': '月前', + + // ============ Merchant AI ============ + 'merchantAi.redeemAssist': '核銷輔助', + 'merchantAi.trafficForecast': '客流預測', + 'merchantAi.anomalyAlert': '異常預警', + 'merchantAi.verifyAuth': '驗券真偽', + 'merchantAi.checkStatus': '查券狀態', + 'merchantAi.batchRedeem': '批量核銷', + 'merchantAi.feedback': '問題回報', + 'merchantAi.quickActions': 'AI 快捷操作', + 'merchantAi.redeemTips': '核銷提示', + 'merchantAi.todayHotRedeem': '今日熱門核銷', + 'merchantAi.countUnit': '筆', + 'merchantAi.aiMarketing': 'AI 行銷建議', + 'merchantAi.crossSellTitle': '推薦搭配銷售', + 'merchantAi.crossSellDesc': '購買咖啡券的顧客同時對糕點券感興趣,建議推薦組合', + 'merchantAi.weekendPromoTitle': '週末促銷建議', + 'merchantAi.weekendPromoDesc': '歷史數據顯示週六核銷量+30%,建議推出週末限時活動', + 'merchantAi.todayForecast': '今日客流預測', + 'merchantAi.expectedRedeem': '預計核銷', + 'merchantAi.peakHours': '高峰時段', + 'merchantAi.expectedRevenue': '預計收入', + 'merchantAi.trafficInsight': '較上週同期增長12%,建議午間增加1名收銀員', + 'merchantAi.hourlyForecast': '分時段預測', + 'merchantAi.weeklyForecast': '本週預測', + 'merchantAi.staffSuggestion': '排班建議', + 'merchantAi.pendingCount': '待處理', + 'merchantAi.resolvedToday': '今日已處理', + 'merchantAi.riskIndex': '風險指數', + 'merchantAi.riskLow': '低', + 'merchantAi.activeAlerts': '活躍預警', + 'merchantAi.highFreqRedeem': '高頻核銷檢測', + 'merchantAi.suspectFakeCode': '疑似偽造券碼', + 'merchantAi.suspiciousPatterns': '可疑模式檢測', + 'merchantAi.consecutiveRedeem': '同一使用者連續核銷', + 'merchantAi.offHoursRedeem': '非營業時間核銷嘗試', + 'merchantAi.expiredRedeemAttempt': '過期券核銷嘗試', + 'merchantAi.statusAbnormal': '異常', + 'merchantAi.statusWarning': '注意', + 'merchantAi.statusNormal': '正常', + 'merchantAi.expiredBlock': '過期券核銷攔截', + 'merchantAi.duplicateBlock': '重複核銷攔截', + 'merchantAi.wrongStoreAlert': '非本店券提醒', + 'merchantAi.insufficientBalance': '餘額不足核銷', + 'merchantAi.systemRetry': '系統逾時重試', + 'merchantAi.monday': '週一', + 'merchantAi.tuesday': '週二', + 'merchantAi.wednesday': '週三', + 'merchantAi.thursday': '週四', + 'merchantAi.friday': '週五', + 'merchantAi.saturday': '週六', + 'merchantAi.sunday': '週日', + + // ============ Issuer additions ============ + 'issuer.unlisted': '已下架', +}; diff --git a/frontend/genex-mobile/lib/app/main_shell.dart b/frontend/genex-mobile/lib/app/main_shell.dart index f2ff41d..3ca5d60 100644 --- a/frontend/genex-mobile/lib/app/main_shell.dart +++ b/frontend/genex-mobile/lib/app/main_shell.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import '../app/theme/app_colors.dart'; +import '../app/i18n/app_localizations.dart'; import '../features/coupons/presentation/pages/home_page.dart'; import '../features/coupons/presentation/pages/market_page.dart'; import '../features/message/presentation/pages/message_page.dart'; @@ -41,15 +42,15 @@ class _MainShellState extends State { selectedIndex: _currentIndex, onDestinationSelected: (index) => setState(() => _currentIndex = index), destinations: [ - _buildDestination(Icons.home_rounded, Icons.home_outlined, '首页'), - _buildDestination(Icons.show_chart_rounded, Icons.show_chart_outlined, '交易'), + _buildDestination(Icons.home_rounded, Icons.home_outlined, context.t('nav.home')), + _buildDestination(Icons.show_chart_rounded, Icons.show_chart_outlined, context.t('nav.trading')), _buildBadgeDestination( Icons.notifications_rounded, Icons.notifications_outlined, - '消息', + context.t('nav.messages'), 2, ), - _buildDestination(Icons.person_rounded, Icons.person_outlined, '我的'), + _buildDestination(Icons.person_rounded, Icons.person_outlined, context.t('nav.profile')), ], ), ), diff --git a/frontend/genex-mobile/lib/features/ai_agent/presentation/pages/agent_chat_page.dart b/frontend/genex-mobile/lib/features/ai_agent/presentation/pages/agent_chat_page.dart index c179647..21a5863 100644 --- a/frontend/genex-mobile/lib/features/ai_agent/presentation/pages/agent_chat_page.dart +++ b/frontend/genex-mobile/lib/features/ai_agent/presentation/pages/agent_chat_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// AI Agent 全屏对话页面(消费者端) /// @@ -16,21 +17,38 @@ class AgentChatPage extends StatefulWidget { class _AgentChatPageState extends State { final _controller = TextEditingController(); final _scrollController = ScrollController(); - final List<_Msg> _messages = [ - _Msg(true, '你好!我是 Genex AI 助手,可以帮你发现高性价比好券、比价分析、组合推荐。试试问我:'), - ]; - final _suggestions = ['推荐适合我的券', '星巴克券值不值得买?', '帮我做比价分析', '我的券快到期了怎么办?']; + late final List<_Msg> _messages; + late final List _suggestions; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (!_initialized) { + _messages = [ + _Msg(true, context.t('aiChat.greeting')), + ]; + _suggestions = [ + context.t('aiChat.suggest1'), + context.t('aiChat.suggest2'), + context.t('aiChat.suggest3'), + context.t('aiChat.suggest4'), + ]; + _initialized = true; + } + } + + bool _initialized = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Row( + title: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(Icons.auto_awesome_rounded, color: AppColors.primary, size: 20), - SizedBox(width: 8), - Text('AI 助手'), + const Icon(Icons.auto_awesome_rounded, color: AppColors.primary, size: 20), + const SizedBox(width: 8), + Text(context.t('aiChat.title')), ], ), actions: [ @@ -79,7 +97,7 @@ class _AgentChatPageState extends State { child: TextField( controller: _controller, decoration: InputDecoration( - hintText: '问我任何关于券的问题...', + hintText: context.t('aiChat.inputHint'), border: OutlineInputBorder( borderRadius: AppSpacing.borderRadiusFull, borderSide: const BorderSide(color: AppColors.borderLight), diff --git a/frontend/genex-mobile/lib/features/ai_agent/presentation/widgets/ai_fab.dart b/frontend/genex-mobile/lib/features/ai_agent/presentation/widgets/ai_fab.dart index 6119fb2..4d8dc85 100644 --- a/frontend/genex-mobile/lib/features/ai_agent/presentation/widgets/ai_fab.dart +++ b/frontend/genex-mobile/lib/features/ai_agent/presentation/widgets/ai_fab.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../../app/i18n/app_localizations.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; @@ -120,7 +121,7 @@ class AiChatPanel extends StatelessWidget { ), ), const SizedBox(width: 10), - Text('AI 助手', style: AppTypography.h3), + Text(context.t('aiChat.title'), style: AppTypography.h3), const Spacer(), IconButton( icon: const Icon(Icons.close_rounded, size: 22), @@ -140,10 +141,10 @@ class AiChatPanel extends StatelessWidget { padding: const EdgeInsets.all(20), children: [ _buildAiMessage( - '你好!我是 Genex AI 助手,可以帮你管理券资产、查找优惠、分析价格。有什么需要帮助的吗?', + context.t('aiFab.greeting'), ), const SizedBox(height: 12), - _buildSuggestionChips(), + _buildSuggestionChips(context), ], ), ), @@ -171,7 +172,7 @@ class AiChatPanel extends StatelessWidget { Expanded( child: TextField( decoration: InputDecoration( - hintText: '输入消息...', + hintText: context.t('aiFab.inputHint'), hintStyle: AppTypography.bodyMedium .copyWith(color: AppColors.textTertiary), border: InputBorder.none, @@ -251,12 +252,12 @@ class AiChatPanel extends StatelessWidget { ); } - Widget _buildSuggestionChips() { + Widget _buildSuggestionChips(BuildContext context) { final suggestions = [ - '帮我找高折扣券', - '我的券快到期了吗?', - '推荐今日好券', - '分析我的券资产', + context.t('aiFab.suggest1'), + context.t('aiFab.suggest2'), + context.t('aiFab.suggest3'), + context.t('aiFab.suggest4'), ]; return Wrap( diff --git a/frontend/genex-mobile/lib/features/auth/presentation/pages/forgot_password_page.dart b/frontend/genex-mobile/lib/features/auth/presentation/pages/forgot_password_page.dart index 8c9b32b..9936d56 100644 --- a/frontend/genex-mobile/lib/features/auth/presentation/pages/forgot_password_page.dart +++ b/frontend/genex-mobile/lib/features/auth/presentation/pages/forgot_password_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A1. 忘记密码 - 手机号/邮箱验证 → 输入验证码 → 设置新密码 → 成功 /// @@ -36,7 +37,7 @@ class _ForgotPasswordPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(_step == 3 ? '' : '找回密码'), + title: Text(_step == 3 ? '' : context.t('forgot.title')), leading: _step == 3 ? const SizedBox.shrink() : IconButton( @@ -79,21 +80,21 @@ class _ForgotPasswordPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 24), - Text('输入手机号或邮箱', style: AppTypography.h1), + Text(context.t('forgot.inputAccount'), style: AppTypography.h1), const SizedBox(height: 8), - Text('我们将向您发送验证码', style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary)), + Text(context.t('forgot.sendHint'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary)), const SizedBox(height: 32), TextField( controller: _phoneController, keyboardType: TextInputType.phone, - decoration: const InputDecoration( - hintText: '手机号 / 邮箱地址', - prefixIcon: Icon(Icons.person_outline_rounded), + decoration: InputDecoration( + hintText: context.t('forgot.accountHint'), + prefixIcon: const Icon(Icons.person_outline_rounded), ), ), const SizedBox(height: 24), GenexButton( - label: '获取验证码', + label: context.t('forgot.getCode'), onPressed: () => setState(() => _step = 1), ), ], @@ -105,19 +106,19 @@ class _ForgotPasswordPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 24), - Text('输入验证码', style: AppTypography.h1), + Text(context.t('forgot.inputCode'), style: AppTypography.h1), const SizedBox(height: 8), Text( - '验证码已发送至 ${_phoneController.text.isNotEmpty ? _phoneController.text : '***'}', + '${context.t('forgot.codeSentTo')} ${_phoneController.text.isNotEmpty ? _phoneController.text : '***'}', style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 32), TextField( controller: _codeController, keyboardType: TextInputType.number, - decoration: const InputDecoration( - hintText: '6位验证码', - prefixIcon: Icon(Icons.lock_outline_rounded), + decoration: InputDecoration( + hintText: context.t('forgot.codeHint'), + prefixIcon: const Icon(Icons.lock_outline_rounded), ), ), const SizedBox(height: 16), @@ -125,12 +126,12 @@ class _ForgotPasswordPageState extends State { alignment: Alignment.centerRight, child: TextButton( onPressed: () {}, - child: const Text('重新发送'), + child: Text(context.t('forgot.resend')), ), ), const SizedBox(height: 8), GenexButton( - label: '下一步', + label: context.t('forgot.next'), onPressed: () => setState(() => _step = 2), ), ], @@ -142,15 +143,15 @@ class _ForgotPasswordPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 24), - Text('设置新密码', style: AppTypography.h1), + Text(context.t('forgot.setNewPassword'), style: AppTypography.h1), const SizedBox(height: 8), - Text('请输入新密码(8位以上)', style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary)), + Text(context.t('forgot.newPasswordHint'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary)), const SizedBox(height: 32), TextField( controller: _passwordController, obscureText: _obscurePassword, decoration: InputDecoration( - hintText: '新密码', + hintText: context.t('forgot.newPassword'), prefixIcon: const Icon(Icons.lock_outline_rounded), suffixIcon: IconButton( icon: Icon(_obscurePassword ? Icons.visibility_off_outlined : Icons.visibility_outlined), @@ -163,7 +164,7 @@ class _ForgotPasswordPageState extends State { controller: _confirmController, obscureText: _obscureConfirm, decoration: InputDecoration( - hintText: '确认新密码', + hintText: context.t('forgot.confirmPassword'), prefixIcon: const Icon(Icons.lock_outline_rounded), suffixIcon: IconButton( icon: Icon(_obscureConfirm ? Icons.visibility_off_outlined : Icons.visibility_outlined), @@ -173,7 +174,7 @@ class _ForgotPasswordPageState extends State { ), const SizedBox(height: 24), GenexButton( - label: '确认修改', + label: context.t('forgot.confirmChange'), onPressed: () => setState(() => _step = 3), ), ], @@ -195,14 +196,14 @@ class _ForgotPasswordPageState extends State { child: const Icon(Icons.check_rounded, color: AppColors.success, size: 40), ), const SizedBox(height: 24), - Text('密码修改成功', style: AppTypography.h1), + Text(context.t('forgot.success'), style: AppTypography.h1), const SizedBox(height: 8), - Text('请使用新密码登录', style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary)), + Text(context.t('forgot.successHint'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary)), const SizedBox(height: 40), SizedBox( width: double.infinity, child: GenexButton( - label: '返回登录', + label: context.t('forgot.backToLogin'), onPressed: () => Navigator.of(context).pushNamedAndRemoveUntil('/login', (_) => false), ), ), diff --git a/frontend/genex-mobile/lib/features/auth/presentation/pages/login_page.dart b/frontend/genex-mobile/lib/features/auth/presentation/pages/login_page.dart index 90b0b41..dc6d887 100644 --- a/frontend/genex-mobile/lib/features/auth/presentation/pages/login_page.dart +++ b/frontend/genex-mobile/lib/features/auth/presentation/pages/login_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A1. 登录页 - 手机号/邮箱+密码 / 验证码快捷登录 class LoginPage extends StatefulWidget { @@ -50,10 +51,10 @@ class _LoginPageState extends State with SingleTickerProviderStateMix crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 16), - Text('欢迎回来', style: AppTypography.displayMedium), + Text(context.t('login.title'), style: AppTypography.displayMedium), const SizedBox(height: 8), Text( - '登录 Genex 管理你的券资产', + context.t('login.subtitle'), style: AppTypography.bodyLarge.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 32), @@ -76,9 +77,9 @@ class _LoginPageState extends State with SingleTickerProviderStateMix labelColor: AppColors.textPrimary, unselectedLabelColor: AppColors.textTertiary, labelStyle: AppTypography.labelMedium, - tabs: const [ - Tab(text: '密码登录'), - Tab(text: '验证码登录'), + tabs: [ + Tab(text: context.t('login.passwordTab')), + Tab(text: context.t('login.codeTab')), ], ), ), @@ -107,9 +108,9 @@ class _LoginPageState extends State with SingleTickerProviderStateMix TextField( controller: _phoneController, keyboardType: TextInputType.phone, - decoration: const InputDecoration( - hintText: '手机号或邮箱', - prefixIcon: Icon(Icons.person_outline_rounded, color: AppColors.textTertiary), + decoration: InputDecoration( + hintText: context.t('login.phoneOrEmail'), + prefixIcon: const Icon(Icons.person_outline_rounded, color: AppColors.textTertiary), ), ), const SizedBox(height: 16), @@ -119,7 +120,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix controller: _passwordController, obscureText: _obscurePassword, decoration: InputDecoration( - hintText: '密码', + hintText: context.t('login.password'), prefixIcon: const Icon(Icons.lock_outline_rounded, color: AppColors.textTertiary), suffixIcon: IconButton( icon: Icon( @@ -140,7 +141,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix onTap: () { Navigator.pushNamed(context, '/forgot-password'); }, - child: Text('忘记密码?', style: AppTypography.labelSmall.copyWith( + child: Text(context.t('login.forgotPassword'), style: AppTypography.labelSmall.copyWith( color: AppColors.primary, )), ), @@ -149,7 +150,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix // Login Button GenexButton( - label: '登录', + label: context.t('login.submit'), onPressed: () { Navigator.pushReplacementNamed(context, '/main'); }, @@ -164,9 +165,9 @@ class _LoginPageState extends State with SingleTickerProviderStateMix // Phone Input TextField( keyboardType: TextInputType.phone, - decoration: const InputDecoration( - hintText: '手机号', - prefixIcon: Icon(Icons.phone_android_rounded, color: AppColors.textTertiary), + decoration: InputDecoration( + hintText: context.t('login.phone'), + prefixIcon: const Icon(Icons.phone_android_rounded, color: AppColors.textTertiary), ), ), const SizedBox(height: 16), @@ -178,9 +179,9 @@ class _LoginPageState extends State with SingleTickerProviderStateMix child: TextField( controller: _codeController, keyboardType: TextInputType.number, - decoration: const InputDecoration( - hintText: '验证码', - prefixIcon: Icon(Icons.shield_outlined, color: AppColors.textTertiary), + decoration: InputDecoration( + hintText: context.t('login.verifyCode'), + prefixIcon: const Icon(Icons.shield_outlined, color: AppColors.textTertiary), ), ), ), @@ -188,7 +189,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix SizedBox( height: AppSpacing.inputHeight, child: GenexButton( - label: '获取验证码', + label: context.t('login.getCode'), variant: GenexButtonVariant.secondary, size: GenexButtonSize.medium, fullWidth: false, @@ -202,7 +203,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix const SizedBox(height: 24), GenexButton( - label: '登录', + label: context.t('login.submit'), onPressed: () { Navigator.pushReplacementNamed(context, '/main'); }, diff --git a/frontend/genex-mobile/lib/features/auth/presentation/pages/register_page.dart b/frontend/genex-mobile/lib/features/auth/presentation/pages/register_page.dart index 697fd2c..deb067e 100644 --- a/frontend/genex-mobile/lib/features/auth/presentation/pages/register_page.dart +++ b/frontend/genex-mobile/lib/features/auth/presentation/pages/register_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A1. 手机号注册页 /// @@ -48,10 +49,10 @@ class _RegisterPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 16), - Text('创建账号', style: AppTypography.displayMedium), + Text(context.t('register.title'), style: AppTypography.displayMedium), const SizedBox(height: 8), Text( - widget.isEmail ? '使用邮箱注册 Genex 账号' : '使用手机号注册 Genex 账号', + widget.isEmail ? context.t('register.emailSubtitle') : context.t('register.phoneSubtitle'), style: AppTypography.bodyLarge.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 40), @@ -62,7 +63,7 @@ class _RegisterPageState extends State { // Account Input (Phone/Email) Text( - widget.isEmail ? '邮箱地址' : '手机号', + widget.isEmail ? context.t('register.email') : context.t('register.phone'), style: AppTypography.labelMedium, ), const SizedBox(height: 8), @@ -71,7 +72,7 @@ class _RegisterPageState extends State { keyboardType: widget.isEmail ? TextInputType.emailAddress : TextInputType.phone, decoration: InputDecoration( - hintText: widget.isEmail ? '请输入邮箱地址' : '请输入手机号', + hintText: widget.isEmail ? context.t('register.emailHint') : context.t('register.phoneHint'), prefixIcon: Icon( widget.isEmail ? Icons.email_outlined : Icons.phone_android_rounded, color: AppColors.textTertiary, @@ -81,7 +82,7 @@ class _RegisterPageState extends State { const SizedBox(height: 20), // Verification Code - Text('验证码', style: AppTypography.labelMedium), + Text(context.t('register.code'), style: AppTypography.labelMedium), const SizedBox(height: 8), Row( children: [ @@ -90,10 +91,10 @@ class _RegisterPageState extends State { controller: _codeController, keyboardType: TextInputType.number, maxLength: 6, - decoration: const InputDecoration( - hintText: '请输入6位验证码', + decoration: InputDecoration( + hintText: context.t('register.codeHint'), counterText: '', - prefixIcon: Icon(Icons.shield_outlined, color: AppColors.textTertiary), + prefixIcon: const Icon(Icons.shield_outlined, color: AppColors.textTertiary), ), ), ), @@ -101,7 +102,7 @@ class _RegisterPageState extends State { SizedBox( height: AppSpacing.inputHeight, child: GenexButton( - label: '获取验证码', + label: context.t('register.getCode'), variant: GenexButtonVariant.secondary, size: GenexButtonSize.medium, fullWidth: false, @@ -113,13 +114,13 @@ class _RegisterPageState extends State { const SizedBox(height: 20), // Password - Text('设置密码', style: AppTypography.labelMedium), + Text(context.t('register.setPassword'), style: AppTypography.labelMedium), const SizedBox(height: 8), TextField( controller: _passwordController, obscureText: _obscurePassword, decoration: InputDecoration( - hintText: '8-20位,含字母和数字', + hintText: context.t('register.passwordHint'), prefixIcon: const Icon(Icons.lock_outline_rounded, color: AppColors.textTertiary), suffixIcon: IconButton( icon: Icon( @@ -159,14 +160,14 @@ class _RegisterPageState extends State { text: TextSpan( style: AppTypography.bodySmall, children: [ - const TextSpan(text: '我已阅读并同意 '), + TextSpan(text: '${context.t('register.agreement')} '), TextSpan( - text: '《用户协议》', + text: context.t('register.userAgreement'), style: AppTypography.bodySmall.copyWith(color: AppColors.primary), ), const TextSpan(text: ' 和 '), TextSpan( - text: '《隐私政策》', + text: context.t('register.privacyPolicy'), style: AppTypography.bodySmall.copyWith(color: AppColors.primary), ), ], @@ -180,7 +181,7 @@ class _RegisterPageState extends State { // Register Button GenexButton( - label: '注册', + label: context.t('register.submit'), onPressed: _agreeTerms ? () { Navigator.pushReplacementNamed(context, '/main'); } : null, @@ -196,11 +197,11 @@ class _RegisterPageState extends State { Widget _buildStepIndicator() { return Row( children: [ - _buildStep(1, '验证', true), + _buildStep(1, context.t('register.stepVerify'), true), _buildStepLine(true), - _buildStep(2, '设密码', true), + _buildStep(2, context.t('register.stepPassword'), true), _buildStepLine(false), - _buildStep(3, '完成', false), + _buildStep(3, context.t('register.stepDone'), false), ], ); } @@ -255,11 +256,11 @@ class _RegisterPageState extends State { return Row( children: [ - _buildCheck('8位以上', hasLength), + _buildCheck(context.t('register.rule8chars'), hasLength), const SizedBox(width: 16), - _buildCheck('含字母', hasLetter), + _buildCheck(context.t('register.ruleLetter'), hasLetter), const SizedBox(width: 16), - _buildCheck('含数字', hasDigit), + _buildCheck(context.t('register.ruleNumber'), hasDigit), ], ); } diff --git a/frontend/genex-mobile/lib/features/auth/presentation/pages/welcome_page.dart b/frontend/genex-mobile/lib/features/auth/presentation/pages/welcome_page.dart index c0800c5..f7422ef 100644 --- a/frontend/genex-mobile/lib/features/auth/presentation/pages/welcome_page.dart +++ b/frontend/genex-mobile/lib/features/auth/presentation/pages/welcome_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A1. 欢迎页 - 品牌展示 + 注册/登录入口 /// @@ -49,7 +50,7 @@ class WelcomePage extends StatelessWidget { // Slogan Text( - '让每一张券都有价值', + context.t('welcome.slogan'), style: AppTypography.bodyLarge.copyWith( color: AppColors.textSecondary, ), @@ -59,7 +60,7 @@ class WelcomePage extends StatelessWidget { // Phone Register GenexButton( - label: '手机号注册', + label: context.t('welcome.phoneRegister'), icon: Icons.phone_android_rounded, onPressed: () { Navigator.pushNamed(context, '/register'); @@ -69,7 +70,7 @@ class WelcomePage extends StatelessWidget { // Email Register GenexButton( - label: '邮箱注册', + label: context.t('welcome.emailRegister'), icon: Icons.email_outlined, variant: GenexButtonVariant.outline, onPressed: () { @@ -84,7 +85,7 @@ class WelcomePage extends StatelessWidget { const Expanded(child: Divider(color: AppColors.border)), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text('其他方式登录', style: AppTypography.caption), + child: Text(context.t('welcome.otherLogin'), style: AppTypography.caption), ), const Expanded(child: Divider(color: AppColors.border)), ], @@ -118,14 +119,14 @@ class WelcomePage extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('已有账号?', style: AppTypography.bodyMedium.copyWith( + Text(context.t('welcome.hasAccount'), style: AppTypography.bodyMedium.copyWith( color: AppColors.textSecondary, )), GestureDetector( onTap: () { Navigator.pushNamed(context, '/login'); }, - child: Text('登录', style: AppTypography.labelMedium.copyWith( + child: Text(context.t('welcome.login'), style: AppTypography.labelMedium.copyWith( color: AppColors.primary, )), ), @@ -135,7 +136,7 @@ class WelcomePage extends StatelessWidget { // Terms Text( - '注册即表示同意《用户协议》和《隐私政策》', + context.t('welcome.agreement'), style: AppTypography.caption.copyWith(fontSize: 10), textAlign: TextAlign.center, ), diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/coupon_detail_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/coupon_detail_page.dart index 6bee969..9e35f56 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/coupon_detail_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/coupon_detail_page.dart @@ -5,6 +5,7 @@ import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/price_tag.dart'; import '../../../../shared/widgets/credit_badge.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A2. 券详情页 /// @@ -108,7 +109,7 @@ class CouponDetailPage extends StatelessWidget { ), const SizedBox(height: 8), Text( - '比面值节省 \$3.75', + '${context.t('couponDetail.saveBadge')} \$3.75', style: AppTypography.bodySmall.copyWith(color: AppColors.success), ), ], @@ -119,37 +120,37 @@ class CouponDetailPage extends StatelessWidget { // Info Cards Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 0), - child: _buildInfoSection(), + child: _buildInfoSection(context), ), // Usage Rules Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 0), - child: _buildUsageRules(), + child: _buildUsageRules(context), ), // Available Stores Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 0), - child: _buildStores(), + child: _buildStores(context), ), // Nearby Redemption (附近核销) Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 0), - child: _buildNearbyRedemption(), + child: _buildNearbyRedemption(context), ), // Price Trend (Optional) Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 0), - child: _buildPriceTrend(), + child: _buildPriceTrend(context), ), // Similar Coupons Padding( padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), - child: Text('同类券推荐', style: AppTypography.h3), + child: Text(context.t('couponDetail.similar'), style: AppTypography.h3), ), SizedBox( height: 180, @@ -183,7 +184,7 @@ class CouponDetailPage extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.favorite_border_rounded, color: AppColors.textTertiary, size: 22), - Text('收藏', style: AppTypography.caption), + Text(context.t('couponDetail.favorite'), style: AppTypography.caption), ], ), const SizedBox(width: 24), @@ -191,7 +192,7 @@ class CouponDetailPage extends StatelessWidget { // Buy Button Expanded( child: GenexButton( - label: '立即购买 \$21.25', + label: '${context.t('couponDetail.buyNow')} \$21.25', onPressed: () { Navigator.pushNamed(context, '/order/confirm'); }, @@ -217,12 +218,12 @@ class CouponDetailPage extends StatelessWidget { ); } - Widget _buildInfoSection() { + Widget _buildInfoSection(BuildContext context) { final items = [ - ('面值', '\$25.00'), - ('有效期', '2026/12/31'), - ('类型', '消费券'), - ('发行方', 'Starbucks Inc.'), + (context.t('couponDetail.faceValue'), '\$25.00'), + (context.t('couponDetail.validUntil'), '2026/12/31'), + (context.t('couponDetail.type'), context.t('couponDetail.consumeCoupon')), + (context.t('couponDetail.issuer'), 'Starbucks Inc.'), ]; return Container( @@ -257,7 +258,7 @@ class CouponDetailPage extends StatelessWidget { ); } - Widget _buildUsageRules() { + Widget _buildUsageRules(BuildContext context) { return Container( padding: AppSpacing.cardPadding, decoration: BoxDecoration( @@ -268,13 +269,13 @@ class CouponDetailPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('使用说明', style: AppTypography.labelMedium), + Text(context.t('couponDetail.usageNote'), style: AppTypography.labelMedium), const SizedBox(height: 12), - _buildRuleItem(Icons.check_circle_outline, '全国星巴克门店通用'), - _buildRuleItem(Icons.check_circle_outline, '可转赠给好友'), - _buildRuleItem(Icons.check_circle_outline, '有效期内随时使用'), - _buildRuleItem(Icons.info_outline_rounded, '不可叠加使用'), - _buildRuleItem(Icons.info_outline_rounded, '不可兑换现金'), + _buildRuleItem(Icons.check_circle_outline, context.t('couponDetail.allStores')), + _buildRuleItem(Icons.check_circle_outline, context.t('couponDetail.canTransfer')), + _buildRuleItem(Icons.check_circle_outline, context.t('couponDetail.useAnytime')), + _buildRuleItem(Icons.info_outline_rounded, context.t('couponDetail.noStack')), + _buildRuleItem(Icons.info_outline_rounded, context.t('couponDetail.noCash')), ], ), ); @@ -294,7 +295,7 @@ class CouponDetailPage extends StatelessWidget { ); } - Widget _buildStores() { + Widget _buildStores(BuildContext context) { return Container( padding: AppSpacing.cardPadding, decoration: BoxDecoration( @@ -308,15 +309,15 @@ class CouponDetailPage extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('使用门店', style: AppTypography.labelMedium), - Text('全国 12,800+ 门店', style: AppTypography.caption.copyWith( + Text(context.t('couponDetail.stores'), style: AppTypography.labelMedium), + Text(context.t('couponDetail.storeCount'), style: AppTypography.caption.copyWith( color: AppColors.primary, )), ], ), const SizedBox(height: 12), Text( - '支持全国所有星巴克直营门店使用', + context.t('couponDetail.storeDesc'), style: AppTypography.bodySmall, ), ], @@ -324,7 +325,7 @@ class CouponDetailPage extends StatelessWidget { ); } - Widget _buildPriceTrend() { + Widget _buildPriceTrend(BuildContext context) { return Container( padding: AppSpacing.cardPadding, decoration: BoxDecoration( @@ -338,8 +339,8 @@ class CouponDetailPage extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('价格走势', style: AppTypography.labelMedium), - Text('近30天', style: AppTypography.caption), + Text(context.t('couponDetail.priceTrend'), style: AppTypography.labelMedium), + Text(context.t('couponDetail.last30Days'), style: AppTypography.caption), ], ), const SizedBox(height: 16), @@ -361,10 +362,10 @@ class CouponDetailPage extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _buildTrendStat('最高', '\$22.50', AppColors.error), - _buildTrendStat('最低', '\$20.00', AppColors.success), - _buildTrendStat('均价', '\$21.10', AppColors.textSecondary), - _buildTrendStat('历史成交', '1,234笔', AppColors.primary), + _buildTrendStat(context.t('couponDetail.highest'), '\$22.50', AppColors.error), + _buildTrendStat(context.t('couponDetail.lowest'), '\$20.00', AppColors.success), + _buildTrendStat(context.t('couponDetail.average'), '\$21.10', AppColors.textSecondary), + _buildTrendStat(context.t('couponDetail.tradeHistory'), '1,234笔', AppColors.primary), ], ), ], @@ -382,11 +383,11 @@ class CouponDetailPage extends StatelessWidget { ); } - Widget _buildNearbyRedemption() { + Widget _buildNearbyRedemption(BuildContext context) { final stores = [ - ('星巴克 中关村店', '距离 0.3km', '营业中'), - ('星巴克 海淀黄庄店', '距离 0.8km', '营业中'), - ('星巴克 五道口店', '距离 1.2km', '营业中'), + ('星巴克 中关村店', '${context.t('couponDetail.distance')} 0.3km', context.t('couponDetail.open')), + ('星巴克 海淀黄庄店', '${context.t('couponDetail.distance')} 0.8km', context.t('couponDetail.open')), + ('星巴克 五道口店', '${context.t('couponDetail.distance')} 1.2km', context.t('couponDetail.open')), ]; return Container( @@ -407,10 +408,10 @@ class CouponDetailPage extends StatelessWidget { const Icon(Icons.location_on_rounded, size: 18, color: AppColors.primary), const SizedBox(width: 6), - Text('附近可用门店', style: AppTypography.labelMedium), + Text(context.t('couponDetail.nearbyStores'), style: AppTypography.labelMedium), ], ), - Text('查看全部', + Text(context.t('home.viewAllCoupons'), style: AppTypography.caption .copyWith(color: AppColors.primary)), ], diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/home_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/home_page.dart index fc02649..6defd9a 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/home_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/home_page.dart @@ -5,6 +5,7 @@ import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/coupon_card.dart'; import '../../../ai_agent/presentation/widgets/ai_fab.dart'; import '../widgets/receive_coupon_sheet.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 首页 - 轻量持仓卡 + 分类网格 + AI推荐 + 精选券 /// @@ -40,10 +41,10 @@ class HomePage extends StatelessWidget { SliverToBoxAdapter(child: _buildWalletCard(context)), // Category Grid (8 items, 4x2) - SliverToBoxAdapter(child: _buildCategoryGrid()), + SliverToBoxAdapter(child: _buildCategoryGrid(context)), // AI Smart Suggestions - SliverToBoxAdapter(child: _buildAiSuggestions()), + SliverToBoxAdapter(child: _buildAiSuggestions(context)), // Section: Featured Coupons SliverToBoxAdapter( @@ -52,10 +53,10 @@ class HomePage extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('精选好券', style: AppTypography.h2), + Text(context.t('home.featuredCoupons'), style: AppTypography.h2), GestureDetector( onTap: () {}, - child: Text('查看全部', style: AppTypography.labelSmall.copyWith( + child: Text(context.t('home.viewAllCoupons'), style: AppTypography.labelSmall.copyWith( color: AppColors.primary, )), ), @@ -126,7 +127,7 @@ class HomePage extends StatelessWidget { const Icon(Icons.search_rounded, size: 20, color: AppColors.textTertiary), const SizedBox(width: 8), Text( - '搜索券、品牌、分类...', + context.t('home.searchHint'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textTertiary), ), ], @@ -158,11 +159,11 @@ class HomePage extends StatelessWidget { const Icon(Icons.inventory_2_rounded, size: 20, color: Colors.white), const SizedBox(width: 8), - Text('持仓', + Text(context.t('home.position'), style: AppTypography.labelMedium.copyWith(color: Colors.white)), const Spacer(), // Summary - Text('持有 ', + Text('${context.t('home.hold')} ', style: AppTypography.bodySmall.copyWith( color: Colors.white.withValues(alpha: 0.7), )), @@ -171,7 +172,7 @@ class HomePage extends StatelessWidget { color: Colors.white, fontWeight: FontWeight.w700, )), - Text(' 张券 总值 ', + Text(' ${context.t('home.couponUnit')} ${context.t('home.totalValue')} ', style: AppTypography.bodySmall.copyWith( color: Colors.white.withValues(alpha: 0.7), )), @@ -192,7 +193,7 @@ class HomePage extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - _buildQuickAction(context, Icons.qr_code_rounded, '接收', () { + _buildQuickAction(context, Icons.qr_code_rounded, context.t('home.receive'), () { showModalBottomSheet( context: context, isScrollControlled: true, @@ -200,13 +201,13 @@ class HomePage extends StatelessWidget { builder: (_) => const ReceiveCouponSheet(), ); }), - _buildQuickAction(context, Icons.card_giftcard_rounded, '转赠', () { + _buildQuickAction(context, Icons.card_giftcard_rounded, context.t('home.transfer'), () { Navigator.pushNamed(context, '/transfer'); }), - _buildQuickAction(context, Icons.sell_rounded, '出售', () { + _buildQuickAction(context, Icons.sell_rounded, context.t('home.sell'), () { Navigator.pushNamed(context, '/sell'); }), - _buildQuickAction(context, Icons.check_circle_outline_rounded, '核销', () { + _buildQuickAction(context, Icons.check_circle_outline_rounded, context.t('home.redeem'), () { Navigator.pushNamed(context, '/redeem'); }), ], @@ -250,16 +251,16 @@ class HomePage extends StatelessWidget { // ============================================================ // Category Grid (8 items, 4x2, A+C savings-focused) // ============================================================ - Widget _buildCategoryGrid() { + Widget _buildCategoryGrid(BuildContext context) { final categories = [ - ('限时抢购', Icons.flash_on_rounded, AppColors.error), - ('新券首发', Icons.fiber_new_rounded, AppColors.primary), - ('折扣排行', Icons.trending_up_rounded, AppColors.couponEntertainment), - ('即将到期', Icons.timer_rounded, AppColors.warning), - ('比价', Icons.compare_arrows_rounded, AppColors.couponShopping), - ('转让市场', Icons.swap_horiz_rounded, AppColors.info), - ('热门交易', Icons.local_fire_department_rounded, AppColors.couponDining), - ('全部', Icons.grid_view_rounded, AppColors.textSecondary), + (context.t('home.flashSale'), Icons.flash_on_rounded, AppColors.error), + (context.t('home.newRelease'), Icons.fiber_new_rounded, AppColors.primary), + (context.t('home.discountRank'), Icons.trending_up_rounded, AppColors.couponEntertainment), + (context.t('home.expiringSoon'), Icons.timer_rounded, AppColors.warning), + (context.t('home.priceCompare'), Icons.compare_arrows_rounded, AppColors.couponShopping), + (context.t('home.resaleMarket'), Icons.swap_horiz_rounded, AppColors.info), + (context.t('home.hotTrades'), Icons.local_fire_department_rounded, AppColors.couponDining), + (context.t('home.viewAll'), Icons.grid_view_rounded, AppColors.textSecondary), ]; return Padding( @@ -303,7 +304,7 @@ class HomePage extends StatelessWidget { ); } - Widget _buildAiSuggestions() { + Widget _buildAiSuggestions(BuildContext context) { return Container( margin: const EdgeInsets.fromLTRB(20, 16, 20, 0), padding: const EdgeInsets.all(14), @@ -328,12 +329,12 @@ class HomePage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('AI 推荐', style: AppTypography.labelSmall.copyWith( + Text(context.t('home.aiRecommend'), style: AppTypography.labelSmall.copyWith( color: AppColors.primary, )), const SizedBox(height: 2), Text( - '根据你的偏好,发现了3张高性价比券', + context.t('home.aiRecommendDesc'), style: AppTypography.bodySmall, maxLines: 1, overflow: TextOverflow.ellipsis, diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/market_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/market_page.dart index 312eabc..c4f2111 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/market_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/market_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 交易 - 币安风格交易所 /// @@ -37,7 +38,7 @@ class _MarketPageState extends State Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('交易'), + title: Text(context.t('market.title')), actions: [ IconButton( icon: const Icon(Icons.search_rounded, size: 22), @@ -63,9 +64,9 @@ class _MarketPageState extends State dividerColor: Colors.transparent, labelColor: AppColors.textPrimary, unselectedLabelColor: AppColors.textTertiary, - tabs: const [ - Tab(text: '一级市场'), - Tab(text: '二级市场'), + tabs: [ + Tab(text: context.t('market.primary')), + Tab(text: context.t('market.secondary')), ], ), ), @@ -100,13 +101,13 @@ class _MarketPageState extends State // ============================================================ Widget _buildCategoryFilter() { final categories = [ - (null, '全部'), - ('dining', '餐饮'), - ('shopping', '购物'), - ('entertainment', '娱乐'), - ('travel', '出行'), - ('life', '生活'), - ('sports', '运动'), + (null, context.t('market.title')), + ('dining', context.t('market.dining')), + ('shopping', context.t('market.shopping')), + ('entertainment', context.t('market.entertainment')), + ('travel', context.t('market.travel')), + ('life', context.t('market.lifestyle')), + ('sports', context.t('market.sports')), ]; return SizedBox( @@ -148,7 +149,12 @@ class _MarketPageState extends State // 排序栏 // ============================================================ Widget _buildSortBar() { - final sorts = ['折扣率', '价格↑', '价格↓', '到期时间']; + final sorts = [ + context.t('market.discountRate'), + context.t('market.priceUp'), + context.t('market.priceDown'), + context.t('market.expiryDate'), + ]; return Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: Row( @@ -250,7 +256,7 @@ class _MarketPageState extends State borderRadius: AppSpacing.borderRadiusFull, ), child: Text( - launch.statusText, + launch.getStatusText(context), style: AppTypography.caption.copyWith( color: launch.statusColor, fontWeight: FontWeight.w600, @@ -265,15 +271,15 @@ class _MarketPageState extends State // Price info Row( children: [ - _buildLaunchInfo('发行价', + _buildLaunchInfo(context.t('market.issuePrice'), '\$${launch.issuePrice.toStringAsFixed(2)}'), _buildLaunchInfo( - '面值', '\$${launch.faceValue.toStringAsFixed(0)}'), + context.t('market.faceValue'), '\$${launch.faceValue.toStringAsFixed(0)}'), _buildLaunchInfo( - '折扣', - '${(launch.issuePrice / launch.faceValue * 10).toStringAsFixed(1)}折'), + context.t('market.discount'), + '${(launch.issuePrice / launch.faceValue * 10).toStringAsFixed(1)}${context.t('market.discountSuffix')}'), _buildLaunchInfo( - '发行量', '${launch.totalSupply}'), + context.t('market.totalSupply'), '${launch.totalSupply}'), ], ), @@ -286,7 +292,7 @@ class _MarketPageState extends State Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('销售进度', style: AppTypography.caption), + Text(context.t('market.salesProgress'), style: AppTypography.caption), Text( '${(launch.soldPercent * 100).toStringAsFixed(1)}%', style: AppTypography.caption.copyWith( @@ -318,7 +324,7 @@ class _MarketPageState extends State size: 14, color: AppColors.warning), const SizedBox(width: 4), Text( - '距开始: ${launch.countdown}', + '${context.t('market.timeToStart')}: ${launch.countdown}', style: AppTypography.caption.copyWith( color: AppColors.warning, fontWeight: FontWeight.w500, @@ -363,15 +369,15 @@ class _MarketPageState extends State children: [ Expanded( flex: 3, - child: Text('券名/品牌', style: AppTypography.caption)), + child: Text(context.t('market.couponBrand'), style: AppTypography.caption)), Expanded( flex: 2, - child: Text('最新价', + child: Text(context.t('market.latestPrice'), style: AppTypography.caption, textAlign: TextAlign.right)), Expanded( flex: 2, - child: Text('24h涨跌', + child: Text(context.t('market.change24h'), style: AppTypography.caption, textAlign: TextAlign.right)), ], @@ -459,7 +465,7 @@ class _MarketPageState extends State ), ), Text( - '面值 \$${item.faceValue.toStringAsFixed(0)}', + '${context.t('market.faceValue')} \$${item.faceValue.toStringAsFixed(0)}', style: AppTypography.caption, ), ], @@ -521,14 +527,14 @@ class _LaunchItem { this.countdown = '', }); - String get statusText { + String getStatusText(BuildContext context) { switch (status) { case 0: - return '即将开始'; + return context.t('market.upcoming'); case 1: - return '申购中'; + return context.t('market.subscribing'); case 2: - return '已结束'; + return context.t('market.ended'); default: return ''; } diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/my_coupon_detail_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/my_coupon_detail_page.dart index 0fef4df..40b34ad 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/my_coupon_detail_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/my_coupon_detail_page.dart @@ -4,6 +4,7 @@ import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; import '../../../../shared/widgets/status_tag.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A4. 券详情(持有券)- QR码/条形码 + 转赠/出售/提取 /// @@ -20,7 +21,7 @@ class MyCouponDetailPage extends StatelessWidget { icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20), onPressed: () => Navigator.of(context).pop(), ), - title: const Text('券详情'), + title: Text(context.t('myCoupon.title')), actions: [ IconButton( icon: const Icon(Icons.more_horiz_rounded), @@ -66,7 +67,7 @@ class MyCouponDetailPage extends StatelessWidget { color: Colors.white24, borderRadius: AppSpacing.borderRadiusFull, ), - child: Text('可使用', style: AppTypography.caption.copyWith( + child: Text(context.t('myCoupon.active'), style: AppTypography.caption.copyWith( color: Colors.white, fontWeight: FontWeight.w600, )), @@ -101,7 +102,7 @@ class MyCouponDetailPage extends StatelessWidget { // Instructions Text( - '出示此二维码给商户扫描核销', + context.t('myCoupon.showQrHint'), style: AppTypography.bodySmall.copyWith(color: Colors.white70), ), @@ -112,7 +113,7 @@ class MyCouponDetailPage extends StatelessWidget { onPressed: () {}, icon: const Icon(Icons.view_headline_rounded, size: 18, color: Colors.white70), - label: Text('切换条形码', style: AppTypography.labelSmall.copyWith( + label: Text(context.t('myCoupon.switchBarcode'), style: AppTypography.labelSmall.copyWith( color: Colors.white70, )), ), @@ -131,15 +132,15 @@ class MyCouponDetailPage extends StatelessWidget { ), child: Column( children: [ - _infoRow('面值', '\$25.00'), + _infoRow(context.t('myCoupon.faceValue'), '\$25.00'), const Padding(padding: EdgeInsets.symmetric(vertical: 10), child: Divider()), - _infoRow('购买价格', '\$21.25'), + _infoRow(context.t('myCoupon.purchasePrice'), '\$21.25'), const Padding(padding: EdgeInsets.symmetric(vertical: 10), child: Divider()), - _infoRow('有效期', '2026/12/31'), + _infoRow(context.t('myCoupon.validUntil'), '2026/12/31'), const Padding(padding: EdgeInsets.symmetric(vertical: 10), child: Divider()), - _infoRow('订单号', 'GNX-20260209-001234'), + _infoRow(context.t('myCoupon.orderNo'), 'GNX-20260209-001234'), const Padding(padding: EdgeInsets.symmetric(vertical: 10), child: Divider()), - _infoRow('剩余可转售次数', '3次'), + _infoRow(context.t('myCoupon.resellCount'), '3次'), ], ), ), @@ -150,7 +151,7 @@ class MyCouponDetailPage extends StatelessWidget { children: [ Expanded( child: GenexButton( - label: '转赠', + label: context.t('myCoupon.transfer'), icon: Icons.card_giftcard_rounded, variant: GenexButtonVariant.secondary, onPressed: () { @@ -161,7 +162,7 @@ class MyCouponDetailPage extends StatelessWidget { const SizedBox(width: 12), Expanded( child: GenexButton( - label: '出售', + label: context.t('myCoupon.sell'), icon: Icons.sell_rounded, variant: GenexButtonVariant.outline, onPressed: () { @@ -185,12 +186,12 @@ class MyCouponDetailPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('使用说明', style: AppTypography.labelMedium), + Text(context.t('myCoupon.usageNote'), style: AppTypography.labelMedium), const SizedBox(height: 12), - _ruleItem('全国星巴克门店通用'), - _ruleItem('请在有效期内使用'), - _ruleItem('每次消费仅可使用一张'), - _ruleItem('不可兑换现金'), + _ruleItem(context.t('myCoupon.useInStore')), + _ruleItem(context.t('myCoupon.useInTime')), + _ruleItem(context.t('myCoupon.onePerVisit')), + _ruleItem(context.t('myCoupon.noCash')), ], ), ), @@ -249,11 +250,11 @@ class MyCouponDetailPage extends StatelessWidget { ), ), const SizedBox(height: 16), - _optionTile(Icons.wallet_rounded, '提取到外部钱包', '需KYC L2+认证', () {}), + _optionTile(Icons.wallet_rounded, context.t('myCoupon.extractToWallet'), context.t('myCoupon.requireKycL2'), () {}), const Divider(), - _optionTile(Icons.receipt_long_rounded, '查看交易记录', '', () {}), + _optionTile(Icons.receipt_long_rounded, context.t('myCoupon.viewTrades'), '', () {}), const Divider(), - _optionTile(Icons.help_outline_rounded, '使用帮助', '', () {}), + _optionTile(Icons.help_outline_rounded, context.t('myCoupon.help'), '', () {}), ], ), ), diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/my_coupons_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/my_coupons_page.dart index 139db64..ed1fda9 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/my_coupons_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/my_coupons_page.dart @@ -5,6 +5,7 @@ import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/coupon_card.dart'; import '../../../../shared/widgets/status_tag.dart'; import '../../../../shared/widgets/empty_state.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A4. 我的券列表 /// @@ -37,7 +38,7 @@ class _MyCouponsPageState extends State Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('我的券'), + title: Text(context.t('walletCoupons.title')), actions: [ IconButton( icon: const Icon(Icons.sort_rounded, size: 22), @@ -46,11 +47,11 @@ class _MyCouponsPageState extends State ], bottom: TabBar( controller: _tabController, - tabs: const [ - Tab(text: '全部'), - Tab(text: '可使用'), - Tab(text: '待核销'), - Tab(text: '已过期'), + tabs: [ + Tab(text: context.t('home.viewAll')), + Tab(text: context.t('walletCoupons.usable')), + Tab(text: context.t('walletCoupons.pendingRedeem')), + Tab(text: context.t('walletCoupons.expired')), ], ), ), @@ -160,7 +161,7 @@ class _MyCouponCard extends StatelessWidget { const SizedBox(height: 4), Row( children: [ - Text('面值 \$${faceValue.toStringAsFixed(0)}', + Text('${context.t('walletCoupons.faceValue')} \$${faceValue.toStringAsFixed(0)}', style: AppTypography.bodySmall), const SizedBox(width: 8), _statusWidget, @@ -187,14 +188,14 @@ class _MyCouponCard extends StatelessWidget { Icon(Icons.access_time_rounded, size: 14, color: _expiryColor), const SizedBox(width: 4), Text( - _expiryText, + _getExpiryText(context), style: AppTypography.caption.copyWith(color: _expiryColor), ), const Spacer(), if (status == CouponStatus.active) ...[ - _quickAction('转赠', Icons.card_giftcard_rounded), + _quickAction(context, context.t('myCoupon.transfer'), Icons.card_giftcard_rounded), const SizedBox(width: 12), - _quickAction('出售', Icons.sell_rounded), + _quickAction(context, context.t('myCoupon.sell'), Icons.sell_rounded), ], ], ), @@ -218,7 +219,7 @@ class _MyCouponCard extends StatelessWidget { } } - Widget _quickAction(String label, IconData icon) { + Widget _quickAction(BuildContext context, String label, IconData icon) { return Row( mainAxisSize: MainAxisSize.min, children: [ @@ -232,12 +233,12 @@ class _MyCouponCard extends StatelessWidget { ); } - String get _expiryText { + String _getExpiryText(BuildContext context) { final days = expiryDate.difference(DateTime.now()).inDays; - if (days < 0) return '已过期'; - if (days == 0) return '今天到期'; - if (days <= 7) return '$days天后到期'; - return '${expiryDate.year}/${expiryDate.month}/${expiryDate.day}到期'; + if (days < 0) return context.t('walletCoupons.expiredText'); + if (days == 0) return context.t('walletCoupons.expiringToday'); + if (days <= 7) return '$days${context.t('walletCoupons.daysToExpiry')}'; + return '${expiryDate.year}/${expiryDate.month}/${expiryDate.day}${context.t('walletCoupons.expiryFormat')}'; } Color get _expiryColor { diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/order_confirm_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/order_confirm_page.dart index 65ca215..45578d7 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/order_confirm_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/order_confirm_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A3. 确认订单 + 支付页面 /// @@ -30,7 +31,7 @@ class _OrderConfirmPageState extends State { icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20), onPressed: () => Navigator.of(context).pop(), ), - title: const Text('确认订单'), + title: Text(context.t('orderConfirm.title')), ), body: SingleChildScrollView( padding: AppSpacing.pagePadding, @@ -69,7 +70,7 @@ class _OrderConfirmPageState extends State { const SizedBox(width: 8), Expanded( child: Text( - '您正在购买消费券用于消费', + context.t('orderConfirm.buyingNote'), style: AppTypography.bodySmall.copyWith( color: AppColors.gray700, ), @@ -97,7 +98,7 @@ class _OrderConfirmPageState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('合计', style: AppTypography.caption), + Text(context.t('orderConfirm.total'), style: AppTypography.caption), Text( '\$${_totalPrice.toStringAsFixed(2)}', style: AppTypography.priceMedium, @@ -107,7 +108,7 @@ class _OrderConfirmPageState extends State { const SizedBox(width: 20), Expanded( child: GenexButton( - label: '确认支付', + label: context.t('orderConfirm.confirmPay'), onPressed: () => _showPaymentAuth(context), ), ), @@ -183,7 +184,7 @@ class _OrderConfirmPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('购买数量', style: AppTypography.labelMedium), + Text(context.t('orderConfirm.quantity'), style: AppTypography.labelMedium), Row( children: [ _buildQtyButton(Icons.remove_rounded, () { @@ -221,7 +222,7 @@ class _OrderConfirmPageState extends State { Widget _buildPaymentMethods() { final methods = [ - ('银行卡/信用卡', Icons.credit_card_rounded), + (context.t('orderConfirm.bankCard'), Icons.credit_card_rounded), ('Apple Pay', Icons.apple_rounded), ]; @@ -235,7 +236,7 @@ class _OrderConfirmPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('支付方式', style: AppTypography.labelMedium), + Text(context.t('orderConfirm.paymentMethod'), style: AppTypography.labelMedium), const SizedBox(height: 12), ...methods.asMap().entries.map((entry) { final isSelected = _selectedPayment == entry.key; @@ -285,15 +286,15 @@ class _OrderConfirmPageState extends State { ), child: Column( children: [ - _priceRow('单价', '\$${_unitPrice.toStringAsFixed(2)}'), + _priceRow(context.t('orderConfirm.unitPrice'), '\$${_unitPrice.toStringAsFixed(2)}'), const SizedBox(height: 8), - _priceRow('数量', '×$_quantity'), + _priceRow(context.t('orderConfirm.count'), '×$_quantity'), const Padding( padding: EdgeInsets.symmetric(vertical: 10), child: Divider(), ), _priceRow( - '合计', + context.t('orderConfirm.total'), '\$${_totalPrice.toStringAsFixed(2)}', valueStyle: AppTypography.priceMedium, ), @@ -301,7 +302,7 @@ class _OrderConfirmPageState extends State { Align( alignment: Alignment.centerRight, child: Text( - '比面值节省 \$${(25.0 * _quantity - _totalPrice).toStringAsFixed(2)}', + '${context.t('orderConfirm.saveBadge')} \$${(25.0 * _quantity - _totalPrice).toStringAsFixed(2)}', style: AppTypography.caption.copyWith(color: AppColors.success), ), ), @@ -337,7 +338,7 @@ class _OrderConfirmPageState extends State { ), ), const SizedBox(height: 20), - Text('确认支付', style: AppTypography.h2), + Text(context.t('orderConfirm.confirmPay'), style: AppTypography.h2), const SizedBox(height: 8), Text( '\$${_totalPrice.toStringAsFixed(2)}', @@ -358,10 +359,10 @@ class _OrderConfirmPageState extends State { size: 36, color: AppColors.primary), ), const SizedBox(height: 12), - Text('请验证指纹或面容以完成支付', style: AppTypography.bodySmall), + Text(context.t('orderConfirm.biometricHint'), style: AppTypography.bodySmall), const SizedBox(height: 24), GenexButton( - label: '使用密码支付', + label: context.t('orderConfirm.usePasswordPay'), variant: GenexButtonVariant.text, onPressed: () { Navigator.pop(ctx); diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/payment_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/payment_page.dart index a45e207..362a0e5 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/payment_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/payment_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A6. 支付方式选择页 /// @@ -17,17 +18,16 @@ class PaymentPage extends StatefulWidget { class _PaymentPageState extends State { int _selectedMethod = 0; - final _methods = const [ - _PaymentMethod('Visa •••• 4242', Icons.credit_card_rounded, 'visa'), - _PaymentMethod('Apple Pay', Icons.apple_rounded, 'apple_pay'), - _PaymentMethod('Google Pay', Icons.account_balance_wallet_rounded, 'google_pay'), - _PaymentMethod('银行转账', Icons.account_balance_rounded, 'bank'), - ]; - @override Widget build(BuildContext context) { + final methods = [ + _PaymentMethod('Visa •••• 4242', Icons.credit_card_rounded, 'visa'), + _PaymentMethod('Apple Pay', Icons.apple_rounded, 'apple_pay'), + _PaymentMethod('Google Pay', Icons.account_balance_wallet_rounded, 'google_pay'), + _PaymentMethod(context.t('payment.bankTransfer'), Icons.account_balance_rounded, 'bank'), + ]; return Scaffold( - appBar: AppBar(title: const Text('选择支付方式')), + appBar: AppBar(title: Text(context.t('payment.title'))), body: Column( children: [ // Order Summary @@ -70,9 +70,9 @@ class _PaymentPageState extends State { Expanded( child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 20), - itemCount: _methods.length, + itemCount: methods.length, itemBuilder: (context, index) { - final method = _methods[index]; + final method = methods[index]; final isSelected = _selectedMethod == index; return GestureDetector( onTap: () => setState(() => _selectedMethod = index), @@ -110,7 +110,7 @@ class _PaymentPageState extends State { child: TextButton.icon( onPressed: () {}, icon: const Icon(Icons.add_rounded), - label: const Text('添加新支付方式'), + label: Text(context.t('payment.addNew')), ), ), @@ -125,7 +125,7 @@ class _PaymentPageState extends State { // 后端自动完成法币→稳定币→链上原子交换 Navigator.pushNamed(context, '/payment/success'); }, - child: const Text('确认支付 \$21.25'), + child: Text('${context.t('payment.confirmPay')} \$21.25'), ), ), ), diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/payment_success_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/payment_success_page.dart index 27ae308..74e04fc 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/payment_success_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/payment_success_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A3. 支付成功页 /// @@ -45,10 +46,10 @@ class PaymentSuccessPage extends StatelessWidget { ), const SizedBox(height: 24), - Text('支付成功', style: AppTypography.h1), + Text(context.t('paymentSuccess.title'), style: AppTypography.h1), const SizedBox(height: 8), Text( - '券已到账,可在「我的券」中查看', + context.t('paymentSuccess.hint'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 32), @@ -64,19 +65,19 @@ class PaymentSuccessPage extends StatelessWidget { ), child: Column( children: [ - _infoRow('券名称', couponName), + _infoRow(context.t('paymentSuccess.couponName'), couponName), const SizedBox(height: 10), const Divider(), const SizedBox(height: 10), - _infoRow('支付金额', '\$$amount'), + _infoRow(context.t('paymentSuccess.payAmount'), '\$$amount'), const SizedBox(height: 10), const Divider(), const SizedBox(height: 10), - _infoRow('订单号', orderNumber), + _infoRow(context.t('paymentSuccess.orderNo'), orderNumber), const SizedBox(height: 10), const Divider(), const SizedBox(height: 10), - _infoRow('支付时间', '2026-02-09 14:32:15'), + _infoRow(context.t('paymentSuccess.payTime'), '2026-02-09 14:32:15'), ], ), ), @@ -85,14 +86,14 @@ class PaymentSuccessPage extends StatelessWidget { // Actions GenexButton( - label: '查看我的券', + label: context.t('paymentSuccess.viewMyCoupon'), onPressed: () { Navigator.pushNamedAndRemoveUntil(context, '/main', (route) => false); }, ), const SizedBox(height: 12), GenexButton( - label: '继续逛', + label: context.t('paymentSuccess.continueBrowse'), variant: GenexButtonVariant.outline, onPressed: () { Navigator.pushNamedAndRemoveUntil(context, '/main', (route) => false); diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/redeem_qr_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/redeem_qr_page.dart index 6ba8a49..faeab65 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/redeem_qr_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/redeem_qr_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A8. 出示券码页面(消费者端) /// @@ -45,7 +46,7 @@ class _RedeemQrPageState extends State { appBar: AppBar( backgroundColor: AppColors.gray900, foregroundColor: Colors.white, - title: const Text('出示券码'), + title: Text(context.t('redeem.title')), ), body: Center( child: Column( @@ -54,7 +55,7 @@ class _RedeemQrPageState extends State { // Coupon Info Text('星巴克 \$25 礼品卡', style: AppTypography.h2.copyWith(color: Colors.white)), const SizedBox(height: 4), - Text('面值 \$25.00', style: AppTypography.bodyMedium.copyWith(color: Colors.white60)), + Text('${context.t('redeem.faceValue')} \$25.00', style: AppTypography.bodyMedium.copyWith(color: Colors.white60)), const SizedBox(height: 32), // QR Code Area @@ -103,7 +104,7 @@ class _RedeemQrPageState extends State { const Icon(Icons.timer_outlined, color: Colors.white54, size: 18), const SizedBox(width: 6), Text( - '有效时间 $_formattedTime', + '${context.t('redeem.validTime')} $_formattedTime', style: AppTypography.bodyMedium.copyWith(color: Colors.white54), ), ], @@ -113,7 +114,7 @@ class _RedeemQrPageState extends State { onPressed: () { setState(() => _remainingSeconds = 300); }, - child: Text('刷新券码', style: AppTypography.labelMedium.copyWith(color: AppColors.primaryLight)), + child: Text(context.t('redeem.refresh'), style: AppTypography.labelMedium.copyWith(color: AppColors.primaryLight)), ), const SizedBox(height: 40), @@ -131,7 +132,7 @@ class _RedeemQrPageState extends State { const SizedBox(width: 10), Expanded( child: Text( - '请将此码出示给商户扫描,屏幕已自动调至最高亮度', + context.t('redeem.showHint'), style: AppTypography.caption.copyWith(color: Colors.white54), ), ), diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/search_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/search_page.dart index 3938fc7..20476e9 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/search_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/search_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/coupon_card.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A3. 搜索页 - 搜索券、品牌、分类 /// @@ -27,7 +28,7 @@ class _SearchPageState extends State { actions: [ TextButton( onPressed: () => Navigator.pop(context), - child: const Text('取消'), + child: Text(context.t('search.cancel')), ), ], ), @@ -46,11 +47,11 @@ class _SearchPageState extends State { child: TextField( controller: _searchController, autofocus: true, - decoration: const InputDecoration( - hintText: '搜索券、品牌、分类...', - prefixIcon: Icon(Icons.search_rounded, size: 20), + decoration: InputDecoration( + hintText: context.t('search.hint'), + prefixIcon: const Icon(Icons.search_rounded, size: 20), border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 10), + contentPadding: const EdgeInsets.symmetric(vertical: 10), ), onChanged: (v) => setState(() => _hasInput = v.isNotEmpty), ), @@ -68,7 +69,7 @@ class _SearchPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('热门搜索', style: AppTypography.h3), + Text(context.t('search.hotSearch'), style: AppTypography.h3), GestureDetector(onTap: () {}, child: const Icon(Icons.refresh_rounded, size: 18, color: AppColors.textTertiary)), ], ), @@ -76,7 +77,7 @@ class _SearchPageState extends State { Wrap( spacing: 8, runSpacing: 8, - children: ['星巴克', 'Amazon', '餐饮券', '折扣券', '旅游', 'Nike'].map((tag) { + children: ['星巴克', 'Amazon', context.t('search.diningCoupon'), context.t('search.discountCoupon'), context.t('search.travel'), 'Nike'].map((tag) { return GestureDetector( onTap: () { _searchController.text = tag; @@ -100,10 +101,10 @@ class _SearchPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('搜索历史', style: AppTypography.h3), + Text(context.t('search.history'), style: AppTypography.h3), GestureDetector( onTap: () {}, - child: Text('清空', style: AppTypography.labelSmall.copyWith(color: AppColors.textTertiary)), + child: Text(context.t('search.clear'), style: AppTypography.labelSmall.copyWith(color: AppColors.textTertiary)), ), ], ), diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/wallet_coupons_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/wallet_coupons_page.dart index 99091fc..e605a5d 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/wallet_coupons_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/wallet_coupons_page.dart @@ -6,6 +6,7 @@ import '../../../../shared/widgets/coupon_card.dart'; import '../../../../shared/widgets/status_tag.dart'; import '../../../../shared/widgets/empty_state.dart'; import '../widgets/receive_coupon_sheet.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 完整持仓页面 - 融合"我的券"所有功能 /// @@ -39,13 +40,13 @@ class _WalletCouponsPageState extends State Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('我的持仓'), + title: Text(context.t('walletCoupons.title')), actions: [ // 接收券 IconButton( icon: const Icon(Icons.qr_code_rounded, size: 22), onPressed: () => _showReceiveSheet(context), - tooltip: '接收券', + tooltip: context.t('walletCoupons.receiveTip'), ), // 排序 IconButton( @@ -55,11 +56,11 @@ class _WalletCouponsPageState extends State ], bottom: TabBar( controller: _tabController, - tabs: const [ - Tab(text: '全部'), - Tab(text: '可使用'), - Tab(text: '待核销'), - Tab(text: '已过期'), + tabs: [ + Tab(text: context.t('home.viewAll')), + Tab(text: context.t('walletCoupons.usable')), + Tab(text: context.t('walletCoupons.pendingRedeem')), + Tab(text: context.t('walletCoupons.expired')), ], ), ), @@ -107,7 +108,7 @@ class _WalletCouponsPageState extends State fontWeight: FontWeight.w700, )), const SizedBox(height: 2), - Text('持有券数', + Text(context.t('walletCoupons.holdCount'), style: AppTypography.caption.copyWith( color: Colors.white.withValues(alpha: 0.7), )), @@ -129,7 +130,7 @@ class _WalletCouponsPageState extends State fontWeight: FontWeight.w700, )), const SizedBox(height: 2), - Text('总面值', + Text(context.t('walletCoupons.totalFaceValue'), style: AppTypography.caption.copyWith( color: Colors.white.withValues(alpha: 0.7), )), @@ -151,7 +152,7 @@ class _WalletCouponsPageState extends State fontWeight: FontWeight.w700, )), const SizedBox(height: 2), - Text('已节省', + Text(context.t('walletCoupons.saved'), style: AppTypography.caption.copyWith( color: Colors.white.withValues(alpha: 0.7), )), @@ -222,7 +223,7 @@ class _WalletCouponsPageState extends State const SizedBox(height: 4), Row( children: [ - Text('面值 \$${coupon.faceValue.toStringAsFixed(0)}', + Text('${context.t('walletCoupons.faceValue')} \$${coupon.faceValue.toStringAsFixed(0)}', style: AppTypography.bodySmall), const SizedBox(width: 8), _statusWidget(coupon.status), @@ -250,17 +251,17 @@ class _WalletCouponsPageState extends State size: 14, color: _expiryColor(coupon.expiryDate)), const SizedBox(width: 4), Text( - _expiryText(coupon.expiryDate), + _expiryText(context, coupon.expiryDate), style: AppTypography.caption.copyWith( color: _expiryColor(coupon.expiryDate)), ), const Spacer(), if (coupon.status == CouponStatus.active) ...[ - _quickAction('转赠', Icons.card_giftcard_rounded, () { + _quickAction(context.t('myCoupon.transfer'), Icons.card_giftcard_rounded, () { Navigator.pushNamed(context, '/transfer'); }), const SizedBox(width: 12), - _quickAction('出售', Icons.sell_rounded, () { + _quickAction(context.t('myCoupon.sell'), Icons.sell_rounded, () { Navigator.pushNamed(context, '/sell'); }), ], @@ -305,12 +306,12 @@ class _WalletCouponsPageState extends State ); } - String _expiryText(DateTime expiryDate) { + String _expiryText(BuildContext context, DateTime expiryDate) { final days = expiryDate.difference(DateTime.now()).inDays; - if (days < 0) return '已过期'; - if (days == 0) return '今天到期'; - if (days <= 7) return '$days天后到期'; - return '${expiryDate.year}/${expiryDate.month}/${expiryDate.day}到期'; + if (days < 0) return context.t('walletCoupons.expiredText'); + if (days == 0) return context.t('walletCoupons.expiringToday'); + if (days <= 7) return '$days${context.t('walletCoupons.daysToExpiry')}'; + return '${expiryDate.year}/${expiryDate.month}/${expiryDate.day}${context.t('walletCoupons.expiryFormat')}'; } Color _expiryColor(DateTime expiryDate) { diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/widgets/receive_coupon_sheet.dart b/frontend/genex-mobile/lib/features/coupons/presentation/widgets/receive_coupon_sheet.dart index 4939109..db851ad 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/widgets/receive_coupon_sheet.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/widgets/receive_coupon_sheet.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 接收券 - 底部弹出Sheet /// @@ -39,7 +40,7 @@ class ReceiveCouponSheet extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('接收券', style: AppTypography.h2), + Text(context.t('receiveCoupon.title'), style: AppTypography.h2), GestureDetector( onTap: () => Navigator.pop(context), child: const Icon(Icons.close_rounded, @@ -55,7 +56,7 @@ class ReceiveCouponSheet extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Text( - '向他人展示下方二维码或接收ID,对方可通过扫码或输入ID将券转赠到你的钱包。', + context.t('receiveCoupon.hint'), style: AppTypography.bodySmall, ), ), @@ -131,15 +132,15 @@ class ReceiveCouponSheet extends StatelessWidget { const SizedBox(height: 16), // Receive ID - Text('接收ID', style: AppTypography.caption), + Text(context.t('receiveCoupon.id'), style: AppTypography.caption), const SizedBox(height: 6), GestureDetector( onTap: () { Clipboard.setData( const ClipboardData(text: _mockReceiveId)); ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('接收ID已复制到剪贴板'), + SnackBar( + content: Text(context.t('receiveCoupon.idCopied')), duration: Duration(seconds: 2), ), ); @@ -194,7 +195,7 @@ class ReceiveCouponSheet extends StatelessWidget { const SizedBox(width: 8), Expanded( child: Text( - '接收的券将自动存入你的钱包,可在首页钱包中查看和管理。', + context.t('receiveCoupon.note'), style: AppTypography.bodySmall .copyWith(color: AppColors.info), ), diff --git a/frontend/genex-mobile/lib/features/issuer/presentation/pages/issuer_main_page.dart b/frontend/genex-mobile/lib/features/issuer/presentation/pages/issuer_main_page.dart index 24d784d..08b78d2 100644 --- a/frontend/genex-mobile/lib/features/issuer/presentation/pages/issuer_main_page.dart +++ b/frontend/genex-mobile/lib/features/issuer/presentation/pages/issuer_main_page.dart @@ -5,6 +5,7 @@ import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; import '../../../../shared/widgets/credit_badge.dart'; import '../../../ai_agent/presentation/widgets/ai_fab.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// C. 发行方管理后台App - 主页 + 底部导航 /// @@ -35,17 +36,17 @@ class _IssuerMainPageState extends State { bottomNavigationBar: NavigationBar( selectedIndex: _currentIndex, onDestinationSelected: (i) => setState(() => _currentIndex = i), - destinations: const [ - NavigationDestination(icon: Icon(Icons.dashboard_outlined), - selectedIcon: Icon(Icons.dashboard_rounded), label: '总览'), - NavigationDestination(icon: Icon(Icons.add_card_outlined), - selectedIcon: Icon(Icons.add_card_rounded), label: '发券'), - NavigationDestination(icon: Icon(Icons.fact_check_outlined), - selectedIcon: Icon(Icons.fact_check_rounded), label: '核销'), - NavigationDestination(icon: Icon(Icons.account_balance_outlined), - selectedIcon: Icon(Icons.account_balance_rounded), label: '财务'), - NavigationDestination(icon: Icon(Icons.more_horiz_rounded), - selectedIcon: Icon(Icons.more_horiz_rounded), label: '更多'), + destinations: [ + NavigationDestination(icon: const Icon(Icons.dashboard_outlined), + selectedIcon: const Icon(Icons.dashboard_rounded), label: context.t('issuer.overview')), + NavigationDestination(icon: const Icon(Icons.add_card_outlined), + selectedIcon: const Icon(Icons.add_card_rounded), label: context.t('issuer.issue')), + NavigationDestination(icon: const Icon(Icons.fact_check_outlined), + selectedIcon: const Icon(Icons.fact_check_rounded), label: context.t('issuer.redeem')), + NavigationDestination(icon: const Icon(Icons.account_balance_outlined), + selectedIcon: const Icon(Icons.account_balance_rounded), label: context.t('issuer.finance')), + NavigationDestination(icon: const Icon(Icons.more_horiz_rounded), + selectedIcon: const Icon(Icons.more_horiz_rounded), label: context.t('issuer.settingsItemDesc').split('/').last), ], ), floatingActionButton: AiFab( @@ -64,7 +65,7 @@ class _IssuerDashboard extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('发行方管理'), + title: Text(context.t('issuer.title')), actions: [ IconButton(icon: const Icon(Icons.notifications_outlined), onPressed: () {}), ], @@ -113,7 +114,7 @@ class _IssuerDashboard extends StatelessWidget { )), ), const SizedBox(width: 8), - Text('已认证发行方', style: AppTypography.bodySmall.copyWith( + Text(context.t('issuer.verified'), style: AppTypography.bodySmall.copyWith( color: Colors.white70, )), ], @@ -159,29 +160,29 @@ class _IssuerDashboard extends StatelessWidget { const SizedBox(height: 20), // Stats Grid - _buildStatsGrid(), + _buildStatsGrid(context), const SizedBox(height: 24), // Quick Actions - Text('快捷操作', style: AppTypography.h3), + Text(context.t('issuer.quickActions'), style: AppTypography.h3), const SizedBox(height: 12), Row( children: [ - _quickAction(Icons.add_card_rounded, '创建券', AppColors.primary), + _quickAction(Icons.add_card_rounded, context.t('issuer.createCoupon'), AppColors.primary), const SizedBox(width: 12), - _quickAction(Icons.people_outline_rounded, '门店管理', AppColors.info), + _quickAction(Icons.people_outline_rounded, context.t('issuer.storeManage'), AppColors.info), const SizedBox(width: 12), - _quickAction(Icons.analytics_outlined, '销售分析', AppColors.success), + _quickAction(Icons.analytics_outlined, context.t('issuer.salesAnalysis'), AppColors.success), const SizedBox(width: 12), - _quickAction(Icons.download_rounded, '对账单', AppColors.warning), + _quickAction(Icons.download_rounded, context.t('issuer.statement'), AppColors.warning), ], ), const SizedBox(height: 24), // Recent Coupons - Text('我的券', style: AppTypography.h3), + Text(context.t('issuer.myCoupons'), style: AppTypography.h3), const SizedBox(height: 12), - ...List.generate(3, (i) => _couponItem(i)), + ...List.generate(3, (i) => _couponItem(context, i)), const SizedBox(height: 80), ], @@ -190,12 +191,12 @@ class _IssuerDashboard extends StatelessWidget { ); } - Widget _buildStatsGrid() { + Widget _buildStatsGrid(BuildContext context) { final stats = [ - ('发行总量', '12,800', AppColors.primary), - ('已售出', '9,650', AppColors.success), - ('已核销', '6,240', AppColors.info), - ('核销率', '64.7%', AppColors.warning), + (context.t('issuer.totalIssued'), '12,800', AppColors.primary), + (context.t('issuer.totalSold'), '9,650', AppColors.success), + (context.t('issuer.totalRedeemed'), '6,240', AppColors.info), + (context.t('issuer.redeemRate'), '64.7%', AppColors.warning), ]; return GridView.count( @@ -245,9 +246,9 @@ class _IssuerDashboard extends StatelessWidget { ); } - Widget _couponItem(int index) { + Widget _couponItem(BuildContext context, int index) { final names = ['\$25 礼品卡', '\$50 满减券', '\$10 折扣券']; - final statuses = ['已上架', '审核中', '已售罄']; + final statuses = [context.t('issuer.listed'), context.t('issuer.underReview'), context.t('issuer.soldOut')]; final colors = [AppColors.success, AppColors.warning, AppColors.textTertiary]; return Container( @@ -275,7 +276,7 @@ class _IssuerDashboard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(names[index], style: AppTypography.labelMedium), - Text('发行 1,000 / 已售 ${[850, 0, 500][index]}', + Text('${context.t('issuer.issuedSlash')} 1,000 / ${context.t('issuer.sold')} ${[850, 0, 500][index]}', style: AppTypography.caption), ], ), @@ -303,7 +304,7 @@ class _CouponCenter extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('发券中心')), + appBar: AppBar(title: Text(context.t('issuer.issueCenter'))), body: SingleChildScrollView( padding: AppSpacing.pagePadding, child: Column( @@ -312,7 +313,7 @@ class _CouponCenter extends StatelessWidget { const SizedBox(height: 16), // Template Selection - Text('选择券模板', style: AppTypography.h3), + Text(context.t('issuer.selectTemplate'), style: AppTypography.h3), const SizedBox(height: 12), GridView.count( crossAxisCount: 2, @@ -322,10 +323,10 @@ class _CouponCenter extends StatelessWidget { crossAxisSpacing: 12, childAspectRatio: 1.2, children: [ - _templateCard('满减券', Icons.local_offer_rounded, AppColors.couponDining), - _templateCard('折扣券', Icons.percent_rounded, AppColors.couponShopping), - _templateCard('礼品卡', Icons.card_giftcard_rounded, AppColors.couponEntertainment), - _templateCard('储值券', Icons.account_balance_wallet_rounded, AppColors.couponTravel), + _templateCard(context.t('issuer.voucherType'), Icons.local_offer_rounded, AppColors.couponDining), + _templateCard(context.t('issuer.discountType'), Icons.percent_rounded, AppColors.couponShopping), + _templateCard(context.t('issuer.giftCardType'), Icons.card_giftcard_rounded, AppColors.couponEntertainment), + _templateCard(context.t('issuer.storedValueType'), Icons.account_balance_wallet_rounded, AppColors.couponTravel), ], ), const SizedBox(height: 24), @@ -334,13 +335,13 @@ class _CouponCenter extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('券管理', style: AppTypography.h3), - TextButton(onPressed: () {}, child: const Text('查看全部')), + Text(context.t('issuer.couponManage'), style: AppTypography.h3), + TextButton(onPressed: () {}, child: Text(context.t('issuer.viewAll'))), ], ), ...List.generate(5, (i) { final statusColors = [AppColors.success, AppColors.warning, AppColors.success, AppColors.textTertiary, AppColors.error]; - final statuses = ['已上架', '审核中', '已上架', '已下架', '已售罄']; + final statuses = [context.t('issuer.listed'), context.t('issuer.underReview'), context.t('issuer.listed'), context.t('issuer.unlisted'), context.t('issuer.soldOut')]; return ListTile( contentPadding: EdgeInsets.zero, leading: Container( @@ -352,8 +353,8 @@ class _CouponCenter extends StatelessWidget { child: const Icon(Icons.confirmation_number_outlined, color: AppColors.primary, size: 20), ), - title: Text('券活动 ${i + 1}', style: AppTypography.labelMedium), - subtitle: Text('已售 ${(i + 1) * 120} / ${(i + 1) * 200}', + title: Text('${context.t('issuer.couponEvents')} ${i + 1}', style: AppTypography.labelMedium), + subtitle: Text('${context.t('issuer.sold')} ${(i + 1) * 120} / ${(i + 1) * 200}', style: AppTypography.caption), trailing: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), @@ -379,7 +380,7 @@ class _CouponCenter extends StatelessWidget { backgroundColor: AppColors.primary, foregroundColor: Colors.white, icon: const Icon(Icons.add_rounded), - label: const Text('创建新券'), + label: Text(context.t('issuer.createNew')), ), ); } @@ -410,7 +411,7 @@ class _RedeemManagement extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('核销管理')), + appBar: AppBar(title: Text(context.t('issuer.redeemManage'))), body: SingleChildScrollView( padding: AppSpacing.pagePadding, child: Column( @@ -439,7 +440,7 @@ class _RedeemManagement extends StatelessWidget { border: Border.all(color: AppColors.borderLight), ), child: Center( - child: Text('核销趋势图 (fl_chart)', + child: Text('${context.t('issuer.redeemTrend')} (fl_chart)', style: AppTypography.bodySmall.copyWith(color: AppColors.textTertiary)), ), ), @@ -449,8 +450,8 @@ class _RedeemManagement extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('门店管理', style: AppTypography.h3), - TextButton(onPressed: () {}, child: const Text('全部门店')), + Text(context.t('issuer.storeManage'), style: AppTypography.h3), + TextButton(onPressed: () {}, child: Text(context.t('issuer.allStores'))), ], ), const SizedBox(height: 8), @@ -476,7 +477,7 @@ class _RedeemManagement extends StatelessWidget { ], ), ), - Text('${[3, 2, 1][i]} 名员工', style: AppTypography.caption), + Text('${[3, 2, 1][i]} ${context.t('issuer.employees')}', style: AppTypography.caption), ], ), )), @@ -514,7 +515,7 @@ class _FinancePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('财务管理')), + appBar: AppBar(title: Text(context.t('issuer.financeManage'))), body: SingleChildScrollView( padding: AppSpacing.pagePadding, child: Column( @@ -532,7 +533,7 @@ class _FinancePage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('总销售额', style: AppTypography.bodySmall.copyWith( + Text(context.t('issuer.totalSales'), style: AppTypography.bodySmall.copyWith( color: Colors.white70, )), const SizedBox(height: 4), @@ -542,11 +543,11 @@ class _FinancePage extends StatelessWidget { const SizedBox(height: 20), Row( children: [ - _revenueItem('已到账', '\$98,200'), + _revenueItem(context.t('issuer.settled'), '\$98,200'), const SizedBox(width: 24), - _revenueItem('待结算', '\$24,250'), + _revenueItem(context.t('issuer.pendingSettle'), '\$24,250'), const SizedBox(width: 24), - _revenueItem('Breakage', '\$6,000'), + _revenueItem(context.t('issuer.breakage'), '\$6,000'), ], ), ], @@ -559,7 +560,7 @@ class _FinancePage extends StatelessWidget { children: [ Expanded( child: GenexButton( - label: '提现', + label: context.t('issuer.withdrawBtn'), icon: Icons.account_balance_rounded, onPressed: () {}, ), @@ -567,7 +568,7 @@ class _FinancePage extends StatelessWidget { const SizedBox(width: 12), Expanded( child: GenexButton( - label: '对账报表', + label: context.t('issuer.reportBtn'), icon: Icons.receipt_long_rounded, variant: GenexButtonVariant.outline, onPressed: () {}, @@ -578,7 +579,7 @@ class _FinancePage extends StatelessWidget { const SizedBox(height: 24), // Settlement details - Text('结算明细', style: AppTypography.h3), + Text(context.t('issuer.settleDetail'), style: AppTypography.h3), const SizedBox(height: 12), ...List.generate(5, (i) => Container( margin: const EdgeInsets.only(bottom: 8), @@ -643,7 +644,7 @@ class _IssuerMore extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('更多')), + appBar: AppBar(title: Text(context.t('common.more'))), body: ListView( padding: AppSpacing.pagePadding, children: [ @@ -663,7 +664,7 @@ class _IssuerMore extends StatelessWidget { children: [ const Icon(Icons.verified_rounded, color: AppColors.creditAAA), const SizedBox(width: 8), - Text('信用等级', style: AppTypography.labelMedium), + Text(context.t('issuer.creditLevel'), style: AppTypography.labelMedium), const Spacer(), const CreditBadge(rating: 'AAA', size: CreditBadgeSize.large), ], @@ -675,14 +676,14 @@ class _IssuerMore extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('发行额度', style: AppTypography.caption), + Text(context.t('issuer.issueQuota'), style: AppTypography.caption), Text('\$500,000', style: AppTypography.h2.copyWith(color: AppColors.primary)), ], ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Text('已用额度', style: AppTypography.caption), + Text(context.t('issuer.usedQuota'), style: AppTypography.caption), Text('\$128,450', style: AppTypography.h3), ], ), @@ -704,13 +705,13 @@ class _IssuerMore extends StatelessWidget { const SizedBox(height: 16), // Menu items - _menuItem(Icons.bar_chart_rounded, '数据中心', '发行量/销量/兑付率'), - _menuItem(Icons.people_rounded, '用户画像', '购买用户分布分析'), - _menuItem(Icons.shield_outlined, '信用详情', '评分详情与提升建议'), - _menuItem(Icons.history_rounded, '额度变动', '历史额度调整记录'), - _menuItem(Icons.business_rounded, '企业信息', '营业执照/联系人'), - _menuItem(Icons.settings_outlined, '设置', '通知/安全/语言'), - _menuItem(Icons.help_outline_rounded, '帮助中心', '常见问题与客服'), + _menuItem(Icons.bar_chart_rounded, context.t('issuer.dataCenter'), context.t('issuer.issueSalesRate')), + _menuItem(Icons.people_rounded, context.t('issuer.userProfile'), context.t('issuer.userProfileDesc')), + _menuItem(Icons.shield_outlined, context.t('issuer.creditDetail'), context.t('issuer.creditDetailDesc')), + _menuItem(Icons.history_rounded, context.t('issuer.quotaChange'), context.t('issuer.quotaChangeDesc')), + _menuItem(Icons.business_rounded, context.t('issuer.companyInfo'), context.t('issuer.companyInfoDesc')), + _menuItem(Icons.settings_outlined, context.t('issuer.settingsItem'), context.t('issuer.settingsItemDesc')), + _menuItem(Icons.help_outline_rounded, context.t('issuer.helpItem'), context.t('issuer.helpItemDesc')), ], ), ); diff --git a/frontend/genex-mobile/lib/features/merchant/presentation/pages/merchant_ai_assistant_page.dart b/frontend/genex-mobile/lib/features/merchant/presentation/pages/merchant_ai_assistant_page.dart index 50fcaab..ff55785 100644 --- a/frontend/genex-mobile/lib/features/merchant/presentation/pages/merchant_ai_assistant_page.dart +++ b/frontend/genex-mobile/lib/features/merchant/presentation/pages/merchant_ai_assistant_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 商户端 AI 助手页面 /// @@ -37,13 +38,13 @@ class _MerchantAiAssistantPageState extends State Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('AI 助手'), + title: Text(context.t('aiChat.title')), bottom: TabBar( controller: _tabController, - tabs: const [ - Tab(text: '核销辅助'), - Tab(text: '客流预测'), - Tab(text: '异常预警'), + tabs: [ + Tab(text: context.t('merchantAi.redeemAssist')), + Tab(text: context.t('merchantAi.trafficForecast')), + Tab(text: context.t('merchantAi.anomalyAlert')), ], labelColor: AppColors.primary, unselectedLabelColor: AppColors.textTertiary, @@ -92,10 +93,10 @@ class _MerchantAiAssistantPageState extends State Widget _buildAiQuickActions() { final actions = [ - ('验券真伪', Icons.verified_user_rounded, AppColors.success), - ('查券状态', Icons.search_rounded, AppColors.info), - ('批量核销', Icons.playlist_add_check_rounded, AppColors.primary), - ('问题反馈', Icons.feedback_rounded, AppColors.warning), + (context.t('merchantAi.verifyAuth'), Icons.verified_user_rounded, AppColors.success), + (context.t('merchantAi.checkStatus'), Icons.search_rounded, AppColors.info), + (context.t('merchantAi.batchRedeem'), Icons.playlist_add_check_rounded, AppColors.primary), + (context.t('merchantAi.feedback'), Icons.feedback_rounded, AppColors.warning), ]; return Container( @@ -120,9 +121,9 @@ class _MerchantAiAssistantPageState extends State const Center(child: Text('✨', style: TextStyle(fontSize: 16))), ), const SizedBox(width: 10), - const Text( - 'AI 快捷操作', - style: TextStyle( + Text( + context.t('merchantAi.quickActions'), + style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: Colors.white), @@ -183,7 +184,7 @@ class _MerchantAiAssistantPageState extends State const Icon(Icons.lightbulb_outline_rounded, color: AppColors.warning, size: 20), const SizedBox(width: 8), - Text('核销提示', style: AppTypography.labelLarge), + Text(context.t('merchantAi.redeemTips'), style: AppTypography.labelLarge), ], ), const SizedBox(height: 12), @@ -261,7 +262,7 @@ class _MerchantAiAssistantPageState extends State const Icon(Icons.local_fire_department_rounded, color: AppColors.error, size: 20), const SizedBox(width: 8), - Text('今日热门核销', style: AppTypography.labelLarge), + Text(context.t('merchantAi.todayHotRedeem'), style: AppTypography.labelLarge), ], ), const SizedBox(height: 12), @@ -292,7 +293,7 @@ class _MerchantAiAssistantPageState extends State borderRadius: AppSpacing.borderRadiusFull, ), child: Text( - '$count笔', + '$count${context.t('merchantAi.countUnit')}', style: AppTypography.labelSmall .copyWith(color: AppColors.primary), ), @@ -321,18 +322,18 @@ class _MerchantAiAssistantPageState extends State const Icon(Icons.auto_awesome_rounded, color: AppColors.primary, size: 20), const SizedBox(width: 8), - Text('AI 营销建议', style: AppTypography.labelLarge), + Text(context.t('merchantAi.aiMarketing'), style: AppTypography.labelLarge), ], ), const SizedBox(height: 12), _buildSuggestionItem( - '推荐搭配销售', - '购买咖啡券的顾客同时对糕点券感兴趣,建议推荐组合', + context.t('merchantAi.crossSellTitle'), + context.t('merchantAi.crossSellDesc'), Icons.restaurant_rounded, ), _buildSuggestionItem( - '周末促销建议', - '历史数据显示周六核销量+30%,建议推出周末限时活动', + context.t('merchantAi.weekendPromoTitle'), + context.t('merchantAi.weekendPromoDesc'), Icons.campaign_rounded, ), ], @@ -404,13 +405,13 @@ class _MerchantAiAssistantPageState extends State ), child: Column( children: [ - const Row( + Row( children: [ - Icon(Icons.insights_rounded, color: Colors.white, size: 22), - SizedBox(width: 10), + const Icon(Icons.insights_rounded, color: Colors.white, size: 22), + const SizedBox(width: 10), Text( - '今日客流预测', - style: TextStyle( + context.t('merchantAi.todayForecast'), + style: const TextStyle( fontSize: 17, fontWeight: FontWeight.w700, color: Colors.white), @@ -421,9 +422,9 @@ class _MerchantAiAssistantPageState extends State Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - _predictionStat('预计核销', '45笔'), - _predictionStat('高峰时段', '11:30-13:00'), - _predictionStat('预计收入', '\$892'), + _predictionStat(context.t('merchantAi.expectedRedeem'), '45${context.t('merchantAi.countUnit')}'), + _predictionStat(context.t('merchantAi.peakHours'), '11:30-13:00'), + _predictionStat(context.t('merchantAi.expectedRevenue'), '\$892'), ], ), const SizedBox(height: 16), @@ -440,7 +441,7 @@ class _MerchantAiAssistantPageState extends State const SizedBox(width: 8), Expanded( child: Text( - '较上周同期增长12%,建议午间增加1名收银员', + context.t('merchantAi.trafficInsight'), style: TextStyle( fontSize: 12, color: Colors.white.withValues(alpha: 0.9), @@ -498,7 +499,7 @@ class _MerchantAiAssistantPageState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('分时段预测', style: AppTypography.labelLarge), + Text(context.t('merchantAi.hourlyForecast'), style: AppTypography.labelLarge), const SizedBox(height: 16), ...hours.map((h) { final (time, count) = h; @@ -531,7 +532,7 @@ class _MerchantAiAssistantPageState extends State SizedBox( width: 30, child: Text( - '$count笔', + '$count${context.t('merchantAi.countUnit')}', style: TextStyle( fontSize: 11, fontWeight: isPeak ? FontWeight.w600 : FontWeight.w400, @@ -550,13 +551,13 @@ class _MerchantAiAssistantPageState extends State Widget _buildWeeklyForecast() { final days = [ - ('周一', 38, false), - ('周二', 42, false), - ('周三', 45, true), - ('周四', 40, false), - ('周五', 52, false), - ('周六', 68, false), - ('周日', 55, false), + (context.t('merchantAi.monday'), 38, false), + (context.t('merchantAi.tuesday'), 42, false), + (context.t('merchantAi.wednesday'), 45, true), + (context.t('merchantAi.thursday'), 40, false), + (context.t('merchantAi.friday'), 52, false), + (context.t('merchantAi.saturday'), 68, false), + (context.t('merchantAi.sunday'), 55, false), ]; return Container( @@ -569,7 +570,7 @@ class _MerchantAiAssistantPageState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('本周预测', style: AppTypography.labelLarge), + Text(context.t('merchantAi.weeklyForecast'), style: AppTypography.labelLarge), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, @@ -630,7 +631,7 @@ class _MerchantAiAssistantPageState extends State const Icon(Icons.people_alt_rounded, color: AppColors.primary, size: 20), const SizedBox(width: 8), - Text('排班建议', style: AppTypography.labelLarge), + Text(context.t('merchantAi.staffSuggestion'), style: AppTypography.labelLarge), ], ), const SizedBox(height: 12), @@ -697,11 +698,11 @@ class _MerchantAiAssistantPageState extends State Widget _buildAlertSummary() { return Row( children: [ - _alertStatCard('待处理', '2', AppColors.error), + _alertStatCard(context.t('merchantAi.pendingCount'), '2', AppColors.error), const SizedBox(width: 12), - _alertStatCard('今日已处理', '5', AppColors.success), + _alertStatCard(context.t('merchantAi.resolvedToday'), '5', AppColors.success), const SizedBox(width: 12), - _alertStatCard('风险指数', '低', AppColors.info), + _alertStatCard(context.t('merchantAi.riskIndex'), context.t('merchantAi.riskLow'), AppColors.info), ], ); } @@ -744,14 +745,14 @@ class _MerchantAiAssistantPageState extends State const Icon(Icons.warning_amber_rounded, color: AppColors.error, size: 20), const SizedBox(width: 8), - Text('活跃预警', + Text(context.t('merchantAi.activeAlerts'), style: AppTypography.labelLarge.copyWith(color: AppColors.error)), ], ), const SizedBox(height: 12), _alertItem( - '高频核销检测', + context.t('merchantAi.highFreqRedeem'), '用户#78901 在 5 分钟内尝试核销 3 张同品牌券', '2 分钟前', AppColors.error, @@ -759,7 +760,7 @@ class _MerchantAiAssistantPageState extends State ), const Divider(height: 20), _alertItem( - '疑似伪造券码', + context.t('merchantAi.suspectFakeCode'), '券码 GNX-FAKE-001 格式异常,不在系统记录中', '15 分钟前', AppColors.warning, @@ -820,18 +821,18 @@ class _MerchantAiAssistantPageState extends State const Icon(Icons.pattern_rounded, color: AppColors.warning, size: 20), const SizedBox(width: 8), - Text('可疑模式检测', style: AppTypography.labelLarge), + Text(context.t('merchantAi.suspiciousPatterns'), style: AppTypography.labelLarge), ], ), const SizedBox(height: 12), _patternItem( - '同一用户连续核销', '3次/5分钟 (阈值: 2次/5分钟)', 0.8, AppColors.error), + context.t('merchantAi.consecutiveRedeem'), '3次/5分钟 (阈值: 2次/5分钟)', 0.8, AppColors.error), const SizedBox(height: 10), _patternItem( - '非营业时间核销尝试', '0次/本周', 0.0, AppColors.success), + context.t('merchantAi.offHoursRedeem'), '0次/本周', 0.0, AppColors.success), const SizedBox(height: 10), _patternItem( - '过期券核销尝试', '2次/今日', 0.4, AppColors.warning), + context.t('merchantAi.expiredRedeemAttempt'), '2次/今日', 0.4, AppColors.warning), ], ), ); @@ -854,10 +855,10 @@ class _MerchantAiAssistantPageState extends State ), child: Text( severity > 0.6 - ? '异常' + ? context.t('merchantAi.statusAbnormal') : severity > 0.2 - ? '注意' - : '正常', + ? context.t('merchantAi.statusWarning') + : context.t('merchantAi.statusNormal'), style: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: color), ), @@ -891,13 +892,13 @@ class _MerchantAiAssistantPageState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('今日已处理', style: AppTypography.labelLarge), + Text(context.t('merchantAi.resolvedToday'), style: AppTypography.labelLarge), const SizedBox(height: 12), - _resolvedItem('过期券核销拦截', '系统自动拦截', '10:24'), - _resolvedItem('重复核销拦截', '同一券码二次扫描', '11:05'), - _resolvedItem('非本店券提醒', '引导至正确门店', '12:30'), - _resolvedItem('余额不足核销', '告知顾客充值', '13:15'), - _resolvedItem('系统超时重试', '网络恢复后自动完成', '14:02'), + _resolvedItem(context.t('merchantAi.expiredBlock'), '系统自动拦截', '10:24'), + _resolvedItem(context.t('merchantAi.duplicateBlock'), '同一券码二次扫描', '11:05'), + _resolvedItem(context.t('merchantAi.wrongStoreAlert'), '引导至正确门店', '12:30'), + _resolvedItem(context.t('merchantAi.insufficientBalance'), '告知顾客充值', '13:15'), + _resolvedItem(context.t('merchantAi.systemRetry'), '网络恢复后自动完成', '14:02'), ], ), ); diff --git a/frontend/genex-mobile/lib/features/merchant/presentation/pages/merchant_home_page.dart b/frontend/genex-mobile/lib/features/merchant/presentation/pages/merchant_home_page.dart index 0f9b40b..0b52c9a 100644 --- a/frontend/genex-mobile/lib/features/merchant/presentation/pages/merchant_home_page.dart +++ b/frontend/genex-mobile/lib/features/merchant/presentation/pages/merchant_home_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// B. 商户核销端 - 主界面 /// @@ -20,10 +21,10 @@ class MerchantHomePage extends StatelessWidget { child: Column( children: [ // Header - _buildHeader(), + _buildHeader(context), // Network Status - _buildNetworkStatus(isOnline: true), + _buildNetworkStatus(context, isOnline: true), // Main Scanner Area Expanded(child: _buildScannerArea(context)), @@ -36,7 +37,7 @@ class MerchantHomePage extends StatelessWidget { ); } - Widget _buildHeader() { + Widget _buildHeader(BuildContext context) { return Container( padding: const EdgeInsets.fromLTRB(20, 12, 20, 12), child: Row( @@ -72,7 +73,7 @@ class MerchantHomePage extends StatelessWidget { children: [ const Icon(Icons.check_circle_rounded, size: 14, color: AppColors.success), const SizedBox(width: 4), - Text('今日 23 笔', style: AppTypography.labelSmall.copyWith( + Text('${context.t('merchant.today')} 23 笔', style: AppTypography.labelSmall.copyWith( color: AppColors.success, )), ], @@ -83,7 +84,7 @@ class MerchantHomePage extends StatelessWidget { ); } - Widget _buildNetworkStatus({required bool isOnline}) { + Widget _buildNetworkStatus(BuildContext context, {required bool isOnline}) { return Container( margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), @@ -103,7 +104,7 @@ class MerchantHomePage extends StatelessWidget { ), const SizedBox(width: 6), Text( - isOnline ? '在线模式' : '离线模式 - 待同步 3 笔', + isOnline ? context.t('merchant.onlineMode') : '${context.t('merchant.offlineMode')} - ${context.t('merchant.pendingSync')} 3 ${context.t('merchant.syncUnit')}', style: AppTypography.caption.copyWith( color: isOnline ? AppColors.success : AppColors.warning, fontWeight: FontWeight.w500, @@ -153,7 +154,7 @@ class MerchantHomePage extends StatelessWidget { ), const SizedBox(height: 20), Text( - '将券二维码对准扫描框', + context.t('merchant.scanHint'), style: AppTypography.bodyMedium.copyWith(color: Colors.white70), ), ], @@ -182,7 +183,7 @@ class MerchantHomePage extends StatelessWidget { color: Colors.white70, size: 22), ), const SizedBox(height: 4), - Text('手电筒', style: AppTypography.caption.copyWith(color: Colors.white54)), + Text(context.t('merchant.flashlight'), style: AppTypography.caption.copyWith(color: Colors.white54)), ], ), ), @@ -247,15 +248,15 @@ class MerchantHomePage extends StatelessWidget { padding: const EdgeInsets.fromLTRB(20, 12, 20, 16), child: Row( children: [ - _bottomAction(Icons.keyboard_rounded, '手动输码', () { + _bottomAction(Icons.keyboard_rounded, context.t('merchant.manualInput'), () { _showManualInput(context); }), const SizedBox(width: 16), - _bottomAction(Icons.history_rounded, '核销记录', () { + _bottomAction(Icons.history_rounded, context.t('merchant.redeemRecords'), () { // Navigator: → RedeemHistoryPage }), const SizedBox(width: 16), - _bottomAction(Icons.bar_chart_rounded, '门店数据', () { + _bottomAction(Icons.bar_chart_rounded, context.t('merchant.storeData'), () { // Navigator: → StoreDashboardPage }), ], @@ -310,20 +311,20 @@ class MerchantHomePage extends StatelessWidget { ), ), const SizedBox(height: 20), - Text('手动输入券码', style: AppTypography.h2), + Text(context.t('merchant.inputCode'), style: AppTypography.h2), const SizedBox(height: 16), TextField( autofocus: true, - decoration: const InputDecoration( - hintText: '请输入券码', - prefixIcon: Icon(Icons.confirmation_number_outlined, + decoration: InputDecoration( + hintText: context.t('merchant.inputCodeHint'), + prefixIcon: const Icon(Icons.confirmation_number_outlined, color: AppColors.textTertiary), ), textCapitalization: TextCapitalization.characters, ), const SizedBox(height: 16), GenexButton( - label: '查询', + label: context.t('merchant.query'), onPressed: () {}, ), ], @@ -369,8 +370,8 @@ class RedeemConfirmSheet extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('用户昵称', style: AppTypography.labelMedium), - Text('消费者', style: AppTypography.caption), + Text(context.t('merchant.userNickname'), style: AppTypography.labelMedium), + Text(context.t('merchant.consumer'), style: AppTypography.caption), ], ), ], @@ -387,20 +388,20 @@ class RedeemConfirmSheet extends StatelessWidget { ), child: Column( children: [ - _row('券名称', '星巴克 \$25 礼品卡'), + _row(context.t('merchant.couponName'), '星巴克 \$25 礼品卡'), const SizedBox(height: 8), - _row('面值', '\$25.00'), + _row(context.t('merchant.faceValue'), '\$25.00'), const SizedBox(height: 8), - _row('有效期', '2026/12/31'), + _row(context.t('merchant.validUntil'), '2026/12/31'), const SizedBox(height: 8), - _row('使用条件', '无最低消费'), + _row(context.t('merchant.useCondition'), context.t('merchant.noMinSpend')), ], ), ), const SizedBox(height: 24), GenexButton( - label: '确认核销', + label: context.t('merchant.confirmRedeem'), onPressed: () { Navigator.of(context).pop(); // Show success @@ -408,7 +409,7 @@ class RedeemConfirmSheet extends StatelessWidget { ), const SizedBox(height: 8), GenexButton( - label: '取消', + label: context.t('common.cancel'), variant: GenexButtonVariant.text, onPressed: () => Navigator.of(context).pop(), ), @@ -456,14 +457,14 @@ class RedeemSuccessSheet extends StatelessWidget { child: const Icon(Icons.check_rounded, color: Colors.white, size: 36), ), const SizedBox(height: 16), - Text('核销成功', style: AppTypography.h1), + Text(context.t('merchant.redeemSuccess'), style: AppTypography.h1), const SizedBox(height: 8), Text('星巴克 \$25 礼品卡', style: AppTypography.bodyMedium.copyWith( color: AppColors.textSecondary, )), const SizedBox(height: 32), GenexButton( - label: '继续核销', + label: context.t('merchant.continueRedeem'), onPressed: () => Navigator.of(context).pop(), ), ], @@ -484,11 +485,11 @@ class RedeemHistoryPage extends StatelessWidget { icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20), onPressed: () => Navigator.of(context).pop(), ), - title: const Text('核销记录'), + title: Text(context.t('merchant.redeemRecords')), actions: [ TextButton( onPressed: () {}, - child: Text('今日', style: AppTypography.labelSmall.copyWith( + child: Text(context.t('merchant.today'), style: AppTypography.labelSmall.copyWith( color: AppColors.primary, )), ), @@ -528,13 +529,13 @@ class RedeemHistoryPage extends StatelessWidget { children: [ Text('品牌 ${index + 1} \$${(index + 1) * 10} 券', style: AppTypography.labelSmall), - Text('核销员: 张三 · 14:${30 + index}', + Text('${context.t('merchant.redeemOperator')}: 张三 · 14:${30 + index}', style: AppTypography.caption), ], ), ), Text( - isSync ? '已同步' : '待同步', + isSync ? context.t('merchant.synced') : context.t('merchant.pendingSyncLabel'), style: AppTypography.caption.copyWith( color: isSync ? AppColors.success : AppColors.warning, ), @@ -560,7 +561,7 @@ class StoreDashboardPage extends StatelessWidget { icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20), onPressed: () => Navigator.of(context).pop(), ), - title: const Text('门店数据'), + title: Text(context.t('merchant.storeData')), ), body: SingleChildScrollView( padding: AppSpacing.pagePadding, @@ -572,15 +573,15 @@ class StoreDashboardPage extends StatelessWidget { // Today Stats Row( children: [ - _statCard('今日核销', '23笔', Icons.check_circle_rounded, AppColors.success), + _statCard(context.t('merchant.todayRedeem'), '23笔', Icons.check_circle_rounded, AppColors.success), const SizedBox(width: 12), - _statCard('核销金额', '\$1,456', Icons.attach_money_rounded, AppColors.primary), + _statCard(context.t('merchant.redeemAmount'), '\$1,456', Icons.attach_money_rounded, AppColors.primary), ], ), const SizedBox(height: 24), // Weekly Trend (placeholder) - Text('本周趋势', style: AppTypography.h3), + Text(context.t('merchant.weekTrend'), style: AppTypography.h3), const SizedBox(height: 12), Container( height: 200, @@ -597,7 +598,7 @@ class StoreDashboardPage extends StatelessWidget { const SizedBox(height: 24), // Staff Ranking - Text('核销员排行', style: AppTypography.h3), + Text(context.t('merchant.operatorRank'), style: AppTypography.h3), const SizedBox(height: 12), ...List.generate(3, (index) { final names = ['张三', '李四', '王五']; diff --git a/frontend/genex-mobile/lib/features/message/presentation/pages/message_detail_page.dart b/frontend/genex-mobile/lib/features/message/presentation/pages/message_detail_page.dart index 1815d49..ab8a9dd 100644 --- a/frontend/genex-mobile/lib/features/message/presentation/pages/message_detail_page.dart +++ b/frontend/genex-mobile/lib/features/message/presentation/pages/message_detail_page.dart @@ -2,25 +2,27 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 消息详情页面 /// /// 查看单条通知的详细内容 /// 类型:交易通知、到期提醒、系统通知、活动推送 class MessageDetailPage extends StatelessWidget { - final String title; + final String? title; final String type; const MessageDetailPage({ super.key, - this.title = '交易成功通知', + this.title, this.type = 'transaction', }); @override Widget build(BuildContext context) { + final resolvedTitle = title ?? context.t('message.tradeSuccess'); return Scaffold( - appBar: AppBar(title: const Text('消息详情')), + appBar: AppBar(title: Text(context.t('message.detailTitle'))), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( @@ -45,14 +47,14 @@ class MessageDetailPage extends StatelessWidget { color: _typeColor.withValues(alpha: 0.1), borderRadius: AppSpacing.borderRadiusFull, ), - child: Text(_typeLabel, style: TextStyle(fontSize: 11, color: _typeColor, fontWeight: FontWeight.w600)), + child: Text(_getTypeLabel(context), style: TextStyle(fontSize: 11, color: _typeColor, fontWeight: FontWeight.w600)), ), ], ), const SizedBox(height: 16), // Title - Text(title, style: AppTypography.h1), + Text(resolvedTitle, style: AppTypography.h1), const SizedBox(height: 8), Text('2026年2月10日 14:32', style: AppTypography.bodySmall), const SizedBox(height: 24), @@ -65,19 +67,19 @@ class MessageDetailPage extends StatelessWidget { borderRadius: AppSpacing.borderRadiusMd, border: Border.all(color: AppColors.borderLight), ), - child: const Column( + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + const Text( '您成功购买了 星巴克 \$25 礼品卡,支付金额 \$21.25。', style: TextStyle(fontSize: 15, height: 1.6), ), - SizedBox(height: 16), - _DetailRow('券名称', '星巴克 \$25 礼品卡'), - _DetailRow('面值', '\$25.00'), - _DetailRow('支付金额', '\$21.25'), - _DetailRow('订单号', 'GNX20260210001'), - _DetailRow('支付方式', 'Visa •••• 4242'), + const SizedBox(height: 16), + _DetailRow(context.t('message.couponName'), '星巴克 \$25 礼品卡'), + _DetailRow(context.t('message.faceValue'), '\$25.00'), + _DetailRow(context.t('message.payAmount'), '\$21.25'), + _DetailRow(context.t('message.orderNo'), 'GNX20260210001'), + _DetailRow(context.t('message.payMethod'), 'Visa •••• 4242'), ], ), ), @@ -88,7 +90,7 @@ class MessageDetailPage extends StatelessWidget { width: double.infinity, child: OutlinedButton( onPressed: () {}, - child: const Text('查看券详情'), + child: Text(context.t('message.viewCouponDetail')), ), ), ], @@ -115,12 +117,12 @@ class MessageDetailPage extends StatelessWidget { } } - String get _typeLabel { + String _getTypeLabel(BuildContext context) { switch (type) { - case 'transaction': return '交易通知'; - case 'expiry': return '到期提醒'; - case 'system': return '系统通知'; - default: return '活动推送'; + case 'transaction': return context.t('message.tradeNotify'); + case 'expiry': return context.t('message.expiryRemind'); + case 'system': return context.t('message.systemNotify'); + default: return context.t('message.promoNotify'); } } } diff --git a/frontend/genex-mobile/lib/features/message/presentation/pages/message_page.dart b/frontend/genex-mobile/lib/features/message/presentation/pages/message_page.dart index e01960f..822b625 100644 --- a/frontend/genex-mobile/lib/features/message/presentation/pages/message_page.dart +++ b/frontend/genex-mobile/lib/features/message/presentation/pages/message_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/empty_state.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A8. 消息模块 /// @@ -35,22 +36,22 @@ class _MessagePageState extends State Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('消息'), + title: Text(context.t('message.title')), actions: [ TextButton( onPressed: () {}, - child: Text('全部已读', style: AppTypography.labelSmall.copyWith( + child: Text(context.t('message.markAllRead'), style: AppTypography.labelSmall.copyWith( color: AppColors.primary, )), ), ], bottom: TabBar( controller: _tabController, - tabs: const [ - Tab(text: '全部'), - Tab(text: '交易'), - Tab(text: '到期'), - Tab(text: '公告'), + tabs: [ + Tab(text: context.t('common.all')), + Tab(text: context.t('message.tabTrade')), + Tab(text: context.t('message.tabExpiry')), + Tab(text: context.t('message.tabAnnouncement')), ], ), ), diff --git a/frontend/genex-mobile/lib/features/profile/presentation/pages/kyc_page.dart b/frontend/genex-mobile/lib/features/profile/presentation/pages/kyc_page.dart index 56236ba..c485b1f 100644 --- a/frontend/genex-mobile/lib/features/profile/presentation/pages/kyc_page.dart +++ b/frontend/genex-mobile/lib/features/profile/presentation/pages/kyc_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// KYC认证页面 /// @@ -13,34 +14,37 @@ class KycPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('身份认证')), + appBar: AppBar(title: Text(context.t('kyc.title'))), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( children: [ // Current Level - _buildCurrentLevel(), + _buildCurrentLevel(context), const SizedBox(height: 24), // KYC Levels _buildLevel( - 'L1 基础认证', - '手机号 + 邮箱验证', - ['每日购买限额 \$500', '可购买券、出示核销'], + context, + context.t('kyc.l1Title'), + context.t('kyc.l1Desc'), + [context.t('kyc.l1Limit'), context.t('kyc.l1Feature')], true, AppColors.success, ), _buildLevel( - 'L2 身份认证', - '身份证/护照验证', - ['每日购买限额 \$5,000', '解锁二级市场交易、P2P转赠'], + context, + context.t('kyc.l2Title'), + context.t('kyc.l2Desc'), + [context.t('kyc.l2Limit'), context.t('kyc.l2Feature')], false, AppColors.info, ), _buildLevel( - 'L3 高级认证', - '视频面审 + 地址证明', - ['无限额', '解锁大额交易、提现无限制'], + context, + context.t('kyc.l3Title'), + context.t('kyc.l3Desc'), + [context.t('kyc.l3Limit'), context.t('kyc.l3Feature')], false, AppColors.primary, ), @@ -50,7 +54,7 @@ class KycPage extends StatelessWidget { ); } - Widget _buildCurrentLevel() { + Widget _buildCurrentLevel(BuildContext context) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( @@ -73,11 +77,11 @@ class KycPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('当前认证等级', style: AppTypography.bodySmall.copyWith(color: Colors.white70)), + Text(context.t('kyc.currentLevel'), style: AppTypography.bodySmall.copyWith(color: Colors.white70)), const SizedBox(height: 4), - Text('L1 基础认证', style: AppTypography.h1.copyWith(color: Colors.white)), + Text(context.t('kyc.l1Title'), style: AppTypography.h1.copyWith(color: Colors.white)), const SizedBox(height: 4), - Text('每日购买限额 \$500', style: AppTypography.bodySmall.copyWith(color: Colors.white60)), + Text(context.t('kyc.l1Limit'), style: AppTypography.bodySmall.copyWith(color: Colors.white60)), ], ), ), @@ -87,6 +91,7 @@ class KycPage extends StatelessWidget { } Widget _buildLevel( + BuildContext context, String title, String requirement, List benefits, @@ -136,7 +141,7 @@ class KycPage extends StatelessWidget { color: AppColors.successLight, borderRadius: AppSpacing.borderRadiusFull, ), - child: Text('已完成', style: AppTypography.caption.copyWith(color: AppColors.success)), + child: Text(context.t('kyc.completed'), style: AppTypography.caption.copyWith(color: AppColors.success)), ) else ElevatedButton( @@ -145,7 +150,7 @@ class KycPage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), minimumSize: Size.zero, ), - child: const Text('去认证', style: TextStyle(fontSize: 13)), + child: Text(context.t('kyc.goVerify'), style: const TextStyle(fontSize: 13)), ), ], ), diff --git a/frontend/genex-mobile/lib/features/profile/presentation/pages/payment_management_page.dart b/frontend/genex-mobile/lib/features/profile/presentation/pages/payment_management_page.dart index 00e8daa..11eec72 100644 --- a/frontend/genex-mobile/lib/features/profile/presentation/pages/payment_management_page.dart +++ b/frontend/genex-mobile/lib/features/profile/presentation/pages/payment_management_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 支付管理页面 /// @@ -12,11 +13,11 @@ class PaymentManagementPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('支付管理')), + appBar: AppBar(title: Text(context.t('payManage.title'))), body: ListView( padding: const EdgeInsets.all(20), children: [ - Text('我的银行卡', style: AppTypography.h3), + Text(context.t('payManage.myCards'), style: AppTypography.h3), const SizedBox(height: 12), // Card List @@ -32,19 +33,19 @@ class PaymentManagementPage extends StatelessWidget { border: Border.all(color: AppColors.border, style: BorderStyle.solid), borderRadius: AppSpacing.borderRadiusMd, ), - child: const Row( + child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(Icons.add_circle_outline_rounded, color: AppColors.primary), - SizedBox(width: 8), - Text('添加新银行卡', style: TextStyle(color: AppColors.primary, fontWeight: FontWeight.w600)), + const Icon(Icons.add_circle_outline_rounded, color: AppColors.primary), + const SizedBox(width: 8), + Text(context.t('payManage.addCard'), style: const TextStyle(color: AppColors.primary, fontWeight: FontWeight.w600)), ], ), ), const SizedBox(height: 32), // Bank Account - Text('银行账户(提现用)', style: AppTypography.h3), + Text(context.t('payManage.bankAccount'), style: AppTypography.h3), const SizedBox(height: 12), Container( padding: AppSpacing.cardPadding, @@ -62,7 +63,7 @@ class PaymentManagementPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Bank of America', style: AppTypography.labelMedium), - Text('•••• 6789 · 储蓄账户', style: AppTypography.caption), + Text('•••• 6789 · ${context.t('withdraw.savingsAccount')}', style: AppTypography.caption), ], ), ), @@ -73,11 +74,11 @@ class PaymentManagementPage extends StatelessWidget { const SizedBox(height: 32), // Payment Security - Text('支付安全', style: AppTypography.h3), + Text(context.t('payManage.paymentSecurity'), style: AppTypography.h3), const SizedBox(height: 12), - _buildSettingTile('支付密码', '已设置', Icons.password_rounded), - _buildSettingTile('指纹/面容支付', '已开启', Icons.fingerprint_rounded), - _buildSettingTile('免密支付', '单笔≤\$10', Icons.flash_on_rounded), + _buildSettingTile(context.t('payManage.paymentPassword'), context.t('payManage.passwordSet'), Icons.password_rounded), + _buildSettingTile(context.t('payManage.biometricPay'), context.t('payManage.biometricEnabled'), Icons.fingerprint_rounded), + _buildSettingTile(context.t('payManage.noPasswordPay'), context.t('payManage.noPasswordLimit'), Icons.flash_on_rounded), ], ), ); diff --git a/frontend/genex-mobile/lib/features/profile/presentation/pages/pro_mode_page.dart b/frontend/genex-mobile/lib/features/profile/presentation/pages/pro_mode_page.dart index b5b3971..7d78572 100644 --- a/frontend/genex-mobile/lib/features/profile/presentation/pages/pro_mode_page.dart +++ b/frontend/genex-mobile/lib/features/profile/presentation/pages/pro_mode_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 高级模式(Pro Mode)设置页 /// @@ -27,7 +28,7 @@ class _ProModePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('高级模式')), + appBar: AppBar(title: Text(context.t('profile.proMode'))), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( @@ -90,7 +91,7 @@ class _ProModePageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '高级模式 (Pro)', + '${context.t('profile.proMode')} (Pro)', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w700, @@ -99,7 +100,7 @@ class _ProModePageState extends State { ), const SizedBox(height: 2), Text( - '开启后可查看链上信息和连接外部钱包', + context.t('proMode.toggleDesc'), style: TextStyle( fontSize: 12, color: _proModeEnabled ? Colors.white70 : AppColors.textSecondary, @@ -124,9 +125,9 @@ class _ProModePageState extends State { color: Colors.white.withValues(alpha: 0.15), borderRadius: AppSpacing.borderRadiusFull, ), - child: const Text( - '需要 KYC L2 及以上认证', - style: TextStyle(fontSize: 11, color: Colors.white70), + child: Text( + context.t('proMode.requireKycL2'), + style: const TextStyle(fontSize: 11, color: Colors.white70), ), ), ], @@ -159,7 +160,7 @@ class _ProModePageState extends State { color: AppColors.successLight, borderRadius: AppSpacing.borderRadiusFull, ), - child: const Text('已连接', style: TextStyle(fontSize: 11, color: AppColors.success, fontWeight: FontWeight.w600)), + child: Text(context.t('proMode.connected'), style: const TextStyle(fontSize: 11, color: AppColors.success, fontWeight: FontWeight.w600)), ), ], ), @@ -186,7 +187,7 @@ class _ProModePageState extends State { ), TextButton( onPressed: () => setState(() => _walletConnected = false), - child: const Text('断开', style: TextStyle(color: AppColors.error, fontSize: 13)), + child: Text(context.t('proMode.disconnect'), style: const TextStyle(color: AppColors.error, fontSize: 13)), ), ], ), @@ -197,12 +198,12 @@ class _ProModePageState extends State { child: OutlinedButton.icon( onPressed: () => setState(() => _walletConnected = true), icon: const Icon(Icons.link_rounded, size: 18), - label: const Text('连接外部钱包'), + label: Text(context.t('proMode.connectWallet')), ), ), const SizedBox(height: 8), Text( - '连接外部钱包后可将平台资产提取至自有地址', + context.t('proMode.walletDesc'), style: AppTypography.caption, ), ], @@ -221,8 +222,8 @@ class _ProModePageState extends State { child: Column( children: [ SwitchListTile( - title: Text('显示链上地址', style: AppTypography.labelMedium), - subtitle: Text('在券详情中展示合约地址', style: AppTypography.caption), + title: Text(context.t('proMode.showChainAddress'), style: AppTypography.labelMedium), + subtitle: Text(context.t('proMode.showChainAddressDesc'), style: AppTypography.caption), value: _showChainAddress, onChanged: (v) => setState(() => _showChainAddress = v), activeColor: AppColors.primary, @@ -230,8 +231,8 @@ class _ProModePageState extends State { ), const Divider(height: 1), SwitchListTile( - title: Text('显示交易Hash', style: AppTypography.labelMedium), - subtitle: Text('在交易记录中展示链上Hash', style: AppTypography.caption), + title: Text(context.t('proMode.showTxHash'), style: AppTypography.labelMedium), + subtitle: Text(context.t('proMode.showTxHashDesc'), style: AppTypography.caption), value: _showTxHash, onChanged: (v) => setState(() => _showTxHash = v), activeColor: AppColors.primary, @@ -257,18 +258,18 @@ class _ProModePageState extends State { children: [ const Icon(Icons.explore_rounded, color: AppColors.primary, size: 20), const SizedBox(width: 8), - Text('交易浏览器', style: AppTypography.labelLarge), + Text(context.t('proMode.txExplorer'), style: AppTypography.labelLarge), ], ), const SizedBox(height: 12), - _buildTxItem('购买 星巴克 \$25 礼品卡', '0xabc1...def3', '已确认', AppColors.success), - _buildTxItem('出售 Amazon \$100 券', '0x789a...bc12', '已确认', AppColors.success), - _buildTxItem('转赠给 Alice', '0xdef4...5678', '确认中', AppColors.warning), + _buildTxItem(context.t('proMode.txBuyExample'), '0xabc1...def3', context.t('proMode.confirmed'), AppColors.success), + _buildTxItem(context.t('proMode.txSellExample'), '0x789a...bc12', context.t('proMode.confirmed'), AppColors.success), + _buildTxItem('${context.t('txRecords.transferTo')} Alice', '0xdef4...5678', context.t('proMode.confirming'), AppColors.warning), const SizedBox(height: 8), Center( child: TextButton( onPressed: () {}, - child: const Text('查看全部链上交易'), + child: Text(context.t('proMode.viewAllTx')), ), ), ], @@ -318,19 +319,19 @@ class _ProModePageState extends State { children: [ const Icon(Icons.token_rounded, color: AppColors.primary, size: 20), const SizedBox(width: 8), - Text('链上资产', style: AppTypography.labelLarge), + Text(context.t('proMode.chainAssets'), style: AppTypography.labelLarge), ], ), const SizedBox(height: 12), - _buildAssetRow('平台托管钱包', '0x1234...abcd', '5 张券'), - if (_walletConnected) _buildAssetRow('外部钱包 (MetaMask)', '0x7a3b...c4f2', '0 张券'), + _buildAssetRow(context.t('proMode.custodialWallet'), '0x1234...abcd', context.t('proMode.couponCount5')), + if (_walletConnected) _buildAssetRow(context.t('proMode.externalWallet'), '0x7a3b...c4f2', context.t('proMode.couponCount0')), const SizedBox(height: 12), if (_walletConnected) SizedBox( width: double.infinity, child: OutlinedButton( onPressed: () {}, - child: const Text('提取至外部钱包'), + child: Text(context.t('proMode.extractToWallet')), ), ), ], @@ -373,16 +374,16 @@ class _ProModePageState extends State { children: [ const Icon(Icons.swap_horiz_rounded, color: AppColors.primary, size: 20), const SizedBox(width: 8), - Text('交易轨道', style: AppTypography.labelLarge), + Text(context.t('proMode.tradeTrack'), style: AppTypography.labelLarge), ], ), const SizedBox(height: 12), - _buildTrackOption('Utility Track', '券有效期≤12个月,无需证券牌照', AppColors.success, true), + _buildTrackOption('Utility Track', context.t('proMode.utilityTrackDesc'), AppColors.success, true), const SizedBox(height: 8), - _buildTrackOption('Securities Track', '长期投资型券产品(即将推出)', AppColors.warning, false), + _buildTrackOption('Securities Track', context.t('proMode.securitiesTrackDesc'), AppColors.warning, false), const SizedBox(height: 8), Text( - '当前MVP版本仅支持Utility Track', + context.t('proMode.mvpNote'), style: AppTypography.caption.copyWith(color: AppColors.textTertiary), ), ], @@ -419,7 +420,7 @@ class _ProModePageState extends State { ), ), if (active) Icon(Icons.check_circle_rounded, color: color, size: 20), - if (!active) Text('敬请期待', style: AppTypography.caption.copyWith(color: AppColors.textTertiary)), + if (!active) Text(context.t('proMode.comingSoon'), style: AppTypography.caption.copyWith(color: AppColors.textTertiary)), ], ), ); @@ -438,15 +439,10 @@ class _ProModePageState extends State { children: [ const Icon(Icons.info_outline_rounded, color: AppColors.textTertiary, size: 40), const SizedBox(height: 12), - Text('什么是高级模式?', style: AppTypography.h3), + Text(context.t('proMode.whatIsTitle'), style: AppTypography.h3), const SizedBox(height: 8), Text( - '高级模式面向有区块链经验的用户,开启后可以:\n' - '• 连接外部钱包(MetaMask等)\n' - '• 查看链上地址和交易Hash\n' - '• 将资产提取至自有钱包\n' - '• 查看底层链上数据\n\n' - '需要完成 KYC L2 认证后方可开启。', + context.t('proMode.whatIsDesc'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary, height: 1.6), ), ], diff --git a/frontend/genex-mobile/lib/features/profile/presentation/pages/profile_page.dart b/frontend/genex-mobile/lib/features/profile/presentation/pages/profile_page.dart index aa90437..a848f0d 100644 --- a/frontend/genex-mobile/lib/features/profile/presentation/pages/profile_page.dart +++ b/frontend/genex-mobile/lib/features/profile/presentation/pages/profile_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/kyc_badge.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A7. 个人中心 /// @@ -20,33 +21,33 @@ class ProfilePage extends StatelessWidget { SliverToBoxAdapter(child: _buildProfileHeader(context)), // Quick Stats - SliverToBoxAdapter(child: _buildQuickStats()), + SliverToBoxAdapter(child: _buildQuickStats(context)), // Menu Sections - SliverToBoxAdapter(child: _buildMenuSection('账户', [ - _MenuItem(Icons.verified_user_outlined, 'KYC 认证', '已完成 L1 认证', true, + SliverToBoxAdapter(child: _buildMenuSection(context.t('profile.account'), [ + _MenuItem(Icons.verified_user_outlined, context.t('profile.kyc'), '${context.t('kyc.completed')} L1', true, onTap: () => Navigator.pushNamed(context, '/kyc')), - _MenuItem(Icons.credit_card_rounded, '支付管理', '已绑定 2 张卡', true, + _MenuItem(Icons.credit_card_rounded, context.t('profile.paymentManage'), '', true, onTap: () => Navigator.pushNamed(context, '/payment/manage')), - _MenuItem(Icons.account_balance_wallet_outlined, '我的余额', '\$1,234.56', true, + _MenuItem(Icons.account_balance_wallet_outlined, context.t('wallet.myBalance'), '\$1,234.56', true, onTap: () => Navigator.pushNamed(context, '/wallet')), ])), - SliverToBoxAdapter(child: _buildMenuSection('交易', [ - _MenuItem(Icons.receipt_long_rounded, '交易记录', '', true, + SliverToBoxAdapter(child: _buildMenuSection(context.t('profile.trade'), [ + _MenuItem(Icons.receipt_long_rounded, context.t('wallet.records'), '', true, onTap: () => Navigator.pushNamed(context, '/trading')), - _MenuItem(Icons.storefront_rounded, '我的挂单', '2笔出售中', true, + _MenuItem(Icons.storefront_rounded, context.t('tradingPage.pendingOrders'), context.t('status.onSale'), true, onTap: () => Navigator.pushNamed(context, '/trading')), - _MenuItem(Icons.favorite_border_rounded, '我的收藏', '', true), + _MenuItem(Icons.favorite_border_rounded, context.t('profile.myFavorites'), '', true), ])), - SliverToBoxAdapter(child: _buildMenuSection('设置', [ - _MenuItem(Icons.notifications_outlined, '通知设置', '', true), - _MenuItem(Icons.language_rounded, '语言', '简体中文', true), - _MenuItem(Icons.shield_outlined, '安全设置', '', true), - _MenuItem(Icons.tune_rounded, '高级设置', 'Pro模式', true, + SliverToBoxAdapter(child: _buildMenuSection(context.t('profile.settings'), [ + _MenuItem(Icons.notifications_outlined, context.t('settings.notifications'), '', true), + _MenuItem(Icons.language_rounded, context.t('settings.language'), context.t('profile.simplifiedChinese'), true), + _MenuItem(Icons.shield_outlined, context.t('profile.securitySettings'), '', true), + _MenuItem(Icons.tune_rounded, context.t('profile.advancedSettings'), context.t('profile.proMode'), true, onTap: () => Navigator.pushNamed(context, '/pro-mode')), - _MenuItem(Icons.info_outline_rounded, '关于 Genex', 'v1.0.0', true), + _MenuItem(Icons.info_outline_rounded, context.t('profile.aboutGenex'), 'v1.0.0', true), ])), // Logout @@ -57,7 +58,7 @@ class ProfilePage extends StatelessWidget { onPressed: () { Navigator.of(context).pushNamedAndRemoveUntil('/', (_) => false); }, - child: Text('退出登录', style: AppTypography.labelMedium.copyWith( + child: Text(context.t('settings.logout'), style: AppTypography.labelMedium.copyWith( color: AppColors.error, )), ), @@ -97,7 +98,7 @@ class ProfilePage extends StatelessWidget { children: [ Row( children: [ - Text('用户昵称', style: AppTypography.h2.copyWith(color: Colors.white)), + Text(context.t('merchant.userNickname'), style: AppTypography.h2.copyWith(color: Colors.white)), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), @@ -120,7 +121,7 @@ class ProfilePage extends StatelessWidget { ], ), const SizedBox(height: 4), - Text('信用积分: 750', style: AppTypography.bodySmall.copyWith( + Text('${context.t('profile.creditScore')}: 750', style: AppTypography.bodySmall.copyWith( color: Colors.white70, )), ], @@ -139,12 +140,12 @@ class ProfilePage extends StatelessWidget { ); } - Widget _buildQuickStats() { + Widget _buildQuickStats(BuildContext context) { final stats = [ - ('持券', '12'), - ('交易', '28'), - ('节省', '\$156'), - ('信用', '750'), + (context.t('profile.holdCoupons'), '12'), + (context.t('profile.trade'), '28'), + (context.t('profile.saved'), '\$156'), + (context.t('profile.credit'), '750'), ]; return Container( diff --git a/frontend/genex-mobile/lib/features/profile/presentation/pages/settings_page.dart b/frontend/genex-mobile/lib/features/profile/presentation/pages/settings_page.dart index d7930d9..0c28be8 100644 --- a/frontend/genex-mobile/lib/features/profile/presentation/pages/settings_page.dart +++ b/frontend/genex-mobile/lib/features/profile/presentation/pages/settings_page.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; +import '../../../../app/i18n/locale_manager.dart'; /// 设置页面 /// @@ -15,83 +17,87 @@ class SettingsPage extends StatefulWidget { } class _SettingsPageState extends State { - // 当前选中的货币 (实际应用中从持久化/状态管理读取) - _CurrencyOption _selectedCurrency = _currencyOptions[0]; // 默认 USD - String _selectedLanguage = '简体中文'; + _CurrencyOption _selectedCurrency = _currencyOptions[0]; - // 通知开关 bool _notifyTrade = true; bool _notifyExpiry = true; bool _notifyMarket = false; bool _notifyMarketing = false; + String get _currentLanguageDisplay { + final locale = LocaleManager.userLocale.value ?? + Localizations.localeOf(context); + return LocaleManager.localeDisplayName(locale); + } + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('设置')), + appBar: AppBar(title: Text(context.t('settings.title'))), body: ListView( children: [ - // Account & Security - _buildSection('账号与安全', [ - _buildTile('手机号', + _buildSection(context.t('settings.accountSecurity'), [ + _buildTile(context.t('settings.phone'), subtitle: '138****8888', icon: Icons.phone_rounded), - _buildTile('邮箱', + _buildTile(context.t('settings.email'), subtitle: 'u***@email.com', icon: Icons.email_rounded), - _buildTile('修改密码', icon: Icons.lock_rounded), - _buildTile('身份认证', - subtitle: 'L1 基础认证', + _buildTile(context.t('settings.changePassword'), + icon: Icons.lock_rounded), + _buildTile(context.t('settings.identity'), + subtitle: 'L1', icon: Icons.verified_user_rounded, onTap: () { Navigator.pushNamed(context, '/kyc'); }), ]), - // Payment - _buildSection('支付管理', [ - _buildTile('支付方式', + _buildSection(context.t('settings.paymentManage'), [ + _buildTile(context.t('settings.paymentMethod'), subtitle: 'Visa •••• 4242', icon: Icons.credit_card_rounded), - _buildTile('银行账户', + _buildTile(context.t('settings.bankAccount'), subtitle: 'BoA •••• 6789', icon: Icons.account_balance_rounded), - _buildTile('支付密码', icon: Icons.password_rounded), + _buildTile(context.t('settings.paymentPassword'), + icon: Icons.password_rounded), ]), - // Notifications - _buildSection('通知设置', [ - _buildSwitchTile('交易通知', _notifyTrade, + _buildSection(context.t('settings.notifications'), [ + _buildSwitchTile(context.t('settings.tradeNotify'), _notifyTrade, (v) => setState(() => _notifyTrade = v)), - _buildSwitchTile('到期提醒', _notifyExpiry, + _buildSwitchTile(context.t('settings.expiryRemind'), _notifyExpiry, (v) => setState(() => _notifyExpiry = v)), - _buildSwitchTile('行情变动', _notifyMarket, + _buildSwitchTile(context.t('settings.marketChange'), _notifyMarket, (v) => setState(() => _notifyMarket = v)), - _buildSwitchTile('营销推送', _notifyMarketing, + _buildSwitchTile( + context.t('settings.marketingPush'), _notifyMarketing, (v) => setState(() => _notifyMarketing = v)), ]), - // General - _buildSection('通用', [ - _buildTile('语言', - subtitle: _selectedLanguage, + _buildSection(context.t('settings.general'), [ + _buildTile(context.t('settings.language'), + subtitle: _currentLanguageDisplay, icon: Icons.language_rounded, onTap: () => _showLanguagePicker(context)), - _buildTile('货币', + _buildTile(context.t('settings.currency'), subtitle: '${_selectedCurrency.code} (${_selectedCurrency.symbol})', icon: Icons.attach_money_rounded, onTap: () => _showCurrencyPicker(context)), - _buildTile('清除缓存', icon: Icons.cleaning_services_rounded), + _buildTile(context.t('settings.clearCache'), + icon: Icons.cleaning_services_rounded), ]), - // About - _buildSection('关于', [ - _buildTile('版本', + _buildSection(context.t('settings.about'), [ + _buildTile(context.t('settings.version'), subtitle: 'v1.0.0', icon: Icons.info_outline_rounded), - _buildTile('用户协议', icon: Icons.description_rounded), - _buildTile('隐私政策', icon: Icons.privacy_tip_rounded), - _buildTile('帮助中心', icon: Icons.help_outline_rounded), + _buildTile(context.t('settings.userAgreement'), + icon: Icons.description_rounded), + _buildTile(context.t('settings.privacyPolicy'), + icon: Icons.privacy_tip_rounded), + _buildTile(context.t('settings.helpCenter'), + icon: Icons.help_outline_rounded), ]), - // Logout Padding( padding: const EdgeInsets.all(20), child: OutlinedButton( @@ -104,7 +110,7 @@ class _SettingsPageState extends State { side: const BorderSide(color: AppColors.error), minimumSize: const Size(double.infinity, 48), ), - child: const Text('退出登录'), + child: Text(context.t('settings.logout')), ), ), ], @@ -112,9 +118,6 @@ class _SettingsPageState extends State { ); } - // ============================================================ - // Currency Picker - // ============================================================ void _showCurrencyPicker(BuildContext context) { showModalBottomSheet( context: context, @@ -127,7 +130,6 @@ class _SettingsPageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - // Handle Container( margin: const EdgeInsets.only(top: 12), width: 36, @@ -137,28 +139,29 @@ class _SettingsPageState extends State { borderRadius: AppSpacing.borderRadiusFull, ), ), - // Title Padding( padding: const EdgeInsets.all(16), - child: Text('选择计价货币', style: AppTypography.h3), + child: Text(context.t('settings.selectCurrency'), + style: AppTypography.h3), ), const Divider(height: 1), - // Currency list ..._currencyOptions.map((option) { final isSelected = _selectedCurrency.code == option.code; return ListTile( - leading: Text(option.flag, style: const TextStyle(fontSize: 24)), + leading: + Text(option.flag, style: const TextStyle(fontSize: 24)), title: Text( '${option.name} (${option.code})', style: AppTypography.bodyMedium.copyWith( - fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, + fontWeight: + isSelected ? FontWeight.w600 : FontWeight.w400, color: isSelected ? AppColors.primary : AppColors.textPrimary, ), ), subtitle: Text( - '符号: ${option.symbol}', + '${context.t('settings.currencySymbol')}: ${option.symbol}', style: AppTypography.caption, ), trailing: isSelected @@ -171,11 +174,10 @@ class _SettingsPageState extends State { }, ); }), - // Note Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 24), child: Text( - '此设置影响交易页面中所有价格的计价货币显示', + context.t('settings.currencyNote'), style: AppTypography.caption.copyWith( color: AppColors.textTertiary, ), @@ -188,17 +190,17 @@ class _SettingsPageState extends State { ); } - // ============================================================ - // Language Picker - // ============================================================ void _showLanguagePicker(BuildContext context) { final languages = [ - ('简体中文', 'zh-CN', '🇨🇳'), - ('繁體中文', 'zh-TW', '🇹🇼'), - ('English', 'en', '🇺🇸'), - ('日本語', 'ja', '🇯🇵'), + ('简体中文', const Locale('zh', 'CN'), '🇨🇳'), + ('繁體中文', const Locale('zh', 'TW'), '🇹🇼'), + ('English', const Locale('en'), '🇺🇸'), + ('日本語', const Locale('ja'), '🇯🇵'), ]; + final currentLocale = LocaleManager.userLocale.value ?? + Localizations.localeOf(context); + showModalBottomSheet( context: context, backgroundColor: Colors.transparent, @@ -221,18 +223,24 @@ class _SettingsPageState extends State { ), Padding( padding: const EdgeInsets.all(16), - child: Text('选择语言', style: AppTypography.h3), + child: Text(context.t('settings.selectLanguage'), + style: AppTypography.h3), ), const Divider(height: 1), ...languages.map((lang) { - final (name, _, flag) = lang; - final isSelected = _selectedLanguage == name; + final (name, locale, flag) = lang; + final isSelected = + currentLocale.languageCode == locale.languageCode && + (locale.countryCode == null || + currentLocale.countryCode == locale.countryCode); return ListTile( - leading: Text(flag, style: const TextStyle(fontSize: 24)), + leading: + Text(flag, style: const TextStyle(fontSize: 24)), title: Text( name, style: AppTypography.bodyMedium.copyWith( - fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, + fontWeight: + isSelected ? FontWeight.w600 : FontWeight.w400, color: isSelected ? AppColors.primary : AppColors.textPrimary, @@ -243,7 +251,8 @@ class _SettingsPageState extends State { color: AppColors.primary, size: 22) : null, onTap: () { - setState(() => _selectedLanguage = name); + LocaleManager.userLocale.value = locale; + setState(() {}); Navigator.pop(context); }, ); @@ -255,9 +264,6 @@ class _SettingsPageState extends State { ); } - // ============================================================ - // Shared Builders - // ============================================================ Widget _buildSection(String title, List children) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -301,9 +307,6 @@ class _SettingsPageState extends State { } } -// ============================================================ -// Currency Options -// ============================================================ class _CurrencyOption { final String code; final String symbol; @@ -319,10 +322,10 @@ class _CurrencyOption { } const _currencyOptions = [ - _CurrencyOption(code: 'USD', symbol: '\$', name: '美元', flag: '🇺🇸'), - _CurrencyOption(code: 'CNY', symbol: '¥', name: '人民币', flag: '🇨🇳'), - _CurrencyOption(code: 'EUR', symbol: '€', name: '欧元', flag: '🇪🇺'), - _CurrencyOption(code: 'GBP', symbol: '£', name: '英镑', flag: '🇬🇧'), - _CurrencyOption(code: 'JPY', symbol: '¥', name: '日元', flag: '🇯🇵'), - _CurrencyOption(code: 'HKD', symbol: 'HK\$', name: '港币', flag: '🇭🇰'), + _CurrencyOption(code: 'USD', symbol: '\$', name: 'USD', flag: '🇺🇸'), + _CurrencyOption(code: 'CNY', symbol: '¥', name: 'CNY', flag: '🇨🇳'), + _CurrencyOption(code: 'EUR', symbol: '€', name: 'EUR', flag: '🇪🇺'), + _CurrencyOption(code: 'GBP', symbol: '£', name: 'GBP', flag: '🇬🇧'), + _CurrencyOption(code: 'JPY', symbol: '¥', name: 'JPY', flag: '🇯🇵'), + _CurrencyOption(code: 'HKD', symbol: 'HK\$', name: 'HKD', flag: '🇭🇰'), ]; diff --git a/frontend/genex-mobile/lib/features/trading/presentation/pages/sell_order_page.dart b/frontend/genex-mobile/lib/features/trading/presentation/pages/sell_order_page.dart index e43f91c..e63c8c2 100644 --- a/frontend/genex-mobile/lib/features/trading/presentation/pages/sell_order_page.dart +++ b/frontend/genex-mobile/lib/features/trading/presentation/pages/sell_order_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A10. 挂单出售页面 /// @@ -26,7 +27,7 @@ class _SellOrderPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('挂单出售')), + appBar: AppBar(title: Text(context.t('sellOrder.title'))), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( @@ -58,7 +59,7 @@ class _SellOrderPageState extends State { children: [ Text('星巴克 \$25 礼品卡', style: AppTypography.labelLarge), const SizedBox(height: 4), - Text('面值 \$$_faceValue · 信用 AAA', style: AppTypography.bodySmall), + Text('${context.t('sellOrder.faceValue')} \$$_faceValue · ${context.t('sellOrder.credit')} AAA', style: AppTypography.bodySmall), ], ), ), @@ -68,14 +69,14 @@ class _SellOrderPageState extends State { const SizedBox(height: 24), // Price Input - Text('设定售价', style: AppTypography.h3), + Text(context.t('sellOrder.setPrice'), style: AppTypography.h3), const SizedBox(height: 12), TextField( controller: _priceController, keyboardType: const TextInputType.numberWithOptions(decimal: true), - decoration: const InputDecoration( + decoration: InputDecoration( prefixText: '\$ ', - labelText: '售价', + labelText: context.t('sellOrder.price'), suffixText: 'USD', ), style: AppTypography.priceLarge, @@ -96,7 +97,7 @@ class _SellOrderPageState extends State { const SizedBox(width: 8), Expanded( child: Text( - 'AI建议售价:\$22.50(9折),此价格成交概率最高', + '${context.t('sellOrder.aiSuggest')}: \$22.50, ${context.t('sellOrder.bestDealRate')}', style: AppTypography.caption.copyWith(color: AppColors.primary), ), ), @@ -115,11 +116,11 @@ class _SellOrderPageState extends State { ), child: Column( children: [ - _buildRow('售价', '\$${_price.toStringAsFixed(2)}'), - _buildRow('折扣率', '${_discount.toStringAsFixed(1)}%'), - _buildRow('平台手续费 (1.5%)', '-\$${_fee.toStringAsFixed(2)}'), + _buildRow(context.t('sellOrder.price'), '\$${_price.toStringAsFixed(2)}'), + _buildRow(context.t('sellOrder.discountRate'), '${_discount.toStringAsFixed(1)}%'), + _buildRow(context.t('sellOrder.platformFee'), '-\$${_fee.toStringAsFixed(2)}'), const Divider(height: 24), - _buildRow('预计到账', '\$${_receive.toStringAsFixed(2)}', isBold: true), + _buildRow(context.t('sellOrder.estimatedReceive'), '\$${_receive.toStringAsFixed(2)}', isBold: true), ], ), ), @@ -138,7 +139,7 @@ class _SellOrderPageState extends State { const SizedBox(width: 8), Expanded( child: Text( - '当前市场均价 \$22.80 · 最近24小时成交 42 笔', + '${context.t('sellOrder.marketAvg')} \$22.80 · ${context.t('sellOrder.recent24hTrades')} 42 ${context.t('sellOrder.tradesUnit')}', style: AppTypography.caption.copyWith(color: AppColors.info), ), ), @@ -154,7 +155,7 @@ class _SellOrderPageState extends State { height: AppSpacing.buttonHeight, child: ElevatedButton( onPressed: () => _confirmSell(context), - child: const Text('确认挂单'), + child: Text(context.t('sellOrder.confirmList')), ), ), ), @@ -178,10 +179,10 @@ class _SellOrderPageState extends State { showDialog( context: context, builder: (ctx) => AlertDialog( - title: const Text('挂单成功'), - content: const Text('您的券已挂到市场,当有买家下单时将自动成交。'), + title: Text(context.t('sellOrder.success')), + content: Text(context.t('sellOrder.successHint')), actions: [ - TextButton(onPressed: () { Navigator.pop(ctx); Navigator.pop(context); }, child: const Text('确定')), + TextButton(onPressed: () { Navigator.pop(ctx); Navigator.pop(context); }, child: Text(context.t('sellOrder.ok'))), ], ), ); diff --git a/frontend/genex-mobile/lib/features/trading/presentation/pages/trading_detail_page.dart b/frontend/genex-mobile/lib/features/trading/presentation/pages/trading_detail_page.dart index 197ed21..2b4d58b 100644 --- a/frontend/genex-mobile/lib/features/trading/presentation/pages/trading_detail_page.dart +++ b/frontend/genex-mobile/lib/features/trading/presentation/pages/trading_detail_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 券交易详情页 - 币安风格 /// @@ -178,7 +179,7 @@ class _TradingDetailPageState extends State ), const SizedBox(height: 2), Text( - '面值 $_currencySymbol${_coupon.faceValue.toStringAsFixed(0)} · 到期 ${_coupon.expiryDate}', + '${context.t('market.faceValue')} $_currencySymbol${_coupon.faceValue.toStringAsFixed(0)} · ${context.t('market.expiryDate')} ${_coupon.expiryDate}', style: AppTypography.caption, ), ], @@ -215,7 +216,7 @@ class _TradingDetailPageState extends State ), const SizedBox(width: 8), Text( - '折扣 ${(_coupon.currentPrice / _coupon.faceValue * 10).toStringAsFixed(1)}折', + '${context.t('trading.discountLabel')} ${(_coupon.currentPrice / _coupon.faceValue * 10).toStringAsFixed(1)}${context.t('market.discountSuffix')}', style: AppTypography.bodySmall.copyWith( color: AppColors.textTertiary, ), @@ -244,10 +245,10 @@ class _TradingDetailPageState extends State // 24h OHLC stats Row( children: [ - _buildOhlcStat('24h高', '${_currencySymbol}21.75', AppColors.error), - _buildOhlcStat('24h低', '${_currencySymbol}20.85', AppColors.success), - _buildOhlcStat('开盘', '${_currencySymbol}20.87', AppColors.textPrimary), - _buildOhlcStat('24h量', '342.5K', AppColors.textPrimary), + _buildOhlcStat(context.t('trading.high24h'), '${_currencySymbol}21.75', AppColors.error), + _buildOhlcStat(context.t('trading.low24h'), '${_currencySymbol}20.85', AppColors.success), + _buildOhlcStat(context.t('trading.open'), '${_currencySymbol}20.87', AppColors.textPrimary), + _buildOhlcStat(context.t('trading.vol24h'), '342.5K', AppColors.textPrimary), ], ), ], @@ -354,7 +355,7 @@ class _TradingDetailPageState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('交易深度', style: AppTypography.h3), + Text(context.t('trading.depth'), style: AppTypography.h3), const SizedBox(height: 12), Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -373,7 +374,7 @@ class _TradingDetailPageState extends State Widget _buildOrderBookSide({required bool isAsk}) { final color = isAsk ? AppColors.error : AppColors.success; - final label = isAsk ? '卖盘' : '买盘'; + final label = isAsk ? context.t('trading.askSide') : context.t('trading.bidSide'); final prices = isAsk ? [21.45, 21.42, 21.40, 21.38, 21.35] : [21.28, 21.25, 21.22, 21.20, 21.18]; @@ -390,7 +391,7 @@ class _TradingDetailPageState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: AppTypography.caption.copyWith(color: color)), - Text('数量(张)', style: AppTypography.caption), + Text(context.t('trading.quantitySheets'), style: AppTypography.caption), ], ), const SizedBox(height: 6), @@ -454,7 +455,7 @@ class _TradingDetailPageState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('下单', style: AppTypography.h3), + Text(context.t('trading.placeOrder'), style: AppTypography.h3), const SizedBox(height: 12), // Buy/Sell toggle @@ -476,7 +477,7 @@ class _TradingDetailPageState extends State ), alignment: Alignment.center, child: Text( - '买入', + context.t('trading.buy'), style: AppTypography.labelMedium.copyWith( color: _isBuy ? Colors.white : AppColors.textTertiary, fontWeight: FontWeight.w600, @@ -497,7 +498,7 @@ class _TradingDetailPageState extends State ), alignment: Alignment.center, child: Text( - '卖出', + context.t('trading.sell'), style: AppTypography.labelMedium.copyWith( color: !_isBuy ? Colors.white : AppColors.textTertiary, @@ -516,9 +517,9 @@ class _TradingDetailPageState extends State // Order type selector Row( children: [ - _buildOrderTypeChip('限价单', 'limit'), + _buildOrderTypeChip(context.t('trading.limitOrder'), 'limit'), const SizedBox(width: 8), - _buildOrderTypeChip('市价单', 'market'), + _buildOrderTypeChip(context.t('trading.marketOrder'), 'market'), ], ), @@ -526,12 +527,12 @@ class _TradingDetailPageState extends State // Price input (hidden for market orders) if (_orderType == 'limit') ...[ - _buildInputField('价格', '21.30', _currencySymbol), + _buildInputField(context.t('trading.price'), '21.30', _currencySymbol), const SizedBox(height: 8), ], // Amount input - _buildInputField('数量', '', '张'), + _buildInputField(context.t('trading.quantity'), '', context.t('trading.sheetsUnit')), const SizedBox(height: 8), @@ -554,11 +555,11 @@ class _TradingDetailPageState extends State Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('可用', style: AppTypography.caption), + Text(context.t('trading.available'), style: AppTypography.caption), Text( _isBuy ? '${_currencySymbol}1,234.56' - : '3 张 ${_coupon.couponName}', + : '3 ${context.t('trading.sheetsUnit')} ${_coupon.couponName}', style: AppTypography.caption.copyWith( color: AppColors.textPrimary, fontWeight: FontWeight.w500, @@ -584,7 +585,7 @@ class _TradingDetailPageState extends State ), ), child: Text( - _isBuy ? '买入 ${_coupon.couponName}' : '卖出 ${_coupon.couponName}', + _isBuy ? '${context.t('trading.buy')} ${_coupon.couponName}' : '${context.t('trading.sell')} ${_coupon.couponName}', style: AppTypography.labelLarge.copyWith(color: Colors.white), ), ), @@ -694,10 +695,10 @@ class _TradingDetailPageState extends State Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('当前委托', style: AppTypography.h3), + Text(context.t('trading.currentOrders'), style: AppTypography.h3), GestureDetector( onTap: () {}, - child: Text('历史委托', + child: Text(context.t('trading.historyOrders'), style: AppTypography.labelSmall .copyWith(color: AppColors.primary)), ), @@ -707,10 +708,10 @@ class _TradingDetailPageState extends State // Mock orders _buildOrderItem( - '买入', _coupon.couponName, '21.20', '5', '限价', AppColors.success), + context.t('trading.buy'), _coupon.couponName, '21.20', '5', context.t('trading.limitOrder'), AppColors.success), const Divider(height: 1), _buildOrderItem( - '卖出', _coupon.couponName, '21.50', '2', '限价', AppColors.error), + context.t('trading.sell'), _coupon.couponName, '21.50', '2', context.t('trading.limitOrder'), AppColors.error), ], ), ); @@ -742,7 +743,7 @@ class _TradingDetailPageState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(couponName, style: AppTypography.labelSmall), - Text('$type · ${amount}张 @ $_currencySymbol$price', + Text('$type · ${amount}${context.t('trading.sheetsUnit')} @ $_currencySymbol$price', style: AppTypography.caption), ], ), @@ -756,7 +757,7 @@ class _TradingDetailPageState extends State border: Border.all(color: AppColors.borderLight), borderRadius: AppSpacing.borderRadiusFull, ), - child: Text('撤销', + child: Text(context.t('trading.cancelOrder'), style: AppTypography.caption .copyWith(color: AppColors.textSecondary)), ), @@ -790,7 +791,7 @@ class _TradingDetailPageState extends State borderRadius: AppSpacing.borderRadiusSm, ), ), - child: Text('买入', + child: Text(context.t('trading.buy'), style: AppTypography.labelMedium .copyWith(color: Colors.white)), ), @@ -809,7 +810,7 @@ class _TradingDetailPageState extends State borderRadius: AppSpacing.borderRadiusSm, ), ), - child: Text('卖出', + child: Text(context.t('trading.sell'), style: AppTypography.labelMedium .copyWith(color: Colors.white)), ), diff --git a/frontend/genex-mobile/lib/features/trading/presentation/pages/trading_page.dart b/frontend/genex-mobile/lib/features/trading/presentation/pages/trading_page.dart index 0d3605e..e9a2c63 100644 --- a/frontend/genex-mobile/lib/features/trading/presentation/pages/trading_page.dart +++ b/frontend/genex-mobile/lib/features/trading/presentation/pages/trading_page.dart @@ -4,6 +4,7 @@ import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/status_tag.dart'; import '../../../../shared/widgets/empty_state.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A5. 交易模块(二级市场) /// @@ -35,12 +36,12 @@ class _TradingPageState extends State Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('我的交易'), + title: Text(context.t('tradingPage.title')), bottom: TabBar( controller: _tabController, - tabs: const [ - Tab(text: '我的挂单'), - Tab(text: '交易记录'), + tabs: [ + Tab(text: context.t('tradingPage.pendingOrders')), + Tab(text: context.t('tradingPage.tradeRecords')), ], ), ), @@ -96,7 +97,7 @@ class _TradingPageState extends State const SizedBox(height: 4), Row( children: [ - Text('挂单价 ', style: AppTypography.caption), + Text('${context.t('tradingPage.listPrice')} ', style: AppTypography.caption), Text('\$${[21.25, 42.50, 68.00][index]}', style: AppTypography.priceSmall.copyWith(fontSize: 14)), ], @@ -117,12 +118,12 @@ class _TradingPageState extends State child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('挂单时间: 2026/02/${9 - index}', + Text('${context.t('tradingPage.listTime')}: 2026/02/${9 - index}', style: AppTypography.caption), if (index == 0) GestureDetector( onTap: () {}, - child: Text('撤单', style: AppTypography.labelSmall.copyWith( + child: Text(context.t('tradingPage.cancelOrder'), style: AppTypography.labelSmall.copyWith( color: AppColors.error, )), ), @@ -171,7 +172,7 @@ class _TradingPageState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - isBuy ? '买入' : '卖出', + isBuy ? context.t('trading.buy') : context.t('trading.sell'), style: AppTypography.labelMedium, ), Text( diff --git a/frontend/genex-mobile/lib/features/trading/presentation/pages/transfer_page.dart b/frontend/genex-mobile/lib/features/trading/presentation/pages/transfer_page.dart index f77550f..f3497bc 100644 --- a/frontend/genex-mobile/lib/features/trading/presentation/pages/transfer_page.dart +++ b/frontend/genex-mobile/lib/features/trading/presentation/pages/transfer_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 转赠页面 - 混合方案 /// @@ -21,11 +22,11 @@ class _TransferPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('转赠'), + title: Text(context.t('transfer.title')), actions: [ TextButton( onPressed: () {}, - child: Text('转赠记录', + child: Text(context.t('transfer.transferHistory'), style: AppTypography.labelSmall .copyWith(color: AppColors.primary)), ), @@ -83,11 +84,11 @@ class _TransferPageState extends State { size: 26, color: Colors.white), ), const SizedBox(height: 10), - Text('扫码转赠', + Text(context.t('transfer.scanTransfer'), style: AppTypography.labelMedium .copyWith(color: Colors.white)), const SizedBox(height: 4), - Text('扫描对方接收码', + Text(context.t('transfer.scanDesc').split('\n')[0], style: AppTypography.caption.copyWith( color: Colors.white.withValues(alpha: 0.7), )), @@ -123,9 +124,9 @@ class _TransferPageState extends State { size: 26, color: AppColors.primary), ), const SizedBox(height: 10), - Text('输入ID', style: AppTypography.labelMedium), + Text(context.t('transfer.inputTransfer').split('/')[0], style: AppTypography.labelMedium), const SizedBox(height: 4), - Text('邮箱 / 手机 / 接收ID', + Text(context.t('transfer.recipientIdHint'), style: AppTypography.caption.copyWith( color: AppColors.textTertiary, )), @@ -148,10 +149,10 @@ class _TransferPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('最近转赠', style: AppTypography.h3), + Text(context.t('transfer.recentRecipients'), style: AppTypography.h3), GestureDetector( onTap: () {}, - child: Text('管理', + child: Text(context.t('transfer.manage'), style: AppTypography.caption .copyWith(color: AppColors.primary)), ), @@ -180,11 +181,11 @@ class _TransferPageState extends State { Icon(Icons.people_outline_rounded, size: 36, color: AppColors.textDisabled), const SizedBox(height: 8), - Text('暂无转赠记录', + Text(context.t('transfer.noRecent').split('\n')[0], style: AppTypography.bodySmall .copyWith(color: AppColors.textTertiary)), const SizedBox(height: 4), - Text('通过扫码或输入ID开始第一次转赠', + Text(context.t('transfer.noRecent').split('\n')[1], style: AppTypography.caption .copyWith(color: AppColors.textDisabled)), ], @@ -253,7 +254,7 @@ class _TransferPageState extends State { borderRadius: AppSpacing.borderRadiusFull, ), child: Text( - recipient.contactTypeLabel, + recipient.contactTypeLabel(context), style: AppTypography.caption.copyWith( fontSize: 10, color: isExpired @@ -275,7 +276,7 @@ class _TransferPageState extends State { ), const SizedBox(width: 8), Text( - '上次: ${recipient.lastTransferText}', + '${context.t('transfer.lastTransfer')}: ${recipient.lastTransferText(context)}', style: AppTypography.caption.copyWith( color: AppColors.textDisabled, fontSize: 10, @@ -296,7 +297,7 @@ class _TransferPageState extends State { color: AppColors.gray100, borderRadius: AppSpacing.borderRadiusFull, ), - child: Text('已过期', + child: Text(context.t('transfer.expired'), style: AppTypography.caption.copyWith( color: AppColors.textDisabled, fontSize: 10, @@ -318,7 +319,7 @@ class _TransferPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('最近转赠记录', style: AppTypography.h3), + Text(context.t('transfer.transferHistory'), style: AppTypography.h3), const SizedBox(height: 12), ..._mockHistory.map((h) => Container( @@ -345,14 +346,14 @@ class _TransferPageState extends State { children: [ Text(h.couponName, style: AppTypography.labelSmall), Text( - '${h.isOutgoing ? "转赠给" : "收到来自"} ${h.personName}', + '${h.isOutgoing ? context.t('transfer.transferTo') : context.t('transfer.incoming')} ${h.personName}', style: AppTypography.caption .copyWith(color: AppColors.textTertiary), ), ], ), ), - Text(h.dateText, + Text(h.dateText(context), style: AppTypography.caption .copyWith(color: AppColors.textDisabled)), ], @@ -382,9 +383,9 @@ class _TransferPageState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('输入收款人', style: AppTypography.h2), + Text(context.t('transfer.inputRecipient'), style: AppTypography.h2), const SizedBox(height: 4), - Text('支持接收ID、邮箱或手机号', + Text(context.t('transfer.recipientIdHint'), style: AppTypography.bodySmall .copyWith(color: AppColors.textTertiary)), const SizedBox(height: 20), @@ -403,7 +404,7 @@ class _TransferPageState extends State { controller.text = data!.text!; } }, - tooltip: '粘贴', + tooltip: context.t('transfer.paste'), ), ), ), @@ -418,7 +419,7 @@ class _TransferPageState extends State { _navigateToSelectCouponWithInput( context, controller.text); }, - child: const Text('下一步:选择券'), + child: Text(context.t('transfer.selectCoupon')), ), ), ], @@ -434,24 +435,23 @@ class _TransferPageState extends State { showDialog( context: context, builder: (ctx) => AlertDialog( - title: const Text('联系方式已过期'), + title: Text(context.t('transfer.expired')), content: Text( - '${recipient.displayName} 的${recipient.contactTypeLabel}' - '已超过90天未验证,请重新输入确认。', + '${recipient.displayName} ${recipient.contactTypeLabel(context)}', style: AppTypography.bodyMedium .copyWith(color: AppColors.textSecondary), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), - child: const Text('取消'), + child: Text(context.t('transfer.refresh')), ), TextButton( onPressed: () { Navigator.pop(ctx); _showInputRecipient(context); }, - child: const Text('重新输入'), + child: Text(context.t('transfer.inputRecipient')), ), ], ), @@ -496,9 +496,9 @@ class _TransferPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('选择要转赠的券', style: AppTypography.h2), + Text(context.t('transfer.selectCoupon'), style: AppTypography.h2), const SizedBox(height: 2), - Text('转赠给 $recipientName', + Text('${context.t('transfer.transferTo')} $recipientName', style: AppTypography.caption .copyWith(color: AppColors.primary)), ], @@ -598,7 +598,7 @@ class _TransferPageState extends State { const Icon(Icons.card_giftcard_rounded, color: AppColors.primary, size: 48), const SizedBox(height: 16), - Text('确认转赠', style: AppTypography.h2), + Text(context.t('transfer.confirmTransfer'), style: AppTypography.h2), const SizedBox(height: 12), // 券信息 @@ -613,7 +613,7 @@ class _TransferPageState extends State { children: [ Text(coupon.name, style: AppTypography.labelMedium), const SizedBox(height: 4), - Text('面值 \$${coupon.faceValue.toStringAsFixed(2)}', + Text('${context.t('market.faceValue')} \$${coupon.faceValue.toStringAsFixed(2)}', style: AppTypography.bodySmall), ], ), @@ -627,7 +627,7 @@ class _TransferPageState extends State { const Icon(Icons.arrow_downward_rounded, size: 16, color: AppColors.primary), const SizedBox(width: 6), - Text('转赠给 ', + Text('${context.t('transfer.transferTo')} ', style: AppTypography.bodyMedium .copyWith(color: AppColors.textSecondary)), Text(recipientName, @@ -637,7 +637,7 @@ class _TransferPageState extends State { ), const SizedBox(height: 8), - Text('转赠后您将不再持有此券', + Text(context.t('transfer.confirmTransfer'), style: AppTypography.caption .copyWith(color: AppColors.warning)), const SizedBox(height: 24), @@ -647,7 +647,7 @@ class _TransferPageState extends State { Expanded( child: OutlinedButton( onPressed: () => Navigator.pop(ctx), - child: const Text('取消'), + child: Text(context.t('trading.cancelOrder')), ), ), const SizedBox(width: 12), @@ -657,7 +657,7 @@ class _TransferPageState extends State { Navigator.pop(ctx); _showSuccess(context, recipientName, coupon); }, - child: const Text('确认转赠'), + child: Text(context.t('transfer.confirmBtn')), ), ), ], @@ -679,9 +679,9 @@ class _TransferPageState extends State { const Icon(Icons.check_circle_rounded, color: AppColors.success, size: 56), const SizedBox(height: 16), - Text('转赠成功', style: AppTypography.h2), + Text(context.t('transfer.title'), style: AppTypography.h2), const SizedBox(height: 8), - Text('${coupon.name} 已转赠给 $recipientName', + Text('${coupon.name} ${context.t('transfer.transferTo')} $recipientName', style: AppTypography.bodyMedium .copyWith(color: AppColors.textSecondary), textAlign: TextAlign.center), @@ -693,7 +693,7 @@ class _TransferPageState extends State { Navigator.pop(ctx); Navigator.pop(context); }, - child: const Text('完成'), + child: Text(context.t('sellOrder.ok')), ), ], ), @@ -726,12 +726,12 @@ class _RecentRecipient { String get avatarLetter => displayName.isNotEmpty ? displayName[0] : '?'; - String get contactTypeLabel { + String contactTypeLabel(BuildContext context) { switch (contactType) { case _ContactType.email: - return '邮箱'; + return context.t('transfer.contactEmail'); case _ContactType.phone: - return '手机'; + return context.t('transfer.contactPhone'); case _ContactType.receiveId: return 'ID'; } @@ -755,13 +755,13 @@ class _RecentRecipient { } } - String get lastTransferText { + String lastTransferText(BuildContext context) { final days = DateTime.now().difference(lastTransfer).inDays; - if (days == 0) return '今天'; - if (days == 1) return '昨天'; - if (days < 7) return '$days天前'; - if (days < 30) return '${days ~/ 7}周前'; - return '${days ~/ 30}月前'; + if (days == 0) return context.t('common.today'); + if (days == 1) return context.t('transfer.yesterday'); + if (days < 7) return '$days${context.t('transfer.daysAgo')}'; + if (days < 30) return '${days ~/ 7}${context.t('transfer.weeksAgo')}'; + return '${days ~/ 30}${context.t('transfer.monthsAgo')}'; } } @@ -790,11 +790,11 @@ class _TransferRecord { required this.date, }); - String get dateText { + String dateText(BuildContext context) { final days = DateTime.now().difference(date).inDays; - if (days == 0) return '今天'; - if (days == 1) return '昨天'; - if (days < 7) return '$days天前'; + if (days == 0) return context.t('common.today'); + if (days == 1) return context.t('transfer.yesterday'); + if (days < 7) return '$days${context.t('transfer.daysAgo')}'; return '${date.month}/${date.day}'; } } diff --git a/frontend/genex-mobile/lib/features/wallet/presentation/pages/deposit_page.dart b/frontend/genex-mobile/lib/features/wallet/presentation/pages/deposit_page.dart index adc19ed..a6af20e 100644 --- a/frontend/genex-mobile/lib/features/wallet/presentation/pages/deposit_page.dart +++ b/frontend/genex-mobile/lib/features/wallet/presentation/pages/deposit_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 充值页面 /// @@ -22,7 +23,7 @@ class _DepositPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('充值')), + appBar: AppBar(title: Text(context.t('deposit.title'))), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( @@ -38,7 +39,7 @@ class _DepositPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('当前余额', style: AppTypography.bodySmall.copyWith(color: Colors.white70)), + Text(context.t('deposit.currentBalance'), style: AppTypography.bodySmall.copyWith(color: Colors.white70)), const SizedBox(height: 4), Text('\$128.50', style: AppTypography.displayLarge.copyWith(color: Colors.white)), ], @@ -46,7 +47,7 @@ class _DepositPageState extends State { ), const SizedBox(height: 24), - Text('充值金额', style: AppTypography.h3), + Text(context.t('deposit.amount'), style: AppTypography.h3), const SizedBox(height: 12), // Preset Amounts @@ -90,8 +91,8 @@ class _DepositPageState extends State { TextField( controller: _amountController, keyboardType: const TextInputType.numberWithOptions(decimal: true), - decoration: const InputDecoration( - labelText: '自定义金额', + decoration: InputDecoration( + labelText: context.t('deposit.custom'), prefixText: '\$ ', ), onChanged: (_) => setState(() => _selectedPreset = null), @@ -99,7 +100,7 @@ class _DepositPageState extends State { const SizedBox(height: 24), // Payment Method - Text('支付方式', style: AppTypography.h3), + Text(context.t('deposit.paymentMethod'), style: AppTypography.h3), const SizedBox(height: 12), _buildPaymentOption('Visa •••• 4242', Icons.credit_card_rounded, true), _buildPaymentOption('Apple Pay', Icons.apple_rounded, false), @@ -112,7 +113,7 @@ class _DepositPageState extends State { height: AppSpacing.buttonHeight, child: ElevatedButton( onPressed: _amountController.text.isNotEmpty ? () {} : null, - child: Text('充值 \$${_amountController.text.isNotEmpty ? _amountController.text : '0'}'), + child: Text('${context.t('deposit.submit')} \$${_amountController.text.isNotEmpty ? _amountController.text : '0'}'), ), ), ), diff --git a/frontend/genex-mobile/lib/features/wallet/presentation/pages/transaction_records_page.dart b/frontend/genex-mobile/lib/features/wallet/presentation/pages/transaction_records_page.dart index 6df4cfc..ed272b4 100644 --- a/frontend/genex-mobile/lib/features/wallet/presentation/pages/transaction_records_page.dart +++ b/frontend/genex-mobile/lib/features/wallet/presentation/pages/transaction_records_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 交易记录页面 /// @@ -16,30 +17,30 @@ class TransactionRecordsPage extends StatelessWidget { length: 4, child: Scaffold( appBar: AppBar( - title: const Text('交易记录'), - bottom: const TabBar( + title: Text(context.t('txRecords.title')), + bottom: TabBar( isScrollable: true, tabs: [ - Tab(text: '全部'), - Tab(text: '购买'), - Tab(text: '出售'), - Tab(text: '转赠'), + Tab(text: context.t('common.all')), + Tab(text: context.t('txRecords.buy')), + Tab(text: context.t('txRecords.sell')), + Tab(text: context.t('txRecords.transfer')), ], ), ), body: TabBarView( children: [ - _buildList(_allRecords), - _buildList(_allRecords.where((r) => r.type == '购买').toList()), - _buildList(_allRecords.where((r) => r.type == '出售').toList()), - _buildList(_allRecords.where((r) => r.type == '转赠').toList()), + _buildList(context, _allRecords), + _buildList(context, _allRecords.where((r) => r.type == 'buy').toList()), + _buildList(context, _allRecords.where((r) => r.type == 'sell').toList()), + _buildList(context, _allRecords.where((r) => r.type == 'transfer').toList()), ], ), ), ); } - Widget _buildList(List<_TxRecord> records) { + Widget _buildList(BuildContext context, List<_TxRecord> records) { if (records.isEmpty) { return Center( child: Column( @@ -47,7 +48,7 @@ class TransactionRecordsPage extends StatelessWidget { children: [ const Icon(Icons.receipt_long_rounded, size: 48, color: AppColors.textTertiary), const SizedBox(height: 12), - Text('暂无记录', style: AppTypography.bodyMedium.copyWith(color: AppColors.textTertiary)), + Text(context.t('txRecords.noRecords'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textTertiary)), ], ), ); @@ -124,9 +125,9 @@ class _TxRecord { } const _allRecords = [ - _TxRecord(type: '购买', title: '购买 星巴克 \$25 礼品卡', subtitle: '订单号 GNX20260210001', amount: '-\$21.25', time: '今天 14:32', icon: Icons.shopping_cart_rounded, color: AppColors.primary), - _TxRecord(type: '出售', title: '出售 Amazon \$100 购物券', subtitle: '订单号 GNX20260210002', amount: '+\$92.00', time: '今天 12:15', icon: Icons.sell_rounded, color: AppColors.success), - _TxRecord(type: '转赠', title: '转赠给 Alice', subtitle: 'Nike \$80 运动券', amount: '\$0', time: '昨天 18:45', icon: Icons.card_giftcard_rounded, color: AppColors.info), - _TxRecord(type: '购买', title: '购买 Target \$30 折扣券', subtitle: '订单号 GNX20260209001', amount: '-\$24.00', time: '昨天 10:20', icon: Icons.shopping_cart_rounded, color: AppColors.primary), - _TxRecord(type: '出售', title: '出售 Walmart \$50 生活券', subtitle: '订单号 GNX20260208003', amount: '+\$46.50', time: '2天前', icon: Icons.sell_rounded, color: AppColors.success), + _TxRecord(type: 'buy', title: '购买 星巴克 \$25 礼品卡', subtitle: '订单号 GNX20260210001', amount: '-\$21.25', time: '今天 14:32', icon: Icons.shopping_cart_rounded, color: AppColors.primary), + _TxRecord(type: 'sell', title: '出售 Amazon \$100 购物券', subtitle: '订单号 GNX20260210002', amount: '+\$92.00', time: '今天 12:15', icon: Icons.sell_rounded, color: AppColors.success), + _TxRecord(type: 'transfer', title: '转赠给 Alice', subtitle: 'Nike \$80 运动券', amount: '\$0', time: '昨天 18:45', icon: Icons.card_giftcard_rounded, color: AppColors.info), + _TxRecord(type: 'buy', title: '购买 Target \$30 折扣券', subtitle: '订单号 GNX20260209001', amount: '-\$24.00', time: '昨天 10:20', icon: Icons.shopping_cart_rounded, color: AppColors.primary), + _TxRecord(type: 'sell', title: '出售 Walmart \$50 生活券', subtitle: '订单号 GNX20260208003', amount: '+\$46.50', time: '2天前', icon: Icons.sell_rounded, color: AppColors.success), ]; diff --git a/frontend/genex-mobile/lib/features/wallet/presentation/pages/wallet_page.dart b/frontend/genex-mobile/lib/features/wallet/presentation/pages/wallet_page.dart index 00559ef..98ff843 100644 --- a/frontend/genex-mobile/lib/features/wallet/presentation/pages/wallet_page.dart +++ b/frontend/genex-mobile/lib/features/wallet/presentation/pages/wallet_page.dart @@ -3,6 +3,7 @@ import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// A6. 账户模块 - 我的余额 /// @@ -15,13 +16,13 @@ class WalletPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('我的余额'), + title: Text(context.t('wallet.myBalance')), ), body: SingleChildScrollView( child: Column( children: [ // Balance Card - _buildBalanceCard(), + _buildBalanceCard(context), // Quick Actions Padding( @@ -30,7 +31,7 @@ class WalletPage extends StatelessWidget { children: [ Expanded( child: GenexButton( - label: '充值', + label: context.t('wallet.deposit'), icon: Icons.add_rounded, variant: GenexButtonVariant.primary, onPressed: () { @@ -41,7 +42,7 @@ class WalletPage extends StatelessWidget { const SizedBox(width: 12), Expanded( child: GenexButton( - label: '提现', + label: context.t('wallet.withdraw'), icon: Icons.account_balance_rounded, variant: GenexButtonVariant.outline, onPressed: () { @@ -60,14 +61,14 @@ class WalletPage extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('交易记录', style: AppTypography.h3), + Text(context.t('wallet.records'), style: AppTypography.h3), GestureDetector( onTap: () { Navigator.pushNamed(context, '/wallet/records'); }, child: Row( children: [ - Text('筛选', style: AppTypography.labelSmall.copyWith( + Text(context.t('common.filter'), style: AppTypography.labelSmall.copyWith( color: AppColors.textTertiary, )), const Icon(Icons.filter_list_rounded, size: 16, @@ -81,7 +82,7 @@ class WalletPage extends StatelessWidget { const SizedBox(height: 12), // Transaction List - _buildTransactionList(), + _buildTransactionList(context), const SizedBox(height: 80), ], @@ -90,7 +91,7 @@ class WalletPage extends StatelessWidget { ); } - Widget _buildBalanceCard() { + Widget _buildBalanceCard(BuildContext context) { return Container( margin: const EdgeInsets.fromLTRB(20, 16, 20, 16), padding: const EdgeInsets.all(24), @@ -102,7 +103,7 @@ class WalletPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('总余额', style: AppTypography.bodySmall.copyWith( + Text(context.t('wallet.totalBalance'), style: AppTypography.bodySmall.copyWith( color: Colors.white70, )), const SizedBox(height: 8), @@ -116,9 +117,9 @@ class WalletPage extends StatelessWidget { const SizedBox(height: 20), Row( children: [ - _balanceItem('可提现', '\$1,034.56'), + _balanceItem(context.t('wallet.withdrawable'), '\$1,034.56'), const SizedBox(width: 32), - _balanceItem('冻结中', '\$200.00'), + _balanceItem(context.t('wallet.frozen'), '\$200.00'), ], ), ], @@ -137,14 +138,14 @@ class WalletPage extends StatelessWidget { ); } - Widget _buildTransactionList() { + Widget _buildTransactionList(BuildContext context) { final transactions = [ - ('买入 星巴克 \$25 礼品卡', '-\$21.25', Icons.shopping_cart_rounded, AppColors.textPrimary, '今天 14:32'), - ('卖出 Amazon \$50 购物券', '+\$42.50', Icons.sell_rounded, AppColors.success, '今天 10:15'), - ('充值', '+\$500.00', Icons.add_circle_outline_rounded, AppColors.info, '昨天 09:20'), - ('转赠 Target 券', '-\$30.00', Icons.card_giftcard_rounded, AppColors.textPrimary, '02/07 16:45'), - ('核销 Nike 运动券', '使用', Icons.check_circle_outline_rounded, AppColors.success, '02/06 12:00'), - ('提现', '-\$200.00', Icons.account_balance_rounded, AppColors.textPrimary, '02/05 08:30'), + ('${context.t('wallet.buyIn')} Star Bucks \$25', '-\$21.25', Icons.shopping_cart_rounded, AppColors.textPrimary, '14:32'), + ('${context.t('wallet.sellOut')} Amazon \$50', '+\$42.50', Icons.sell_rounded, AppColors.success, '10:15'), + (context.t('wallet.deposit'), '+\$500.00', Icons.add_circle_outline_rounded, AppColors.info, '09:20'), + ('${context.t('wallet.giftTransfer')} Target', '-\$30.00', Icons.card_giftcard_rounded, AppColors.textPrimary, '02/07 16:45'), + ('${context.t('wallet.redeemUse')} Nike', '', Icons.check_circle_outline_rounded, AppColors.success, '02/06 12:00'), + (context.t('wallet.withdraw'), '-\$200.00', Icons.account_balance_rounded, AppColors.textPrimary, '02/05 08:30'), ]; return ListView.separated( diff --git a/frontend/genex-mobile/lib/features/wallet/presentation/pages/withdraw_page.dart b/frontend/genex-mobile/lib/features/wallet/presentation/pages/withdraw_page.dart index 37b9885..5eb9569 100644 --- a/frontend/genex-mobile/lib/features/wallet/presentation/pages/withdraw_page.dart +++ b/frontend/genex-mobile/lib/features/wallet/presentation/pages/withdraw_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; +import '../../../../app/i18n/app_localizations.dart'; /// 提现页面 /// @@ -25,20 +26,20 @@ class _WithdrawPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('提现')), + appBar: AppBar(title: Text(context.t('withdraw.title'))), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Balance - Text('可提现余额', style: AppTypography.bodySmall), + Text(context.t('withdraw.availableBalance'), style: AppTypography.bodySmall), const SizedBox(height: 4), Text('\$${_balance.toStringAsFixed(2)}', style: AppTypography.displayMedium), const SizedBox(height: 24), // Amount Input - Text('提现金额', style: AppTypography.h3), + Text(context.t('withdraw.amount'), style: AppTypography.h3), const SizedBox(height: 12), TextField( controller: _amountController, @@ -50,7 +51,7 @@ class _WithdrawPageState extends State { _amountController.text = _balance.toStringAsFixed(2); setState(() {}); }, - child: const Text('全部'), + child: Text(context.t('withdraw.all')), ), ), style: AppTypography.priceLarge, @@ -59,7 +60,7 @@ class _WithdrawPageState extends State { const SizedBox(height: 24), // Withdraw To - Text('提现到', style: AppTypography.h3), + Text(context.t('withdraw.to'), style: AppTypography.h3), const SizedBox(height: 12), Container( padding: const EdgeInsets.all(14), @@ -77,7 +78,7 @@ class _WithdrawPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Bank of America •••• 6789', style: AppTypography.labelMedium), - Text('储蓄账户', style: AppTypography.caption), + Text(context.t('withdraw.savingsAccount'), style: AppTypography.caption), ], ), ), @@ -97,16 +98,16 @@ class _WithdrawPageState extends State { ), child: Column( children: [ - _buildRow('提现金额', '\$${_amount.toStringAsFixed(2)}'), - _buildRow('手续费 (0.5%)', '-\$${_fee.toStringAsFixed(2)}'), + _buildRow(context.t('withdraw.amount'), '\$${_amount.toStringAsFixed(2)}'), + _buildRow(context.t('withdraw.fee'), '-\$${_fee.toStringAsFixed(2)}'), const Divider(height: 16), - _buildRow('实际到账', '\$${_receive.toStringAsFixed(2)}', bold: true), + _buildRow(context.t('withdraw.actualReceive'), '\$${_receive.toStringAsFixed(2)}', bold: true), const SizedBox(height: 8), Row( children: [ const Icon(Icons.schedule_rounded, size: 14, color: AppColors.textTertiary), const SizedBox(width: 4), - Text('预计 1-2 个工作日到账', style: AppTypography.caption), + Text(context.t('withdraw.estimateTime'), style: AppTypography.caption), ], ), ], @@ -122,7 +123,7 @@ class _WithdrawPageState extends State { height: AppSpacing.buttonHeight, child: ElevatedButton( onPressed: _amount > 0 && _amount <= _balance ? () {} : null, - child: Text('确认提现 \$${_amount.toStringAsFixed(2)}'), + child: Text('${context.t('withdraw.submit')} \$${_amount.toStringAsFixed(2)}'), ), ), ), diff --git a/frontend/genex-mobile/lib/main.dart b/frontend/genex-mobile/lib/main.dart index 3f79556..55179a5 100644 --- a/frontend/genex-mobile/lib/main.dart +++ b/frontend/genex-mobile/lib/main.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'app/theme/app_theme.dart'; import 'app/main_shell.dart'; +import 'app/i18n/app_localizations.dart'; +import 'app/i18n/locale_manager.dart'; import 'features/auth/presentation/pages/login_page.dart'; import 'features/auth/presentation/pages/welcome_page.dart'; import 'features/auth/presentation/pages/register_page.dart'; @@ -38,15 +41,59 @@ void main() { /// /// 持仓/交易所/消息/个人中心 /// 持有/接收/转赠/交易/核销数字券 -class GenexConsumerApp extends StatelessWidget { +/// +/// 国际化:首次启动跟随系统语言,用户可在设置中切换 +class GenexConsumerApp extends StatefulWidget { const GenexConsumerApp({super.key}); + @override + State createState() => _GenexConsumerAppState(); +} + +class _GenexConsumerAppState extends State { + @override + void initState() { + super.initState(); + LocaleManager.userLocale.addListener(_onLocaleChanged); + } + + @override + void dispose() { + LocaleManager.userLocale.removeListener(_onLocaleChanged); + super.dispose(); + } + + void _onLocaleChanged() { + setState(() {}); + } + @override Widget build(BuildContext context) { return MaterialApp( title: 'Genex', theme: AppTheme.light, debugShowCheckedModeBanner: false, + + // i18n + locale: LocaleManager.userLocale.value, + supportedLocales: LocaleManager.supportedLocales, + localizationsDelegates: const [ + AppLocalizationsDelegate(), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + localeResolutionCallback: (systemLocale, supportedLocales) { + // 用户未主动选择时,跟随系统语言 + if (LocaleManager.userLocale.value == null) { + return LocaleManager.resolve( + systemLocale != null ? [systemLocale] : null, + supportedLocales, + ); + } + return LocaleManager.userLocale.value; + }, + initialRoute: '/', onGenerateRoute: _generateRoute, ); diff --git a/frontend/genex-mobile/lib/shared/widgets/ai_confirm_dialog.dart b/frontend/genex-mobile/lib/shared/widgets/ai_confirm_dialog.dart index 8a6eb06..48cd9e5 100644 --- a/frontend/genex-mobile/lib/shared/widgets/ai_confirm_dialog.dart +++ b/frontend/genex-mobile/lib/shared/widgets/ai_confirm_dialog.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../app/theme/app_colors.dart'; import '../../app/theme/app_typography.dart'; import '../../app/theme/app_spacing.dart'; +import '../../app/i18n/app_localizations.dart'; import 'genex_button.dart'; /// AI操作确认弹窗组件 @@ -19,7 +20,7 @@ class AiConfirmDialog extends StatelessWidget { final String actionDescription; final List details; final String? riskWarning; - final String confirmText; + final String? confirmText; final String? cancelText; final VoidCallback onConfirm; final VoidCallback? onCancel; @@ -31,8 +32,8 @@ class AiConfirmDialog extends StatelessWidget { required this.actionDescription, required this.details, this.riskWarning, - this.confirmText = '确认执行', - this.cancelText = '取消', + this.confirmText, + this.cancelText, required this.onConfirm, this.onCancel, this.level = AiConfirmLevel.normal, @@ -59,7 +60,7 @@ class AiConfirmDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ // Header with AI icon - _buildHeader(), + _buildHeader(context), // Body Padding( @@ -162,7 +163,7 @@ class AiConfirmDialog extends StatelessWidget { // Buttons GenexButton( - label: confirmText, + label: confirmText ?? context.t('aiChat.confirmAction'), onPressed: () { Navigator.of(context).pop(true); onConfirm(); @@ -170,7 +171,7 @@ class AiConfirmDialog extends StatelessWidget { ), const SizedBox(height: 8), GenexButton( - label: cancelText ?? '取消', + label: cancelText ?? context.t('common.cancel'), variant: GenexButtonVariant.text, onPressed: () { Navigator.of(context).pop(false); @@ -186,7 +187,7 @@ class AiConfirmDialog extends StatelessWidget { ); } - Widget _buildHeader() { + Widget _buildHeader(BuildContext context) { return Container( padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), child: Row( @@ -211,7 +212,7 @@ class AiConfirmDialog extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('AI助手请求确认', style: AppTypography.labelMedium), + Text(context.t('aiChat.title'), style: AppTypography.labelMedium), const SizedBox(height: 2), Text( actionTitle, @@ -228,7 +229,7 @@ class AiConfirmDialog extends StatelessWidget { borderRadius: AppSpacing.borderRadiusFull, ), child: Text( - _levelText, + _getLevelText(context), style: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, @@ -252,14 +253,14 @@ class AiConfirmDialog extends StatelessWidget { } } - String get _levelText { + String _getLevelText(BuildContext context) { switch (level) { case AiConfirmLevel.low: - return '低风险'; + return context.t('aiChat.riskLow'); case AiConfirmLevel.normal: - return '需确认'; + return context.t('aiChat.riskNormal'); case AiConfirmLevel.high: - return '高风险'; + return context.t('aiChat.riskHigh'); } } @@ -270,7 +271,7 @@ class AiConfirmDialog extends StatelessWidget { required String actionDescription, required List details, String? riskWarning, - String confirmText = '确认执行', + String? confirmText, String? cancelText, AiConfirmLevel level = AiConfirmLevel.normal, }) { diff --git a/frontend/genex-mobile/lib/shared/widgets/coupon_card.dart b/frontend/genex-mobile/lib/shared/widgets/coupon_card.dart index ebe5447..1a233e4 100644 --- a/frontend/genex-mobile/lib/shared/widgets/coupon_card.dart +++ b/frontend/genex-mobile/lib/shared/widgets/coupon_card.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../app/theme/app_colors.dart'; import '../../app/theme/app_typography.dart'; import '../../app/theme/app_spacing.dart'; +import '../../app/i18n/app_localizations.dart'; /// 券卡片组件 - 全端通用核心组件 /// @@ -36,14 +37,14 @@ class CouponCard extends StatelessWidget { }); double get discountRate => currentPrice / faceValue; - String get discountText => '${(discountRate * 10).toStringAsFixed(1)}折'; + String discountText(BuildContext context) => '${(discountRate * 10).toStringAsFixed(1)}${context.t('market.discountSuffix')}'; @override Widget build(BuildContext context) { - return style == CouponCardStyle.grid ? _buildGridCard() : _buildListCard(); + return style == CouponCardStyle.grid ? _buildGridCard(context) : _buildListCard(context); } - Widget _buildListCard() { + Widget _buildListCard(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( @@ -106,7 +107,7 @@ class CouponCard extends StatelessWidget { style: AppTypography.priceOriginal, ), const Spacer(), - _buildDiscountBadge(), + _buildDiscountBadge(context), ], ), @@ -117,7 +118,7 @@ class CouponCard extends StatelessWidget { Icon(Icons.access_time_rounded, size: 12, color: _expiryColor), const SizedBox(width: 3), Text( - _expiryText, + _getExpiryText(context), style: AppTypography.caption.copyWith(color: _expiryColor), ), ], @@ -135,7 +136,7 @@ class CouponCard extends StatelessWidget { ); } - Widget _buildGridCard() { + Widget _buildGridCard(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( @@ -171,7 +172,7 @@ class CouponCard extends StatelessWidget { style: AppTypography.priceSmall.copyWith(fontSize: 15), ), const SizedBox(width: 4), - _buildDiscountBadge(), + _buildDiscountBadge(context), ], ), const SizedBox(height: 4), @@ -248,14 +249,14 @@ class CouponCard extends StatelessWidget { ); } - Widget _buildDiscountBadge() { + Widget _buildDiscountBadge(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( gradient: AppColors.primaryGradient, borderRadius: AppSpacing.borderRadiusFull, ), - child: Text(discountText, style: AppTypography.discountBadge), + child: Text(discountText(context), style: AppTypography.discountBadge), ); } @@ -294,14 +295,14 @@ class CouponCard extends StatelessWidget { } } - String get _expiryText { + String _getExpiryText(BuildContext context) { if (expiryDate == null) return ''; final days = expiryDate!.difference(DateTime.now()).inDays; - if (days < 0) return '已过期'; - if (days == 0) return '今天到期'; - if (days <= 3) return '$days天后到期'; - if (days <= 30) return '$days天'; - return '${expiryDate!.month}/${expiryDate!.day}到期'; + if (days < 0) return context.t('walletCoupons.expiredText'); + if (days == 0) return context.t('walletCoupons.expiringToday'); + if (days <= 3) return '$days${context.t('walletCoupons.daysToExpiry')}'; + if (days <= 30) return '$days${context.t('walletCoupons.daysToExpiry')}'; + return '${expiryDate!.month}/${expiryDate!.day}${context.t('walletCoupons.expiryFormat')}'; } Color get _expiryColor { diff --git a/frontend/genex-mobile/lib/shared/widgets/empty_state.dart b/frontend/genex-mobile/lib/shared/widgets/empty_state.dart index c4c4425..4b468b7 100644 --- a/frontend/genex-mobile/lib/shared/widgets/empty_state.dart +++ b/frontend/genex-mobile/lib/shared/widgets/empty_state.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../app/theme/app_colors.dart'; import '../../app/theme/app_typography.dart'; import '../../app/theme/app_spacing.dart'; +import '../../app/i18n/app_localizations.dart'; /// 空状态页组件 /// @@ -67,38 +68,41 @@ class EmptyState extends StatelessWidget { ); } - // 快捷工厂方法 - factory EmptyState.noCoupons({VoidCallback? onBrowse}) => EmptyState( + // 快捷工厂方法(需要传入 context 以支持国际化) + static EmptyState noCoupons(BuildContext context, {VoidCallback? onBrowse}) => + EmptyState( icon: Icons.confirmation_number_outlined, - title: '还没有券', - subtitle: '去市场看看有什么好券吧', - actionText: '去逛逛', + title: context.t('empty.noCoupons'), + subtitle: context.t('empty.noCouponsHint'), + actionText: context.t('empty.browse'), onAction: onBrowse, ); - factory EmptyState.noOrders() => const EmptyState( + static EmptyState noOrders(BuildContext context) => EmptyState( icon: Icons.receipt_long_outlined, - title: '暂无交易记录', - subtitle: '完成首笔交易后这里会显示记录', + title: context.t('empty.noTrades'), + subtitle: context.t('empty.noTradesHint'), ); - factory EmptyState.noResults() => const EmptyState( + static EmptyState noResults(BuildContext context) => EmptyState( icon: Icons.search_off_rounded, - title: '没有找到结果', - subtitle: '换个关键词试试', + title: context.t('empty.noResults'), + subtitle: context.t('empty.noResultsHint'), ); - factory EmptyState.noMessages() => const EmptyState( + static EmptyState noMessages(BuildContext context) => EmptyState( icon: Icons.notifications_none_rounded, - title: '暂无消息', - subtitle: '交易通知和系统公告会显示在这里', + title: context.t('empty.noMessages'), + subtitle: context.t('empty.noMessagesHint'), ); - factory EmptyState.networkError({VoidCallback? onRetry}) => EmptyState( + static EmptyState networkError(BuildContext context, + {VoidCallback? onRetry}) => + EmptyState( icon: Icons.wifi_off_rounded, - title: '网络连接失败', - subtitle: '请检查网络设置后重试', - actionText: '重试', + title: context.t('empty.networkError'), + subtitle: context.t('empty.networkErrorHint'), + actionText: context.t('common.retry'), onAction: onRetry, ); } diff --git a/frontend/genex-mobile/lib/shared/widgets/kyc_badge.dart b/frontend/genex-mobile/lib/shared/widgets/kyc_badge.dart index 9121866..9ed410f 100644 --- a/frontend/genex-mobile/lib/shared/widgets/kyc_badge.dart +++ b/frontend/genex-mobile/lib/shared/widgets/kyc_badge.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../app/theme/app_colors.dart'; import '../../app/theme/app_typography.dart'; import '../../app/theme/app_spacing.dart'; +import '../../app/i18n/app_localizations.dart'; /// KYC等级标识组件 /// @@ -32,7 +33,7 @@ class KycBadge extends StatelessWidget { Icon(Icons.shield_rounded, size: 12, color: _color), const SizedBox(width: 3), Text( - showLabel ? 'L$level 认证' : 'L$level', + showLabel ? 'L$level ${context.t('kyc.badgeLabel')}' : 'L$level', style: AppTypography.caption.copyWith( color: _color, fontWeight: FontWeight.w600, diff --git a/frontend/genex-mobile/lib/shared/widgets/price_tag.dart b/frontend/genex-mobile/lib/shared/widgets/price_tag.dart index 6f30a9d..f8b2db3 100644 --- a/frontend/genex-mobile/lib/shared/widgets/price_tag.dart +++ b/frontend/genex-mobile/lib/shared/widgets/price_tag.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../app/theme/app_colors.dart'; import '../../app/theme/app_typography.dart'; import '../../app/theme/app_spacing.dart'; +import '../../app/i18n/app_localizations.dart'; /// 价格标签组件 /// @@ -22,7 +23,7 @@ class PriceTag extends StatelessWidget { }); double get discountRate => currentPrice / faceValue; - String get discountText => '${(discountRate * 10).toStringAsFixed(1)}折'; + String discountText(BuildContext context) => '${(discountRate * 10).toStringAsFixed(1)}${context.t('market.discountSuffix')}'; double get savedAmount => faceValue - currentPrice; @override @@ -66,12 +67,12 @@ class PriceTag extends StatelessWidget { gradient: AppColors.primaryGradient, borderRadius: AppSpacing.borderRadiusFull, ), - child: Text( - discountText, + child: Builder(builder: (context) => Text( + discountText(context), style: AppTypography.discountBadge.copyWith( fontSize: size == PriceTagSize.large ? 13 : 11, ), - ), + )), ); } diff --git a/frontend/genex-mobile/lib/shared/widgets/status_tag.dart b/frontend/genex-mobile/lib/shared/widgets/status_tag.dart index e620e83..b77d938 100644 --- a/frontend/genex-mobile/lib/shared/widgets/status_tag.dart +++ b/frontend/genex-mobile/lib/shared/widgets/status_tag.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../app/theme/app_colors.dart'; import '../../app/theme/app_typography.dart'; import '../../app/theme/app_spacing.dart'; +import '../../app/i18n/app_localizations.dart'; /// 状态标签组件 /// @@ -73,13 +74,13 @@ enum StatusType { success, pending, error, info, neutral } class StatusTags { StatusTags._(); - static StatusTag active() => const StatusTag(label: '可使用', type: StatusType.success); - static StatusTag pending() => const StatusTag(label: '待核销', type: StatusType.pending); - static StatusTag expired() => const StatusTag(label: '已过期', type: StatusType.neutral); - static StatusTag used() => const StatusTag(label: '已使用', type: StatusType.neutral); - static StatusTag processing() => const StatusTag(label: '处理中', type: StatusType.info); - static StatusTag completed() => const StatusTag(label: '已完成', type: StatusType.success); - static StatusTag cancelled() => const StatusTag(label: '已取消', type: StatusType.neutral); - static StatusTag refunding() => const StatusTag(label: '退款中', type: StatusType.pending); - static StatusTag onSale() => const StatusTag(label: '出售中', type: StatusType.info); + static StatusTag active(BuildContext context) => StatusTag(label: context.t('status.active'), type: StatusType.success); + static StatusTag pending(BuildContext context) => StatusTag(label: context.t('status.pending'), type: StatusType.pending); + static StatusTag expired(BuildContext context) => StatusTag(label: context.t('status.expired'), type: StatusType.neutral); + static StatusTag used(BuildContext context) => StatusTag(label: context.t('status.used'), type: StatusType.neutral); + static StatusTag processing(BuildContext context) => StatusTag(label: context.t('status.processing'), type: StatusType.info); + static StatusTag completed(BuildContext context) => StatusTag(label: context.t('status.completed'), type: StatusType.success); + static StatusTag cancelled(BuildContext context) => StatusTag(label: context.t('status.cancelled'), type: StatusType.neutral); + static StatusTag refunding(BuildContext context) => StatusTag(label: context.t('status.refunding'), type: StatusType.pending); + static StatusTag onSale(BuildContext context) => StatusTag(label: context.t('status.onSale'), type: StatusType.info); } diff --git a/frontend/genex-mobile/pubspec.yaml b/frontend/genex-mobile/pubspec.yaml index ea85c4b..f649b91 100644 --- a/frontend/genex-mobile/pubspec.yaml +++ b/frontend/genex-mobile/pubspec.yaml @@ -9,6 +9,9 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter + intl: any dev_dependencies: flutter_test: