Browse Source

feat(i18n): 添加并完善翻译键和国际化文本

添加用量统计模块的翻译键和文本
完善图片翻译模块的国际化支持
统一各模块的时间范围和统计项翻译
更新翻译控制器和视图以使用翻译键
newdev_shunjiawei
lxm 10 months ago
parent
commit
1f470ee659
  1. 110
      lib/core/translations/language/en_us.dart
  2. 102
      lib/core/translations/language/zh_cn.dart
  3. 41
      lib/data/models/translation_usage_stats.dart
  4. 2
      lib/modules/common/widgets/language_selector_bar.dart
  5. 8
      lib/modules/common/widgets/recharge_dialog.dart
  6. 3
      lib/modules/meeting/controllers/meeting_home_controller.dart
  7. 87
      lib/modules/picture_translation/controllers/picture_translation_controller.dart
  8. 60
      lib/modules/picture_translation/views/picture_translation_history_detail_view.dart
  9. 192
      lib/modules/picture_translation/views/picture_translation_view.dart
  10. 8
      lib/modules/text_translation/controllers/text_translation_controller.dart
  11. 8
      lib/modules/text_translation/views/text_translation_view.dart
  12. 2
      lib/modules/translation/middleware/controllers/translation_middleware_controller.dart
  13. 8
      lib/modules/translation/views/dialogs/language_selection_dialog.dart
  14. 4
      lib/modules/translation/views/translation_view.dart
  15. 4
      lib/modules/translation/views/widgets/translation_item_widget.dart
  16. 72
      lib/modules/usage/controllers/meeting_stats_controller.dart
  17. 34
      lib/modules/usage/controllers/translation_stats_controller.dart
  18. 4
      lib/modules/usage/views/aichat_stats_view.dart
  19. 22
      lib/modules/usage/views/meeting_stats_view.dart
  20. 76
      lib/modules/usage/views/translation_stats_view.dart
  21. 2
      lib/modules/usage/views/usage_view.dart

110
lib/core/translations/language/en_us.dart

@ -186,6 +186,52 @@ const Map<String, String> enUS = {
"usageStatsInfo": "View Your Usage Statistics", "usageStatsInfo": "View Your Usage Statistics",
"scene": "Scenarios", "scene": "Scenarios",
/// 用量统计模块
"aiChat": "AI Chat",
"translationStats": "Translation Statistics",
"meetingStats": "Meeting Statistics",
"today": "Today",
"thisWeek": "This Week",
"thisMonth": "This Month",
"history": "History",
"currentSessionStats": "Current Session Stats",
"translationCount": "Translation Count",
"sessionDuration": "Session Duration",
"characters": "Characters",
"recordingDuration": "Recording Duration",
"totalApiCalls": "Total API Calls",
"sessionCount": "Session Count",
"totalCharacters": "Total Characters",
"apiCallStats": "API Call Stats",
"translationApi": "Translation API",
"asr": "ASR",
"tts": "TTS",
// "aiSummary": "AI Summary",
"dataVolumeStats": "Data Volume Stats",
"sourceTextCharacters": "Source Text Characters",
"ttsTextCharacters": "TTS Text Characters",
"effectiveAudioDuration": "Effective Audio Duration",
"totalSessionDuration": "Total Session Duration",
"costStats": "Cost Stats",
"estimatedCost": "Estimated Cost",
"dailyCostThreshold": "Daily Cost Threshold",
"monthlyCostThreshold": "Monthly Cost Threshold",
"usageBehaviorStats": "Usage Behavior Stats",
"mostUsedLanguagePair": "Most-used language pair",
"mostUsedMode": "Most-used mode",
"asrApiCalls": "ASR Calls",
"totalAudioDuration": "Total Audio Duration",
"audioFileSize": "Audio File Size",
"transcriptBytes": "Transcript Bytes",
//通知文案
"loadFailed": "Load Failed",
"statsLoadFailedTryLater":
"Statistics failed to load. Please try again later.",
"cleanupComplete": "Cleanup Complete",
"cleanedOldData": "Cleaned data older than 90 days",
"exportFeature": "Export Feature",
"exportInDevelopment": "Data export is under development",
// 商品页面(AI/翻译/会议) // 商品页面(AI/翻译/会议)
'availablePackages': 'Available Packages', 'availablePackages': 'Available Packages',
'remainingQuota': 'Remaining Quota', 'remainingQuota': 'Remaining Quota',
@ -1150,6 +1196,7 @@ const Map<String, String> enUS = {
// 翻译主页 (translation_view.dart) // 翻译主页 (translation_view.dart)
"translationHistory": "Translation History", "translationHistory": "Translation History",
"historyRecords": "History Records",
"enableRecording": "Enable Recording", "enableRecording": "Enable Recording",
"viewAudioFiles": "View Audio Files", "viewAudioFiles": "View Audio Files",
"headsetUser": "Headset User", "headsetUser": "Headset User",
@ -1162,9 +1209,15 @@ const Map<String, String> enUS = {
// 通用对话按钮与选项(用于统一语言选择弹窗等) // 通用对话按钮与选项(用于统一语言选择弹窗等)
// 翻译键代表的中文文本内容:取消/确认/自动检测 // 翻译键代表的中文文本内容:取消/确认/自动检测
"autoDetect": "Auto Detect", "autoDetect": "Auto Detect",
// Text translation & language detection
"languageDetectionFailed": "Language detection failed",
"translationFailed": "Translation failed",
"pleaseInputText": "Please input text",
"textTooLong": "Text too long, max 5000 characters",
// 翻译历史页 // 翻译历史页
"noTranslationHistory": "No translation history", "noTranslationHistory": "No translation history",
"clearResults": "Clear Results",
"clearHistory": "Clear History", "clearHistory": "Clear History",
"clearHistoryConfirm": "clearHistoryConfirm":
"Are you sure you want to clear all translation history? This action cannot be undone.", "Are you sure you want to clear all translation history? This action cannot be undone.",
@ -1173,6 +1226,57 @@ const Map<String, String> enUS = {
"deleteSessionConfirm": "deleteSessionConfirm":
"Are you sure you want to delete all translation records for this session? This action cannot be undone.", "Are you sure you want to delete all translation records for this session? This action cannot be undone.",
// Picture Translation module
"selectImageSource": "Select Image Source",
"imagePreview": "Image Preview",
"translationResult": "Translation Result",
"recognitionResult": "Recognition Result",
"translatedImagePreview": "Translated Image Preview",
"translatedImageLoadFailed": "Failed to load translated image",
"pleaseSelectImage": "Please select an image",
"imageLoadFailed": "Image load failed",
"imageQuality": "Image Quality",
"maxSize": "Max Size",
"close": "Close",
"setImageQuality": "Set Image Quality",
"currentQuality": "Current quality: @value%",
"setMaxSize": "Set Max Size",
"currentMaxSize": "Current: @pxpx",
"waitingForAction": "Waiting for action",
"pleaseSelectImageToStart": "Please select an image to start",
"chooseImage": "Choose Image",
"openingCamera": "Opening camera...",
"openingPhotoAlbum": "Opening photo album...",
"cancelled": "Cancelled",
"userCancelledSelection": "User cancelled selection",
"translatingImage": "Translating Image",
"callingImageTranslateService": "Calling image translation service...",
"savingRecord": "Saving Record",
"savingTranslationResult": "Saving translation result...",
"translationCompleted": "Translation Completed",
"allStepsCompleted": "All steps completed",
"processingFailed": "Processing Failed",
"imageProcessingOrTranslationFailed":
"Image processing or translation failed. Please retry.\nError: {error}",
"imageTooLarge": "Image size exceeds 4MB limit",
"imageTooLargeHint":
"Image size exceeds 4MB. Reduce quality or dimensions and retry",
"emptyTranslationResult": "Image translation API returned empty result",
"imageTranslationFailedTryLater":
"Image translation failed. Network issue or rate limit. Please try later",
"imageTranslationApiException":
"Image translation API exception. Check network and retry",
"permissionRequired": "Permission Required",
"cameraPermissionRequired": "Camera permission required to take photos",
"photoAlbumPermissionRequired":
"Photo album permission required to pick images",
"translationDetails": "Translation Details",
"originalImage": "Original Image",
"sourceLanguage": "Source Language",
"targetLanguage": "Target Language",
"translationTime": "Translation Time",
"shareInDevelopment": "Sharing feature under development",
// 翻译录音文件列表页 // 翻译录音文件列表页
"translateAudioFiles": "Translate Audio Files", "translateAudioFiles": "Translate Audio Files",
"noAudioFiles": "No audio files", "noAudioFiles": "No audio files",
@ -1382,6 +1486,9 @@ const Map<String, String> enUS = {
"justNow": "Just now", "justNow": "Just now",
"minutesAgo": "@minutes minutes ago", "minutesAgo": "@minutes minutes ago",
"yesterday": "Yesterday", "yesterday": "Yesterday",
// Time extensions (used by picture translation)
"daysAgo": "@days days ago",
"hoursAgo": "@hours hours ago",
//补充 //补充
"insufficientIntegral": "Insufficient integral", "insufficientIntegral": "Insufficient integral",
@ -1390,4 +1497,7 @@ const Map<String, String> enUS = {
"insufficientBalanceDesc": "insufficientBalanceDesc":
'Insufficient balance, please go to the corresponding recharge page to recharge.', 'Insufficient balance, please go to the corresponding recharge page to recharge.',
"recharge": 'Go to Recharge', "recharge": 'Go to Recharge',
// 会员到期提示(中文注释)
"aiMemberExpiredDesc":
'AI membership expired, please go to the membership recharge page to renew.',
}; };

102
lib/core/translations/language/zh_cn.dart

@ -179,6 +179,50 @@ const Map<String, String> zhCN = {
"usageStats": "用量统计", "usageStats": "用量统计",
"usageStatsInfo": "查看您的使用统计数据", "usageStatsInfo": "查看您的使用统计数据",
"scene": "场景", "scene": "场景",
// 用量统计模块
"aiChat": "AI聊天",
"translationStats": "翻译统计",
"meetingStats": "会议统计",
"today": "今日",
"thisWeek": "本周",
"thisMonth": "本月",
"history": "历史",
"currentSessionStats": "当前会话统计",
"translationCount": "翻译次数",
"sessionDuration": "会话时长",
"characters": "字符数",
"recordingDuration": "录音时长",
"totalApiCalls": "总调用次数",
"sessionCount": "会话数量",
"totalCharacters": "总字符数",
"apiCallStats": "API调用统计",
"translationApi": "翻译API",
"asr": "语音识别",
"tts": "语音合成",
//"aiSummary": "AI摘要",
"dataVolumeStats": "数据量统计",
"sourceTextCharacters": "源文本字符",
"ttsTextCharacters": "TTS文本字符",
"effectiveAudioDuration": "有效音频时长",
"totalSessionDuration": "总会话时长",
"costStats": "成本统计",
"estimatedCost": "预估成本",
"dailyCostThreshold": "日成本预警阈值",
"monthlyCostThreshold": "月成本预警阈值",
"usageBehaviorStats": "使用行为统计",
"mostUsedLanguagePair": "最常用语言对",
"mostUsedMode": "最常用模式",
"asrApiCalls": "ASR调用次数",
"totalAudioDuration": "音频总时长",
"audioFileSize": "音频文件大小",
"transcriptBytes": "转写字节",
// 通知文案
"loadFailed": "加载失败",
"statsLoadFailedTryLater": "统计数据加载失败,请稍后重试",
"cleanupComplete": "清理完成",
"cleanedOldData": "已清理90天前的过期数据",
"exportFeature": "导出功能",
"exportInDevelopment": "数据导出功能正在开发中",
// 商品页面(AI/翻译/会议) // 商品页面(AI/翻译/会议)
'availablePackages': '可购买套餐', 'availablePackages': '可购买套餐',
@ -1067,15 +1111,68 @@ const Map<String, String> zhCN = {
// 通用对话按钮与选项(用于统一语言选择弹窗等) // 通用对话按钮与选项(用于统一语言选择弹窗等)
// 以下翻译键的中文文本内容如注释所示 // 以下翻译键的中文文本内容如注释所示
"autoDetect": "自动检测", // 自动检测 "autoDetect": "自动检测", // 自动检测
// 文本翻译与语种检测
"languageDetectionFailed": "语言检测失败",
"translationFailed": "翻译失败",
"pleaseInputText": "请输入文本",
"textTooLong": "文本过长,最多5000字符",
// 翻译历史页 // 翻译历史页
"noTranslationHistory": "暂无历史记录", "noTranslationHistory": "暂无历史记录",
"historyRecords": "历史记录",
"clearResults": "清空结果",
"clearHistory": "清除历史记录", "clearHistory": "清除历史记录",
"clearHistoryConfirm": "确定要清除所有翻译历史记录吗?此操作无法撤销。", "clearHistoryConfirm": "确定要清除所有翻译历史记录吗?此操作无法撤销。",
"unknownLanguage": "未知语言", "unknownLanguage": "未知语言",
"deleteSessionRecord": "删除会话记录", "deleteSessionRecord": "删除会话记录",
"deleteSessionConfirm": "确定要删除该会话的所有翻译记录吗?此操作无法撤销。", "deleteSessionConfirm": "确定要删除该会话的所有翻译记录吗?此操作无法撤销。",
// 图片翻译模块(Picture Translation)
"selectImageSource": "选择图片来源",
"imagePreview": "图片预览",
"translationResult": "翻译结果",
"recognitionResult": "识别结果",
"translatedImagePreview": "翻译后的图片预览",
"translatedImageLoadFailed": "翻译图片加载失败",
"pleaseSelectImage": "请选择图片",
"imageLoadFailed": "图片加载失败",
"imageQuality": "图片质量",
"maxSize": "最大尺寸",
"close": "关闭",
"setImageQuality": "设置图片质量",
"currentQuality": "当前质量: @value%",
"setMaxSize": "设置最大尺寸",
"currentMaxSize": "当前: @pxpx",
"waitingForAction": "等待操作",
"pleaseSelectImageToStart": "请选择图片开始翻译",
"chooseImage": "选择图片",
"openingCamera": "正在打开相机...",
"openingPhotoAlbum": "正在打开相册...",
"cancelled": "已取消",
"userCancelledSelection": "用户取消了选择",
"translatingImage": "翻译图片",
"callingImageTranslateService": "正在调用图片翻译服务...",
"savingRecord": "保存记录",
"savingTranslationResult": "正在保存翻译结果...",
"translationCompleted": "翻译完成",
"allStepsCompleted": "所有步骤已完成",
"processingFailed": "处理失败",
"imageProcessingOrTranslationFailed": "图片处理或翻译失败,请重试。\n错误信息: {error}",
"imageTooLarge": "图片大小超过4MB限制",
"imageTooLargeHint": "图片大小超过4MB限制,请降低质量或尺寸后重试",
"emptyTranslationResult": "图片翻译接口返回空结果",
"imageTranslationFailedTryLater": "图片翻译失败,可能的原因:网络异常或接口限流,请稍后重试",
"imageTranslationApiException": "图片翻译接口调用异常,请检查网络后重试",
"permissionRequired": "权限需求",
"cameraPermissionRequired": "需要相机权限才能拍照",
"photoAlbumPermissionRequired": "需要相册权限才能选择图片",
"translationDetails": "翻译详情",
"originalImage": "原图片",
"sourceLanguage": "源语言",
"targetLanguage": "目标语言",
"translationTime": "翻译时间",
"shareInDevelopment": "分享功能开发中",
// 翻译录音文件列表页 // 翻译录音文件列表页
"translateAudioFiles": "翻译录音文件", "translateAudioFiles": "翻译录音文件",
"noAudioFiles": "暂无音频文件", "noAudioFiles": "暂无音频文件",
@ -1266,6 +1363,9 @@ const Map<String, String> zhCN = {
"justNow": "刚刚", "justNow": "刚刚",
"minutesAgo": "@minutes分钟前", "minutesAgo": "@minutes分钟前",
"yesterday": "昨天", "yesterday": "昨天",
// 时间扩展(图片翻译模块使用)
"daysAgo": "@days天前",
"hoursAgo": "@hours小时前",
//补充 //补充
"insufficientIntegral": "积分不足", "insufficientIntegral": "积分不足",
@ -1273,4 +1373,6 @@ const Map<String, String> zhCN = {
//余额不足弹窗 //余额不足弹窗
"insufficientBalanceDesc": "余额不足,请前往对应充值界面进行充值", "insufficientBalanceDesc": "余额不足,请前往对应充值界面进行充值",
"recharge": "前往充值", "recharge": "前往充值",
// 会员到期提示
"aiMemberExpiredDesc": "AI会员已到期,请前往会员充值续费",
}; };

41
lib/data/models/translation_usage_stats.dart

@ -1,5 +1,3 @@
/// 翻译模式统计数据 /// 翻译模式统计数据
class TranslationModeStats { class TranslationModeStats {
final int apiCalls; // API调用次数 final int apiCalls; // API调用次数
@ -46,7 +44,8 @@ class TranslationModeStats {
return TranslationModeStats( return TranslationModeStats(
apiCalls: apiCalls ?? this.apiCalls, apiCalls: apiCalls ?? this.apiCalls,
sourceCharacters: sourceCharacters ?? this.sourceCharacters, sourceCharacters: sourceCharacters ?? this.sourceCharacters,
effectiveAudioDuration: effectiveAudioDuration ?? this.effectiveAudioDuration, effectiveAudioDuration:
effectiveAudioDuration ?? this.effectiveAudioDuration,
ttsCharacters: ttsCharacters ?? this.ttsCharacters, ttsCharacters: ttsCharacters ?? this.ttsCharacters,
cost: cost ?? this.cost, cost: cost ?? this.cost,
); );
@ -158,7 +157,7 @@ class TranslationUsageStats {
final double estimatedCost; // 预估成本 final double estimatedCost; // 预估成本
final String currency; // 货币单位 final String currency; // 货币单位
final Map<String, double> costBreakdown; // 成本分解(ASR/TTS/Translation) final Map<String, double> costBreakdown; // 成本分解(ASR/TTS/Translation)
TranslationUsageStats({ TranslationUsageStats({
required this.id, required this.id,
required this.date, required this.date,
@ -196,7 +195,8 @@ class TranslationUsageStats {
final languagePairStatsMap = <String, LanguagePairStats>{}; final languagePairStatsMap = <String, LanguagePairStats>{};
if (json['languagePairStats'] != null) { if (json['languagePairStats'] != null) {
final languagePairStatsJson = json['languagePairStats'] as Map<String, dynamic>; final languagePairStatsJson =
json['languagePairStats'] as Map<String, dynamic>;
languagePairStatsJson.forEach((key, value) { languagePairStatsJson.forEach((key, value) {
languagePairStatsMap[key] = LanguagePairStats.fromJson(value); languagePairStatsMap[key] = LanguagePairStats.fromJson(value);
}); });
@ -219,9 +219,15 @@ class TranslationUsageStats {
asrApiCalls: json['asrApiCalls'] ?? 0, asrApiCalls: json['asrApiCalls'] ?? 0,
ttsApiCalls: json['ttsApiCalls'] ?? 0, ttsApiCalls: json['ttsApiCalls'] ?? 0,
aiSummaryApiCalls: json['aiSummaryApiCalls'] ?? 0, aiSummaryApiCalls: json['aiSummaryApiCalls'] ?? 0,
sourceTextCharacters: json['sourceTextCharacters'] ?? json['sourceCharacterCount'] ?? 0, // 兼容旧字段 sourceTextCharacters: json['sourceTextCharacters'] ??
effectiveAudioDuration: json['effectiveAudioDuration'] ?? json['audioRecordingDuration'] ?? 0, // 兼容旧字段 json['sourceCharacterCount'] ??
ttsTextCharacters: json['ttsTextCharacters'] ?? json['targetCharacterCount'] ?? 0, // 兼容旧字段 0, // 兼容旧字段
effectiveAudioDuration: json['effectiveAudioDuration'] ??
json['audioRecordingDuration'] ??
0, // 兼容旧字段
ttsTextCharacters: json['ttsTextCharacters'] ??
json['targetCharacterCount'] ??
0, // 兼容旧字段
totalSessionDuration: json['totalSessionDuration'] ?? 0, totalSessionDuration: json['totalSessionDuration'] ?? 0,
asrNetworkBytes: json['asrNetworkBytes'] ?? 0, asrNetworkBytes: json['asrNetworkBytes'] ?? 0,
ttsNetworkBytes: json['ttsNetworkBytes'] ?? 0, ttsNetworkBytes: json['ttsNetworkBytes'] ?? 0,
@ -328,12 +334,14 @@ class TranslationUsageStats {
ttsApiCalls: ttsApiCalls ?? this.ttsApiCalls, ttsApiCalls: ttsApiCalls ?? this.ttsApiCalls,
aiSummaryApiCalls: aiSummaryApiCalls ?? this.aiSummaryApiCalls, aiSummaryApiCalls: aiSummaryApiCalls ?? this.aiSummaryApiCalls,
sourceTextCharacters: sourceTextCharacters ?? this.sourceTextCharacters, sourceTextCharacters: sourceTextCharacters ?? this.sourceTextCharacters,
effectiveAudioDuration: effectiveAudioDuration ?? this.effectiveAudioDuration, effectiveAudioDuration:
effectiveAudioDuration ?? this.effectiveAudioDuration,
ttsTextCharacters: ttsTextCharacters ?? this.ttsTextCharacters, ttsTextCharacters: ttsTextCharacters ?? this.ttsTextCharacters,
totalSessionDuration: totalSessionDuration ?? this.totalSessionDuration, totalSessionDuration: totalSessionDuration ?? this.totalSessionDuration,
asrNetworkBytes: asrNetworkBytes ?? this.asrNetworkBytes, asrNetworkBytes: asrNetworkBytes ?? this.asrNetworkBytes,
ttsNetworkBytes: ttsNetworkBytes ?? this.ttsNetworkBytes, ttsNetworkBytes: ttsNetworkBytes ?? this.ttsNetworkBytes,
translationNetworkBytes: translationNetworkBytes ?? this.translationNetworkBytes, translationNetworkBytes:
translationNetworkBytes ?? this.translationNetworkBytes,
sessionCount: sessionCount ?? this.sessionCount, sessionCount: sessionCount ?? this.sessionCount,
modeStats: modeStats ?? this.modeStats, modeStats: modeStats ?? this.modeStats,
languagePairStats: languagePairStats ?? this.languagePairStats, languagePairStats: languagePairStats ?? this.languagePairStats,
@ -345,7 +353,8 @@ class TranslationUsageStats {
} }
/// 获取总API调用次数 /// 获取总API调用次数
int get totalApiCalls => translationApiCalls + asrApiCalls + ttsApiCalls + aiSummaryApiCalls; int get totalApiCalls =>
translationApiCalls + asrApiCalls + ttsApiCalls + aiSummaryApiCalls;
/// 获取总字符数(精确计费基础) /// 获取总字符数(精确计费基础)
int get totalCharacterCount => sourceTextCharacters + ttsTextCharacters; int get totalCharacterCount => sourceTextCharacters + ttsTextCharacters;
@ -354,19 +363,21 @@ class TranslationUsageStats {
int get totalAudioDuration => effectiveAudioDuration + totalSessionDuration; int get totalAudioDuration => effectiveAudioDuration + totalSessionDuration;
/// 获取总网络流量 /// 获取总网络流量
int get totalNetworkBytes => asrNetworkBytes + ttsNetworkBytes + translationNetworkBytes; int get totalNetworkBytes =>
asrNetworkBytes + ttsNetworkBytes + translationNetworkBytes;
/// 获取最常用的语言对 /// 获取最常用的语言对
String get mostUsedLanguagePair { String get mostUsedLanguagePair {
if (languagePairStats.isEmpty) return '无数据'; if (languagePairStats.isEmpty) return 'null';
return languagePairStats.entries return languagePairStats.entries
.reduce((a, b) => a.value.translationCount > b.value.translationCount ? a : b) .reduce((a, b) =>
a.value.translationCount > b.value.translationCount ? a : b)
.key; .key;
} }
/// 获取最常用的翻译模式 /// 获取最常用的翻译模式
String get mostUsedMode { String get mostUsedMode {
if (modeStats.isEmpty) return '无数据'; if (modeStats.isEmpty) return 'null';
return modeStats.entries return modeStats.entries
.reduce((a, b) => a.value.apiCalls > b.value.apiCalls ? a : b) .reduce((a, b) => a.value.apiCalls > b.value.apiCalls ? a : b)
.key; .key;

2
lib/modules/common/widgets/language_selector_bar.dart

@ -58,7 +58,7 @@ class LanguageSelectorBar extends StatelessWidget {
isDarkMode ? AppColors.primary.withOpacity(0.2) : Colors.blue[50]; isDarkMode ? AppColors.primary.withOpacity(0.2) : Colors.blue[50];
final swapIconColor = isDarkMode ? AppColors.primary : Colors.blue; final swapIconColor = isDarkMode ? AppColors.primary : Colors.blue;
// 规范化特殊项的翻译键,例如:自动检测 -> autoDetect(翻译键代表的中文文本内容) // 规范化特殊项的翻译键,例如:自动检测 -> autoDetect(对应中文:自动检测)
String _normalizeKey(String text) { String _normalizeKey(String text) {
switch (text) { switch (text) {
case '自动检测': case '自动检测':

8
lib/modules/common/widgets/recharge_dialog.dart

@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
/// 显示充值弹窗
/// [type] 充值类型,可选值为 'agent'、'translation'、'meeting'
/// 分别跳转对应ai充值、翻译充值、会议充值界面
void showRechargeDialog(String type) { void showRechargeDialog(String type) {
Get.dialog( Get.dialog(
Dialog( Dialog(
@ -24,7 +27,10 @@ void showRechargeDialog(String type) {
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Text( Text(
'insufficientBalanceDesc'.tr, //'余额不足,请前往对应充值界面进行充值', (type == 'agent'
? 'aiMemberExpiredDesc'
: 'insufficientBalanceDesc')
.tr, // 会员过期描述 / 余额不足描述
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),

3
lib/modules/meeting/controllers/meeting_home_controller.dart

@ -410,7 +410,8 @@ class MeetingHomeController extends GetxController {
Logger.error('importFailed' Logger.error('importFailed'
.tr .tr
.replaceAll('{filePath}', filePath) .replaceAll('{filePath}', filePath)
.replaceAll('{error}', e.toString())); // 翻译键代表的中文文本内容 .replaceAll('{error}',
e.toString())); // 对应中文:导入文件失败: {filePath}, error: {error}
} }
} else { } else {
effectivePath = filePath; effectivePath = filePath;

87
lib/modules/picture_translation/controllers/picture_translation_controller.dart

@ -34,8 +34,8 @@ class PictureTranslationController extends GetxController {
final isProcessing = false.obs; final isProcessing = false.obs;
final pickedImageFile = Rx<File?>(null); final pickedImageFile = Rx<File?>(null);
final currentStep = ProcessingStep( final currentStep = ProcessingStep(
title: '等待操作', title: 'waitingForAction'.tr, // 等待操作
description: '请选择图片开始翻译', description: 'pleaseSelectImageToStart'.tr, // 请选择图片开始翻译
status: ProcessingStatus.idle, status: ProcessingStatus.idle,
).obs; ).obs;
@ -152,13 +152,17 @@ class PictureTranslationController extends GetxController {
if (source == ImageSource.camera) { if (source == ImageSource.camera) {
final status = await Permission.camera.request(); final status = await Permission.camera.request();
if (!status.isGranted) { if (!status.isGranted) {
Get.snackbar('权限需求', '需要相机权限才能拍照'); Get.snackbar(
'permissionRequired'.tr, // 权限需求
'cameraPermissionRequired'.tr); // 需要相机权限才能拍照
return false; return false;
} }
} else { } else {
final status = await Permission.photos.request(); final status = await Permission.photos.request();
if (!status.isGranted) { if (!status.isGranted) {
Get.snackbar('权限需求', '需要相册权限才能选择图片'); Get.snackbar(
'permissionRequired'.tr, // 权限需求
'photoAlbumPermissionRequired'.tr); // 需要相册权限才能选择图片
return false; return false;
} }
} }
@ -194,8 +198,10 @@ class PictureTranslationController extends GetxController {
try { try {
isProcessing.value = true; isProcessing.value = true;
_updateStep( _updateStep(
'选择图片', 'chooseImage'.tr, // 选择图片
'正在打开${source == ImageSource.camera ? '相机' : '相册'}...', (source == ImageSource.camera
? 'openingCamera'.tr // 正在打开相机...
: 'openingPhotoAlbum'.tr), // 正在打开相册...
ProcessingStatus.picking); ProcessingStatus.picking);
final XFile? image = await _imagePicker.pickImage( final XFile? image = await _imagePicker.pickImage(
@ -206,7 +212,10 @@ class PictureTranslationController extends GetxController {
); );
if (image == null) { if (image == null) {
_updateStep('已取消', '用户取消了选择', ProcessingStatus.idle); _updateStep(
'cancelled'.tr, // 已取消
'userCancelledSelection'.tr, // 用户取消了选择
ProcessingStatus.idle);
return; return;
} }
@ -217,23 +226,39 @@ class PictureTranslationController extends GetxController {
pickedImageFile.value = file; pickedImageFile.value = file;
// 直接调用图片翻译API(包含识别+回贴) // 直接调用图片翻译API(包含识别+回贴)
_updateStep('翻译图片', '正在调用图片翻译服务...', ProcessingStatus.translating, _updateStep(
'translatingImage'.tr, // 翻译图片
'callingImageTranslateService'.tr, // 正在调用图片翻译服务...
ProcessingStatus.translating,
progress: 0.6); progress: 0.6);
await _performImageTranslation(file); await _performImageTranslation(file);
// 保存历史记录 // 保存历史记录
if (sourceText.value.isNotEmpty && translatedText.value.isNotEmpty) { if (sourceText.value.isNotEmpty && translatedText.value.isNotEmpty) {
_updateStep('保存记录', '正在保存翻译结果...', ProcessingStatus.saving, _updateStep(
'savingRecord'.tr, // 保存记录
'savingTranslationResult'.tr, // 正在保存翻译结果...
ProcessingStatus.saving,
progress: 0.9); progress: 0.9);
await _saveTranslationResult(file); await _saveTranslationResult(file);
} }
_updateStep('翻译完成', '所有步骤已完成', ProcessingStatus.idle, progress: 1.0); _updateStep(
'translationCompleted'.tr, // 翻译完成
'allStepsCompleted'.tr, // 所有步骤已完成
ProcessingStatus.idle,
progress: 1.0);
} catch (e) { } catch (e) {
Logger.error('PictureTranslation', '处理失败: $e'); Logger.error('PictureTranslation', '处理失败: $e');
_updateStep('处理失败', e.toString(), ProcessingStatus.error); _updateStep(
_showErrorDialog('处理失败', '图片处理或翻译失败,请重试。\n错误信息: $e'); 'processingFailed'.tr, // 处理失败
e.toString(),
ProcessingStatus.error);
_showErrorDialog(
'processingFailed'.tr, // 处理失败
'imageProcessingOrTranslationFailed'.tr.replaceAll(
'{error}', e.toString())); // 图片处理或翻译失败,请重试。错误信息: {error}
} finally { } finally {
isProcessing.value = false; isProcessing.value = false;
} }
@ -286,8 +311,13 @@ class PictureTranslationController extends GetxController {
// 图片大小限制:4MB(文档要求) // 图片大小限制:4MB(文档要求)
const int maxSize = 4 * 1024 * 1024; const int maxSize = 4 * 1024 * 1024;
if (bytes.lengthInBytes > maxSize) { if (bytes.lengthInBytes > maxSize) {
_updateStep('处理失败', '图片大小超过4MB限制', ProcessingStatus.error); _updateStep(
_showErrorDialog('处理失败', '图片大小超过4MB限制,请降低质量或尺寸后重试'); 'processingFailed'.tr, // 处理失败
'imageTooLarge'.tr, // 图片大小超过4MB限制
ProcessingStatus.error);
_showErrorDialog(
'processingFailed'.tr, // 处理失败
'imageTooLargeHint'.tr); // 图片大小超过4MB限制,请降低质量或尺寸后重试
return; return;
} }
@ -301,8 +331,13 @@ class PictureTranslationController extends GetxController {
); );
if (result == null) { if (result == null) {
_updateStep('处理失败', '图片翻译接口返回空结果', ProcessingStatus.error); _updateStep(
_showErrorDialog('处理失败', '图片翻译失败,可能的原因:网络异常或接口限流,请稍后重试'); 'processingFailed'.tr, // 处理失败
'emptyTranslationResult'.tr, // 图片翻译接口返回空结果
ProcessingStatus.error);
_showErrorDialog(
'processingFailed'.tr, // 处理失败
'imageTranslationFailedTryLater'.tr); // 失败原因提示
return; return;
} }
@ -322,8 +357,13 @@ class PictureTranslationController extends GetxController {
} }
} catch (e) { } catch (e) {
Logger.error('PictureTranslation', '图片翻译调用失败: $e'); Logger.error('PictureTranslation', '图片翻译调用失败: $e');
_updateStep('处理失败', e.toString(), ProcessingStatus.error); _updateStep(
_showErrorDialog('处理失败', '图片翻译接口调用异常,请检查网络后重试'); 'processingFailed'.tr, // 处理失败
e.toString(),
ProcessingStatus.error);
_showErrorDialog(
'processingFailed'.tr, // 处理失败
'imageTranslationApiException'.tr); // 接口调用异常
} }
} }
@ -362,7 +402,7 @@ class PictureTranslationController extends GetxController {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: const Text('确定'), child: Text('confirm'.tr), // 确认
), ),
], ],
), ),
@ -403,7 +443,9 @@ class PictureTranslationController extends GetxController {
void copyText(String text) { void copyText(String text) {
if (text.isNotEmpty) { if (text.isNotEmpty) {
// 这里可以添加复制到剪切板的逻辑 // 这里可以添加复制到剪切板的逻辑
Get.snackbar('成功', '文本已复制到剪切板'); Get.snackbar(
'success'.tr, // 成功
'copied'.tr); // 已复制
} }
} }
@ -413,6 +455,9 @@ class PictureTranslationController extends GetxController {
translatedText.value = ''; translatedText.value = '';
translatedImageBytes.value = null; translatedImageBytes.value = null;
pickedImageFile.value = null; pickedImageFile.value = null;
_updateStep('等待操作', '请选择图片开始翻译', ProcessingStatus.idle); _updateStep(
'waitingForAction'.tr, // 等待操作
'pleaseSelectImageToStart'.tr, // 请选择图片开始翻译
ProcessingStatus.idle);
} }
} }

60
lib/modules/picture_translation/views/picture_translation_history_detail_view.dart

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_colors.dart';
@ -20,8 +21,7 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50],
isDarkMode ? AppColors.darkBackground : Colors.grey[50],
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(
@ -32,7 +32,7 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
onPressed: () => Get.back(), onPressed: () => Get.back(),
), ),
centerTitle: true, centerTitle: true,
title: Text('翻译详情', title: Text('translationDetails'.tr, // 翻译详情
style: TextStyle( style: TextStyle(
color: isDarkMode ? AppColors.textPrimary : Colors.black87)), color: isDarkMode ? AppColors.textPrimary : Colors.black87)),
backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
@ -56,9 +56,15 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildLanguageInfo(), _buildLanguageInfo(),
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildTextSection('识别结果', item.sourceText, Colors.blue), _buildTextSection(
'recognitionResult'.tr, // 识别结果
item.sourceText,
Colors.blue),
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildTextSection('翻译结果', item.translatedText, Colors.green), _buildTextSection(
'translationResult'.tr, // 翻译结果
item.translatedText,
Colors.green),
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildTimeInfo(), _buildTimeInfo(),
], ],
@ -89,12 +95,11 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
Padding( Padding(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
child: Text( child: Text(
'原图片', 'originalImage'.tr, // 原图片
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: color: Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
), ),
), ),
), ),
@ -122,7 +127,7 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
size: 48.sp, color: AppColors.error), size: 48.sp, color: AppColors.error),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Text( Text(
'图片加载失败', 'imageLoadFailed'.tr, // 图片加载失败
style: TextStyle( style: TextStyle(
fontSize: 14.sp, color: AppColors.error), fontSize: 14.sp, color: AppColors.error),
), ),
@ -161,7 +166,7 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'源语言', 'sourceLanguage'.tr, // 源语言
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: Get.isDarkMode color: Get.isDarkMode
@ -170,7 +175,7 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
item.sourceLanguageName, item.sourceLanguageName.tr,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -187,7 +192,7 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Text( Text(
'目标语言', 'targetLanguage'.tr, // 目标语言
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: Get.isDarkMode color: Get.isDarkMode
@ -196,7 +201,7 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
item.targetLanguageName, item.targetLanguageName.tr,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -245,11 +250,13 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
icon: Icon(Icons.copy, size: 20.sp, color: color), icon: Icon(Icons.copy, size: 20.sp, color: color),
onPressed: () { onPressed: () {
Clipboard.setData(ClipboardData(text: content)); Clipboard.setData(ClipboardData(text: content));
Get.snackbar('成功', '已复制到剪切板'); Get.snackbar(
'success'.tr, // 成功
'copied'.tr); // 已复制
}, },
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
constraints: const BoxConstraints(), constraints: const BoxConstraints(),
tooltip: '复制', tooltip: 'copy'.tr, // 复制
), ),
], ],
), ),
@ -287,12 +294,11 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'翻译时间', 'translationTime'.tr, // 翻译时间
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: color: Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
), ),
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
@ -300,7 +306,8 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
_formatDateTime(item.timestamp), _formatDateTime(item.timestamp),
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: Get.isDarkMode ? AppColors.textSecondary : Colors.grey[600], color:
Get.isDarkMode ? AppColors.textSecondary : Colors.grey[600],
), ),
), ),
], ],
@ -309,17 +316,18 @@ class PictureTranslationHistoryDetailView extends StatelessWidget {
} }
String _formatDateTime(DateTime dateTime) { String _formatDateTime(DateTime dateTime) {
return '${dateTime.year}年${dateTime.month}月${dateTime.day}日 ' return DateFormat('yyyy-MM-dd HH:mm').format(dateTime);
'${dateTime.hour.toString().padLeft(2, '0')}:'
'${dateTime.minute.toString().padLeft(2, '0')}';
} }
void _shareTranslation() { void _shareTranslation() {
final shareText = '原文:${item.sourceText}\n\n' final shareText = '${'originalText'.tr}:${item.sourceText}\n\n' // 原文
'译文:${item.translatedText}\n\n' '${'translatedText'.tr}:${item.translatedText}\n\n' // 译文
'翻译方向:${item.sourceLanguageName} → ${item.targetLanguageName}'; '${'sourceLanguage'.tr}:${item.sourceLanguageName.tr} → ' // 源语言
'${'targetLanguage'.tr}:${item.targetLanguageName.tr}'; // 目标语言
// 这里可以调用分享插件 // 这里可以调用分享插件
Get.snackbar('提示', '分享功能开发中'); Get.snackbar(
'tip'.tr, // 提示
'shareInDevelopment'.tr); // 分享功能开发中
} }
} }

192
lib/modules/picture_translation/views/picture_translation_view.dart

@ -21,8 +21,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50],
isDarkMode ? AppColors.darkBackground : Colors.grey[50],
appBar: _buildAppBar(isDarkMode), appBar: _buildAppBar(isDarkMode),
body: SafeArea( body: SafeArea(
child: Obx(() { child: Obx(() {
@ -85,7 +84,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
IconButton( IconButton(
icon: const Icon(Icons.history), icon: const Icon(Icons.history),
onPressed: _showHistoryBottomSheet, onPressed: _showHistoryBottomSheet,
tooltip: 'historyRecords'.tr, //历史记录 tooltip: 'historyRecords'.tr, // 历史记录
), ),
PopupMenuButton<String>( PopupMenuButton<String>(
onSelected: _handleMenuAction, onSelected: _handleMenuAction,
@ -157,12 +156,11 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'选择图片来源'.tr, //选择图片来源 'selectImageSource'.tr, // 选择图片来源
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: color: Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
), ),
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
@ -277,12 +275,11 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
Padding( Padding(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
child: Text( child: Text(
'图片预览', 'imagePreview'.tr, // 图片预览
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: color: Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
), ),
), ),
), ),
@ -377,8 +374,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
), ),
); );
case ProcessingStatus.error: case ProcessingStatus.error:
return Icon(Icons.error_outline, return Icon(Icons.error_outline, color: AppColors.error, size: 20.sp);
color: AppColors.error, size: 20.sp);
case ProcessingStatus.idle: case ProcessingStatus.idle:
return Icon(Icons.check_circle_outline, return Icon(Icons.check_circle_outline,
color: AppColors.success, size: 20.sp); color: AppColors.success, size: 20.sp);
@ -423,25 +419,24 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
Padding( Padding(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
child: Text( child: Text(
'翻译结果', 'translationResult'.tr, // 翻译结果
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: color: Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
), ),
), ),
), ),
if (controller.sourceText.value.isNotEmpty) if (controller.sourceText.value.isNotEmpty)
_buildTextCard( _buildTextCard(
'识别结果', 'recognitionResult'.tr, // 识别结果
controller.sourceText.value, controller.sourceText.value,
controller.sourceLanguageChineseName.value, controller.sourceLanguageChineseName.value,
Colors.blue, Colors.blue,
), ),
if (controller.translatedText.value.isNotEmpty) if (controller.translatedText.value.isNotEmpty)
_buildTextCard( _buildTextCard(
'翻译结果', 'translationResult'.tr, // 翻译结果
controller.translatedText.value, controller.translatedText.value,
controller.targetLanguageChineseName.value, controller.targetLanguageChineseName.value,
Colors.green, Colors.green,
@ -449,22 +444,22 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
if (controller.translatedImageBytes.value != null) ...[ if (controller.translatedImageBytes.value != null) ...[
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w), padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Text( child: Text(
'翻译后的图片预览', 'translatedImagePreview'.tr, // 翻译后的图片预览
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: color:
Get.isDarkMode ? AppColors.textPrimary : Colors.black87, Get.isDarkMode ? AppColors.textPrimary : Colors.black87,
),
), ),
), ),
), SizedBox(height: 8.h),
SizedBox(height: 8.h), Container(
Container( height: 200.h,
height: 200.h, margin:
margin: EdgeInsets.symmetric(horizontal: 16.w).copyWith(bottom: 16.h),
EdgeInsets.symmetric(horizontal: 16.w).copyWith(bottom: 16.h), decoration: BoxDecoration(
decoration: BoxDecoration(
color: Get.isDarkMode color: Get.isDarkMode
? AppColors.cardBackground ? AppColors.cardBackground
: Colors.grey[100], : Colors.grey[100],
@ -491,7 +486,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
size: 48.sp, color: Colors.red[400]), size: 48.sp, color: Colors.red[400]),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Text( Text(
'翻译图片加载失败', 'translatedImageLoadFailed'.tr, // 翻译图片加载失败
style: TextStyle( style: TextStyle(
fontSize: 14.sp, color: Colors.red[600]), fontSize: 14.sp, color: Colors.red[600]),
), ),
@ -517,8 +512,8 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: color.withOpacity(Get.isDarkMode ? 0.15 : 0.05), color: color.withOpacity(Get.isDarkMode ? 0.15 : 0.05),
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
border: Border.all( border:
color: color.withOpacity(Get.isDarkMode ? 0.3 : 0.2)), Border.all(color: color.withOpacity(Get.isDarkMode ? 0.3 : 0.2)),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -538,11 +533,11 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
icon: Icon(Icons.copy, size: 18.sp, color: color), icon: Icon(Icons.copy, size: 18.sp, color: color),
onPressed: () { onPressed: () {
Clipboard.setData(ClipboardData(text: content)); Clipboard.setData(ClipboardData(text: content));
Get.snackbar('success'.tr, 'copied'.tr); // 翻译键代表的中文文本内容 Get.snackbar('success'.tr, 'copied'.tr); // 对应中文:复制成功
}, },
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
constraints: const BoxConstraints(), constraints: const BoxConstraints(),
tooltip: 'copy'.tr, // 翻译键代表的中文文本内容 tooltip: 'copy'.tr, // 复制
), ),
], ],
), ),
@ -551,8 +546,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
content, content,
style: TextStyle( style: TextStyle(
fontSize: 15.sp, fontSize: 15.sp,
color: color: Get.isDarkMode ? Colors.white70 : Colors.black87,
Get.isDarkMode ? Colors.white70 : Colors.black87,
height: 1.4), height: 1.4),
), ),
], ],
@ -584,7 +578,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'translationHistory'.tr, // 翻译键代表的中文文本内容 'translationHistory'.tr, // 翻译历史
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -594,7 +588,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
), ),
TextButton( TextButton(
onPressed: _showHistoryBottomSheet, onPressed: _showHistoryBottomSheet,
child: Text('viewAll'.tr), // 翻译键代表的中文文本内容 child: Text('viewAll'.tr), // 查看全部
), ),
], ],
), ),
@ -604,7 +598,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
padding: padding:
EdgeInsets.symmetric(horizontal: 16.w).copyWith(bottom: 16.h), EdgeInsets.symmetric(horizontal: 16.w).copyWith(bottom: 16.h),
child: Text( child: Text(
'noTranslationHistory'.tr, // 翻译键代表的中文文本内容 'noTranslationHistory'.tr, // 暂无历史记录
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: Get.isDarkMode color: Get.isDarkMode
@ -629,8 +623,9 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
child: Container( child: Container(
width: 40.w, width: 40.w,
height: 40.w, height: 40.w,
color: color: Get.isDarkMode
Get.isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[200], ? Colors.white.withOpacity(0.05)
: Colors.grey[200],
child: Icon(Icons.image, child: Icon(Icons.image,
color: Get.isDarkMode ? Colors.white70 : Colors.grey[400]), color: Get.isDarkMode ? Colors.white70 : Colors.grey[400]),
), ),
@ -642,18 +637,16 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
style: TextStyle(fontSize: 14.sp), style: TextStyle(fontSize: 14.sp),
), ),
subtitle: Text( subtitle: Text(
'${item.sourceLanguageName} → ${item.targetLanguageName}', '${item.sourceLanguageName.tr} → ${item.targetLanguageName.tr}', // 源语言名/目标语言名
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: color: Get.isDarkMode ? AppColors.textSecondary : Colors.grey[600]),
Get.isDarkMode ? AppColors.textSecondary : Colors.grey[600]),
), ),
trailing: Text( trailing: Text(
_formatTime(item.timestamp), _formatTime(item.timestamp),
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: color: Get.isDarkMode ? Colors.white54 : Colors.grey[500]),
Get.isDarkMode ? Colors.white54 : Colors.grey[500]),
), ),
onTap: () => _showHistoryDetail(item), onTap: () => _showHistoryDetail(item),
); );
@ -675,19 +668,16 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'translationHistory'.tr, // 翻译键代表的中文文本内容 'translationHistory'.tr, // 翻译历史
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Get.isDarkMode color: Get.isDarkMode ? AppColors.textPrimary : null),
? AppColors.textPrimary
: null),
), ),
IconButton( IconButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
icon: Icon(Icons.close, icon: Icon(Icons.close,
color: color: Get.isDarkMode ? AppColors.textPrimary : null),
Get.isDarkMode ? AppColors.textPrimary : null),
), ),
], ],
), ),
@ -698,7 +688,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
if (controller.historyList.isEmpty) { if (controller.historyList.isEmpty) {
return Center( return Center(
child: Text( child: Text(
'noTranslationHistory'.tr, // 翻译键代表的中文文本内容 'noTranslationHistory'.tr, // 暂无历史记录
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
color: Get.isDarkMode color: Get.isDarkMode
@ -745,13 +735,18 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
final difference = now.difference(timestamp); final difference = now.difference(timestamp);
if (difference.inDays > 0) { if (difference.inDays > 0) {
return '${difference.inDays}天前'; return 'daysAgo'
.tr
.replaceAll('@days', difference.inDays.toString()); // @days天前
} else if (difference.inHours > 0) { } else if (difference.inHours > 0) {
return '${difference.inHours}小时前'; return 'hoursAgo'
.tr
.replaceAll('@hours', difference.inHours.toString()); // @hours小时前
} else if (difference.inMinutes > 0) { } else if (difference.inMinutes > 0) {
return '${difference.inMinutes}分钟前'; return 'minutesAgo'.tr.replaceAll(
'@minutes', difference.inMinutes.toString()); // @minutes分钟前
} else { } else {
return '刚刚'; return 'justNow'.tr; // 刚刚
} }
} }
@ -759,7 +754,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
Get.dialog( Get.dialog(
AlertDialog( AlertDialog(
backgroundColor: Get.isDarkMode ? Colors.grey[900] : Colors.white, backgroundColor: Get.isDarkMode ? Colors.grey[900] : Colors.white,
title: Text('设置', title: Text('settings'.tr, // 设置
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white : Colors.black87)), color: Get.isDarkMode ? Colors.white : Colors.black87)),
content: Column( content: Column(
@ -768,16 +763,17 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
ListTile( ListTile(
leading: Icon(Icons.photo_size_select_actual, leading: Icon(Icons.photo_size_select_actual,
color: Get.isDarkMode ? Colors.white : null), color: Get.isDarkMode ? Colors.white : null),
title: Text('图片质量', title: Text('imageQuality'.tr, // 图片质量
style: TextStyle(
color: Get.isDarkMode ? Colors.white : Colors.black87)),
subtitle: Obx(() => Text(
'currentQuality'.tr.replaceAll(
'@value',
controller.imageQuality.value
.toString()), // 当前质量: @value%
style: TextStyle( style: TextStyle(
color: color:
Get.isDarkMode ? Colors.white : Colors.black87)), Get.isDarkMode ? Colors.white70 : Colors.black54))),
subtitle:
Obx(() => Text('当前: ${controller.imageQuality.value}%',
style: TextStyle(
color: Get.isDarkMode
? Colors.white70
: Colors.black54))),
onTap: () { onTap: () {
Get.back(); Get.back();
_showImageQualityDialog(); _showImageQualityDialog();
@ -786,16 +782,15 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
ListTile( ListTile(
leading: Icon(Icons.photo_size_select_large, leading: Icon(Icons.photo_size_select_large,
color: Get.isDarkMode ? Colors.white : null), color: Get.isDarkMode ? Colors.white : null),
title: Text('最大尺寸', title: Text('maxSize'.tr, // 最大尺寸
style: TextStyle(
color: Get.isDarkMode ? Colors.white : Colors.black87)),
subtitle: Obx(() => Text(
'currentMaxSize'.tr.replaceAll('@px',
controller.maxImageSize.value.toString()), // 当前: @pxpx
style: TextStyle( style: TextStyle(
color: color:
Get.isDarkMode ? Colors.white : Colors.black87)), Get.isDarkMode ? Colors.white70 : Colors.black54))),
subtitle:
Obx(() => Text('当前: ${controller.maxImageSize.value}px',
style: TextStyle(
color: Get.isDarkMode
? Colors.white70
: Colors.black54))),
onTap: () { onTap: () {
Get.back(); Get.back();
_showImageSizeDialog(); _showImageSizeDialog();
@ -806,7 +801,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text('关闭', child: Text('close'.tr, // 关闭
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white70 : Colors.black54)), color: Get.isDarkMode ? Colors.white70 : Colors.black54)),
), ),
@ -819,13 +814,17 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
Get.dialog( Get.dialog(
AlertDialog( AlertDialog(
backgroundColor: Get.isDarkMode ? Colors.grey[900] : Colors.white, backgroundColor: Get.isDarkMode ? Colors.grey[900] : Colors.white,
title: Text('设置图片质量', title: Text('setImageQuality'.tr, // 设置图片质量
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white : Colors.black87)), color: Get.isDarkMode ? Colors.white : Colors.black87)),
content: Obx(() => Column( content: Obx(() => Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text('当前质量: ${controller.imageQuality.value}%', Text(
'currentQuality'.tr.replaceAll(
'@value',
controller.imageQuality.value
.toString()), // 当前质量: @value%
style: TextStyle( style: TextStyle(
color: color:
Get.isDarkMode ? Colors.white70 : Colors.black54)), Get.isDarkMode ? Colors.white70 : Colors.black54)),
@ -843,7 +842,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text('确定', child: Text('confirm'.tr, // 确认
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white70 : Colors.black54)), color: Get.isDarkMode ? Colors.white70 : Colors.black54)),
), ),
@ -857,7 +856,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
Get.dialog( Get.dialog(
AlertDialog( AlertDialog(
backgroundColor: Get.isDarkMode ? Colors.grey[900] : Colors.white, backgroundColor: Get.isDarkMode ? Colors.grey[900] : Colors.white,
title: Text('设置最大尺寸', title: Text('setMaxSize'.tr, // 设置最大尺寸
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white : Colors.black87)), color: Get.isDarkMode ? Colors.white : Colors.black87)),
content: Column( content: Column(
@ -879,7 +878,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text('取消', child: Text('cancel'.tr, // 取消
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white70 : Colors.black54)), color: Get.isDarkMode ? Colors.white70 : Colors.black54)),
), ),
@ -892,16 +891,16 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
Get.dialog( Get.dialog(
AlertDialog( AlertDialog(
backgroundColor: Get.isDarkMode ? Colors.grey[900] : Colors.white, backgroundColor: Get.isDarkMode ? Colors.grey[900] : Colors.white,
title: Text('清空历史', title: Text('clearHistory'.tr, // 清除历史记录
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white : Colors.black87)), color: Get.isDarkMode ? Colors.white : Colors.black87)),
content: Text('确定要清空所有翻译历史吗?此操作不可撤销。', content: Text('clearHistoryConfirm'.tr, // 确定要清除所有翻译历史记录吗?此操作无法撤销。
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white70 : Colors.black54)), color: Get.isDarkMode ? Colors.white70 : Colors.black54)),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text('取消', child: Text('cancel'.tr, // 取消
style: TextStyle( style: TextStyle(
color: Get.isDarkMode ? Colors.white70 : Colors.black54)), color: Get.isDarkMode ? Colors.white70 : Colors.black54)),
), ),
@ -910,7 +909,8 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
controller.clearHistory(); controller.clearHistory();
Get.back(); Get.back();
}, },
child: Text('确定', style: TextStyle(color: AppColors.error)), child: Text('confirm'.tr, // 确认
style: TextStyle(color: AppColors.error)),
), ),
], ],
), ),
@ -923,11 +923,10 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
height: 200.h, height: 200.h,
margin: EdgeInsets.symmetric(horizontal: 16.w).copyWith(bottom: 16.h), margin: EdgeInsets.symmetric(horizontal: 16.w).copyWith(bottom: 16.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: color: Get.isDarkMode ? AppColors.cardBackground : Colors.grey[100],
Get.isDarkMode ? AppColors.cardBackground : Colors.grey[100],
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
border: border: Border.all(
Border.all(color: Get.isDarkMode ? Colors.white12 : Colors.grey[300]!), color: Get.isDarkMode ? Colors.white12 : Colors.grey[300]!),
), ),
child: file == null child: file == null
? Center( ? Center(
@ -936,10 +935,11 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
children: [ children: [
Icon(Icons.image_outlined, Icon(Icons.image_outlined,
size: 48.sp, size: 48.sp,
color: Get.isDarkMode ? Colors.white54 : Colors.grey[400]), color:
Get.isDarkMode ? Colors.white54 : Colors.grey[400]),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Text( Text(
'请选择图片', 'pleaseSelectImage'.tr, // 请选择图片
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: Get.isDarkMode color: Get.isDarkMode
@ -967,7 +967,7 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
size: 48.sp, color: Colors.red[400]), size: 48.sp, color: Colors.red[400]),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Text( Text(
'图片加载失败', 'imageLoadFailed'.tr, // 图片加载失败
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: Get.isDarkMode color: Get.isDarkMode
@ -985,11 +985,15 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
} }
Widget _buildSourceTextBox() { Widget _buildSourceTextBox() {
return _buildTextBox('识别结果', controller.sourceText.value); return _buildTextBox(
'recognitionResult'.tr, // 识别结果
controller.sourceText.value);
} }
Widget _buildTranslatedTextBox() { Widget _buildTranslatedTextBox() {
return _buildTextBox('翻译结果', controller.translatedText.value); return _buildTextBox(
'translationResult'.tr, // 翻译结果
controller.translatedText.value);
} }
Widget _buildTextBox(String title, String content) { Widget _buildTextBox(String title, String content) {

8
lib/modules/text_translation/controllers/text_translation_controller.dart

@ -257,8 +257,8 @@ class TextTranslationController extends GetxController {
if (detectedAsrCode == null || detectedAsrCode.isEmpty) { if (detectedAsrCode == null || detectedAsrCode.isEmpty) {
Logger.error('语种检测失败'); Logger.error('语种检测失败');
Get.snackbar( Get.snackbar(
'error'.tr, // 翻译键代表的中文文本内容 'error'.tr, // 错误
'languageDetectionFailed'.tr, // 翻译键代表的中文文本内容 'languageDetectionFailed'.tr, // 语言检测失败
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.9), backgroundColor: Colors.red.withOpacity(0.9),
colorText: Colors.white, colorText: Colors.white,
@ -288,8 +288,8 @@ class TextTranslationController extends GetxController {
} catch (e) { } catch (e) {
Logger.error('翻译失败: $e'); Logger.error('翻译失败: $e');
Get.snackbar( Get.snackbar(
'error'.tr, 'error'.tr, // 错误
'translationFailed'.tr, 'translationFailed'.tr, // 翻译失败
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.9), backgroundColor: Colors.red.withOpacity(0.9),
colorText: Colors.white, colorText: Colors.white,

8
lib/modules/text_translation/views/text_translation_view.dart

@ -99,10 +99,12 @@ class TextTranslationView extends GetView<TextTranslationController> {
return LanguageSelectorBar( return LanguageSelectorBar(
sourceText: () => controller.sourceLanguageChineseName.value, sourceText: () => controller.sourceLanguageChineseName.value,
targetText: () => controller.targetLanguageChineseName.value, targetText: () => controller.targetLanguageChineseName.value,
onTapSource: () => controller.showLanguageSelector(isSourceLanguage: true), onTapSource: () =>
onTapTarget: () => controller.showLanguageSelector(isSourceLanguage: false), controller.showLanguageSelector(isSourceLanguage: true),
onTapTarget: () =>
controller.showLanguageSelector(isSourceLanguage: false),
onSwap: controller.swapLanguages, onSwap: controller.swapLanguages,
translateLanguageNames: true, // 翻译键代表的中文文本内容 translateLanguageNames: true, // 是否翻译语言名称
fontSizeSp: 15, fontSizeSp: 15,
); );
} }

2
lib/modules/translation/middleware/controllers/translation_middleware_controller.dart

@ -91,7 +91,7 @@ class TranslationMiddlewareController extends GetxController {
void navigateToTranslationFeature(TranslationOption option) { void navigateToTranslationFeature(TranslationOption option) {
// 不可用则提示用户连接设备 // 不可用则提示用户连接设备
if (!isOptionEnabled(option)) { if (!isOptionEnabled(option)) {
// 翻译键代表的中文文本内容 // 连接设备 / 请连接 Voitrans 设备
Get.snackbar('connectDevice'.tr, Get.snackbar('connectDevice'.tr,
'pleaseConnectDeepSoundDevice'.tr, // 连接设备 / 请连接 Voitrans 设备 'pleaseConnectDeepSoundDevice'.tr, // 连接设备 / 请连接 Voitrans 设备
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,

8
lib/modules/translation/views/dialogs/language_selection_dialog.dart

@ -65,8 +65,8 @@ class LanguageSelectionDialog {
padding: EdgeInsets.symmetric(horizontal: 8.w), padding: EdgeInsets.symmetric(horizontal: 8.w),
child: Text( child: Text(
isSource isSource
? 'selectSourceLanguage'.tr // 翻译键代表的中文文本内容 ? 'selectSourceLanguage'.tr // 选择源语言
: 'selectTargetLanguage'.tr, // 翻译键代表的中文文本内容 : 'selectTargetLanguage'.tr, // 选择目标语言
style: TextStyle( style: TextStyle(
fontSize: 18.sp, // 调整字号 fontSize: 18.sp, // 调整字号
fontWeight: FontWeight.w600, // 使用数字字重 fontWeight: FontWeight.w600, // 使用数字字重
@ -94,7 +94,7 @@ class LanguageSelectionDialog {
: displayLanguages[ : displayLanguages[
hasAutoDetect ? index - 1 : index]; hasAutoDetect ? index - 1 : index];
final displayText = isAutoDetectItem final displayText = isAutoDetectItem
? 'autoDetect'.tr // 翻译键代表的中文文本内容 ? 'autoDetect'.tr // 自动检测
: (translateLanguageNames : (translateLanguageNames
? valueText.tr ? valueText.tr
: valueText); : valueText);
@ -196,7 +196,7 @@ class LanguageSelectionDialog {
child: TextButton( child: TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text( child: Text(
'cancel'.tr, // 翻译键代表的中文文本内容 'cancel'.tr, // 取消
style: TextStyle( style: TextStyle(
color: isDarkMode ? const Color(0xFF6200EE) : Colors.blue, color: isDarkMode ? const Color(0xFF6200EE) : Colors.blue,
), ),

4
lib/modules/translation/views/translation_view.dart

@ -314,7 +314,7 @@ class TranslationView extends GetView<TranslationController> {
onTapTarget: () => onTapTarget: () =>
LanguageSelectionDialog.show(false, isDarkMode), LanguageSelectionDialog.show(false, isDarkMode),
onSwap: controller.swapLanguages, onSwap: controller.swapLanguages,
translateLanguageNames: true, // 翻译键代表的中文文本内容 translateLanguageNames: true, // 是否翻译语言名称
fontSizeSp: 15, fontSizeSp: 15,
targetSuffix: controller.currentMode.value == 'call' targetSuffix: controller.currentMode.value == 'call'
? Icon( ? Icon(
@ -1019,7 +1019,7 @@ class TranslationView extends GetView<TranslationController> {
onTapSource: () => LanguageSelectionDialog.show(true, isDarkMode), onTapSource: () => LanguageSelectionDialog.show(true, isDarkMode),
onTapTarget: () => LanguageSelectionDialog.show(false, isDarkMode), onTapTarget: () => LanguageSelectionDialog.show(false, isDarkMode),
onSwap: controller.swapLanguages, onSwap: controller.swapLanguages,
translateLanguageNames: true, // 翻译键代表的中文文本内容 translateLanguageNames: true, // 是否翻译语言名称
fontSizeSp: 15, fontSizeSp: 15,
), ),
); );

4
lib/modules/translation/views/widgets/translation_item_widget.dart

@ -140,7 +140,7 @@ class TranslationItemWidget extends GetView<TranslationController> {
child: Container( child: Container(
padding: EdgeInsets.all(4.w), padding: EdgeInsets.all(4.w),
child: Tooltip( child: Tooltip(
message: 'copy'.tr, // 翻译键代表的中文文本内容 message: 'copy'.tr, // 复制
child: Icon( child: Icon(
Icons.copy, Icons.copy,
size: 20.sp, size: 20.sp,
@ -169,7 +169,7 @@ class TranslationItemWidget extends GetView<TranslationController> {
child: Container( child: Container(
padding: EdgeInsets.all(4.w), padding: EdgeInsets.all(4.w),
child: Tooltip( child: Tooltip(
message: 'delete'.tr, // 翻译键代表的中文文本内容 message: 'delete'.tr, // 删除
child: Icon( child: Icon(
Icons.delete_outline, Icons.delete_outline,
size: 20.sp, size: 20.sp,

72
lib/modules/usage/controllers/meeting_stats_controller.dart

@ -8,34 +8,32 @@ class MeetingStatsController extends GetxController {
final isLoading = false.obs; final isLoading = false.obs;
final selectedTabIndex = 0.obs; final selectedTabIndex = 0.obs;
final selectedTimeRange = '今日'.obs; final selectedTimeRange = 'today'.tr.obs; // 今日
final Rx<MeetingAsrHistoryStats> todayStats = final Rx<MeetingAsrHistoryStats> todayStats = MeetingAsrHistoryStats(
MeetingAsrHistoryStats( userId: '',
userId: '', allAsrCalls: 0,
allAsrCalls: 0, allAudioSeconds: 0,
allAudioSeconds: 0, allAudioFileBytes: 0,
allAudioFileBytes: 0, allTranscriptBytes: 0,
allTranscriptBytes: 0, ).obs;
).obs; final Rx<MeetingAsrHistoryStats> weekStats = MeetingAsrHistoryStats(
final Rx<MeetingAsrHistoryStats> weekStats = userId: '',
MeetingAsrHistoryStats( allAsrCalls: 0,
userId: '', allAudioSeconds: 0,
allAsrCalls: 0, allAudioFileBytes: 0,
allAudioSeconds: 0, allTranscriptBytes: 0,
allAudioFileBytes: 0, ).obs;
allTranscriptBytes: 0, final Rx<MeetingAsrHistoryStats> monthStats = MeetingAsrHistoryStats(
).obs; userId: '',
final Rx<MeetingAsrHistoryStats> monthStats = allAsrCalls: 0,
MeetingAsrHistoryStats( allAudioSeconds: 0,
userId: '', allAudioFileBytes: 0,
allAsrCalls: 0, allTranscriptBytes: 0,
allAudioSeconds: 0, ).obs;
allAudioFileBytes: 0,
allTranscriptBytes: 0, final RxList<MeetingAsrDailyStats> historyDaily =
).obs; <MeetingAsrDailyStats>[].obs;
final RxList<MeetingAsrDailyStats> historyDaily = <MeetingAsrDailyStats>[].obs;
@override @override
void onInit() { void onInit() {
@ -72,7 +70,8 @@ class MeetingStatsController extends GetxController {
final historyStart = now.subtract(const Duration(days: 7)); final historyStart = now.subtract(const Duration(days: 7));
historyDaily.value = await _usageService.getMeetingHistoryStats( historyDaily.value = await _usageService.getMeetingHistoryStats(
startDate: DateTime(historyStart.year, historyStart.month, historyStart.day), startDate:
DateTime(historyStart.year, historyStart.month, historyStart.day),
endDate: DateTime(now.year, now.month, now.day), endDate: DateTime(now.year, now.month, now.day),
); );
@ -88,16 +87,16 @@ class MeetingStatsController extends GetxController {
selectedTabIndex.value = index; selectedTabIndex.value = index;
switch (index) { switch (index) {
case 0: case 0:
selectedTimeRange.value = '今日'; selectedTimeRange.value = 'today'.tr; // 今日
break; break;
case 1: case 1:
selectedTimeRange.value = '本周'; selectedTimeRange.value = 'thisWeek'.tr; // 本周
break; break;
case 2: case 2:
selectedTimeRange.value = '本月'; selectedTimeRange.value = 'thisMonth'.tr; // 本月
break; break;
case 3: case 3:
selectedTimeRange.value = '历史'; selectedTimeRange.value = 'history'.tr; // 历史
break; break;
} }
} }
@ -117,15 +116,15 @@ class MeetingStatsController extends GetxController {
String formatDuration(int seconds) { String formatDuration(int seconds) {
if (seconds < 60) { if (seconds < 60) {
return '$seconds秒'; return '$seconds${'seconds'.tr}'; // 秒
} else if (seconds < 3600) { } else if (seconds < 3600) {
final minutes = seconds ~/ 60; final minutes = seconds ~/ 60;
final remainingSeconds = seconds % 60; final remainingSeconds = seconds % 60;
return '$minutes分$remainingSeconds秒'; return '$minutes${'minutes'.tr}$remainingSeconds${'seconds'.tr}'; // 分钟秒
} else { } else {
final hours = seconds ~/ 3600; final hours = seconds ~/ 3600;
final minutes = (seconds % 3600) ~/ 60; final minutes = (seconds % 3600) ~/ 60;
return '$hours小时$minutes分'; return '$hours${'hours'.tr}$minutes${'minutes'.tr}'; // 小时分钟
} }
} }
@ -149,4 +148,3 @@ class MeetingStatsController extends GetxController {
await loadAllStats(); await loadAllStats();
} }
} }

34
lib/modules/usage/controllers/translation_stats_controller.dart

@ -9,7 +9,7 @@ class TranslationStatsController extends GetxController {
final isLoading = false.obs; final isLoading = false.obs;
final selectedTabIndex = 0.obs; final selectedTabIndex = 0.obs;
final selectedTimeRange = '今日'.obs; final selectedTimeRange = 'today'.tr.obs; // 今日
final Rx<TranslationUsageStats> todayStats = final Rx<TranslationUsageStats> todayStats =
TranslationUsageStats.empty('').obs; TranslationUsageStats.empty('').obs;
@ -62,8 +62,8 @@ class TranslationStatsController extends GetxController {
} catch (e) { } catch (e) {
Logger.error('加载统计数据失败: $e'); Logger.error('加载统计数据失败: $e');
Get.snackbar( Get.snackbar(
'加载失败', 'loadFailed'.tr, // 加载失败
'统计数据加载失败,请稍后重试', 'statsLoadFailedTryLater'.tr, // 统计数据加载失败,请稍后重试
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.error.withValues(alpha: 0.1), backgroundColor: Get.theme.colorScheme.error.withValues(alpha: 0.1),
colorText: Get.theme.colorScheme.error, colorText: Get.theme.colorScheme.error,
@ -180,7 +180,7 @@ class TranslationStatsController extends GetxController {
switch (selectedTabIndex.value) { switch (selectedTabIndex.value) {
case 0: case 0:
data.add({ data.add({
'label': '今日', 'label': 'today'.tr, // 今日
'apiCalls': todayStats.value.totalApiCalls, 'apiCalls': todayStats.value.totalApiCalls,
'cost': 0.0, 'cost': 0.0,
'characters': todayStats.value.totalCharacterCount, 'characters': todayStats.value.totalCharacterCount,
@ -188,7 +188,7 @@ class TranslationStatsController extends GetxController {
break; break;
case 1: case 1:
data.add({ data.add({
'label': '本周', 'label': 'thisWeek'.tr, // 本周
'apiCalls': weekStats.value.totalApiCalls, 'apiCalls': weekStats.value.totalApiCalls,
'cost': 0.0, 'cost': 0.0,
'characters': weekStats.value.totalCharacterCount, 'characters': weekStats.value.totalCharacterCount,
@ -196,7 +196,7 @@ class TranslationStatsController extends GetxController {
break; break;
case 2: case 2:
data.add({ data.add({
'label': '本月', 'label': 'thisMonth'.tr, // 本月
'apiCalls': monthStats.value.totalApiCalls, 'apiCalls': monthStats.value.totalApiCalls,
'cost': 0.0, 'cost': 0.0,
'characters': monthStats.value.totalCharacterCount, 'characters': monthStats.value.totalCharacterCount,
@ -222,16 +222,16 @@ class TranslationStatsController extends GetxController {
switch (index) { switch (index) {
case 0: case 0:
selectedTimeRange.value = '今日'; selectedTimeRange.value = 'today'.tr; // 今日
break; break;
case 1: case 1:
selectedTimeRange.value = '本周'; selectedTimeRange.value = 'thisWeek'.tr; // 本周
break; break;
case 2: case 2:
selectedTimeRange.value = '本月'; selectedTimeRange.value = 'thisMonth'.tr; // 本月
break; break;
case 3: case 3:
selectedTimeRange.value = '历史'; selectedTimeRange.value = 'history'.tr; // 历史
break; break;
} }
@ -258,8 +258,8 @@ class TranslationStatsController extends GetxController {
void cleanupOldData() { void cleanupOldData() {
_usageService.cleanupOldData(); _usageService.cleanupOldData();
Get.snackbar( Get.snackbar(
'清理完成', 'cleanupComplete'.tr, // 清理完成
'已清理90天前的过期数据', 'cleanedOldData'.tr, // 已清理90天前的过期数据
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.primary.withValues(alpha: 0.1), backgroundColor: Get.theme.colorScheme.primary.withValues(alpha: 0.1),
colorText: Get.theme.colorScheme.primary, colorText: Get.theme.colorScheme.primary,
@ -269,8 +269,8 @@ class TranslationStatsController extends GetxController {
Future<void> exportStats() async { Future<void> exportStats() async {
try { try {
Get.snackbar( Get.snackbar(
'导出功能', 'exportFeature'.tr, // 导出功能
'数据导出功能正在开发中', 'exportInDevelopment'.tr, // 数据导出功能正在开发中
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.primary.withValues(alpha: 0.1), backgroundColor: Get.theme.colorScheme.primary.withValues(alpha: 0.1),
colorText: Get.theme.colorScheme.primary, colorText: Get.theme.colorScheme.primary,
@ -282,15 +282,15 @@ class TranslationStatsController extends GetxController {
String formatDuration(int seconds) { String formatDuration(int seconds) {
if (seconds < 60) { if (seconds < 60) {
return '$seconds秒'; return '$seconds${'seconds'.tr}'; // 秒
} else if (seconds < 3600) { } else if (seconds < 3600) {
final minutes = seconds ~/ 60; final minutes = seconds ~/ 60;
final remainingSeconds = seconds % 60; final remainingSeconds = seconds % 60;
return '$minutes分$remainingSeconds秒'; return '$minutes${'minutes'.tr}$remainingSeconds${'seconds'.tr}'; // 分钟秒
} else { } else {
final hours = seconds ~/ 3600; final hours = seconds ~/ 3600;
final minutes = (seconds % 3600) ~/ 60; final minutes = (seconds % 3600) ~/ 60;
return '$hours小时$minutes分'; return '$hours${'hours'.tr}$minutes${'minutes'.tr}'; // 小时分钟
} }
} }

4
lib/modules/usage/views/aichat_stats_view.dart

@ -29,7 +29,7 @@ class AiChatStatsView extends GetView<AiChatStatsController> {
backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.white, backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.white,
elevation: 0.5, elevation: 0.5,
title: Text( title: Text(
'AI chat', 'aiChat'.tr, // AI聊天
style: TextStyle( style: TextStyle(
fontSize: 17.sp, fontSize: 17.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -70,7 +70,7 @@ class AiChatStatsView extends GetView<AiChatStatsController> {
SizedBox(width: 8.w), SizedBox(width: 8.w),
Expanded( Expanded(
child: Text( child: Text(
'AI聊天积分', 'remainingQuota'.tr,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,

22
lib/modules/usage/views/meeting_stats_view.dart

@ -31,7 +31,7 @@ class MeetingStatsView extends GetView<MeetingStatsController> {
backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.white, backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.white,
elevation: 0.5, elevation: 0.5,
title: Text( title: Text(
'会议统计', 'meetingStats'.tr, // 会议统计
style: TextStyle( style: TextStyle(
fontSize: 17.sp, fontSize: 17.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -91,7 +91,7 @@ class MeetingStatsView extends GetView<MeetingStatsController> {
SizedBox(width: 8.w), SizedBox(width: 8.w),
Expanded( Expanded(
child: Text( child: Text(
'会议积分', 'remainingQuota'.tr,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -127,10 +127,10 @@ class MeetingStatsView extends GetView<MeetingStatsController> {
), ),
child: Obx(() => Row( child: Obx(() => Row(
children: [ children: [
_buildTimeRangeTab('今日', 0, isDarkMode), _buildTimeRangeTab('today'.tr, 0, isDarkMode), // 今日
_buildTimeRangeTab('本周', 1, isDarkMode), _buildTimeRangeTab('thisWeek'.tr, 1, isDarkMode), // 本周
_buildTimeRangeTab('本月', 2, isDarkMode), _buildTimeRangeTab('thisMonth'.tr, 2, isDarkMode), // 本月
_buildTimeRangeTab('历史', 3, isDarkMode), _buildTimeRangeTab('history'.tr, 3, isDarkMode), // 历史
], ],
)), )),
); );
@ -186,7 +186,7 @@ class MeetingStatsView extends GetView<MeetingStatsController> {
Icon(Icons.dashboard, color: AppColors.primary, size: 20.sp), Icon(Icons.dashboard, color: AppColors.primary, size: 20.sp),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Obx(() => Text( Obx(() => Text(
'${controller.selectedTimeRange.value}概览', '${controller.selectedTimeRange.value}${'overview'.tr}',
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -199,13 +199,13 @@ class MeetingStatsView extends GetView<MeetingStatsController> {
children: [ children: [
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'ASR调用次数', 'asrApiCalls'.tr, // ASR调用次数
controller.formatNumber(stats.allAsrCalls), controller.formatNumber(stats.allAsrCalls),
Icons.api, Icons.api,
isDarkMode)), isDarkMode)),
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'音频总时长', 'totalAudioDuration'.tr, // 音频总时长
controller.formatDuration(stats.allAudioSeconds), controller.formatDuration(stats.allAudioSeconds),
Icons.mic, Icons.mic,
isDarkMode)), isDarkMode)),
@ -216,13 +216,13 @@ class MeetingStatsView extends GetView<MeetingStatsController> {
children: [ children: [
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'音频文件大小', 'audioFileSize'.tr, // 音频文件大小
controller.formatBytes(stats.allAudioFileBytes), controller.formatBytes(stats.allAudioFileBytes),
Icons.folder, Icons.folder,
isDarkMode)), isDarkMode)),
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'转写字节', 'transcriptBytes'.tr, // 转写字节
controller.formatNumber(stats.allTranscriptBytes), controller.formatNumber(stats.allTranscriptBytes),
Icons.text_snippet, Icons.text_snippet,
isDarkMode)), isDarkMode)),

76
lib/modules/usage/views/translation_stats_view.dart

@ -36,7 +36,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
title: GestureDetector( title: GestureDetector(
onTap: controller.onTitleTap, onTap: controller.onTitleTap,
child: Text( child: Text(
'翻译统计', 'translationStats'.tr, //
style: TextStyle( style: TextStyle(
fontSize: 17.sp, fontSize: 17.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -143,7 +143,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
SizedBox(width: 8.w), SizedBox(width: 8.w),
Expanded( Expanded(
child: Text( child: Text(
'翻译积分', 'remainingQuota'.tr,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -180,10 +180,10 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
child: Obx(() => Row( child: Obx(() => Row(
children: [ children: [
_buildTimeRangeTab('今日', 0, isDarkMode), _buildTimeRangeTab('today'.tr, 0, isDarkMode), // 今日
_buildTimeRangeTab('本周', 1, isDarkMode), _buildTimeRangeTab('thisWeek'.tr, 1, isDarkMode), // 本周
_buildTimeRangeTab('本月', 2, isDarkMode), _buildTimeRangeTab('thisMonth'.tr, 2, isDarkMode), // 本月
_buildTimeRangeTab('历史', 3, isDarkMode), _buildTimeRangeTab('history'.tr, 3, isDarkMode), // 历史
], ],
)), )),
); );
@ -245,7 +245,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'当前会话统计', 'currentSessionStats'.tr, // 当前会话统计
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -259,7 +259,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
children: [ children: [
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'翻译次数', 'translationCount'.tr, // 翻译次数
controller.realtimeStats.translationCount.toString(), controller.realtimeStats.translationCount.toString(),
Icons.translate, Icons.translate,
isDarkMode, isDarkMode,
@ -267,8 +267,8 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'会话时长', 'sessionDuration'.tr, // 会话时长
'${controller.realtimeStats.sessionDurationMinutes}分钟', '${controller.realtimeStats.sessionDurationMinutes}${'minutes'.tr}',
Icons.timer, Icons.timer,
isDarkMode, isDarkMode,
), ),
@ -280,7 +280,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
children: [ children: [
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'字符数', 'characters'.tr, // 字符数
controller.formatNumber( controller.formatNumber(
controller.realtimeStats.sourceCharacters + controller.realtimeStats.sourceCharacters +
controller.realtimeStats.targetCharacters, controller.realtimeStats.targetCharacters,
@ -291,7 +291,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'录音时长', 'recordingDuration'.tr, // 录音时长
controller.formatDuration( controller.formatDuration(
controller.realtimeStats.recordingDuration), controller.realtimeStats.recordingDuration),
Icons.mic, Icons.mic,
@ -331,7 +331,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Obx(() => Text( Obx(() => Text(
'${controller.selectedTimeRange.value}概览', '${controller.selectedTimeRange.value}${'overview'.tr}',
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -345,7 +345,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
children: [ children: [
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'总调用次数', 'totalApiCalls'.tr, // 总调用次数
controller controller
.formatNumber(controller.currentStats.totalApiCalls), .formatNumber(controller.currentStats.totalApiCalls),
Icons.api, Icons.api,
@ -359,7 +359,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
children: [ children: [
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'会话数量', 'sessionCount'.tr, // 会话数量
controller.currentStats.sessionCount.toString(), controller.currentStats.sessionCount.toString(),
Icons.chat, Icons.chat,
isDarkMode, isDarkMode,
@ -367,7 +367,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'总字符数', 'totalCharacters'.tr, // 总字符数
controller.formatNumber( controller.formatNumber(
controller.currentStats.totalCharacterCount), controller.currentStats.totalCharacterCount),
Icons.text_snippet, Icons.text_snippet,
@ -437,7 +437,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'API调用统计', 'apiCallStats'.tr, // API调用统计
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -450,19 +450,25 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
Obx(() => Column( Obx(() => Column(
children: [ children: [
_buildApiCallRow( _buildApiCallRow(
'翻译API', 'translationApi'.tr, // 翻译API
controller.currentStats.translationApiCalls, controller.currentStats.translationApiCalls,
Icons.translate, Icons.translate,
isDarkMode), isDarkMode),
SizedBox(height: 12.h), SizedBox(height: 12.h),
_buildApiCallRow('语音识别', controller.currentStats.asrApiCalls, _buildApiCallRow(
Icons.mic, isDarkMode), 'asr'.tr,
controller.currentStats.asrApiCalls,
Icons.mic,
isDarkMode),
SizedBox(height: 12.h), SizedBox(height: 12.h),
_buildApiCallRow('语音合成', controller.currentStats.ttsApiCalls, _buildApiCallRow(
Icons.volume_up, isDarkMode), 'tts'.tr,
controller.currentStats.ttsApiCalls,
Icons.volume_up,
isDarkMode),
SizedBox(height: 12.h), SizedBox(height: 12.h),
_buildApiCallRow( _buildApiCallRow(
'AI摘要', 'aiSummary'.tr, // AI总结
controller.currentStats.aiSummaryApiCalls, controller.currentStats.aiSummaryApiCalls,
Icons.summarize, Icons.summarize,
isDarkMode), isDarkMode),
@ -530,7 +536,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'数据量统计', 'dataVolumeStats'.tr, // 数据量统计
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -546,7 +552,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
children: [ children: [
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'源文本字符', 'sourceTextCharacters'.tr, // 源文本字符
controller.formatNumber( controller.formatNumber(
controller.currentStats.sourceTextCharacters), controller.currentStats.sourceTextCharacters),
Icons.text_fields, Icons.text_fields,
@ -555,7 +561,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'TTS文本字符', 'ttsTextCharacters'.tr, // TTS文本字符
controller.formatNumber( controller.formatNumber(
controller.currentStats.ttsTextCharacters), controller.currentStats.ttsTextCharacters),
Icons.translate, Icons.translate,
@ -569,7 +575,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
children: [ children: [
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'有效音频时长', 'effectiveAudioDuration'.tr, // 有效音频时长
controller.formatDuration( controller.formatDuration(
controller.currentStats.effectiveAudioDuration), controller.currentStats.effectiveAudioDuration),
Icons.mic, Icons.mic,
@ -578,7 +584,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
Expanded( Expanded(
child: _buildStatItem( child: _buildStatItem(
'总会话时长', 'totalSessionDuration'.tr, // 总会话时长
controller.formatDuration( controller.formatDuration(
controller.currentStats.totalSessionDuration), controller.currentStats.totalSessionDuration),
Icons.volume_up, Icons.volume_up,
@ -620,7 +626,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'成本统计', 'costStats'.tr, // 成本统计
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -642,7 +648,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'${controller.selectedTimeRange.value}预估成本', '${controller.selectedTimeRange.value}${'estimatedCost'.tr}',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -666,7 +672,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'日成本预警阈值', 'dailyCostThreshold'.tr, // 日成本预警阈值
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode ? Colors.white70 : Colors.black54,
@ -687,7 +693,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'月成本预警阈值', 'monthlyCostThreshold'.tr, // 月成本预警阈值
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode ? Colors.white70 : Colors.black54,
@ -736,7 +742,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'使用行为统计', 'usageBehaviorStats'.tr, // 使用行为统计
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -749,14 +755,14 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
Obx(() => Column( Obx(() => Column(
children: [ children: [
_buildUsageBehaviorRow( _buildUsageBehaviorRow(
'最常用语言对', 'mostUsedLanguagePair'.tr, // 最常用语言对
controller.currentStats.mostUsedLanguagePair, controller.currentStats.mostUsedLanguagePair,
Icons.language, Icons.language,
isDarkMode, isDarkMode,
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
_buildUsageBehaviorRow( _buildUsageBehaviorRow(
'最常用模式', 'mostUsedMode'.tr, // 最常用模式
controller.currentStats.mostUsedMode, controller.currentStats.mostUsedMode,
Icons.settings, Icons.settings,
isDarkMode, isDarkMode,

2
lib/modules/usage/views/usage_view.dart

@ -43,7 +43,7 @@ class UsageView extends GetView<UsageController> {
// 4. 使用生成的导航项 // 4. 使用生成的导航项
items: (controller.showAiChat.value items: (controller.showAiChat.value
? const [ ? const [
_NavItem(Icons.chat_bubble_outline, 'AI chat'), _NavItem(Icons.chat_bubble_outline, 'aiChat'),
_NavItem(Icons.translate, 'interpretation'), _NavItem(Icons.translate, 'interpretation'),
_NavItem(Icons.people, 'meeting'), _NavItem(Icons.people, 'meeting'),
] ]

Loading…
Cancel
Save