google multilang search view en/zh

View a google search result in two languages side by side for comparison and language learning.

目前為 2022-06-26 提交的版本,檢視 最新版本

// ==UserScript==
// @name               google multilang search view en/zh
// @name:zh            谷歌多语言搜索 en/zh
// @namespace          [email protected]
// @author             [email protected]
// @version            0.0.1
// @description        View a google search result in two languages side by side for comparison and language learning.
// @description:zh     以并列多语言视角浏览谷歌搜索结果
// @match              https://*.google.com/search?*
// @grant              none
// @run-at             document-end
// @license            GPL-3.0+
// @supportURL         https://github.com/snomiao/userscript.js/issues
// @contributionURL    https://snomiao.com/donate
// @grant   GM_getValue
// @grant   GM_setValue
// ==/UserScript==
// https://rapidapi.com/microsoft-azure-org-microsoft-cognitive-services/api/microsoft-translator-text/
//
// ref:
// [javascript - Resize Cross Domain Iframe Height - Stack Overflow]( https://stackoverflow.com/questions/22086722/resize-cross-domain-iframe-height )
//
(async function () {
    if (parent !== window) return iframeHeightSenderSetup();
    iframeHeightReceiverSetup();

    const akGet = async () =>
        (await globalThis.GM_getValue?.('rapidAPIKey')) ||
        localStorage.getItem('rapidAPIKey');
    const akSet = async (v) =>
        (await (globalThis.GM_setValue &&
            ((await globalThis.GM_setValue('rapidAPIKey', v)) || true))) ||
        localStorage.setItem('rapidAPIKey', v) ||
        true;
    const introURL =
        'https://rapidapi.com/microsoft-azure-org-microsoft-cognitive-services/api/microsoft-translator-text/';
    const apiLink = `[Microsoft Translator Text API Documentation (microsoft-azure-org-microsoft-cognitive-services) | RapidAPI]( ${introURL} )`;
    const keyIntro = 'Input X-RapidAPI-Key you got from ' + apiLink;
    const rapidAPIKey =
        (await akGet()) ||
        (window.open(introURL, '_blank'),
        await akSet(prompt(keyIntro)),
        await akGet());
    globalThis.rapidAPIKey = rapidAPIKey;
    if (!rapidAPIKey) throw new Error('no rapid api key');
    document.querySelector('.main').remove();
    const searchLinks = await getSearchLinks();
    const iframes = searchLinks.map((src) => `<iframe src="${src}"></iframe>`);
    const style = `<style>body {
        margin: 0;
        display: flex;
        flex-direction: row;
    }
    iframe {
        flex: auto;
        height: 100vh;
        overflow: hidden;
        border: none;
    }</style>`;
    document.body.innerHTML = `${style}${iframes}`;
})();
function iframeHeightReceiverSetup() {
    const setHeight = (height = 0) =>
        height &&
        [...document.querySelectorAll('iframe[src]')].map(
            (e) =>
                (e.style.height =
                    Math.max(
                        Number(String(e.style.height).replace(/\D+/g, '') || 0),
                        height
                    ) + 'px')
        );
    window.addEventListener('message', (e) => setHeight(e.data?.height), false);
}
function iframeHeightSenderSetup() {
    iframeScrollbarRemove();
    const sendHeight = () =>
        parent.postMessage?.({ height: document.body.scrollHeight }, '*');
    window.addEventListener('resize', sendHeight, false);
    window.addEventListener('load', sendHeight, false);
    sendHeight();
}

function iframeScrollbarRemove() {
    document.body.style.margin = '-18px auto 0';
}

async function getSearchLinks() {
    const url = new URL(location.href);
    const query = url.searchParams.get('q') || '';
    const result = await bingTranslate(query);
    const searchLinks = result.flatMap((e) =>
        e.translations
            // .filter((t) => t.to !== e.detectedLanguage.language)
            .map((t) => {
                const u2 = new URL(url.href);
                return u2.searchParams.set('q', t.text), u2.href;
            })
    );
    return searchLinks;
}

async function bingTranslate(text = 'hello, world') {
    const body = JSON.stringify([{ Text: text }]);
    const exampleResponse = [
        {
            detectedLanguage: {
                language: 'en',
                score: 1,
            },
            translations: [
                {
                    text: '我真的很想把你的车开几个圈。',
                    to: 'zh-Hans',
                },
                {
                    text: 'I would really like to drive your car around the block a few times.',
                    to: 'en',
                },
            ],
        },
    ];
    if (!globalThis.rapidAPIKey) throw new Error('no rapid api key is found');
    return await fetch(
        'https://microsoft-translator-text.p.rapidapi.com/translate?to%5B0%5D=zh%2Cen&api-version=3.0&profanityAction=NoAction&textType=plain',
        {
            method: 'POST',
            headers: {
                'content-type': 'application/json',
                'X-RapidAPI-Key': globalThis.rapidAPIKey,
                'X-RapidAPI-Host': 'microsoft-translator-text.p.rapidapi.com',
            },
            body,
        }
    ).then((r) => r.json());
}

//     // load iframe
//     const langLnksGet = () =>
//         Object.fromEntries(
//             [...document.querySelectorAll('a.interlanguage-link-target')]
//                 .map((e) => ({
//                     lang: e.getAttribute('lang'),
//                     href: e.href,
//                     language: e.textContent,
//                 }))
//                 .map((e) => [e.lang, e])
//         );
//     const exlangFrameLoad = () => {
//         const langLnks = langLnksGet();
//         const langIframeLoad = (lang = 'en') => {
//             if (!langLnks[lang]) return false;
//             document.body.setAttribute('style', 'width: 50vw');
//             document.body.querySelector('#langIfr')?.remove();
//             document.querySelector('#sidebarCollapse')?.click();
//             const langIfr = Object.assign(document.createElement('iframe'), {
//                 id: 'langIfr',
//                 src: langLnks[lang].href + '#langIfr',
//             });
//             langIfr.setAttribute(
//                 'style',
//                 'border: none; position:absolute; left: 50vw; top: 0vh; width: 50vw'
//             );
//             document.body.appendChild(langIfr);
//             return true;
//         };
//         langIframeLoad('en') || langIframeLoad('zh') || langIframeLoad('ja');
//     };
//     window.addEventListener('load', exlangFrameLoad, false);
// }