GoogleTranslateEngine

googleTranslateEngine

Tento skript by neměl být instalován přímo. Jedná se o knihovnu, kterou by měly jiné skripty využívat pomocí meta příkazu // @require https://update.greatest.deepsurf.us/scripts/465512/1185486/GoogleTranslateEngine.js

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

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(args1, args2, args3) {
        // let rule = {
        //     name: 'WhatsApp',
        //     selector: this.baseSelector('nav', 0, '*').bind(this),
        //     textGetter: this.baseTextGetter.bind(this),
        //     textSetter: this.baseTextSetter.bind(this)
        // };
        let rule = null;
        if (args1 != null && args2 != null && args3 != null) {
            rule = {};
            rule.name = '';
            rule.selector = this.baseSelector(args1, args2, args3).bind(this);
            rule.textGetter = this.baseTextGetter.bind(this);
            rule.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) {
        let text = await this.sessionStorage.getItem('谷歌翻译' + '-' + raw);
        if (text && text != '') {
            return new Promise(async (resolve) => {
                resolve(text);
            });
        }
        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);
        });
    }
}