Greasy Fork

Greasy Fork is available in English.

Auto Translate - TWP Engine Full Port

网页自动翻译,Via浏览器专属优化,苹果安卓通吃,Google/微软/腾讯三引擎,249种语言随便切。

当前为 2026-02-24 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Auto Translate - TWP Engine Full Port
// @name:zh-CN   网页自动翻译 — Via浏览器专属 · 苹果安卓都能用 · 249种语言
// @name:zh-TW   網頁自動翻譯 — Via瀏覽器專屬 · 蘋果安卓都能用 · 249種語言
// @name:en      Auto Translate — Built for Via Browser · Works on iPhone & Android · 249 Languages
// @name:ja      自動翻訳 — Via ブラウザ対応 · iPhone でも Android でも動く · 249言語
// @name:ko      자동 번역 — Via 브라우저 전용 · 아이폰 안드로이드 다 됨 · 249개 언어
// @name:fr      Traduction auto — Conçu pour Via · Fonctionne sur iPhone & Android · 249 langues
// @name:de      Auto-Übersetzer — Für Via Browser · Läuft auf iPhone & Android · 249 Sprachen
// @name:es      Traducción automática — Hecho para Via · Funciona en iPhone y Android · 249 idiomas
// @name:pt      Tradução automática — Feito pro Via · Funciona no iPhone e Android · 249 idiomas
// @name:pt-BR   Tradução automática — Feito pro Via · Funciona no iPhone e Android · 249 idiomas
// @name:ru      Авто-перевод — Для Via браузера · Работает на iPhone и Android · 249 языков
// @name:it      Traduzione automatica — Fatto per Via · Funziona su iPhone e Android · 249 lingue
// @name:tr      Otomatik Çeviri — Via Tarayıcı için · iPhone ve Android'de çalışır · 249 dil
// @name:ar      ترجمة تلقائية — مصمم لمتصفح Via · يشتغل على آيفون وأندرويد · 249 لغة
// @name:th      แปลอัตโนมัติ — สำหรับ Via Browser · ใช้ได้ทั้ง iPhone และ Android · 249 ภาษา
// @name:vi      Tự động dịch — Dành cho Via Browser · Chạy trên cả iPhone lẫn Android · 249 ngôn ngữ
// @name:id      Terjemahan Otomatis — Buat Via Browser · Jalan di iPhone & Android · 249 bahasa
// @name:ms      Terjemahan Auto — Untuk Via Browser · Boleh guna di iPhone & Android · 249 bahasa
// @name:hi      ऑटो ट्रांसलेट — Via ब्राउज़र के लिए · iPhone और Android दोनों पर चलता है · 249 भाषाएँ
// @name:bn      অটো অনুবাদ — Via Browser-এর জন্য · iPhone ও Android দুটোতেই চলে · ২৪৯ ভাষা
// @name:ta      தானியங்கி மொழிபெயர்ப்பு — Via Browser-க்கு · iPhone, Android இரண்டிலும் வேலை செய்யும் · 249 மொழிகள்
// @name:te      ఆటో ట్రాన్స్‌లేట్ — Via Browser కోసం · iPhone, Android రెండింటిలోనూ పనిచేస్తుంది · 249 భాషలు
// @name:ur      آٹو ترجمہ — Via براؤزر کے لیے · آئی فون اور اینڈرائیڈ دونوں پر چلتا ہے · 249 زبانیں
// @name:fa      ترجمه خودکار — برای مرورگر Via · روی آیفون و اندروید کار می‌کنه · 249 زبان
// @name:pl      Auto-tłumaczenie — Dla Via Browser · Działa na iPhone i Android · 249 języków
// @name:uk      Авто-переклад — Для Via браузера · Працює на iPhone та Android · 249 мов
// @name:ro      Traducere automată — Pentru Via Browser · Merge pe iPhone și Android · 249 limbi
// @name:nl      Automatisch vertalen — Voor Via Browser · Werkt op iPhone & Android · 249 talen
// @name:el      Αυτόματη μετάφραση — Για Via Browser · Λειτουργεί σε iPhone & Android · 249 γλώσσες
// @name:cs      Automatický překlad — Pro Via Browser · Funguje na iPhone i Android · 249 jazyků
// @name:hu      Automatikus fordítás — Via Böngészőhöz · iPhone-on és Androidon is megy · 249 nyelv
// @name:sv      Automatisk översättning — För Via Browser · Fungerar på iPhone & Android · 249 språk
// @name:da      Automatisk oversættelse — Til Via Browser · Virker på iPhone & Android · 249 sprog
// @name:fi      Automaattinen käännös — Via-selaimelle · Toimii iPhonella ja Androidilla · 249 kieltä
// @name:no      Automatisk oversettelse — For Via Browser · Fungerer på iPhone og Android · 249 språk
// @name:bg      Автоматичен превод — За Via браузър · Работи на iPhone и Android · 249 езика
// @name:hr      Automatski prijevod — Za Via Browser · Radi na iPhoneu i Androidu · 249 jezika
// @name:sr      Аутоматски превод — За Via прегледач · Ради на iPhone и Android · 249 језика
// @name:sk      Automatický preklad — Pre Via Browser · Funguje na iPhone aj Android · 249 jazykov
// @name:sl      Samodejni prevod — Za Via Browser · Deluje na iPhone in Android · 249 jezikov
// @name:lt      Automatinis vertimas — Via naršyklei · Veikia iPhone ir Android · 249 kalbos
// @name:lv      Automātiskā tulkošana — Via pārlūkam · Darbojas iPhone un Android · 249 valodas
// @name:et      Automaatne tõlge — Via brauserile · Töötab iPhone'is ja Androidis · 249 keelt
// @name:sw      Tafsiri Otomatiki — Kwa Via Browser · Inafanya kazi iPhone na Android · Lugha 249
// @name:fil     Auto Translate — Para sa Via Browser · Gumagana sa iPhone at Android · 249 na wika
// @name:my      အလိုအလျောက် ဘာသာပြန် — Via Browser အတွက် · iPhone နဲ့ Android မှာ အလုပ်လုပ်တယ် · ဘာသာ 249
// @name:km      បកប្រែស្វ័យប្រវត្តិ — សម្រាប់ Via Browser · ដំណើរការលើ iPhone និង Android · ២៤៩ ភាសា
// @name:lo      ແປພາສາອັດຕະໂນມັດ — ສຳລັບ Via Browser · ໃຊ້ໄດ້ທັງ iPhone ແລະ Android · 249 ພາສາ
// @name:ka      ავტომატური თარგმანი — Via ბრაუზერისთვის · მუშაობს iPhone-ზე და Android-ზე · 249 ენა
// @name:hy       Delays նակ թարdays — Via դdays · iPhone ays Android-ays · 249 լdays
// @name:am      ራስ-ሰር ትርጉም — ለ Via Browser · iPhone እና Android ሁለቱም ይሰራል · 249 ቋንቋዎች
// @name:ne      स्वचालित अनुवाद — Via Browser को लागि · iPhone र Android दुवैमा चल्छ · 249 भाषाहरू
// @name:si      ස්වයංක්‍රීය පරිවර්තනය — Via Browser සඳහා · iPhone සහ Android දෙකේම වැඩ කරයි · භාෂා 249
// @name:mn      Автомат орчуулга — Via хөтөч дээр · iPhone болон Android дээр ажилладаг · 249 хэл
// @name:uz      Avtomatik tarjima — Via brauzeri uchun · iPhone va Androidda ishlaydi · 249 til
// @name:kk      Автоматты аударма — Via браузеріне · iPhone мен Android-та жұмыс істейді · 249 тіл
// @name:az      Avtomatik tərcümə — Via Browser üçün · iPhone və Androidda işləyir · 249 dil
// @name:sq      Përkthim automatik — Për Via Browser · Punon në iPhone dhe Android · 249 gjuhë
// @name:mk      Автоматски превод — За Via Browser · Работи на iPhone и Android · 249 јазици
// @name:bs      Automatski prijevod — Za Via Browser · Radi na iPhoneu i Androidu · 249 jezika
// @name:is      Sjálfvirk þýðing — Fyrir Via Browser · Virkar á iPhone og Android · 249 tungumál
// @name:af      Outomatiese vertaling — Vir Via Browser · Werk op iPhone & Android · 249 tale
// @name:yo      Ìtumọ̀ Aládàámọ́ — Fún Via Browser · Ó ń ṣiṣẹ́ lórí iPhone àti Android · Èdè 249
// @name:ha      Fassara ta Atomatik — Don Via Browser · Yana aiki a iPhone da Android · Harsuna 249
// @name:ig      Nsụgharị Akpaaka — Maka Via Browser · Ọ na-arụ ọrụ na iPhone na Android · Asụsụ 249
// @name:zu      Ukuhumusha Okuzenzakalelayo — Kwe-Via Browser · Kusebenza ku-iPhone ne-Android · Izilimi 249
// @name:mg      Dikanteny Mandeha ho azy — Ho an'ny Via Browser · Mandeha amin'ny iPhone sy Android · 249 fiteny
// @name:eo      Aŭtomata traduko — Por Via Retumilo · Funkcias ĉe iPhone kaj Android · 249 lingvoj
// @name:la      Translatio automatica — Pro Via Navigatro · Operatur in iPhone et Android · 249 linguae
// @name:he      תרגום אוטומטי — לדפדפן Via · עובד באייפון ובאנדרואיד · 249 שפות
// @name:gu      ઓટો ટ્રાન્સલેટ — Via Browser માટે · iPhone અને Android બંને પર ચાલે છે · 249 ભાષાઓ
// @name:mr      ऑटो ट्रान्सलेट — Via Browser साठी · iPhone आणि Android दोन्हींवर चालते · 249 भाषा
// @name:pa      ਆਟੋ ਅਨੁਵਾਦ — Via Browser ਲਈ · iPhone ਅਤੇ Android ਦੋਵਾਂ ਤੇ ਚੱਲਦਾ ਹੈ · 249 ਭਾਸ਼ਾਵਾਂ
// @name:kn      ಆಟೋ ಅನುವಾದ — Via Browser ಗಾಗಿ · iPhone ಮತ್ತು Android ಎರಡರಲ್ಲೂ ಕೆಲಸ ಮಾಡುತ್ತದೆ · 249 ಭಾಷೆಗಳು
// @name:ml      ഓട്ടോ ട്രാൻസ്ലേറ്റ് — Via Browser-നായി · iPhone-ലും Android-ലും പ്രവർത്തിക്കുന്നു · 249 ഭാഷകൾ

// @description       网页自动翻译,Via浏览器专属优化,苹果安卓通吃,Google/微软/腾讯三引擎,249种语言随便切。
// @description:zh-CN 网页自动翻译,Via浏览器专属优化,苹果安卓通吃,Google/微软/腾讯三引擎,249种语言随便切。
// @description:zh-TW 網頁自動翻譯,Via瀏覽器專屬優化,蘋果安卓通吃,Google/微軟/騰訊三引擎,249種語言隨便切。
// @description:en    Auto-translate any webpage. Optimized for Via Browser on both iPhone & Android. 3 engines (Google/Microsoft/Tencent), 249 languages, just works.
// @description:ja    Webページを自動翻訳。Via ブラウザに最適化、iPhoneでもAndroidでもそのまま動く。Google/Microsoft/Tencentの3エンジン、249言語対応。
// @description:ko    웹페이지 자동 번역. Via 브라우저에 최적화, 아이폰이든 안드로이드든 그냥 됨. Google/Microsoft/Tencent 3개 엔진, 249개 언어.
// @description:fr    Traduction automatique de pages web. Optimisé pour Via sur iPhone et Android. 3 moteurs (Google/Microsoft/Tencent), 249 langues, ça marche direct.
// @description:de    Automatische Webseiten-Übersetzung. Optimiert für Via Browser auf iPhone und Android. 3 Engines (Google/Microsoft/Tencent), 249 Sprachen, läuft einfach.
// @description:es    Traducción automática de páginas web. Optimizado para Via en iPhone y Android. 3 motores (Google/Microsoft/Tencent), 249 idiomas, funciona y ya.
// @description:pt    Tradução automática de páginas. Otimizado pro Via no iPhone e Android. 3 motores (Google/Microsoft/Tencent), 249 idiomas, só funciona.
// @description:pt-BR Tradução automática de páginas. Otimizado pro Via no iPhone e Android. 3 motores (Google/Microsoft/Tencent), 249 idiomas, só funciona.
// @description:ru    Автоматический перевод страниц. Оптимизирован для Via на iPhone и Android. 3 движка (Google/Microsoft/Tencent), 249 языков, просто работает.
// @description:it    Traduzione automatica delle pagine web. Ottimizzato per Via su iPhone e Android. 3 motori (Google/Microsoft/Tencent), 249 lingue, funziona e basta.
// @description:tr    Web sayfalarını otomatik çevir. Via Browser için optimize edildi, iPhone ve Android'de çalışır. 3 motor (Google/Microsoft/Tencent), 249 dil.
// @description:ar    ترجمة تلقائية لصفحات الويب. مُحسّن لمتصفح Via على آيفون وأندرويد. 3 محركات (Google/Microsoft/Tencent)، 249 لغة، يشتغل وخلاص.
// @description:th    แปลเว็บอัตโนมัติ ปรับแต่งสำหรับ Via Browser ใช้ได้ทั้ง iPhone และ Android 3 เครื่องยนต์ (Google/Microsoft/Tencent) 249 ภาษา ใช้ได้เลย
// @description:vi    Tự động dịch trang web. Tối ưu cho Via Browser trên cả iPhone lẫn Android. 3 engine (Google/Microsoft/Tencent), 249 ngôn ngữ, cài vào là chạy.
// @description:id    Terjemahkan halaman web otomatis. Dioptimalkan buat Via Browser di iPhone dan Android. 3 mesin (Google/Microsoft/Tencent), 249 bahasa, langsung jalan.
// @description:ms    Terjemah halaman web secara automatik. Dioptimumkan untuk Via Browser di iPhone dan Android. 3 enjin (Google/Microsoft/Tencent), 249 bahasa.
// @description:hi    वेबपेज ऑटो-ट्रांसलेट। Via Browser के लिए बना है, iPhone और Android दोनों पर चलता है। 3 इंजन (Google/Microsoft/Tencent), 249 भाषाएँ।
// @description:bn    ওয়েবপেজ অটো-ট্রান্সলেট। Via Browser-এর জন্য অপ্টিমাইজ করা, iPhone ও Android দুটোতেই চলে। 3 ইঞ্জিন, 249 ভাষা।
// @description:ta    வலைப்பக்கங்களை தானாக மொழிபெயர்க்கும். Via Browser-க்கு உகந்தது, iPhone மற்றும் Android இரண்டிலும் வேலை செய்யும். 3 இயந்திரங்கள், 249 மொழிகள்.
// @description:te    వెబ్‌పేజీలను ఆటోమేటిక్‌గా అనువదిస్తుంది. Via Browser కోసం ఆప్టిమైజ్ చేయబడింది, iPhone మరియు Android రెండింటిలోనూ పనిచేస్తుంది. 3 ఇంజన్‌లు, 249 భాషలు.
// @description:ur    ویب پیج خودکار ترجمہ۔ Via Browser کے لیے بنایا گیا، آئی فون اور اینڈرائیڈ دونوں پر چلتا ہے۔ 3 انجن، 249 زبانیں۔
// @description:fa    ترجمه خودکار صفحات وب. بهینه‌شده برای مرورگر Via روی آیفون و اندروید. 3 موتور (Google/Microsoft/Tencent)، 249 زبان.
// @description:pl    Automatyczne tłumaczenie stron. Zoptymalizowane pod Via Browser na iPhone i Android. 3 silniki (Google/Microsoft/Tencent), 249 języków, po prostu działa.
// @description:uk    Автоматичний переклад сторінок. Оптимізовано для Via на iPhone та Android. 3 рушії (Google/Microsoft/Tencent), 249 мов, просто працює.
// @description:ro    Traducere automată a paginilor web. Optimizat pentru Via pe iPhone și Android. 3 motoare (Google/Microsoft/Tencent), 249 limbi, merge direct.
// @description:nl    Automatische vertaling van webpagina's. Geoptimaliseerd voor Via Browser op iPhone en Android. 3 engines, 249 talen, werkt gewoon.
// @description:el    Αυτόματη μετάφραση ιστοσελίδων. Βελτιστοποιημένο για Via Browser σε iPhone και Android. 3 μηχανές, 249 γλώσσες, απλά δουλεύει.
// @description:cs    Automatický překlad stránek. Optimalizováno pro Via Browser na iPhone a Android. 3 enginy, 249 jazyků, prostě to funguje.
// @description:hu    Automatikus weblap-fordítás. Via Böngészőhöz optimalizálva, iPhone-on és Androidon is megy. 3 motor, 249 nyelv.
// @description:sv    Automatisk översättning av webbsidor. Optimerad för Via Browser på iPhone och Android. 3 motorer, 249 språk, bara funkar.
// @description:da    Automatisk oversættelse af websider. Optimeret til Via Browser på iPhone og Android. 3 motorer, 249 sprog, virker bare.
// @description:fi    Automaattinen verkkosivujen käännös. Optimoitu Via-selaimelle iPhonella ja Androidilla. 3 moottoria, 249 kieltä, toimii vain.
// @description:no    Automatisk oversettelse av nettsider. Optimalisert for Via Browser på iPhone og Android. 3 motorer, 249 språk, bare fungerer.
// @description:bg    Автоматичен превод на уебстраници. Оптимизиран за Via на iPhone и Android. 3 двигателя, 249 езика, просто работи.
// @description:hr    Automatski prijevod web stranica. Optimizirano za Via Browser na iPhoneu i Androidu. 3 motora, 249 jezika, jednostavno radi.
// @description:sr    Аутоматски превод веб страница. Оптимизовано за Via на iPhoneu и Androidu. 3 мотора, 249 језика, једноставно ради.
// @description:sk    Automatický preklad stránok. Optimalizované pre Via Browser na iPhone a Android. 3 enginy, 249 jazykov, jednoducho funguje.
// @description:sl    Samodejni prevod spletnih strani. Optimizirano za Via Browser na iPhone in Android. 3 motorji, 249 jezikov, preprosto deluje.
// @description:lt    Automatinis tinklalapių vertimas. Optimizuota Via naršyklei iPhone ir Android. 3 varikliai, 249 kalbos, tiesiog veikia.
// @description:lv    Automātiska tīmekļa lapu tulkošana. Optimizēta Via pārlūkam iPhone un Android. 3 dzinēji, 249 valodas, vienkārši strādā.
// @description:et    Automaatne veebilehtede tõlge. Optimeeritud Via brauserile iPhone'is ja Androidis. 3 mootorit, 249 keelt, lihtsalt töötab.
// @description:sw    Tafsiri otomatiki ya kurasa za wavuti. Imebuniwa kwa Via Browser kwenye iPhone na Android. Injini 3, lugha 249, inafanya kazi tu.
// @description:fil   Awtomatikong pagsasalin ng mga webpage. Optimized para sa Via Browser sa iPhone at Android. 3 engine, 249 na wika, gumagana agad.
// @description:my    ဝဘ်စာမျက်နှာများကို အလိုအလျောက် ဘာသာပြန်ပါ။ Via Browser အတွက် iPhone နဲ့ Android မှာ အလုပ်လုပ်တယ်။ အင်ဂျင် 3 ခု၊ ဘာသာ 249။
// @description:km    បកប្រែគេហទំព័រដោយស្វ័យប្រវត្តិ។ ធ្វើឱ្យប្រសើរសម្រាប់ Via Browser នៅលើ iPhone និង Android។ ម៉ាស៊ីន 3, ២៤៩ ភាសា។
// @description:lo    ແປໜ້າເວັບອັດຕະໂນມັດ. ປັບແຕ່ງສຳລັບ Via Browser ໃນ iPhone ແລະ Android. 3 ເຄື່ອງຈັກ, 249 ພາສາ, ໃຊ້ໄດ້ເລີຍ.
// @description:ka    ვებგვერდების ავტომატური თარგმანი. ოპტიმიზებულია Via ბრაუზერისთვის iPhone-ზე და Android-ზე. 3 ძრავი, 249 ენა, უბრალოდ მუშაობს.
// @description:hy    Վdelays պdelays delays delays. Via delays iPhone days Android days. 3 delays, 249 delays.
// @description:am    ድረ-ገጾችን በራስ-ሰር ይተርጉሙ። ለ Via Browser iPhone እና Android ላይ ተመቻችቷል። 3 ሞተሮች፣ 249 ቋንቋዎች፣ በቀላሉ ይሰራል።
// @description:ne    वेबपेज स्वचालित अनुवाद। Via Browser को लागि iPhone र Android दुवैमा अनुकूलित। 3 इन्जिन, 249 भाषा, सिधै चल्छ।
// @description:si    වෙබ් පිටු ස්වයංක්‍රීයව පරිවර්තනය කරන්න. Via Browser සඳහා iPhone සහ Android දෙකේම ප්‍රශස්ත කර ඇත. එන්ජින් 3ක්, භාෂා 249ක්.
// @description:mn    Вэб хуудсыг автоматаар орчуулна. Via хөтөч дээр iPhone болон Android-д зориулж оновчилсон. 3 хөдөлгүүр, 249 хэл.
// @description:uz    Veb-sahifalarni avtomatik tarjima qilish. Via brauzeri uchun iPhone va Androidda optimallashtirilgan. 3 ta dvigatel, 249 til, shunchaki ishlaydi.
// @description:kk    Веб-беттерді автоматты аударма. Via браузеріне iPhone мен Android-та оңтайландырылған. 3 қозғалтқыш, 249 тіл, жай ғана жұмыс істейді.
// @description:az    Veb səhifələrin avtomatik tərcüməsi. Via Browser üçün iPhone və Androidda optimallaşdırılıb. 3 mühərrik, 249 dil, sadəcə işləyir.
// @description:sq    Përkthim automatik i faqeve të internetit. I optimizuar për Via Browser në iPhone dhe Android. 3 motorë, 249 gjuhë, thjesht funksionon.
// @description:mk    Автоматски превод на веб-страници. Оптимизирано за Via Browser на iPhone и Android. 3 мотори, 249 јазици, едноставно работи.
// @description:bs    Automatski prijevod web stranica. Optimizirano za Via Browser na iPhoneu i Androidu. 3 motora, 249 jezika, jednostavno radi.
// @description:is    Sjálfvirk þýðing vefsíðna. Fínstillt fyrir Via Browser á iPhone og Android. 3 vélar, 249 tungumál, virkar bara.
// @description:af    Outomatiese vertaling van webblaaie. Geoptimaliseer vir Via Browser op iPhone en Android. 3 enjins, 249 tale, werk net.
// @description:yo    Ìtumọ̀ aládàámọ́ àwọn ojú-ìwé wẹ́ẹ̀bù. Ṣe ìmúdàgba fún Via Browser lórí iPhone àti Android. Ẹ̀rọ 3, èdè 249.
// @description:ha    Fassara shafukan yanar gizo ta atomatik. An inganta don Via Browser a iPhone da Android. Injuna 3, harsuna 249, kawai tana aiki.
// @description:ig    Nsụgharị akpaaka nke ibe weebụ. Emezigharịrị maka Via Browser na iPhone na Android. Injin 3, asụsụ 249, ọ na-arụ ọrụ.
// @description:zu    Ukuhumusha okuzenzakalelayo kwamakhasi ewebhu. Kuthuthukiselwe i-Via Browser ku-iPhone ne-Android. Izinjini ezi-3, izilimi ezingu-249.
// @description:mg    Dikanteny amin'ny pejy tranonkala mandeha ho azy. Natao ho an'ny Via Browser amin'ny iPhone sy Android. Motera 3, fiteny 249, mandeha fotsiny.
// @description:eo    Aŭtomata traduko de retpaĝoj. Optimumigita por Via Retumilo ĉe iPhone kaj Android. 3 motoroj, 249 lingvoj, simple funkcias.
// @description:la    Translatio automatica paginarum interretialium. Optimata pro Via Navigatro in iPhone et Android. 3 motores, 249 linguae.
// @description:he    תרגום אוטומטי של דפי אינטרנט. מותאם לדפדפן Via באייפון ובאנדרואיד. 3 מנועים, 249 שפות, פשוט עובד.
// @description:gu    વેબપેજ ઓટો-ટ્રાન્સલેટ. Via Browser માટે iPhone અને Android બંને પર ઑપ્ટિમાઇઝ કરેલ. 3 એન્જિન, 249 ભાષાઓ, બસ ચાલે છે.
// @description:mr    वेबपेज ऑटो-ट्रान्सलेट. Via Browser साठी iPhone आणि Android दोन्हींवर ऑप्टिमाइझ केलेले. 3 इंजिन, 249 भाषा, फक्त चालते.
// @description:pa    ਵੈੱਬਪੇਜ ਆਟੋ-ਅਨੁਵਾਦ. Via Browser ਲਈ iPhone ਅਤੇ Android ਦੋਵਾਂ ਤੇ ਅਨੁਕੂਲਿਤ. 3 ਇੰਜਣ, 249 ਭਾਸ਼ਾਵਾਂ, ਬੱਸ ਚੱਲਦਾ ਹੈ.
// @description:kn    ವೆಬ್‌ಪೇಜ್ ಆಟೋ-ಅನುವಾದ. Via Browser ಗಾಗಿ iPhone ಮತ್ತು Android ಎರಡರಲ್ಲೂ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ. 3 ಎಂಜಿನ್‌ಗಳು, 249 ಭಾಷೆಗಳು.
// @description:ml    വെബ്‌പേജ് ഓട്ടോ-ട്രാൻസ്ലേറ്റ്. Via Browser-നായി iPhone-ലും Android-ലും ഒപ്റ്റിമൈസ് ചെയ്തിരിക്കുന്നു. 3 എഞ്ചിനുകൾ, 249 ഭാഷകൾ.
//  @version 6.6.6.6.6.6
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @connect      translate-pa.googleapis.com
// @connect      translate.googleapis.com
// @connect      api-edge.cognitive.microsofttranslator.com
// @connect      edge.microsoft.com
// @connect      transmart.qq.com
// @run-at       document-idle
// @namespace auto-translate-twp
// ==/UserScript==

(async () => {
  'use strict';

  try { if (document.contentType === 'application/xml') return } catch (_) {}

  // ═══════════════════════════════════════════════════════════════
  // TWP完整移植:GoogleHelper_v2 + GoogleHelper(旧版TKK) + 全部249种语言
  // ═══════════════════════════════════════════════════════════════

  const deviceLang = (navigator.language || navigator.userLanguage || 'zh-CN').split('-')[0];

  // ── TWP完整语言列表(249种语言,从TWP源码options.html提取)──
  const ALL_LANGUAGES = {
    // ===== 常用语言 =====
    "zh-CN": "中文(简体)", "zh-TW": "中文(繁體)", "en": "English",
    "ja": "日本語", "ko": "한국어", "fr": "Français", "de": "Deutsch",
    "es": "Español", "ru": "Русский", "pt": "Português", "pt-PT": "Português (Portugal)",
    "ar": "العربية", "th": "ไทย", "vi": "Tiếng Việt", "it": "Italiano",
    "tr": "Türkçe", "id": "Indonesia", "ms": "Bahasa Melayu", "nl": "Nederlands",
    "pl": "Polski", "uk": "Українська", "cs": "Čeština", "sk": "Slovenčina",
    "hu": "Magyar", "ro": "Română", "bg": "Български", "hr": "Hrvatski",
    "sr": "Српски", "sl": "Slovenščina", "lt": "Lietuvių", "lv": "Latviešu",
    "et": "Eesti", "fi": "Suomi", "sv": "Svenska", "da": "Dansk",
    "no": "Norsk", "is": "Íslenska", "el": "Ελληνικά", "he": "עברית",
    "hi": "हिन्दी", "bn": "বাংলা", "ta": "தமிழ்", "te": "తెలుగు",
    "kn": "ಕನ್ನಡ", "ml": "മലയാളം", "pa": "ਪੰਜਾਬੀ", "gu": "ગુજરાતી",
    "mr": "मराठी", "ne": "नेपाली", "si": "සිංහල", "ur": "اردو",
    "fa": "فارسی", "ps": "پښتو", "my": "မြန်မာ", "km": "ខ្មែរ",
    "lo": "ລາວ", "ka": "ქართული", "hy": "Հայերեն", "az": "Azərbaycan",
    "kk": "Қазақ", "uz": "Oʻzbek", "mn": "Монгол", "sq": "Shqip",
    "mk": "Македонски", "be": "Беларуская", "bs": "Bosanski",
    "ca": "Català", "gl": "Galego", "eu": "Euskara", "mt": "Malti",
    "cy": "Cymraeg", "ga": "Gaeilge", "gd": "Gàidhlig", "lb": "Lëtzebuergesch",
    "af": "Afrikaans", "sw": "Kiswahili", "ha": "Hausa", "ig": "Igbo",
    "yo": "Yorùbá", "zu": "isiZulu", "xh": "isiXhosa", "sn": "chiShona",
    "st": "Sesotho", "so": "Soomaali", "am": "አማርኛ", "ti": "ትግርኛ",
    "om": "Oromoo", "mg": "Malagasy", "ny": "Chichewa", "lg": "Luganda",
    "rw": "Kinyarwanda", "tg": "Тоҷикӣ", "tk": "Türkmen", "ky": "Кыргызча",
    "tt": "Татар", "eo": "Esperanto", "la": "Latina", "co": "Corsu",
    "fy": "Frysk", "haw": "ʻŌlelo Hawaiʻi", "sm": "Gagana Samoa",
    "mi": "Te Reo Māori", "ceb": "Cebuano", "fil": "Filipino",
    "jv": "Basa Jawa", "su": "Basa Sunda", "hmn": "Hmong",
    "ht": "Kreyòl Ayisyen", "ku": "Kurdî", "ckb": "کوردی",
    "sd": "سنڌي", "or": "ଓଡ଼ିଆ", "as": "অসমীয়া",
    "sa": "संस्कृतम्", "mai": "मैथिली", "bho": "भोजपुरी",
    "doi": "डोगरी", "ug": "ئۇيغۇرچە", "dv": "ދިވެހި",
    "ak": "Akan", "ee": "Eʋegbe", "gn": "Guarani", "ay": "Aymar",
    "bm": "Bamanankan", "ln": "Lingála", "nso": "Sepedi",
    "ts": "Xitsonga", "qu": "Runasimi", "ilo": "Ilokano",
    "kri": "Krio", "lus": "Mizo tawng", "mni-Mtei": "ꯃꯤꯇꯩꯂꯣꯟ",
    "gom": "कोंकणी",

    // ===== TWP v10.x 新增的114种语言(从Release日志提取)=====
    "ab": "Аԥсуа (Abkhaz)", "ace": "Bahsa Acèh (Acehnese)",
    "ach": "Lwo (Acholi)", "aa": "Qafaraf (Afar)",
    "alz": "Alur", "av": "Авар (Avar)",
    "awa": "अवधी (Awadhi)", "ban": "ᬩᬮᬶ (Balinese)",
    "bal": "بلوچی (Baluchi)", "bci": "Baoulé",
    "ba": "Башҡорт (Bashkir)", "btx": "Batak Karo",
    "bts": "Batak Simalungun", "bbc": "Batak Toba",
    "bem": "Bemba", "bew": "Betawi",
    "bik": "Bikol", "br": "Brezhoneg (Breton)",
    "bua": "Буряад (Buryat)", "yue": "粵語 (Cantonese)",
    "ch": "Chamoru (Chamorro)", "ce": "Нохчийн (Chechen)",
    "chk": "Chuukese", "cv": "Чӑваш (Chuvash)",
    "crh": "Qırımtatar (Crimean Tatar)",
    "prs": "دری (Dari)", "din": "Thuɔŋjäŋ (Dinka)",
    "dov": "Dombe", "dyu": "Julakan (Dyula)",
    "dz": "རྫོང་ཁ (Dzongkha)", "fo": "Føroyskt (Faroese)",
    "fj": "Na Vosa Vakaviti (Fijian)", "fon": "Fɔ̀ngbè (Fon)",
    "fr-CA": "Français (Canada)", "fur": "Furlan (Friulian)",
    "ff": "Pulaar (Fulani)", "gaa": "Gã (Ga)",
    "cnh": "Lai (Hakha Chin)", "hil": "Hiligaynon",
    "hrx": "Hunsrik", "iba": "Iban",
    "iu-Latn": "ᐃᓄᒃᑎᑐᑦ (Inuktut Latin)", "jam": "Jamaican Patois",
    "kac": "Jingpo (景颇语)", "kl": "Kalaallisut",
    "kr": "Kanuri", "pam": "Kapampangan",
    "kha": "Khasi", "cgg": "Rukiga (Kiga)",
    "kg": "Kikongo", "mkw": "Kituba",
    "trp": "Kokborok", "kv": "Коми (Komi)",
    "ltg": "Latgaļu (Latgalian)", "lij": "Lìgure (Ligurian)",
    "li": "Limburgs (Limburgish)", "lmo": "Lombard",
    "luo": "Dholuo (Luo)", "mad": "Madhurâ (Madurese)",
    "mak": "Makassar", "ms-Arab": "بهاس ملايو (Malay Jawi)",
    "mam": "Mam", "gv": "Gaelg (Manx)",
    "mh": "Kajin Majōl (Marshallese)", "mwr": "मारवाड़ी (Marwadi)",
    "mfe": "Kreol Morisien (Mauritian Creole)", "chm": "Марий (Meadow Mari)",
    "min": "Minangkabau", "nhe": "Nahuatl",
    "ndc-ZW": "Ndau", "nr": "isiNdebele (South Ndebele)",
    "new": "नेपाल भाषा (Newari)", "nqo": "ߒߞߏ (NKo)",
    "nus": "Thok Nath (Nuer)", "oc": "Occitan",
    "os": "Ирон (Ossetian)", "pag": "Pangasinan",
    "pap": "Papiamento", "pa-Arab": "پنجابی (Punjabi Shahmukhi)",
    "kek": "Qʼeqchiʼ", "rom": "Romani",
    "rn": "Ikirundi (Rundi)", "se": "Davvisámegiella (North Sami)",
    "sg": "Sängö (Sango)", "bo": "བོད་ཡིག (Tibetan)",
    "dsb": "Dolnoserbšćina (Lower Sorbian)", "hsb": "Hornjoserbšćina (Upper Sorbian)",
    "ikt": "Inuinnaqtun", "iu": "ᐃᓄᒃᑎᑐᑦ (Inuktitut)",
    "lzh": "文言文 (Classical Chinese)", "mvf": "ᠮᠣᠩᠭᠣᠯ (Mongolian Traditional)",
    "brx": "बर' (Bodo)", "hne": "छत्तीसगढ़ी (Chhattisgarhi)",
    "ks": "कॉशुर (Kashmiri)", "mrj": "Мары (Hill Mari)",
    "sa-Latn": "Sanskrit (Latin)", "sc": "Sardu (Sardinian)",
    "scn": "Sicilianu (Sicilian)", "szl": "Ślůnski (Silesian)",
    "su-Latn": "Sunda (Latin)", "tcy": "ತುಳು (Tulu)",
    "vec": "Vèneto (Venetian)", "war": "Winaray (Waray)",
    "wo": "Wolof", "zap": "Zapotec",
    "ms-Latn": "Malay (Latin)"
  };

  // ── 语言代码到显示名称的简短映射(用于UI下拉菜单分组)──
  const LANG_GROUPS = {
    "常用": ["zh-CN","zh-TW","en","ja","ko","fr","de","es","ru","pt","ar","th","vi","it","tr","id"],
    "欧洲": ["nl","pl","uk","cs","sk","hu","ro","bg","hr","sr","sl","lt","lv","et","fi","sv","da","no","is","el","be","bs","ca","gl","eu","mt","cy","ga","gd","lb","af","eo","la","co","fy","fo","br","oc","sc","scn","szl","fur","lij","lmo","li","vec","ltg","dsb","hsb","gv","se"],
    "亚洲": ["hi","bn","ta","te","kn","ml","pa","gu","mr","ne","si","ur","fa","ps","my","km","lo","ka","hy","az","kk","uz","mn","tg","tk","ky","tt","ug","dv","or","as","sa","mai","bho","doi","mni-Mtei","gom","awa","ks","brx","hne","mwr","trp","kac","bo","dz","yue","lzh","ms","fil","ceb","jv","su","hmn","ilo","hil","bik","pam","pag","war","ban","mad","mak","min","ace","btx","bts","bbc","bew","iba","ms-Arab","kha"],
    "非洲": ["sw","ha","ig","yo","zu","xh","sn","st","so","am","ti","om","mg","ny","lg","rw","ak","ee","bm","ln","nso","ts","kri","wo","ff","gaa","fon","bci","dyu","bem","luo","sg","kg","mkw","dov","nus","din","ach","alz","ndc-ZW","nr","rn","mfe"],
    "美洲/大洋洲": ["pt-PT","fr-CA","ht","qu","gn","ay","haw","sm","mi","fj","mh","ch","chk","jam","nhe","mam","kek","pap","hrx","ikt","iu","iu-Latn","kl"],
    "其他": ["ab","av","ba","bua","ce","cv","crh","kv","chm","mrj","os","rom","nqo","aa","bal","cnh","kr","prs","pa-Arab","sd","ckb","ku","he","yi"]
  };

  // ══════════════════════════════════════════════
  //  TWP核心:GoogleHelper_v2 - API密钥认证管理
  //  完整从TWP translationService.js移植
  // ══════════════════════════════════════════════

  const GoogleHelper_v2 = {
    _lastRequestAuthTime: null,
    _translateAuth: null,
    _authNotFound: false,
    _authPromise: null,

    get translateAuth() {
      return this._translateAuth;
    },

    // TWP源码中的备用密钥(字节数组解码)
    _getAlternativeKey() {
      return new TextDecoder().decode(
        new Uint8Array([
          65, 73, 122, 97, 83, 121, 65, 84, 66, 88, 97, 106, 118, 122, 81,
          76, 84, 68, 72, 69, 81, 98, 99, 112, 113, 48, 73, 104, 101, 48,
          118, 87, 68, 72, 109, 79, 53, 50, 48,
        ])
      );
    },

    async findAuth() {
      if (this._authPromise) return await this._authPromise;

      this._authPromise = new Promise((resolve) => {
        let updateGoogleAuth = false;

        if (this._lastRequestAuthTime) {
          const date = new Date();
          if (this._translateAuth) {
            date.setMinutes(date.getMinutes() - 20); // 有效密钥20分钟刷新
          } else if (this._authNotFound) {
            date.setMinutes(date.getMinutes() - 5);  // 未找到5分钟重试
          } else {
            date.setMinutes(date.getMinutes() - 1);  // 其他1分钟重试
          }
          if (date.getTime() > this._lastRequestAuthTime) {
            updateGoogleAuth = true;
          }
        } else {
          updateGoogleAuth = true;
        }

        if (updateGoogleAuth) {
          this._lastRequestAuthTime = Date.now();
          const alternativeKey = this._getAlternativeKey();

          // TWP从Google的JS文件中动态提取x-goog-api-key
          GM_xmlhttpRequest({
            method: 'GET',
            url: 'https://translate.googleapis.com/_/translate_http/_/js/k=translate_http.tr.en_US.YusFYy3P_ro.O/am=AAg/d=1/exm=el_conf/ed=1/rs=AN8SPfq1Hb8iJRleQqQc8zhdzXmF9E56eQ/m=el_main',
            onload: (r) => {
              if (r.responseText && r.responseText.length > 1) {
                const result = r.responseText.match(
                  /['"]x-goog-api-key['"]\s*:\s*['"](\w{39})['"]/i
                );
                if (result && result.length === 2) {
                  this._translateAuth = result[1];
                  this._authNotFound = false;
                } else {
                  this._authNotFound = true;
                  this._translateAuth = alternativeKey;
                }
              } else {
                this._authNotFound = true;
                this._translateAuth = alternativeKey;
              }
              resolve();
            },
            onerror: () => {
              this._translateAuth = alternativeKey;
              resolve();
            },
            ontimeout: () => {
              this._translateAuth = alternativeKey;
              resolve();
            }
          });
        } else {
          resolve();
        }
      });

      const p = this._authPromise;
      p.finally(() => { this._authPromise = null; });
      return await p;
    }
  };

  // ══════════════════════════════════════════════
  //  TWP核心:GoogleHelper旧版 - TKK哈希令牌计算
  //  完整从TWP translationService.js移植
  // ══════════════════════════════════════════════

  const GoogleHelper = {
    googleTranslateTKK: "448487.932609646",

    shiftLeftOrRightThenSumOrXor(num, optString) {
      for (let i = 0; i < optString.length - 2; i += 3) {
        let acc = optString.charAt(i + 2);
        if ("a" <= acc) {
          acc = acc.charCodeAt(0) - 87;
        } else {
          acc = Number(acc);
        }
        if (optString.charAt(i + 1) === "+") {
          acc = num >>> acc;
        } else {
          acc = num << acc;
        }
        if (optString.charAt(i) === "+") {
          num = (num + acc) & 4294967295;
        } else {
          num = num ^ acc;
        }
      }
      return num;
    },

    transformQuery(query) {
      const bytesArray = [];
      let idx = 0;
      for (let i = 0; i < query.length; i++) {
        let charCode = query.charCodeAt(i);
        if (128 > charCode) {
          bytesArray[idx++] = charCode;
        } else {
          if (2048 > charCode) {
            bytesArray[idx++] = (charCode >> 6) | 192;
          } else {
            if (
              55296 === (charCode & 64512) &&
              i + 1 < query.length &&
              56320 === (query.charCodeAt(i + 1) & 64512)
            ) {
              charCode =
                65536 +
                ((charCode & 1023) << 10) +
                (query.charCodeAt(++i) & 1023);
              bytesArray[idx++] = (charCode >> 18) | 240;
              bytesArray[idx++] = ((charCode >> 12) & 63) | 128;
            } else {
              bytesArray[idx++] = (charCode >> 12) | 224;
            }
            bytesArray[idx++] = ((charCode >> 6) & 63) | 128;
          }
          bytesArray[idx++] = (charCode & 63) | 128;
        }
      }
      return bytesArray;
    },

    calcHash(query) {
      const windowTkk = this.googleTranslateTKK;
      const tkkSplited = windowTkk.split(".");
      const tkkIndex = Number(tkkSplited[0]) || 0;
      const tkkKey = Number(tkkSplited[1]) || 0;

      const bytesArray = this.transformQuery(query);

      let encondingRound = tkkIndex;
      for (const item of bytesArray) {
        encondingRound += item;
        encondingRound = this.shiftLeftOrRightThenSumOrXor(
          encondingRound, "+-a^+6"
        );
      }
      encondingRound = this.shiftLeftOrRightThenSumOrXor(
        encondingRound, "+-3^+b+-f"
      );

      encondingRound ^= tkkKey;
      if (encondingRound <= 0) {
        encondingRound = (encondingRound & 2147483647) + 2147483648;
      }

      const normalizedResult = encondingRound % 1000000;
      return normalizedResult.toString() + "." + (normalizedResult ^ tkkIndex);
    }
  };

  // ══════════════════════════════════════════════
  //  TWP核心:HTML转义/反转义工具
  //  从TWP translationService.js移植
  // ══════════════════════════════════════════════

  const Utils = {
    escapeHTML(text) {
      const div = document.createElement('div');
      div.appendChild(document.createTextNode(text));
      return div.innerHTML;
    },
    unescapeHTML(text) {
      const doc = new DOMParser().parseFromString(text, 'text/html');
      return doc.documentElement.textContent;
    }
  };

  // ── 网络层 ──
  function gmFetch(opts) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        timeout: 30000,
        ...opts,
        onload: resolve,
        onerror: reject,
        ontimeout: reject,
      });
    });
  }

  // ══════════════════════════════════════════════
  //  引擎定义 - 完整移植TWP的三种Google通道 + MS + Tencent
  // ══════════════════════════════════════════════

  const Engine = {

    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    //  引擎1:Google v2(TWP新版API - translateHtml端点)
    //  完整移植自TWP googleService
    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    google_v2: {
      name: 'Google (TWP v2)',

      // TWP中的语言代码替换规则
      _fixLang(lang) {
        const replacements = [
          { search: "prs", replace: "fa-AF" },
        ];
        for (const r of replacements) {
          if (lang === r.search) return r.replace;
        }
        return lang;
      },

      // TWP的cbTransformRequest:将文本数组包装为带索引的HTML
      _transformRequest(sourceArray) {
        sourceArray = sourceArray.map(text => Utils.escapeHTML(text));
        if (sourceArray.length > 1) {
          sourceArray = sourceArray.map(
            (text, index) => `<a i="${index}">${text}</a>`
          );
        }
        return `<pre>${sourceArray.join("")}</pre>`;
      },

      // TWP的cbTransformResponse:解析Google返回的HTML格式翻译结果
      _transformResponse(result, dontSortResults) {
        // 移除<pre>标签
        if (result.indexOf("<pre>") !== -1) {
          result = result.replace("<pre>", "");
          const index = result.indexOf(">");
          result = result.slice(index + 1);
        }

        const sentences = [];
        let idx = 0;
        while (true) {
          const sentenceStartIndex = result.indexOf("<b>", idx);
          if (sentenceStartIndex === -1) break;
          const sentenceFinalIndex = result.indexOf("</b>", sentenceStartIndex);
          if (sentenceFinalIndex === -1) {
            sentences.push(result.slice(sentenceStartIndex + 3));
            break;
          } else {
            sentences.push(result.slice(sentenceStartIndex + 3, sentenceFinalIndex));
          }
          idx = sentenceFinalIndex;
        }

        result = sentences.length > 0 ? sentences.join(" ") : result;
        result = result.replace(/<\/b>/g, "");

        // 提取带索引的<a>标签
        let resultArray = [];
        let lastEndPos = 0;
        for (const r of result.matchAll(/(<a i="[0-9]+">)([^<>]*(?=<\/a>))*/g)) {
          const fullLength = r[0].length;
          const pos = r.index;
          if (pos > lastEndPos) {
            const aTag = r[1];
            const insideText = r[2] || "";
            const outsideText = result.slice(lastEndPos, pos).replace(/<\/a>/g, "");
            resultArray.push(aTag + outsideText + insideText);
          } else {
            resultArray.push(r[0]);
          }
          lastEndPos = pos + fullLength;
        }

        let indexes;
        if (resultArray && resultArray.length > 0) {
          indexes = resultArray
            .map(value => parseInt(value.match(/[0-9]+(?=>)/g)?.[0]))
            .filter(value => !isNaN(value));
          resultArray = resultArray.map(value => {
            const resultStartAtIndex = value.indexOf(">");
            return value.slice(resultStartAtIndex + 1);
          });
        } else {
          resultArray = [result];
          indexes = [0];
        }

        resultArray = resultArray.map(value => Utils.unescapeHTML(value));

        if (dontSortResults) {
          return resultArray;
        } else {
          const finalResultArray = [];
          for (const j in indexes) {
            if (finalResultArray[indexes[j]]) {
              finalResultArray[indexes[j]] += " " + resultArray[j];
            } else {
              finalResultArray[indexes[j]] = resultArray[j];
            }
          }
          return finalResultArray;
        }
      },

      async translate(text, toLang) {
        const to = this._fixLang(toLang);
        await GoogleHelper_v2.findAuth();
        if (!GoogleHelper_v2.translateAuth) throw new Error('Google auth not available');

        const requestBody = JSON.stringify([
          [[text], "auto", to],
          "te"
        ]);

        const r = await gmFetch({
          method: 'POST',
          url: 'https://translate-pa.googleapis.com/v1/translateHtml',
          headers: {
            'Content-Type': 'application/json+protobuf',
            'X-Goog-Api-Key': GoogleHelper_v2.translateAuth,
          },
          data: requestBody,
        });

        if (r.status !== 200) throw new Error(`Google v2 API error: ${r.status}`);
        const data = JSON.parse(r.responseText);

        // 解析响应 - data[0] 是翻译结果数组
        if (data && data[0]) {
          const rawResult = Array.isArray(data[0]) ? data[0][0] : data[0];
          // 通过TWP的响应解析器处理
          const parsed = this._transformResponse(rawResult, false);
          return parsed[0] || rawResult;
        }
        throw new Error('Google v2: empty response');
      },

      // TWP风格批量翻译(将多个文本打包为一个请求)
      async translateBatch(texts, toLang) {
        const to = this._fixLang(toLang);
        await GoogleHelper_v2.findAuth();
        if (!GoogleHelper_v2.translateAuth) throw new Error('Google auth not available');

        const requestBody = JSON.stringify([
          [texts, "auto", to],
          "te"
        ]);

        const r = await gmFetch({
          method: 'POST',
          url: 'https://translate-pa.googleapis.com/v1/translateHtml',
          headers: {
            'Content-Type': 'application/json+protobuf',
            'X-Goog-Api-Key': GoogleHelper_v2.translateAuth,
          },
          data: requestBody,
        });

        if (r.status !== 200) throw new Error(`Google v2 batch error: ${r.status}`);
        const data = JSON.parse(r.responseText);

        if (data && data[0] && Array.isArray(data[0])) {
          return data[0].map(item => {
            const parsed = this._transformResponse(item, false);
            return parsed[0] || item;
          });
        }
        // fallback: 单条结果
        if (data && data[0]) {
          const parsed = this._transformResponse(
            Array.isArray(data[0]) ? data[0][0] : data[0], false
          );
          return [parsed[0]];
        }
        throw new Error('Google v2 batch: empty response');
      }
    },

    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    //  引擎2:Google旧版(gtx端点 + TKK哈希)
    //  从TWP旧版代码移植,作为v2的降级备份
    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    google_legacy: {
      name: 'Google (Legacy)',

      langCode(lang) {
        const map = {
          'zh': 'zh-CN', 'zh-CN': 'zh-CN', 'zh-TW': 'zh-TW',
          'en': 'en', 'ja': 'ja', 'ko': 'ko', 'fr': 'fr',
          'de': 'de', 'es': 'es', 'ru': 'ru', 'pt': 'pt',
        };
        return map[lang] || lang;
      },

      async translate(text, toLang) {
        const to = this.langCode(toLang);
        const tk = GoogleHelper.calcHash(text);
        const r = await gmFetch({
          method: 'GET',
          url: `https://translate.googleapis.com/translate_a/single?client=webapp&sl=auto&tl=${to}&hl=${to}&dt=t&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=at&ie=UTF-8&oe=UTF-8&otf=1&ssel=0&tsel=0&kc=7&tk=${tk}&q=${encodeURIComponent(text)}`,
        });
        if (r.status !== 200) {
          // 降级到gtx(无需TKK)
          return await this._translateGtx(text, to);
        }
        const data = JSON.parse(r.responseText);
        return data[0].filter(s => s && s[0]).map(s => s[0]).join('');
      },

      async _translateGtx(text, to) {
        const r = await gmFetch({
          method: 'GET',
          url: `https://translate.googleapis.com/translate_a/single?client=gtx&dt=t&sl=auto&tl=${to}&q=${encodeURIComponent(text)}`,
        });
        if (r.status !== 200) throw new Error('Google gtx error');
        const data = JSON.parse(r.responseText);
        return data[0].filter(s => s && s[0]).map(s => s[0]).join('');
      }
    },

    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    //  引擎3:Google自动(先v2,失败降级legacy)
    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    google: {
      name: 'Google (Auto)',
      async translate(text, toLang) {
        try {
          return await Engine.google_v2.translate(text, toLang);
        } catch (e) {
          console.warn('Google v2 failed, falling back to legacy:', e.message);
          return await Engine.google_legacy.translate(text, toLang);
        }
      },
      async translateBatch(texts, toLang) {
        try {
          return await Engine.google_v2.translateBatch(texts, toLang);
        } catch (e) {
          console.warn('Google v2 batch failed, falling back to single:', e.message);
          // 逐条降级
          const results = [];
          for (const text of texts) {
            try {
              results.push(await Engine.google_legacy.translate(text, toLang));
            } catch (_) {
              results.push(null);
            }
          }
          return results;
        }
      }
    },

    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    //  引擎4:Microsoft(保留原有实现)
    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    microsoft: {
      name: 'Microsoft',
      _token: null,
      _tokenTime: 0,

      async getToken() {
        if (this._token && Date.now() - this._tokenTime < 480000) return this._token;
        const r = await gmFetch({ method: 'GET', url: 'https://edge.microsoft.com/translate/auth' });
        if (r.status !== 200) throw new Error('MS auth error');
        this._token = r.responseText;
        this._tokenTime = Date.now();
        return this._token;
      },

      langCode(lang) {
        const map = {
          'zh': 'zh-Hans', 'zh-CN': 'zh-Hans', 'zh-TW': 'zh-Hant',
          'en': 'en', 'ja': 'ja', 'ko': 'ko', 'fr': 'fr', 'de': 'de',
          'es': 'es', 'ru': 'ru', 'pt': 'pt', 'pt-PT': 'pt-pt',
          'ar': 'ar', 'th': 'th', 'vi': 'vi', 'it': 'it', 'tr': 'tr',
          'id': 'id', 'fil': 'fil', 'ms': 'ms', 'nl': 'nl', 'pl': 'pl',
          'uk': 'uk', 'cs': 'cs', 'sk': 'sk', 'hu': 'hu', 'ro': 'ro',
          'bg': 'bg', 'hr': 'hr', 'sr': 'sr-Cyrl', 'sl': 'sl',
          'lt': 'lt', 'lv': 'lv', 'et': 'et', 'fi': 'fi', 'sv': 'sv',
          'da': 'da', 'no': 'nb', 'is': 'is', 'el': 'el', 'he': 'he',
          'hi': 'hi', 'bn': 'bn', 'ta': 'ta', 'te': 'te', 'kn': 'kn',
          'ml': 'ml', 'pa': 'pa', 'gu': 'gu', 'mr': 'mr', 'ne': 'ne',
          'ur': 'ur', 'fa': 'fa', 'ps': 'ps', 'my': 'my', 'km': 'km',
          'lo': 'lo', 'ka': 'ka', 'az': 'az', 'kk': 'kk', 'uz': 'uz',
          'mn': 'mn', 'sq': 'sq', 'mk': 'mk', 'bs': 'bs',
          'ca': 'ca', 'gl': 'gl', 'mt': 'mt', 'cy': 'cy', 'ga': 'ga',
          'af': 'af', 'sw': 'sw', 'ha': 'ha', 'ig': 'ig', 'yo': 'yo',
          'zu': 'zu', 'mg': 'mg', 'am': 'am', 'ht': 'ht',
          'bo': 'bo', 'dsb': 'dsb', 'hsb': 'hsb', 'ikt': 'ikt',
          'iu': 'iu', 'iu-Latn': 'iu-Latn', 'lzh': 'lzh',
          'fj': 'fj', 'fo': 'fo', 'fr-CA': 'fr-ca',
        };
        return map[lang] || lang;
      },

      async translate(text, toLang) {
        const token = await this.getToken();
        const to = this.langCode(toLang);
        const r = await gmFetch({
          method: 'POST',
          url: `https://api-edge.cognitive.microsofttranslator.com/translate?from=&to=${to}&api-version=3.0`,
          headers: { 'authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
          data: JSON.stringify([{ Text: text }]),
        });
        if (r.status !== 200) throw new Error('MS translate error');
        return JSON.parse(r.responseText)[0].translations[0].text;
      },

      async translateBatch(texts, toLang) {
        const token = await this.getToken();
        const to = this.langCode(toLang);
        const results = [];

        for (let batch = 0; batch < texts.length; batch += 25) {
          const chunk = texts.slice(batch, batch + 25);
          const r = await gmFetch({
            method: 'POST',
            url: `https://api-edge.cognitive.microsofttranslator.com/translate?from=&to=${to}&api-version=3.0`,
            headers: { 'authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
            data: JSON.stringify(chunk.map(t => ({ Text: t }))),
          });
          if (r.status === 200) {
            const data = JSON.parse(r.responseText);
            for (const item of data) {
              results.push(item.translations[0].text);
            }
          } else {
            for (let i = 0; i < chunk.length; i++) results.push(null);
          }
        }
        return results;
      }
    },

    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    //  引擎5:Tencent(保留原有实现)
    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    tencent: {
      name: 'Tencent',
      _clientKey: null,

      getClientKey() {
        if (this._clientKey) return this._clientKey;
        this._clientKey = `browser-chrome-120.0-Windows_10-${crypto.randomUUID()}-${Date.now()}`;
        return this._clientKey;
      },

      langCode(lang) {
        const map = {
          'zh': 'zh', 'zh-CN': 'zh', 'zh-TW': 'zh-TW',
          'en': 'en', 'ja': 'ja', 'ko': 'ko', 'fr': 'fr',
          'de': 'de', 'es': 'es', 'ru': 'ru', 'pt': 'pt',
          'ar': 'ar', 'th': 'th', 'vi': 'vi', 'it': 'it',
          'tr': 'tr', 'id': 'id',
        };
        return map[lang] || lang;
      },

      async translate(text, toLang) {
        const to = this.langCode(toLang);
        const r = await gmFetch({
          method: 'POST',
          url: 'https://transmart.qq.com/api/imt',
          headers: { 'Content-Type': 'application/json' },
          data: JSON.stringify({
            header: { fn: 'auto_translation', session: '', client_key: this.getClientKey(), user: '' },
            type: 'plain', model_category: 'normal', text_domain: 'general',
            source: { lang: 'auto', text_list: [text] },
            target: { lang: to }
          }),
        });
        if (r.status !== 200) throw new Error('Tencent translate error');
        return JSON.parse(r.responseText).auto_translation[0];
      },

      async translateBatch(texts, toLang) {
        const to = this.langCode(toLang);
        const r = await gmFetch({
          method: 'POST',
          url: 'https://transmart.qq.com/api/imt',
          headers: { 'Content-Type': 'application/json' },
          data: JSON.stringify({
            header: { fn: 'auto_translation', session: '', client_key: this.getClientKey(), user: '' },
            type: 'plain', model_category: 'normal', text_domain: 'general',
            source: { lang: 'auto', text_list: texts },
            target: { lang: to }
          }),
        });
        if (r.status !== 200) throw new Error('Tencent batch error');
        return JSON.parse(r.responseText).auto_translation;
      }
    }
  };

  // ── 持久化状态 ──
  let currentEngine = await GM_getValue('engine', 'google');
  let targetLang = await GM_getValue('targetLang', deviceLang === 'zh' ? 'zh-CN' : deviceLang);
  let autoMode = await GM_getValue('autoMode', true);
  let excludedHosts = JSON.parse(await GM_getValue('excludedHosts', '[]'));

  if (excludedHosts.includes(location.host)) return;

  // ── 缓存层 ──
  const cache = new Map();
  const MAX_CACHE = 3000;

  function cacheGet(text) { return cache.get(text); }
  function cacheSet(text, translated) {
    if (cache.size >= MAX_CACHE) {
      const firstKey = cache.keys().next().value;
      cache.delete(firstKey);
    }
    cache.set(text, translated);
  }

  // ── 翻译核心(带引擎自动降级)──
  async function translate(text) {
    if (!text || !text.trim()) return null;
    const trimmed = text.trim();
    if (/^\d+$/.test(trimmed)) return null;

    const cached = cacheGet(trimmed);
    if (cached) return cached;

    try {
      const engine = Engine[currentEngine];
      const result = await engine.translate(trimmed, targetLang);
      if (result && result !== trimmed) {
        cacheSet(trimmed, result);
        return result;
      }
    } catch (e) {
      console.warn(`[${currentEngine}] translate error:`, e.message);
      // 自动降级:Google失败用Microsoft,Microsoft失败用Google legacy
      if (currentEngine === 'google') {
        try {
          const result = await Engine.microsoft.translate(trimmed, targetLang);
          if (result && result !== trimmed) { cacheSet(trimmed, result); return result; }
        } catch (_) {}
      } else if (currentEngine === 'microsoft') {
        try {
          const result = await Engine.google_legacy.translate(trimmed, targetLang);
          if (result && result !== trimmed) { cacheSet(trimmed, result); return result; }
        } catch (_) {}
      }
    }
    return null;
  }

  // ── 批量翻译(利用引擎原生批量能力)──
  async function batchTranslate(texts) {
    const results = new Array(texts.length).fill(null);
    const uncached = [];
    const uncachedIdx = [];

    for (let i = 0; i < texts.length; i++) {
      const t = texts[i].trim();
      if (!t || /^\d+$/.test(t)) continue;
      const c = cacheGet(t);
      if (c) { results[i] = c; continue; }
      uncached.push(t);
      uncachedIdx.push(i);
    }

    if (uncached.length === 0) return results;

    const engine = Engine[currentEngine];

    // 如果引擎支持批量翻译,优先使用
    if (engine.translateBatch && uncached.length > 1) {
      try {
        // 分批处理(每批最多50条,避免请求过大)
        const BATCH_SIZE = currentEngine === 'microsoft' ? 25 : 50;
        for (let batch = 0; batch < uncached.length; batch += BATCH_SIZE) {
          const chunk = uncached.slice(batch, batch + BATCH_SIZE);
          const chunkIdx = uncachedIdx.slice(batch, batch + BATCH_SIZE);

          const batchResults = await engine.translateBatch(chunk, targetLang);
          if (batchResults) {
            for (let j = 0; j < batchResults.length; j++) {
              const translated = batchResults[j];
              if (translated && translated !== chunk[j]) {
                cacheSet(chunk[j], translated);
                results[chunkIdx[j]] = translated;
              }
            }
          }
        }
        return results;
      } catch (e) {
        console.warn('Batch translate failed, falling back to single:', e.message);
      }
    }

    // 逐条翻译(fallback)
    const concurrency = 5;
    for (let i = 0; i < uncached.length; i += concurrency) {
      const batch = uncached.slice(i, i + concurrency);
      const batchIdx = uncachedIdx.slice(i, i + concurrency);
      await Promise.allSettled(
        batch.map(async (text, j) => {
          const result = await translate(text);
          if (result) results[batchIdx[j]] = result;
        })
      );
    }

    return results;
  }

  // ── DOM遍历与替换 ──
  const SKIP_TAGS = /^(script|style|code|pre|svg|math|noscript|iframe|canvas|video|audio|img|br|hr|input|select|option|textarea)$/i;
  const SKIP_CLASS = /translate-ui|notranslate|katex|mathjax/i;

  function shouldSkip(node) {
    if (!node) return true;
    if (node.nodeType === Node.ELEMENT_NODE) {
      if (SKIP_TAGS.test(node.tagName)) return true;
      if (SKIP_CLASS.test(node.className)) return true;
      if (node.isContentEditable) return true;
      if (node.dataset && node.dataset.translated) return true;
    }
    return false;
  }

  function isTargetLang(text) {
    if (!text || !text.trim()) return true;
    const lang = targetLang.split('-')[0];
    if (lang === 'zh') return /^[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\s\d\p{P}]+$/u.test(text.trim());
    if (lang === 'en') return /^[a-zA-Z\s\d\p{P}]+$/u.test(text.trim());
    if (lang === 'ja') return /^[\u3040-\u309f\u30a0-\u30ff\u4e00-\u9fff\s\d\p{P}]+$/u.test(text.trim());
    if (lang === 'ko') return /^[\uac00-\ud7af\u1100-\u11ff\s\d\p{P}]+$/u.test(text.trim());
    if (lang === 'ar') return /^[\u0600-\u06ff\u0750-\u077f\s\d\p{P}]+$/u.test(text.trim());
    if (lang === 'th') return /^[\u0e00-\u0e7f\s\d\p{P}]+$/u.test(text.trim());
    if (lang === 'ru') return /^[\u0400-\u04ff\s\d\p{P}]+$/u.test(text.trim());
    return false;
  }

  function collectTextNodes(root) {
    const nodes = [];
    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
      acceptNode(node) {
        if (shouldSkip(node.parentElement)) return NodeFilter.FILTER_REJECT;
        const text = node.textContent.trim();
        if (!text || text.length < 2) return NodeFilter.FILTER_REJECT;
        if (/^\d+$/.test(text)) return NodeFilter.FILTER_REJECT;
        if (isTargetLang(text)) return NodeFilter.FILTER_REJECT;
        if (node.parentElement?.dataset?.translated) return NodeFilter.FILTER_REJECT;
        return NodeFilter.FILTER_ACCEPT;
      }
    });
    while (walker.nextNode()) nodes.push(walker.currentNode);
    return nodes;
  }

  function collectPlaceholders(root) {
    return [...root.querySelectorAll('input[placeholder], textarea[placeholder]')]
      .filter(el => !el.dataset.translated && el.placeholder.trim() && !isTargetLang(el.placeholder));
  }

  // ── 页面翻译执行 ──
  let isTranslating = false;

  async function translatePage(root) {
    if (isTranslating) return;
    isTranslating = true;

    try {
      root = root || document.body;
      const textNodes = collectTextNodes(root);
      const placeholders = collectPlaceholders(root);

      if (textNodes.length === 0 && placeholders.length === 0) return;

      // 文本节点翻译
      if (textNodes.length > 0) {
        const texts = textNodes.map(n => n.textContent.trim());
        const results = await batchTranslate(texts);

        for (let i = 0; i < textNodes.length; i++) {
          if (!results[i]) continue;
          const node = textNodes[i];
          const parent = node.parentElement;
          if (!parent) continue;
          if (!parent.dataset.originalText) {
            parent.dataset.originalText = node.textContent;
          }
          parent.dataset.translated = '1';
          node.textContent = results[i];
        }
      }

      // placeholder翻译
      if (placeholders.length > 0) {
        const phTexts = placeholders.map(el => el.placeholder.trim());
        const phResults = await batchTranslate(phTexts);
        for (let i = 0; i < placeholders.length; i++) {
          if (!phResults[i]) continue;
          placeholders[i].dataset.originalPlaceholder = placeholders[i].placeholder;
          placeholders[i].placeholder = phResults[i];
          placeholders[i].dataset.translated = '1';
        }
      }
    } finally {
      isTranslating = false;
    }
  }

  // ── 还原原文 ──
  function restorePage() {
    document.querySelectorAll('[data-translated]').forEach(el => {
      if (el.dataset.originalText) {
        for (const child of el.childNodes) {
          if (child.nodeType === Node.TEXT_NODE) {
            child.textContent = el.dataset.originalText;
            break;
          }
        }
        delete el.dataset.originalText;
      }
      if (el.dataset.originalPlaceholder) {
        el.placeholder = el.dataset.originalPlaceholder;
        delete el.dataset.originalPlaceholder;
      }
      delete el.dataset.translated;
    });
  }

  // ── 动态内容监听 ──
  let scrollTimer = null;
  let lastHeight = document.documentElement.scrollHeight;

  function onScroll() {
    if (scrollTimer) clearTimeout(scrollTimer);
    scrollTimer = setTimeout(() => {
      const h = document.documentElement.scrollHeight;
      if (h > lastHeight) {
        lastHeight = h;
        if (autoMode) translatePage();
      }
    }, 800);
  }

  let mutationTimer = null;
  const observer = new MutationObserver((mutations) => {
    if (!autoMode) return;
    if (mutationTimer) clearTimeout(mutationTimer);
    mutationTimer = setTimeout(() => {
      const roots = new Set();
      for (const m of mutations) {
        for (const node of m.addedNodes) {
          if (node.nodeType === Node.ELEMENT_NODE && !shouldSkip(node)) {
            roots.add(node);
          }
        }
      }
      roots.forEach(root => translatePage(root));
    }, 1000);
  });

  // ══════════════════════════════════════════════
  //  UI - 支持249种语言的完整选择器
  // ══════════════════════════════════════════════

  // 构建语言选项HTML
  function buildLangOptions() {
    let html = '';
    for (const [group, codes] of Object.entries(LANG_GROUPS)) {
      html += `<optgroup label="${group}">`;
      for (const code of codes) {
        const name = ALL_LANGUAGES[code] || code;
        const selected = code === targetLang ? ' selected' : '';
        html += `<option value="${code}"${selected}>${name}</option>`;
      }
      html += '</optgroup>';
    }
    return html;
  }

  GM_addStyle(`
    .translate-ui{position:fixed;bottom:20px;right:20px;z-index:999999;font-family:system-ui,-apple-system,sans-serif}
    .translate-ui *{box-sizing:border-box;margin:0;padding:0}
    .tu-btn{width:42px;height:42px;border-radius:50%;border:none;background:rgba(0,0,0,0.5);color:#fff;cursor:pointer;
      display:flex;align-items:center;justify-content:center;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
      box-shadow:0 2px 8px rgba(0,0,0,0.15);transition:transform .2s,background .2s;touch-action:manipulation}
    .tu-btn:active{transform:scale(0.9)}
    .tu-btn.active{background:rgba(34,128,255,0.8)}
    .tu-panel{position:absolute;bottom:52px;right:0;width:240px;max-height:80vh;overflow-y:auto;
      background:rgba(255,255,255,0.97);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
      border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,0.12);padding:12px;display:none;color:#333;font-size:13px}
    .tu-panel.show{display:block}
    .tu-panel label{display:block;margin:8px 0 4px;font-size:11px;color:#888;text-transform:uppercase;letter-spacing:.5px}
    .tu-panel select{width:100%;padding:6px 8px;border:1px solid #ddd;border-radius:6px;font-size:12px;
      background:#fff;color:#333;outline:none;appearance:auto}
    .tu-panel select:focus{border-color:#4a9eff}
    .tu-panel optgroup{font-weight:bold;color:#666;font-size:11px}
    .tu-panel option{font-weight:normal;color:#333;font-size:12px}
    .tu-status{margin-top:8px;padding:6px;background:#f8f8f8;border-radius:6px;font-size:11px;color:#666;text-align:center}
    .tu-row{display:flex;gap:6px;margin-top:10px}
    .tu-row button{flex:1;padding:7px 0;border:none;border-radius:6px;font-size:12px;cursor:pointer;
      transition:background .2s;touch-action:manipulation}
    .tu-row .tu-restore{background:#f0f0f0;color:#555}
    .tu-row .tu-restore:active{background:#ddd}
    .tu-row .tu-go{background:#4a9eff;color:#fff}
    .tu-row .tu-go:active{background:#3080dd}
    .tu-row .tu-exclude{background:#ff6b6b;color:#fff;font-size:11px}
    .tu-row .tu-exclude:active{background:#e55}
    @media(prefers-color-scheme:dark){
      .tu-panel{background:rgba(30,30,30,0.97);color:#eee}
      .tu-panel select{background:#2a2a2a;color:#eee;border-color:#444}
      .tu-panel optgroup{color:#aaa}
      .tu-panel option{color:#eee}
      .tu-row .tu-restore{background:#333;color:#ccc}
      .tu-status{background:#222;color:#999}
    }
  `);

  const ui = document.createElement('div');
  ui.className = 'translate-ui';
  ui.innerHTML = `
    <div class="tu-panel" id="tuPanel">
      <label>翻译引擎 / Engine</label>
      <select id="tuEngine">
        <option value="google">Google (TWP Auto)</option>
        <option value="google_v2">Google (TWP v2 API)</option>
        <option value="google_legacy">Google (Legacy gtx)</option>
        <option value="microsoft">Microsoft</option>
        <option value="tencent">Tencent</option>
      </select>
      <label>目标语言 / Target (${Object.keys(ALL_LANGUAGES).length} languages)</label>
      <select id="tuLang">${buildLangOptions()}</select>
      <div class="tu-status" id="tuStatus">Ready</div>
      <div class="tu-row">
        <button class="tu-restore" id="tuRestore">还原</button>
        <button class="tu-go" id="tuGo">翻译</button>
      </div>
      <div class="tu-row">
        <button class="tu-exclude" id="tuExclude">排除此站</button>
      </div>
    </div>
    <button class="tu-btn${autoMode ? ' active' : ''}" id="tuBtn">
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
        <path d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"/>
      </svg>
    </button>`;
  document.body.appendChild(ui);

  const btn = document.getElementById('tuBtn');
  const panel = document.getElementById('tuPanel');
  const engineSelect = document.getElementById('tuEngine');
  const langSelect = document.getElementById('tuLang');
  const statusEl = document.getElementById('tuStatus');

  engineSelect.value = currentEngine;
  langSelect.value = targetLang;

  function setStatus(msg) {
    if (statusEl) statusEl.textContent = msg;
  }

  btn.addEventListener('click', (e) => {
    e.stopPropagation();
    panel.classList.toggle('show');
  });

  document.addEventListener('click', (e) => {
    if (!ui.contains(e.target)) panel.classList.remove('show');
  });

  engineSelect.addEventListener('change', () => {
    currentEngine = engineSelect.value;
    GM_setValue('engine', currentEngine);
    cache.clear();
    setStatus(`Engine: ${Engine[currentEngine]?.name || currentEngine}`);
  });

  langSelect.addEventListener('change', () => {
    targetLang = langSelect.value;
    GM_setValue('targetLang', targetLang);
    cache.clear();
    setStatus(`Target: ${ALL_LANGUAGES[targetLang] || targetLang}`);
  });

  document.getElementById('tuGo').addEventListener('click', async () => {
    panel.classList.remove('show');
    btn.classList.add('active');
    autoMode = true;
    GM_setValue('autoMode', true);
    restorePage();
    cache.clear();
    lastHeight = document.documentElement.scrollHeight;
    setStatus('翻译中...');
    const start = Date.now();
    await translatePage();
    setStatus(`完成 (${((Date.now() - start) / 1000).toFixed(1)}s)`);
  });

  document.getElementById('tuRestore').addEventListener('click', () => {
    panel.classList.remove('show');
    btn.classList.remove('active');
    autoMode = false;
    GM_setValue('autoMode', false);
    restorePage();
    setStatus('已还原');
  });

  document.getElementById('tuExclude').addEventListener('click', () => {
    if (!excludedHosts.includes(location.host)) {
      excludedHosts.push(location.host);
      GM_setValue('excludedHosts', JSON.stringify(excludedHosts));
    }
    restorePage();
    ui.remove();
    observer.disconnect();
    window.removeEventListener('scroll', onScroll);
  });

  // ── 菜单命令 ──
  GM_registerMenuCommand('翻译当前页面', () => translatePage());
  GM_registerMenuCommand('还原当前页面', () => restorePage());
  GM_registerMenuCommand('切换Google引擎', () => {
    currentEngine = 'google';
    engineSelect.value = 'google';
    GM_setValue('engine', 'google');
    cache.clear();
  });
  GM_registerMenuCommand('切换Microsoft引擎', () => {
    currentEngine = 'microsoft';
    engineSelect.value = 'microsoft';
    GM_setValue('engine', 'microsoft');
    cache.clear();
  });

  // ── 启动 ──
  function isPageInTargetLang() {
    const lang = (document.documentElement.lang || '').split('-')[0].toLowerCase();
    const target = targetLang.split('-')[0].toLowerCase();
    return lang === target;
  }

  window.addEventListener('scroll', onScroll, { passive: true });
  observer.observe(document.body, { childList: true, subtree: true });

  // 预初始化Google Auth(后台静默执行)
  GoogleHelper_v2.findAuth().catch(() => {});

  if (autoMode && !isPageInTargetLang()) {
    setTimeout(async () => {
      setStatus('自动翻译中...');
      const start = Date.now();
      await translatePage();
      setStatus(`完成 (${((Date.now() - start) / 1000).toFixed(1)}s)`);
    }, 1500);
  }

})();