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 _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(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(); } }