您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
googleTranslateEngine
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greatest.deepsurf.us/scripts/465512/1185473/GoogleTranslateEngine.js
class TranslateMachine { constructor() { this.sessionStorage = { getItem: async function (key) { document.defaultView.localStorage.getItem(key); }, setItem: async function (key, value) { document.defaultView.localStorage.setItem(key, value); } }; this.transdict = { 谷歌翻译: this.translate_gg.bind(this), 谷歌翻译mobile: this.translate_ggm.bind(this) }; this.remove_url = true; this.show_info = true; this.fullscrenn_hidden = true; this.globalProcessingSave = []; } async init() { let rule = { name: 'WhatsApp', matcher: /https:\/\/web\.whatsapp\.com\//, selector: this.baseSelector('nav', 1, '*').bind(this), textGetter: this.baseTextGetter.bind(this), textSetter: this.baseTextSetter.bind(this) }; let main = async (_) => { if (!rule) return; const choice = '谷歌翻译'; const temp = [...new Set(rule.selector())]; for (let i = 0; i < temp.length; i++) { const now = temp[i]; if (this.globalProcessingSave.includes(now)) continue; this.globalProcessingSave.push(now); const text = this.remove_url ? this.url_filter(rule.textGetter(now)) : rule.textGetter(now); if (text.length == 0) continue; if (await this.sessionStorage.getItem(choice + '-' + text)) { rule.textSetter(now, await this.sessionStorage.getItem(choice + '-' + text)); this.removeItem(this.globalProcessingSave, now); } else { this.pass_lang(text) .then((lang) => this.transdict[choice](text, lang)) .then((s) => { let result = s['result']; let origin = s['origin']; rule.textSetter(now, result); this.removeItem(this.globalProcessingSave, now); }); } } }; this.PromiseRetryWrap(null).then(() => { document.js_translater = setInterval(main, 200); }); } ReviseDom(dom, text, OldText = null) { this.baseTextSetter(dom, text, OldText); } pause() { clearInterval(document.js_translater); document.js_translater = null; } resume() { if (document.js_translater !== null) { clearInterval(document.js_translater); } this.init(); } removeItem(arr, item) { const index = arr.indexOf(item); if (index > -1) arr.splice(index, 1); } baseSelector(selector, parent = 0, childSelector = null) { return () => { let items = document.querySelectorAll(selector); let filteredItems = []; if (parent !== 0) { items = Array.prototype.slice.call(items); items = items.map((item) => { let currentNode = item; for (let i = 0; i < parent; i++) { currentNode = currentNode.parentNode; } return currentNode; }); } for (let i = 0; i < items.length; i++) { if (childSelector !== null) { let childNode = items[i].querySelector(childSelector); if (childNode !== null) { filteredItems.push(childNode); continue; } } const node = items[i].querySelector('[data-translate]'); if (node === null || node.parentNode !== items[i]) { filteredItems.push(items[i]); } } return filteredItems; }; } baseTextGetter(e) { return e.innerText; } baseTextSetter(e, text, OldText = null) { if ((text || '').length == 0) text = '翻译异常'; let original = e.innerText; if (OldText !== null) { original = OldText; } e.innerText = text; let name = '谷歌翻译'; $(e).attr('data-translate', name); $(e).css('color', '#40c2af'); $(e) .parent() .before("<div data-translate='" + name + "' style='white-space:pre-wrap;'>" + original + '</div>'); let height = $(e).parent().height(); $(e) .parent() .css('height', height + 20 + 'px'); } url_filter(text) { return text.replace(/(https?|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/g, ''); } async pass_lang(raw) { try { const result = await this.check_lang(raw); if (result == 'zh') return new Promise(() => {}); return result; } catch (err) { console.log(err); return; } } async check_lang(raw) { const options = { method: 'POST', url: 'https://fanyi.baidu.com/langdetect', data: 'query=' + encodeURIComponent(raw.replace(/[\uD800-\uDBFF]$/, '').slice(0, 50)), headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; const res = await this.Request(options); try { let r = res.responseText; if (typeof r == 'string') { r = JSON.parse(r).lan; } else { r = r.lan; } return r; } catch (err) { console.log(err); return; } } guid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { let r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } async Translate(raw, sourceLang, targetLang, Record = true) { const options = { method: 'POST', url: 'https://translate.google.com/_/TranslateWebserverUi/data/batchexecute', data: `f.req=${encodeURIComponent(JSON.stringify([[['MkEWBc', JSON.stringify([[raw, sourceLang, targetLang, true], [null]]), null, 'generic']]]))}`, headers: { 'content-type': 'application/x-www-form-urlencoded', Host: 'translate.google.com' }, anonymous: true, nocache: true }; return await this.BaseTranslate( '谷歌翻译', raw, options, function (res) { var slicedRes = res.slice(res.indexOf('[')); var parsedRes = JSON.parse(slicedRes); var extractedRes = parsedRes[0][2]; if (typeof extractedRes == 'string') { extractedRes = JSON.parse(extractedRes); } let original = extractedRes[1][4][0]; var finalRes = extractedRes[1][0][0][5] .map(function (item) { return item[0]; }) .join(''); return { finalRes, original }; }, Record ); } async 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 this.BaseTranslate('谷歌翻译', raw, options, function (res) { var slicedRes = res.slice(res.indexOf('[')); var parsedRes = JSON.parse(slicedRes); var extractedRes = parsedRes[0][2]; if (typeof extractedRes == 'string') { extractedRes = JSON.parse(extractedRes); } let original = extractedRes[1][4][0]; var finalRes = extractedRes[1][0][0][5] .map(function (item) { return item[0]; }) .join(''); return { finalRes, original }; }); } async translate_ggm(raw) { const options = { method: 'GET', url: 'https://translate.google.com/m?tl=zh-CN&q=' + encodeURIComponent(raw), headers: { Host: 'translate.google.com' }, anonymous: true, nocache: true }; return await this.BaseTranslate('谷歌翻译mobile', raw, options, (res) => /class="result-container">((?:.|\n)*?)<\/div/.exec(res)[1]); } tk(a, b) { var d = b.split('.'); b = Number(d[0]) || 0; for (var e = [], f = 0, g = 0; g < a.length; g++) { var k = a.charCodeAt(g); 128 > k ? (e[f++] = k) : (2048 > k ? (e[f++] = (k >> 6) | 192) : (55296 == (k & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? ((k = 65536 + ((k & 1023) << 10) + (a.charCodeAt(++g) & 1023)), (e[f++] = (k >> 18) | 240), (e[f++] = ((k >> 12) & 63) | 128)) : (e[f++] = (k >> 12) | 224), (e[f++] = ((k >> 6) & 63) | 128)), (e[f++] = (k & 63) | 128)); } a = b; for (f = 0; f < e.length; f++) a = Fo(a + e[f], '+-a^+6'); a = Fo(a, '+-3^+b+-f'); a ^= Number(d[1]) || 0; 0 > a && (a = (a & 2147483647) + 2147483648); a %= 1e6; return a.toString() + '.' + (a ^ b); } Fo(a, b) { for (var c = 0; c < b.length - 2; c += 3) { var d = b.charAt(c + 2); d = 'a' <= d ? d.charCodeAt(0) - 87 : Number(d); d = '+' == b.charAt(c + 1) ? a >>> d : a << d; a = '+' == b.charAt(c) ? (a + d) & 4294967295 : a ^ d; } return a; } async 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 BaseTranslate(name, raw, options, processer, Record = true) { const toDo = async () => { var tmp; try { const data = await this.Request(options); tmp = data.responseText; const { finalRes, original } = await processer(tmp); let result = finalRes; if (result) { try { if (Record) { await this.sessionStorage.setItem(name + '-' + raw, result).bind(this); } } catch (e) {} } return { result, original }; } catch (err) { throw { responseText: tmp, err: err }; } }; return await this.PromiseRetryWrap(toDo, { RetryTimes: 3, ErrProcesser: () => '翻译出错' }); } Request(options) { return new Promise(async (resolve) => { options.onload = function (res) { if (res.status !== 200) { resolve(''); return; } resolve(res); }; GM_xmlhttpRequest(options); }); } }