// ==UserScript== // @name Translate // @namespace http://tampermonkey.net/ // @version 1.1 // @description 页面选择自动翻译! // @author DQLean // @license MIT // @match *://*/* // @connect fanyi.baidu.com // @connect translate.google.com // @connect ifanyi.iciba.com // @connect www.bing.com // @connect fanyi.youdao.com // @connect dict.youdao.com // @connect m.youdao.com // @connect api.interpreter.caiyunai.com // @connect papago.naver.com // @connect fanyi.qq.com // @connect translate.alibaba.com // @connect www2.deepl.com // @connect transmart.qq.com // @icon https://www.rabbithome.top/favicon.ico // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js // @require https://cdn.jsdelivr.net/npm/js-base64@3.7.4/base64.min.js // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @downloadURL none // ==/UserScript== (function () { 'use strict'; const translateSourceName = GM_getValue("translateSource", "") const transdict = { '谷歌翻译': translate_gg, '谷歌翻译mobile': translate_ggm, '有道翻译mobile': translate_youdao_mobile, '必应翻译': translate_biying, '阿里翻译': translate_alibaba, '爱词霸翻译': translate_icib, '腾讯AI翻译': translate_tencentai, } for (let key in transdict) { const name = key GM_registerMenuCommand(name == translateSourceName ? "🟢" + name : "⚪" + name, () => changeTranslateSource(name)) } function changeTranslateSource(source) { GM_setValue("translateSource", source) window.location.reload() } function getTranslateFunc() { if (!transdict[translateSourceName]) { return transdict["谷歌翻译"] } return transdict[translateSourceName] } function Request(options) { return new Promise((reslove, reject) => GM_xmlhttpRequest({ ...options, onload: reslove, onerror: reject })) } async function promiseRetryWrap(task, options, ...values) { const { RetryTimes, ErrProcesser } = options || {}; let retryTimes = RetryTimes || 5; const usedErrProcesser = ErrProcesser || (err => { throw err }); if (!task) return; while (true) { try { return await task(...values); } catch (err) { if (!--retryTimes) { console.log(err); return usedErrProcesser(err); } } } } async function baseTranslate(name, raw, options, processer) { const toDo = async () => { var tmp; try { const data = await Request(options); tmp = data.responseText; const result = await processer(tmp); if (result) sessionStorage.setItem(name + '-' + raw, result); return result } catch (err) { throw { responseText: tmp, err: err } } } return await promiseRetryWrap(toDo, { RetryTimes: 3, ErrProcesser: () => "翻译出错" }) } async function translate_alibaba(raw) { const options = { method: 'POST', url: 'https://translate.alibaba.com/translationopenseviceapp/trans/TranslateTextAddAlignment.do', data: `srcLanguage=auto&tgtLanguage=zh&bizType=message&srcText=${encodeURIComponent(raw)}`, headers: { "Content-Type": "application/x-www-form-urlencoded", "origin": "https://translate.alibaba.com", "referer": "https://translate.alibaba.com/", "sec-fetch-site": "same-origin", } } return await baseTranslate('阿里翻译', raw, options, res => JSON.parse(res).listTargetText[0]) } async function translate_tencentai(raw) { const data = { "header": { "fn": "auto_translation" }, "type": "plain", "model_category": "normal", "text_domain": "general", "source": { "lang": "auto", "text_list": [raw] }, "target": { "lang": "auto" } } const options = { method: 'POST', url: 'https://transmart.qq.com/api/imt', data: JSON.stringify(data), headers: { 'Content-Type': 'application/json', 'Host': 'transmart.qq.com', 'Origin': 'https://transmart.qq.com', 'Referer': 'https://transmart.qq.com/' }, anonymous: true, nocache: true, } return await baseTranslate('腾讯AI翻译', raw, options, res => JSON.parse(res).auto_translation[0]) } async function translate_icib(raw) { const sign = CryptoJS.MD5("6key_web_fanyi" + "ifanyiweb8hc9s98e" + raw.replace(/(^\s*)|(\s*$)/g, "")).toString().substring(0, 16) const options = { method: "POST", url: `https://ifanyi.iciba.com/index.php?c=trans&m=fy&client=6&auth_user=key_web_fanyi&sign=${sign}`, data: 'from=auto&t=auto&q=' + encodeURIComponent(raw), headers: { "Content-Type": "application/x-www-form-urlencoded", }, } return await baseTranslate('爱词霸翻译', raw, options, res => JSON.parse(res).content.out) } async function translate_biying(raw) { const options = { method: "POST", url: 'https://www.bing.com/ttranslatev3', data: 'fromLang=auto-detect&to=auto&text=' + encodeURIComponent(raw), headers: { "Content-Type": "application/x-www-form-urlencoded", }, } return await baseTranslate('必应翻译', raw, options, res => JSON.parse(res)[0].translations[0].text) } async function translate_ggm(raw) { const options = { method: "GET", url: "https://translate.google.com/m?tl=auto&q=" + encodeURIComponent(raw), headers: { "Host": "translate.google.com", }, anonymous: true, nocache: true, } return await baseTranslate('谷歌翻译mobile', raw, options, res => /class="result-container">((?:.|\n)*?)<\/div/.exec(res)[1]) } async function translate_gg(raw) { const options = { method: "POST", url: "https://translate.google.com/_/TranslateWebserverUi/data/batchexecute", data: "f.req=" + encodeURIComponent(JSON.stringify([[["MkEWBc", JSON.stringify([[raw, "auto", "zh-CN", true], [null]]), null, "generic"]]])), headers: { "content-type": "application/x-www-form-urlencoded", "Host": "translate.google.com", }, anonymous: true, nocache: true, } return await baseTranslate('谷歌翻译', raw, options, res => JSON.parse(JSON.parse(res.slice(res.indexOf('[')))[0][2])[1][0][0][5].map(item => item[0]).join('')) } async function translate_youdao_mobile(raw) { const options = { method: "POST", url: 'http://m.youdao.com/translate', data: "inputtext=" + encodeURIComponent(raw) + "&type=AUTO", anonymous: true, headers: { "Content-Type": "application/x-www-form-urlencoded" } } return await baseTranslate('有道翻译mobile', raw, options, res => /id="translateResult">\s*?