90 lines
3.2 KiB
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();
|
|
}
|
|
}
|