You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
392 lines
12 KiB
392 lines
12 KiB
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
|
|
import '../../../data/services/volcano_translation_service.dart';
|
|
import '../../../data/services/language_manager.dart';
|
|
import '../../../core/utils/logger.dart';
|
|
|
|
/// 文本翻译控制器
|
|
class TextTranslationController extends GetxController {
|
|
// 依赖服务
|
|
final VolcanoTranslationService _translationService =
|
|
Get.find<VolcanoTranslationService>();
|
|
final LanguageManager _languageManager = Get.find<LanguageManager>();
|
|
final GetStorage _storage = GetStorage();
|
|
|
|
// 文本输入控制器
|
|
final TextEditingController textController = TextEditingController();
|
|
|
|
// 响应式状态
|
|
final sourceLanguageChineseName = '中文(简体)'.obs;
|
|
final targetLanguageChineseName = '英语'.obs;
|
|
final sourceLanguageCode = 'zh-CN'.obs;
|
|
final targetLanguageCode = 'en-US'.obs;
|
|
final translatedText = ''.obs;
|
|
final isTranslating = false.obs;
|
|
final inputText = ''.obs;
|
|
|
|
// 支持的语言列表
|
|
List<String> get supportedLanguages => _languageManager.getAllChineseNames();
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
|
|
// 初始化翻译服务
|
|
_initializeTranslationService();
|
|
|
|
_loadSavedLanguages();
|
|
|
|
// 监听文本输入变化
|
|
textController.addListener(() {
|
|
inputText.value = textController.text;
|
|
});
|
|
}
|
|
|
|
/// 初始化翻译服务
|
|
Future<void> _initializeTranslationService() async {
|
|
try {
|
|
// 已初始化则跳过
|
|
if (_translationService.isInitialized) {
|
|
Logger.info('翻译服务已初始化');
|
|
return;
|
|
}
|
|
// final initialized =
|
|
final initialized = await _translationService.initialize();
|
|
if (!initialized) {
|
|
Logger.error('翻译服务初始化失败');
|
|
Get.snackbar(
|
|
'error'.tr,
|
|
'翻译服务初始化失败,请检查网络连接',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.9),
|
|
colorText: Colors.white,
|
|
);
|
|
} else {
|
|
Logger.info('翻译服务初始化成功');
|
|
}
|
|
} catch (e) {
|
|
Logger.error('翻译服务初始化异常: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
textController.dispose();
|
|
super.onClose();
|
|
}
|
|
|
|
/// 加载保存的语言设置
|
|
void _loadSavedLanguages() {
|
|
try {
|
|
final savedSourceLanguage =
|
|
_storage.read('text_translation_source_language');
|
|
final savedTargetLanguage =
|
|
_storage.read('text_translation_target_language');
|
|
|
|
if (savedSourceLanguage != null &&
|
|
supportedLanguages.contains(savedSourceLanguage)) {
|
|
sourceLanguageChineseName.value = savedSourceLanguage;
|
|
// 同时更新语言代码
|
|
final asrCode =
|
|
_languageManager.getAsrCodeByChineseName(savedSourceLanguage);
|
|
if (asrCode != null) {
|
|
sourceLanguageCode.value = asrCode;
|
|
}
|
|
}
|
|
|
|
if (savedTargetLanguage != null &&
|
|
supportedLanguages.contains(savedTargetLanguage)) {
|
|
targetLanguageChineseName.value = savedTargetLanguage;
|
|
// 同时更新语言代码
|
|
final asrCode =
|
|
_languageManager.getAsrCodeByChineseName(savedTargetLanguage);
|
|
if (asrCode != null) {
|
|
targetLanguageCode.value = asrCode;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
Logger.error('加载语言设置失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 保存语言设置
|
|
void _saveLanguageSettings() {
|
|
try {
|
|
_storage.write(
|
|
'text_translation_source_language', sourceLanguageChineseName.value);
|
|
_storage.write(
|
|
'text_translation_target_language', targetLanguageChineseName.value);
|
|
} catch (e) {
|
|
Logger.error('保存语言设置失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 设置源语言
|
|
void setSourceLanguage(String language) {
|
|
if (language != sourceLanguageChineseName.value) {
|
|
sourceLanguageChineseName.value = language;
|
|
|
|
// 同时更新语言代码;当选择“自动检测”时,将源语言代码置空,后续翻译流程会进行语种检测
|
|
if (language == '自动检测') {
|
|
sourceLanguageCode.value = '';
|
|
} else {
|
|
final asrCode = _languageManager.getAsrCodeByChineseName(language);
|
|
if (asrCode != null) {
|
|
sourceLanguageCode.value = asrCode;
|
|
}
|
|
}
|
|
|
|
_saveLanguageSettings();
|
|
|
|
// 如果已有翻译结果,清空结果
|
|
if (translatedText.value.isNotEmpty) {
|
|
translatedText.value = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 设置目标语言
|
|
void setTargetLanguage(String language) {
|
|
if (language != targetLanguageChineseName.value) {
|
|
targetLanguageChineseName.value = language;
|
|
|
|
// 同时更新语言代码
|
|
final asrCode = _languageManager.getAsrCodeByChineseName(language);
|
|
if (asrCode != null) {
|
|
targetLanguageCode.value = asrCode;
|
|
}
|
|
|
|
_saveLanguageSettings();
|
|
|
|
// 如果已有翻译结果,清空结果
|
|
if (translatedText.value.isNotEmpty) {
|
|
translatedText.value = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 交换源语言和目标语言
|
|
void swapLanguages() {
|
|
final tempSource = sourceLanguageChineseName.value;
|
|
final tempTarget = targetLanguageChineseName.value;
|
|
final tempSourceCode = sourceLanguageCode.value;
|
|
final tempTargetCode = targetLanguageCode.value;
|
|
final tempInputText = textController.text;
|
|
final tempTranslatedText = translatedText.value;
|
|
|
|
sourceLanguageChineseName.value = tempTarget;
|
|
targetLanguageChineseName.value = tempSource;
|
|
sourceLanguageCode.value = tempTargetCode;
|
|
targetLanguageCode.value = tempSourceCode;
|
|
|
|
// 交换输入文本和翻译结果
|
|
textController.text = tempTranslatedText;
|
|
translatedText.value = tempInputText;
|
|
|
|
_saveLanguageSettings();
|
|
}
|
|
|
|
/// 执行翻译
|
|
Future<void> translateText() async {
|
|
final text = textController.text.trim();
|
|
|
|
// 验证输入
|
|
if (text.isEmpty) {
|
|
// Get.snackbar(
|
|
// 'error'.tr,
|
|
// 'pleaseInputText'.tr,
|
|
// snackPosition: SnackPosition.TOP,
|
|
// backgroundColor: Colors.orange.withOpacity(0.9),
|
|
// colorText: Colors.white,
|
|
// );
|
|
return;
|
|
}
|
|
|
|
if (text.length > 5000) {
|
|
// 5000 字符限制
|
|
Get.snackbar(
|
|
'error'.tr,
|
|
'textTooLong'.tr,
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.orange.withOpacity(0.9),
|
|
colorText: Colors.white,
|
|
);
|
|
return;
|
|
}
|
|
|
|
// 检查源语言和目标语言是否相同
|
|
if (sourceLanguageChineseName.value == targetLanguageChineseName.value) {
|
|
translatedText.value = text;
|
|
return;
|
|
}
|
|
|
|
isTranslating.value = true;
|
|
translatedText.value = '';
|
|
|
|
try {
|
|
Logger.info('开始翻译: $text');
|
|
Logger.info(
|
|
'源语言: ${sourceLanguageChineseName.value} -> ${sourceLanguageCode.value}');
|
|
Logger.info(
|
|
'目标语言: ${targetLanguageChineseName.value} -> ${targetLanguageCode.value}');
|
|
|
|
// 确保翻译服务已初始化
|
|
Logger.info('确保翻译服务已初始化...');
|
|
bool initialized = _translationService.isInitialized;
|
|
if (!initialized) {
|
|
initialized = await _translationService.initialize();
|
|
}
|
|
Logger.info('翻译服务初始化结果: $initialized');
|
|
if (!initialized) {
|
|
throw Exception('翻译服务初始化失败');
|
|
}
|
|
|
|
Logger.info('调用翻译服务...');
|
|
|
|
// 若选择了“自动检测”,先进行语种检测
|
|
if (sourceLanguageChineseName.value == '自动检测' ||
|
|
sourceLanguageCode.value.isEmpty) {
|
|
Logger.info('源语言为自动检测,开始语种检测…');
|
|
final detectedAsrCode =
|
|
await _translationService.detectTextLanguage(text: text);
|
|
if (detectedAsrCode == null || detectedAsrCode.isEmpty) {
|
|
Logger.error('语种检测失败');
|
|
Get.snackbar(
|
|
'错误', // 翻译键代表的中文文本内容
|
|
'语种检测失败,请手动选择源语言或稍后重试', // 翻译键代表的中文文本内容
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.9),
|
|
colorText: Colors.white,
|
|
);
|
|
return;
|
|
}
|
|
sourceLanguageCode.value = detectedAsrCode;
|
|
Logger.info('语种检测结果: $detectedAsrCode');
|
|
}
|
|
|
|
// 调用翻译服务,使用检测或手动选择的源语言代码
|
|
final result = await _translationService.translateText(
|
|
text: text,
|
|
sourceLanguageCode: sourceLanguageCode.value,
|
|
targetLanguageCode: targetLanguageCode.value,
|
|
);
|
|
|
|
Logger.info('翻译服务返回结果: $result');
|
|
|
|
if (result != null && result.isNotEmpty) {
|
|
translatedText.value = result;
|
|
Logger.info('翻译成功: $result');
|
|
} else {
|
|
Logger.error('翻译服务返回空结果');
|
|
throw Exception('翻译服务返回空结果');
|
|
}
|
|
} catch (e) {
|
|
Logger.error('翻译失败: $e');
|
|
Get.snackbar(
|
|
'error'.tr,
|
|
'translationFailed'.tr,
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.9),
|
|
colorText: Colors.white,
|
|
);
|
|
} finally {
|
|
isTranslating.value = false;
|
|
}
|
|
}
|
|
|
|
/// 复制翻译结果
|
|
void copyResult() {
|
|
if (translatedText.value.isNotEmpty) {
|
|
Clipboard.setData(ClipboardData(text: translatedText.value));
|
|
Get.snackbar(
|
|
'success'.tr,
|
|
'copied'.tr,
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.green.withOpacity(0.9),
|
|
colorText: Colors.white,
|
|
duration: const Duration(seconds: 2),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 清空输入
|
|
void clearInput() {
|
|
textController.clear();
|
|
translatedText.value = '';
|
|
}
|
|
|
|
/// 显示语言选择对话框
|
|
void showLanguageSelector({required bool isSourceLanguage}) {
|
|
Get.bottomSheet(
|
|
Container(
|
|
height: Get.height * 0.6,
|
|
decoration: const BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
isSourceLanguage
|
|
? 'sourceLanguage'.tr
|
|
: 'targetLanguage'.tr,
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
IconButton(
|
|
onPressed: () => Get.back(),
|
|
icon: const Icon(Icons.close),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const Divider(height: 1),
|
|
Expanded(
|
|
child: ListView.builder(
|
|
itemCount: (isSourceLanguage
|
|
? (supportedLanguages.length + 1)
|
|
: supportedLanguages.length),
|
|
itemBuilder: (context, index) {
|
|
final language = isSourceLanguage
|
|
? (index == 0
|
|
? '自动检测'
|
|
: supportedLanguages[index - 1])
|
|
: supportedLanguages[index];
|
|
final isSelected = isSourceLanguage
|
|
? language == sourceLanguageChineseName.value
|
|
: language == targetLanguageChineseName.value;
|
|
|
|
return ListTile(
|
|
title: Text(language),
|
|
trailing: isSelected
|
|
? const Icon(Icons.check, color: Colors.blue)
|
|
: null,
|
|
onTap: () {
|
|
if (isSourceLanguage) {
|
|
setSourceLanguage(language);
|
|
} else {
|
|
setTargetLanguage(language);
|
|
}
|
|
Get.back();
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
isScrollControlled: true,
|
|
);
|
|
}
|
|
}
|
|
|