feat: input_method_auto_translate

This commit is contained in:
xkeyC 2024-11-23 21:51:36 +08:00
parent cd788b6fe5
commit f8a846e706
17 changed files with 229 additions and 24 deletions

View File

@ -98,6 +98,23 @@ class Api {
return r;
}
static Future<String?> doGoogleTranslate(String input) async {
final out = await RSHttp.getText(
"${URLConf.googleTranslateApiUrl}/translate_a/single?client=gtx&dt=t&sl=auto&tl=en&q=${Uri.encodeComponent(input)}");
// [[["Hello","你好",null,null,10]],null,"zh-CN",null,null,null,1,[],[["zh-CN"],null,[1],["zh-CN"]]]
final list = json.decode(out);
if (list is List && list.isNotEmpty) {
final data = list.first;
if (data is List && data.isNotEmpty) {
final text = data.first;
if (text is List && text.isNotEmpty) {
return text.first;
}
}
}
return null;
}
static Future<bool> isUseInternalDNS() async {
final userBox = await Hive.openBox("app_conf");
final isUseInternalDNS =

View File

@ -34,6 +34,9 @@ class URLConf {
static String get rssTextUrl2 =>
"$rssApiHome/baidu/tieba/user/%E7%81%AC%E7%81%ACG%E7%81%AC%E7%81%AC&";
static const String googleTranslateApiUrl =
"https://translate-g-proxy.xkeyc.com";
static const feedbackUrl = "https://txc.qq.com/products/614843";
static const feedbackFAQUrl =
"https://support.qq.com/products/410309/faqs-more/?id=160084";

View File

@ -711,6 +711,14 @@ class MessageLookup extends MessageLookupByLibrary {
"If the address failed, please check the computer IP manually"),
"input_method_auto_copy":
MessageLookupByLibrary.simpleMessage("Automatic replication"),
"input_method_auto_translate":
MessageLookupByLibrary.simpleMessage("Bilingual translation:"),
"input_method_auto_translate_dialog_title":
MessageLookupByLibrary.simpleMessage(
"Enable bilingual translation?"),
"input_method_auto_translate_dialog_title_content":
MessageLookupByLibrary.simpleMessage(
"After use, you will use Google translation service to add English copies to your input content, which may lead to lagging response. If the function is abnormal, please close it. \n\nThe text will be forwarded to Google server, please refer to Google\'s privacy policy."),
"input_method_community_input_method_not_installed":
MessageLookupByLibrary.simpleMessage(
"Supporting community input method support"),

View File

@ -636,6 +636,13 @@ class MessageLookup extends MessageLookupByLibrary {
"input_method_address_fetch_failed":
MessageLookupByLibrary.simpleMessage("获取地址失败请手动查看电脑IP"),
"input_method_auto_copy": MessageLookupByLibrary.simpleMessage("自动复制"),
"input_method_auto_translate":
MessageLookupByLibrary.simpleMessage("双语翻译:"),
"input_method_auto_translate_dialog_title":
MessageLookupByLibrary.simpleMessage("启用双语翻译?"),
"input_method_auto_translate_dialog_title_content":
MessageLookupByLibrary.simpleMessage(
"启用后,将使用 Google 翻译服务为您的输入内容增加英文副本,可能会导致响应滞后,若功能异常请关闭。\n\n文本将转发给 Google 服务器,请参阅 Google 的隐私政策。"),
"input_method_community_input_method_not_installed":
MessageLookupByLibrary.simpleMessage("未安装社区输入法支持"),
"input_method_community_input_method_support_version": m36,

View File

@ -638,6 +638,13 @@ class MessageLookup extends MessageLookupByLibrary {
"input_method_address_fetch_failed":
MessageLookupByLibrary.simpleMessage("獲取地址失敗請手動查看電腦IP"),
"input_method_auto_copy": MessageLookupByLibrary.simpleMessage("自動複製"),
"input_method_auto_translate":
MessageLookupByLibrary.simpleMessage("雙語翻譯:"),
"input_method_auto_translate_dialog_title":
MessageLookupByLibrary.simpleMessage("啟用雙語翻譯?"),
"input_method_auto_translate_dialog_title_content":
MessageLookupByLibrary.simpleMessage(
"啟用後,將使用 Google 翻譯服務為您的輸入內容增加英文副本,可能會導致響應滯後,若功能異常請關閉。 \n\n文本將轉發給 Google 服務器,請參閱 Google 的隱私政策。"),
"input_method_community_input_method_not_installed":
MessageLookupByLibrary.simpleMessage("未安裝社區輸入法支持"),
"input_method_community_input_method_support_version": m36,

View File

@ -5273,6 +5273,36 @@ class S {
args: [v0],
);
}
/// `Bilingual translation:`
String get input_method_auto_translate {
return Intl.message(
'Bilingual translation:',
name: 'input_method_auto_translate',
desc: '',
args: [],
);
}
/// `Enable bilingual translation?`
String get input_method_auto_translate_dialog_title {
return Intl.message(
'Enable bilingual translation?',
name: 'input_method_auto_translate_dialog_title',
desc: '',
args: [],
);
}
/// `After use, you will use Google translation service to add English copies to your input content, which may lead to lagging response. If the function is abnormal, please close it. \n\nThe text will be forwarded to Google server, please refer to Google's privacy policy.`
String get input_method_auto_translate_dialog_title_content {
return Intl.message(
'After use, you will use Google translation service to add English copies to your input content, which may lead to lagging response. If the function is abnormal, please close it. \n\nThe text will be forwarded to Google server, please refer to Google\'s privacy policy.',
name: 'input_method_auto_translate_dialog_title_content',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@ -1004,5 +1004,8 @@
"input_method_input_text_instructions": "Enter the text in the text box above and paste the text (Ctrl+V) to the text box of the game in the text box above the text box.",
"input_method_online_version_prompt": "This function provides another online independent version, click to visit>",
"input_method_support_updated": "Community input method support has been updated",
"input_method_support_updated_to_version": "Community input method support has been updated to: {v0}"
"input_method_support_updated_to_version": "Community input method support has been updated to: {v0}",
"input_method_auto_translate": "Bilingual translation:",
"input_method_auto_translate_dialog_title": "Enable bilingual translation?",
"input_method_auto_translate_dialog_title_content": "After use, you will use Google translation service to add English copies to your input content, which may lead to lagging response. If the function is abnormal, please close it. \n\nThe text will be forwarded to Google server, please refer to Google's privacy policy."
}

View File

@ -837,5 +837,8 @@
"input_method_community_input_method_support_version": "社区输入法支持:{v0}",
"input_method_online_version_prompt": "本功能另有提供在线独立版,点击访问 >",
"input_method_support_updated": "社区输入法支持已更新",
"input_method_support_updated_to_version": "社区输入法支持已更新到:{v0}"
"input_method_support_updated_to_version": "社区输入法支持已更新到:{v0}",
"input_method_auto_translate": "双语翻译:",
"input_method_auto_translate_dialog_title": "启用双语翻译?",
"input_method_auto_translate_dialog_title_content": "启用后,将使用 Google 翻译服务为您的输入内容增加英文副本,可能会导致响应滞后,若功能异常请关闭。\n\n文本将转发给 Google 服务器,请参阅 Google 的隐私政策。"
}

View File

@ -837,5 +837,8 @@
"input_method_community_input_method_support_version": "社區輸入法支持:{v0}",
"input_method_online_version_prompt": "本功能另有提供在線獨立版,點擊訪問 >",
"input_method_support_updated": "社區輸入法支持已更新",
"input_method_support_updated_to_version": "社區輸入法支持已更新到:{v0}"
"input_method_support_updated_to_version": "社區輸入法支持已更新到:{v0}",
"input_method_auto_translate": "雙語翻譯:",
"input_method_auto_translate_dialog_title": "啟用雙語翻譯?",
"input_method_auto_translate_dialog_title_content": "啟用後,將使用 Google 翻譯服務為您的輸入內容增加英文副本,可能會導致響應滯後,若功能異常請關閉。 \n\n文本將轉發給 Google 服務器,請參閱 Google 的隱私政策。"
}

View File

@ -157,13 +157,13 @@ class _$Aria2cModelStateImpl
(identical(other.aria2cDir, aria2cDir) ||
other.aria2cDir == aria2cDir) &&
(identical(other.aria2c, aria2c) || other.aria2c == aria2c) &&
(identical(other.aria2globalStat, aria2globalStat) ||
other.aria2globalStat == aria2globalStat));
const DeepCollectionEquality()
.equals(other.aria2globalStat, aria2globalStat));
}
@override
int get hashCode =>
Object.hash(runtimeType, aria2cDir, aria2c, aria2globalStat);
int get hashCode => Object.hash(runtimeType, aria2cDir, aria2c,
const DeepCollectionEquality().hash(aria2globalStat));
/// Create a copy of Aria2cModelState
/// with the given fields replaced by the non-null parameter values.

View File

@ -240,7 +240,7 @@ class _$Unp4kcStateImpl with DiagnosticableTreeMixin implements _Unp4kcState {
other is _$Unp4kcStateImpl &&
(identical(other.startUp, startUp) || other.startUp == startUp) &&
const DeepCollectionEquality().equals(other._files, _files) &&
(identical(other.fs, fs) || other.fs == fs) &&
const DeepCollectionEquality().equals(other.fs, fs) &&
(identical(other.curPath, curPath) || other.curPath == curPath) &&
(identical(other.endMessage, endMessage) ||
other.endMessage == endMessage) &&
@ -255,7 +255,7 @@ class _$Unp4kcStateImpl with DiagnosticableTreeMixin implements _Unp4kcState {
runtimeType,
startUp,
const DeepCollectionEquality().hash(_files),
fs,
const DeepCollectionEquality().hash(fs),
curPath,
endMessage,
tempOpenFile,

View File

@ -196,8 +196,8 @@ class _$HomeDownloaderUIStateImpl implements _HomeDownloaderUIState {
.equals(other._waitingTasks, _waitingTasks) &&
const DeepCollectionEquality()
.equals(other._stoppedTasks, _stoppedTasks) &&
(identical(other.globalStat, globalStat) ||
other.globalStat == globalStat));
const DeepCollectionEquality()
.equals(other.globalStat, globalStat));
}
@override
@ -206,7 +206,7 @@ class _$HomeDownloaderUIStateImpl implements _HomeDownloaderUIState {
const DeepCollectionEquality().hash(_tasks),
const DeepCollectionEquality().hash(_waitingTasks),
const DeepCollectionEquality().hash(_stoppedTasks),
globalStat);
const DeepCollectionEquality().hash(globalStat));
/// Create a copy of HomeDownloaderUIState
/// with the given fields replaced by the non-null parameter values.

View File

@ -70,16 +70,28 @@ class InputMethodDialogUI extends HookConsumerWidget {
placeholderStyle:
TextStyle(color: Colors.white.withOpacity(.6)),
style: TextStyle(fontSize: 16, color: Colors.white),
onChanged: (str) {
onChanged: (str) async {
final text = model.onTextChange("src", str);
destTextCtrl.text = text ?? "";
if (text != null) {
destTextCtrl.text = text;
model.checkAutoTranslate();
}
},
),
SizedBox(height: 16),
Center(
child: Icon(FluentIcons.down),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (state.isAutoTranslateWorking)
SizedBox(width: 24, height: 24, child: ProgressRing())
else
SizedBox(
width: 24,
height: 24,
child: Icon(FluentIcons.down))
],
),
),
SizedBox(height: 16),
TextFormBox(
@ -101,6 +113,18 @@ class InputMethodDialogUI extends HookConsumerWidget {
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
children: [
Text(S.current.input_method_auto_translate),
SizedBox(width: 6),
ToggleSwitch(
checked: state.isEnableAutoTranslate,
onChanged: (b) =>
_onSwitchAutoTranslate(context, model, b),
),
],
),
SizedBox(width: 24),
Row(
children: [
Text(S.current.input_method_remote_input_service),
@ -214,4 +238,16 @@ class InputMethodDialogUI extends HookConsumerWidget {
await serverModel.stopServer().unwrap(context: context);
}
}
_onSwitchAutoTranslate(
BuildContext context, InputMethodDialogUIModel model, bool b) async {
if (b) {
final ok = await showConfirmDialogs(
context,
S.current.input_method_auto_translate_dialog_title,
Text(S.current.input_method_auto_translate_dialog_title_content));
if (ok != true) return;
}
model.toggleAutoTranslate(b);
}
}

View File

@ -5,6 +5,7 @@ import 'package:flutter/widgets.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hive/hive.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:starcitizen_doctor/api/api.dart';
import 'package:starcitizen_doctor/common/utils/log.dart';
import 'package:starcitizen_doctor/ui/home/localization/localization_ui_model.dart';
@ -18,6 +19,8 @@ class InputMethodDialogUIState with _$InputMethodDialogUIState {
Map<String, String>? keyMaps,
Map<String, String>? worldMaps, {
@Default(false) bool enableAutoCopy,
@Default(false) bool isEnableAutoTranslate,
@Default(false) bool isAutoTranslateWorking,
}) = _InputMethodDialogUIState;
}
@ -43,10 +46,13 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
final worldMaps = keyMaps?.map((key, value) => MapEntry(value.trim(), key));
final appBox = await Hive.openBox("app_conf");
final enableAutoCopy = appBox.get("enableAutoCopy", defaultValue: false);
final isEnableAutoTranslate =
appBox.get("isEnableAutoTranslate", defaultValue: false);
state = state.copyWith(
keyMaps: keyMaps,
worldMaps: worldMaps,
enableAutoCopy: enableAutoCopy,
isEnableAutoTranslate: isEnableAutoTranslate,
);
}
@ -104,6 +110,7 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
// 1
void _handleAutoCopy(String text) {
if (state.isEnableAutoTranslate) return;
if (_autoCopyTimer != null) {
_autoCopyTimer?.cancel();
_autoCopyTimer = null;
@ -135,8 +142,42 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
_srcTextCtrl?.text = text;
_destTextCtrl?.text = onTextChange("src", text) ?? "";
if (_destTextCtrl?.text.isEmpty ?? true) return;
if (autoCopy) {
checkAutoTranslate(webMessage: true);
if (autoCopy && !state.isAutoTranslateWorking) {
Clipboard.setData(ClipboardData(text: _destTextCtrl?.text ?? ""));
}
}
toggleAutoTranslate(bool b) async {
state = state.copyWith(isEnableAutoTranslate: b);
final appConf = await Hive.openBox("app_conf");
await appConf.put("isEnableAutoTranslate", b);
}
Timer? _translateTimer;
Future<void> checkAutoTranslate({bool webMessage = false}) async {
final sourceText = _srcTextCtrl?.text ?? "";
final content = _destTextCtrl?.text ?? "";
if (sourceText.trim().isEmpty) return;
if (state.isEnableAutoTranslate) {
if (_translateTimer != null) _translateTimer?.cancel();
state = state.copyWith(isAutoTranslateWorking: true);
_translateTimer =
Timer(Duration(milliseconds: webMessage ? 1 : 400), () async {
try {
final r = await Api.doGoogleTranslate(sourceText);
if (r != null) {
_destTextCtrl?.text = "$content\n[en] $r";
if (state.enableAutoCopy || webMessage) {
Clipboard.setData(ClipboardData(text: _destTextCtrl?.text ?? ""));
}
}
} catch (e) {
dPrint("[InputMethodDialogUIModel] AutoTranslate error: $e");
}
state = state.copyWith(isAutoTranslateWorking: false);
});
}
}
}

View File

@ -19,6 +19,8 @@ mixin _$InputMethodDialogUIState {
Map<String, String>? get keyMaps => throw _privateConstructorUsedError;
Map<String, String>? get worldMaps => throw _privateConstructorUsedError;
bool get enableAutoCopy => throw _privateConstructorUsedError;
bool get isEnableAutoTranslate => throw _privateConstructorUsedError;
bool get isAutoTranslateWorking => throw _privateConstructorUsedError;
/// Create a copy of InputMethodDialogUIState
/// with the given fields replaced by the non-null parameter values.
@ -36,7 +38,9 @@ abstract class $InputMethodDialogUIStateCopyWith<$Res> {
$Res call(
{Map<String, String>? keyMaps,
Map<String, String>? worldMaps,
bool enableAutoCopy});
bool enableAutoCopy,
bool isEnableAutoTranslate,
bool isAutoTranslateWorking});
}
/// @nodoc
@ -58,6 +62,8 @@ class _$InputMethodDialogUIStateCopyWithImpl<$Res,
Object? keyMaps = freezed,
Object? worldMaps = freezed,
Object? enableAutoCopy = null,
Object? isEnableAutoTranslate = null,
Object? isAutoTranslateWorking = null,
}) {
return _then(_value.copyWith(
keyMaps: freezed == keyMaps
@ -72,6 +78,14 @@ class _$InputMethodDialogUIStateCopyWithImpl<$Res,
? _value.enableAutoCopy
: enableAutoCopy // ignore: cast_nullable_to_non_nullable
as bool,
isEnableAutoTranslate: null == isEnableAutoTranslate
? _value.isEnableAutoTranslate
: isEnableAutoTranslate // ignore: cast_nullable_to_non_nullable
as bool,
isAutoTranslateWorking: null == isAutoTranslateWorking
? _value.isAutoTranslateWorking
: isAutoTranslateWorking // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@ -88,7 +102,9 @@ abstract class _$$InputMethodDialogUIStateImplCopyWith<$Res>
$Res call(
{Map<String, String>? keyMaps,
Map<String, String>? worldMaps,
bool enableAutoCopy});
bool enableAutoCopy,
bool isEnableAutoTranslate,
bool isAutoTranslateWorking});
}
/// @nodoc
@ -109,6 +125,8 @@ class __$$InputMethodDialogUIStateImplCopyWithImpl<$Res>
Object? keyMaps = freezed,
Object? worldMaps = freezed,
Object? enableAutoCopy = null,
Object? isEnableAutoTranslate = null,
Object? isAutoTranslateWorking = null,
}) {
return _then(_$InputMethodDialogUIStateImpl(
freezed == keyMaps
@ -123,6 +141,14 @@ class __$$InputMethodDialogUIStateImplCopyWithImpl<$Res>
? _value.enableAutoCopy
: enableAutoCopy // ignore: cast_nullable_to_non_nullable
as bool,
isEnableAutoTranslate: null == isEnableAutoTranslate
? _value.isEnableAutoTranslate
: isEnableAutoTranslate // ignore: cast_nullable_to_non_nullable
as bool,
isAutoTranslateWorking: null == isAutoTranslateWorking
? _value.isAutoTranslateWorking
: isAutoTranslateWorking // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@ -132,7 +158,9 @@ class __$$InputMethodDialogUIStateImplCopyWithImpl<$Res>
class _$InputMethodDialogUIStateImpl implements _InputMethodDialogUIState {
_$InputMethodDialogUIStateImpl(
final Map<String, String>? keyMaps, final Map<String, String>? worldMaps,
{this.enableAutoCopy = false})
{this.enableAutoCopy = false,
this.isEnableAutoTranslate = false,
this.isAutoTranslateWorking = false})
: _keyMaps = keyMaps,
_worldMaps = worldMaps;
@ -159,10 +187,16 @@ class _$InputMethodDialogUIStateImpl implements _InputMethodDialogUIState {
@override
@JsonKey()
final bool enableAutoCopy;
@override
@JsonKey()
final bool isEnableAutoTranslate;
@override
@JsonKey()
final bool isAutoTranslateWorking;
@override
String toString() {
return 'InputMethodDialogUIState(keyMaps: $keyMaps, worldMaps: $worldMaps, enableAutoCopy: $enableAutoCopy)';
return 'InputMethodDialogUIState(keyMaps: $keyMaps, worldMaps: $worldMaps, enableAutoCopy: $enableAutoCopy, isEnableAutoTranslate: $isEnableAutoTranslate, isAutoTranslateWorking: $isAutoTranslateWorking)';
}
@override
@ -174,7 +208,11 @@ class _$InputMethodDialogUIStateImpl implements _InputMethodDialogUIState {
const DeepCollectionEquality()
.equals(other._worldMaps, _worldMaps) &&
(identical(other.enableAutoCopy, enableAutoCopy) ||
other.enableAutoCopy == enableAutoCopy));
other.enableAutoCopy == enableAutoCopy) &&
(identical(other.isEnableAutoTranslate, isEnableAutoTranslate) ||
other.isEnableAutoTranslate == isEnableAutoTranslate) &&
(identical(other.isAutoTranslateWorking, isAutoTranslateWorking) ||
other.isAutoTranslateWorking == isAutoTranslateWorking));
}
@override
@ -182,7 +220,9 @@ class _$InputMethodDialogUIStateImpl implements _InputMethodDialogUIState {
runtimeType,
const DeepCollectionEquality().hash(_keyMaps),
const DeepCollectionEquality().hash(_worldMaps),
enableAutoCopy);
enableAutoCopy,
isEnableAutoTranslate,
isAutoTranslateWorking);
/// Create a copy of InputMethodDialogUIState
/// with the given fields replaced by the non-null parameter values.
@ -197,7 +237,9 @@ class _$InputMethodDialogUIStateImpl implements _InputMethodDialogUIState {
abstract class _InputMethodDialogUIState implements InputMethodDialogUIState {
factory _InputMethodDialogUIState(
final Map<String, String>? keyMaps, final Map<String, String>? worldMaps,
{final bool enableAutoCopy}) = _$InputMethodDialogUIStateImpl;
{final bool enableAutoCopy,
final bool isEnableAutoTranslate,
final bool isAutoTranslateWorking}) = _$InputMethodDialogUIStateImpl;
@override
Map<String, String>? get keyMaps;
@ -205,6 +247,10 @@ abstract class _InputMethodDialogUIState implements InputMethodDialogUIState {
Map<String, String>? get worldMaps;
@override
bool get enableAutoCopy;
@override
bool get isEnableAutoTranslate;
@override
bool get isAutoTranslateWorking;
/// Create a copy of InputMethodDialogUIState
/// with the given fields replaced by the non-null parameter values.

View File

@ -7,7 +7,7 @@ part of 'input_method_dialog_ui_model.dart';
// **************************************************************************
String _$inputMethodDialogUIModelHash() =>
r'8c703de14c98fb6b2f26dbae04c2c9c06f50eb8c';
r'ec8d0bb5118b74fa12341ed8048dde9335f57878';
/// See also [InputMethodDialogUIModel].
@ProviderFor(InputMethodDialogUIModel)

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:freezed_annotation/freezed_annotation.dart';