|
|
|
@ -26,13 +26,15 @@ class PictureTranslationController extends GetxController { |
|
|
|
final LanguageManager languageManager = Get.find<LanguageManager>(); |
|
|
|
|
|
|
|
// UI状态 |
|
|
|
final sourceLanguageChineseName = '中文(简体)'.obs; |
|
|
|
final sourceLanguageChineseName = 'autoDetect'.obs; |
|
|
|
final targetLanguageChineseName = '英语'.obs; |
|
|
|
final sourceText = ''.obs; |
|
|
|
final translatedText = ''.obs; |
|
|
|
final translatedImageBytes = Rx<Uint8List?>(null); // 回贴后的图片二进制 |
|
|
|
final isProcessing = false.obs; |
|
|
|
final pickedImageFile = Rx<File?>(null); |
|
|
|
final isSourceLanguageUserSet = false.obs; |
|
|
|
final isTargetLanguageUserSet = false.obs; |
|
|
|
final currentStep = ProcessingStep( |
|
|
|
title: 'waitingForAction'.tr, // 等待操作 |
|
|
|
description: 'pleaseSelectImageToStart'.tr, // 请选择图片开始翻译 |
|
|
|
@ -49,6 +51,7 @@ class PictureTranslationController extends GetxController { |
|
|
|
|
|
|
|
static const String _historyKey = 'picture_translation_history'; |
|
|
|
static const String _settingsKey = 'picture_translation_settings'; |
|
|
|
String? _lastDetectedSourceLanguageCode; |
|
|
|
|
|
|
|
@override |
|
|
|
void onInit() { |
|
|
|
@ -68,11 +71,76 @@ class PictureTranslationController extends GetxController { |
|
|
|
try { |
|
|
|
final settings = _storage.read(_settingsKey) as Map<String, dynamic>?; |
|
|
|
if (settings != null) { |
|
|
|
sourceLanguageChineseName.value = |
|
|
|
settings['sourceLanguage'] ?? '中文(简体)'; |
|
|
|
targetLanguageChineseName.value = settings['targetLanguage'] ?? '英语'; |
|
|
|
final langs = languageManager.getAllChineseNames(); |
|
|
|
|
|
|
|
final appDefaultTargetLanguage = _getLanguageByAppLocale(); |
|
|
|
|
|
|
|
final bool sourceUserSet = |
|
|
|
(settings['sourceLanguageUserSet'] == true) || |
|
|
|
((settings['sourceLanguageUserSet'] == null) && |
|
|
|
(settings['sourceLanguage'] != null) && |
|
|
|
(settings['sourceLanguage'] != '中文(简体)')); |
|
|
|
|
|
|
|
bool targetUserSet = settings['targetLanguageUserSet'] == true; |
|
|
|
final bool shouldMigrateTargetUserSetKey = |
|
|
|
settings['targetLanguageUserSet'] == null; |
|
|
|
if (shouldMigrateTargetUserSetKey) { |
|
|
|
final savedTargetLanguage = settings['targetLanguage']; |
|
|
|
if (savedTargetLanguage != null && |
|
|
|
appDefaultTargetLanguage != null && |
|
|
|
savedTargetLanguage == appDefaultTargetLanguage) { |
|
|
|
settings.remove('targetLanguage'); |
|
|
|
targetUserSet = false; |
|
|
|
} else { |
|
|
|
targetUserSet = savedTargetLanguage != null && |
|
|
|
savedTargetLanguage != 'autoDetect' && |
|
|
|
savedTargetLanguage != '英语'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
isSourceLanguageUserSet.value = sourceUserSet; |
|
|
|
isTargetLanguageUserSet.value = targetUserSet; |
|
|
|
|
|
|
|
final savedSourceLanguage = settings['sourceLanguage']; |
|
|
|
if (sourceUserSet && |
|
|
|
savedSourceLanguage != null && |
|
|
|
(savedSourceLanguage == 'autoDetect' || |
|
|
|
langs.contains(savedSourceLanguage))) { |
|
|
|
sourceLanguageChineseName.value = savedSourceLanguage; |
|
|
|
} else { |
|
|
|
sourceLanguageChineseName.value = 'autoDetect'; |
|
|
|
} |
|
|
|
|
|
|
|
final savedTargetLanguage = settings['targetLanguage']; |
|
|
|
if (targetUserSet && |
|
|
|
savedTargetLanguage != null && |
|
|
|
savedTargetLanguage != 'autoDetect' && |
|
|
|
langs.contains(savedTargetLanguage)) { |
|
|
|
targetLanguageChineseName.value = savedTargetLanguage; |
|
|
|
} else { |
|
|
|
if (appDefaultTargetLanguage != null && |
|
|
|
langs.contains(appDefaultTargetLanguage)) { |
|
|
|
targetLanguageChineseName.value = appDefaultTargetLanguage; |
|
|
|
} else { |
|
|
|
targetLanguageChineseName.value = '英语'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (shouldMigrateTargetUserSetKey) { |
|
|
|
_saveSettings(); |
|
|
|
} |
|
|
|
|
|
|
|
imageQuality.value = settings['imageQuality'] ?? 85; |
|
|
|
maxImageSize.value = settings['maxImageSize'] ?? 1920; |
|
|
|
} else { |
|
|
|
final appDefaultTargetLanguage = _getLanguageByAppLocale(); |
|
|
|
final langs = languageManager.getAllChineseNames(); |
|
|
|
if (appDefaultTargetLanguage != null && |
|
|
|
langs.contains(appDefaultTargetLanguage)) { |
|
|
|
targetLanguageChineseName.value = appDefaultTargetLanguage; |
|
|
|
} else { |
|
|
|
targetLanguageChineseName.value = '英语'; |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
Logger.error('PictureTranslation', '加载设置失败: $e'); |
|
|
|
@ -83,8 +151,12 @@ class PictureTranslationController extends GetxController { |
|
|
|
void _saveSettings() { |
|
|
|
try { |
|
|
|
_storage.write(_settingsKey, { |
|
|
|
'sourceLanguage': sourceLanguageChineseName.value, |
|
|
|
'targetLanguage': targetLanguageChineseName.value, |
|
|
|
if (isSourceLanguageUserSet.value) |
|
|
|
'sourceLanguage': sourceLanguageChineseName.value, |
|
|
|
if (isTargetLanguageUserSet.value) |
|
|
|
'targetLanguage': targetLanguageChineseName.value, |
|
|
|
'sourceLanguageUserSet': isSourceLanguageUserSet.value, |
|
|
|
'targetLanguageUserSet': isTargetLanguageUserSet.value, |
|
|
|
'imageQuality': imageQuality.value, |
|
|
|
'maxImageSize': maxImageSize.value, |
|
|
|
}); |
|
|
|
@ -323,7 +395,7 @@ class PictureTranslationController extends GetxController { |
|
|
|
|
|
|
|
final targetCode = languageManager |
|
|
|
.getAsrCodeByChineseName(targetLanguageChineseName.value) ?? |
|
|
|
'zh-CN'; |
|
|
|
'en-US'; |
|
|
|
|
|
|
|
final result = await _translationService.translateImage( |
|
|
|
imageBytes: bytes, |
|
|
|
@ -355,6 +427,9 @@ class PictureTranslationController extends GetxController { |
|
|
|
sourceText.value = ''; |
|
|
|
translatedText.value = ''; |
|
|
|
} |
|
|
|
|
|
|
|
_lastDetectedSourceLanguageCode = |
|
|
|
_resolveDetectedSourceLanguageAsrCode(result); |
|
|
|
} catch (e) { |
|
|
|
Logger.error('PictureTranslation', '图片翻译调用失败: $e'); |
|
|
|
_updateStep( |
|
|
|
@ -370,9 +445,17 @@ class PictureTranslationController extends GetxController { |
|
|
|
/// 保存翻译结果 |
|
|
|
Future<void> _saveTranslationResult(File imageFile) async { |
|
|
|
try { |
|
|
|
final sourceCode = languageManager |
|
|
|
.getAsrCodeByChineseName(sourceLanguageChineseName.value) ?? |
|
|
|
'zh-CN'; |
|
|
|
String sourceCode = 'zh-CN'; |
|
|
|
if (isSourceLanguageUserSet.value && |
|
|
|
sourceLanguageChineseName.value != 'autoDetect') { |
|
|
|
sourceCode = languageManager |
|
|
|
.getAsrCodeByChineseName(sourceLanguageChineseName.value) ?? |
|
|
|
'zh-CN'; |
|
|
|
} else if (_lastDetectedSourceLanguageCode != null && |
|
|
|
_lastDetectedSourceLanguageCode!.isNotEmpty) { |
|
|
|
sourceCode = _lastDetectedSourceLanguageCode!; |
|
|
|
} |
|
|
|
|
|
|
|
final targetCode = languageManager |
|
|
|
.getAsrCodeByChineseName(targetLanguageChineseName.value) ?? |
|
|
|
'en-US'; |
|
|
|
@ -413,6 +496,7 @@ class PictureTranslationController extends GetxController { |
|
|
|
void setSourceLanguage(String language) { |
|
|
|
if (sourceLanguageChineseName.value != language) { |
|
|
|
sourceLanguageChineseName.value = language; |
|
|
|
isSourceLanguageUserSet.value = language != 'autoDetect'; |
|
|
|
_saveSettings(); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -420,13 +504,20 @@ class PictureTranslationController extends GetxController { |
|
|
|
/// 设置目标语言 |
|
|
|
void setTargetLanguage(String language) { |
|
|
|
if (targetLanguageChineseName.value != language) { |
|
|
|
if (language == 'autoDetect') return; |
|
|
|
targetLanguageChineseName.value = language; |
|
|
|
isTargetLanguageUserSet.value = true; |
|
|
|
_saveSettings(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 交换语言 |
|
|
|
void swapLanguages() { |
|
|
|
if (sourceLanguageChineseName.value == 'autoDetect' || |
|
|
|
targetLanguageChineseName.value == 'autoDetect') { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
final temp = sourceLanguageChineseName.value; |
|
|
|
sourceLanguageChineseName.value = targetLanguageChineseName.value; |
|
|
|
targetLanguageChineseName.value = temp; |
|
|
|
@ -436,6 +527,8 @@ class PictureTranslationController extends GetxController { |
|
|
|
sourceText.value = translatedText.value; |
|
|
|
translatedText.value = tempText; |
|
|
|
|
|
|
|
isSourceLanguageUserSet.value = true; |
|
|
|
isTargetLanguageUserSet.value = true; |
|
|
|
_saveSettings(); |
|
|
|
} |
|
|
|
|
|
|
|
@ -460,4 +553,42 @@ class PictureTranslationController extends GetxController { |
|
|
|
'pleaseSelectImageToStart'.tr, // 请选择图片开始翻译 |
|
|
|
ProcessingStatus.idle); |
|
|
|
} |
|
|
|
|
|
|
|
String? _getLanguageByAppLocale() { |
|
|
|
final Locale? locale = Get.locale ?? Get.deviceLocale; |
|
|
|
if (locale == null) return null; |
|
|
|
|
|
|
|
final languageCode = locale.languageCode.toLowerCase(); |
|
|
|
final countryCode = locale.countryCode?.toUpperCase(); |
|
|
|
final scriptCode = locale.scriptCode; |
|
|
|
|
|
|
|
if (languageCode == 'zh') { |
|
|
|
if (countryCode == 'HK' || countryCode == 'MO') { |
|
|
|
return languageManager.getChineseNameByAsrCode('zh-HK'); |
|
|
|
} |
|
|
|
if (countryCode == 'TW') { |
|
|
|
return languageManager.getChineseNameByAsrCode('zh-TW'); |
|
|
|
} |
|
|
|
if (scriptCode == 'Hant') { |
|
|
|
return languageManager.getChineseNameByAsrCode('zh-TW'); |
|
|
|
} |
|
|
|
return languageManager.getChineseNameByAsrCode('zh-CN'); |
|
|
|
} |
|
|
|
|
|
|
|
final normalizedShortCode = switch (languageCode) { |
|
|
|
'nb' => 'no', |
|
|
|
_ => languageCode, |
|
|
|
}; |
|
|
|
|
|
|
|
return languageManager.getChineseNameByShortCode(normalizedShortCode); |
|
|
|
} |
|
|
|
|
|
|
|
String? _resolveDetectedSourceLanguageAsrCode(ImageTranslationResult result) { |
|
|
|
final shortCode = result.detectedLanguage ?? |
|
|
|
(result.blocks.isNotEmpty |
|
|
|
? result.blocks.first.detectedLanguage |
|
|
|
: null); |
|
|
|
if (shortCode == null || shortCode.isEmpty) return null; |
|
|
|
return languageManager.getAsrCodeByShortCode(shortCode); |
|
|
|
} |
|
|
|
} |
|
|
|
|