JJ_MKVMaker/lib/ui/browser/page_translation.dart

90 lines
3.2 KiB
Dart

import 'dart:async';
import '../../core/web_translate.dart';
import '../../l10n/tr.dart';
import 'web_nav.dart';
/// 페이지 번역 (환경 설정 > 웹 브라우저 > 웹 페이지 자동 번역).
/// 약속: [now] 는 켜져 있으면 지금 번역하고, 켜져 있는 동안 2초마다 새로 나온 글자를 번역한다 (YouTube 처럼 글이 계속 바뀌는 페이지).
/// 세 번 실패하면 [resetFails] (다른 페이지로 감) 까지 쉬고 [onGiveUp] 으로 한 번 알린다. [setOn] 으로 끄면 원문으로 되돌린다.
class PageTranslation {
static final _translator = WebPageTranslator();
/// 지금 페이지의 조작 (아직 웹뷰가 붙지 않았으면 null)
final WebNav? Function() nav;
/// 지금 번역해도 되는지 (화면이 가려져 있지 않고 웹 주소일 때)
final bool Function() canRun;
final void Function(Object error) onGiveUp;
PageTranslation({required this.on, required this.nav, required this.canRun, required this.onGiveUp});
/// 번역 중인지. 처음엔 설정을 따르고, 도구 막대의 [번역] 버튼으로 바꾼다.
bool on;
/// 버튼으로 직접 켰다: 이미 화면 언어로 보이는 페이지도 번역한다
bool force = false;
bool _busy = false;
int _fails = 0;
Timer? _timer;
bool _disposed = false;
/// 다른 페이지: 번역을 다시 시도
void resetFails() => _fails = 0;
void setOn(bool v, {required bool force}) {
on = v;
this.force = v && force;
_fails = 0;
if (v) {
now();
} else {
_timer?.cancel();
_timer = null;
nav()?.evaluate(webTranslateRestoreScript);
}
}
void now() {
if (!on || _disposed) return;
_timer ??= Timer.periodic(const Duration(seconds: 2), (_) => _tick());
_tick();
}
Future<void> _tick() async {
final n = nav();
if (!on || _busy || n == null || !canRun() || _fails >= 3) return;
_busy = true;
try {
final target = googleLang(uiLanguage); // 실제 화면 언어 (시스템 언어 따르기면 그 언어)
// 한 번에 300개씩, 페이지가 크면 몇 번 더
for (var round = 0; round < 10 && !_disposed && on; round++) {
final got = CollectedText.parse(await n.evaluate(webTranslateCollectScript(target, force: force)));
if (got == null || got.texts.isEmpty) break;
final out = await _translator.translate(got.texts, target);
// 대부분 이미 화면 언어면 (직접 켠 게 아니면) 이 페이지는 더 번역하지 않는다
final chars = got.texts.fold<int>(0, (a, t) => a + t.length);
var same = 0;
for (var i = 0; i < out.length; i++) {
if (out[i] == null) same += got.texts[i].length;
}
if (_disposed || !on) break;
await n.evaluate(webTranslateApplyScript({for (var i = 0; i < got.ids.length; i++) got.ids[i]: out[i]},
same: round == 0 && chars > 0 && same >= chars * 0.8));
if (!got.more) break;
}
_fails = 0;
} catch (e) {
// 번역 서버에 닿지 않음 등: 세 번 실패하면 다른 페이지로 갈 때까지 쉰다
if (++_fails == 3 && !_disposed) onGiveUp(e);
} finally {
_busy = false;
}
}
void dispose() {
_disposed = true;
_timer?.cancel();
}
}