google multilang search view en/zh

[snolab] Mulango - Walkers for bilingual learners. View a google search result in two languages side by side for comparison and language learning. now supports Bing & Google,

目前為 2022-09-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name               google multilang search view en/zh
// @name:zh            谷歌多语言搜索 en/zh
// @namespace          [email protected]
// @author             [email protected]
// @version            1.0.0
// @description        [snolab] Mulango - Walkers for bilingual learners. View a google search result in two languages side by side for comparison and language learning. now supports Bing & Google,
// @description:zh     [snolab] Mulango - 双语学习者的学步车,以并列多语言视角浏览谷歌搜索结果 现支持 Bing & Google,
// @match              https://*.google.com/search?*
// @match              https://*.bing.com/search?*
// @match              https://*/search*
// @grant              none
// @run-at             document-start
// @license            GPL-3.0+
// @supportURL         https://github.com/snomiao/userscript.js/issues
// @contributionURL    https://snomiao.com/donate
// @grant   GM_getValue
// @grant   GM_setValue
// ==/UserScript==

// const introURL =
//     'https://rapidapi.com/microsoft-azure-org-microsoft-cognitive-services/api/microsoft-translator-text/';
// // [Microsoft Translator Text API Documentation (microsoft-azure-org-microsoft-cognitive-services) | RapidAPI]( ${introURL} )
// const introPrompt = `
// 1. I will open ${introURL} for you.
//     1. Sign up or login
//     2. Go to Endpoints tab
//     3. Click Subscribe to Test
// 2. Copy your API key
//     5. Close the page
//     3. Come back
// 3. paste API Key in prompt
// 4. OK
// `.trim();

(async function () {
    if (!location.hostname.match(/google|bing/)) return;
    if (parent !== window) return iframeSetup();
    iframeHeightReceiverSetup();
    //
    // const rapidAPIKey = (globalThis.rapidAPIKey = await rapidAPIKeyLoad());
    // if (!rapidAPIKey) throw new Error('no rapid api key');
    const searchLinks = await mulangoSearchLinksFetch();
    searchLinks.length && mulangoPageReplace(searchLinks);
})();

function mulangoPageReplace(searchLinks) {
    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 iframeSetup() {
    iframeScrollbarRemove();
    const sendHeight = () =>
        parent.postMessage?.({ height: document.body.scrollHeight }, "*");
    window.addEventListener("resize", sendHeight, false);
    window.addEventListener("load", sendHeight, false);
    sendHeight();
    window.addEventListener("load", iframeLinksSetup, false);
}

function iframeLinksSetup() {
    return [...document.querySelectorAll("a[href]")]
        .filter(({ href }) => new URL(href).origin === location.origin)
        .map((e) => (e.target = "_parent"));
}

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

async function mulangoSearchLinksFetch() {
    const url = new URL(location.href);
    const query = url.searchParams.get("q") || "";
    if (!query) return [];
    const result = await bilangTranslate(query);
    const searchLinks = result.flatMap((e) =>
        e.translations.map((t) => {
            const u2 = new URL(url.href);
            return u2.searchParams.set("q", t.text), u2.href;
        })
    );
    return searchLinks;
}
async function bilangTranslate(s) {
    const translate = (
        await import(
            "https://cdn.skypack.dev/@snomiao/google-translate-api-browser"
        )
    ).setCORS("https://google-translate-cors.vercel.app/api?url=", {
        encode: true,
    });
    // const { translate } = await import("https://cdn.skypack.dev/snotran");
    await translate('give me a try', { to: "zh" })
            .then((e) => e.text)
            .catch(console.error)
    return [
        await translate(s, { to: "zh" })
            .then((e) => e.text)
            .catch(console.error),
        await translate(s, { to: "en" })
            .then((e) => e.text)
            .catch(console.error),
    ].filter((e) => e);
}

// 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',
//                 },
//             ],
//         },
//     ];
//     const rak = await rakGet();
//     if (!rak) throw new Error('no rapid api key is found');
//     const response = 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': rak,
//                 'X-RapidAPI-Host': 'microsoft-translator-text.p.rapidapi.com',
//             },
//             body,
//         }
//     )
//         .then((r) => r.json())
//         .catch(async (e) => await rapidAPIKeyLoadNew('error: ' + e.message));
//     return response /* as exampleResponse */;
// }

// async function rapidAPIKeyLoad() {
//     return (await rakGet()) || (await rapidAPIKeyLoadNew());
// }
// async function rapidAPIKeyLoadNew(msg = '') {
//     alert(introPrompt);
//     const w = window.open(
//         introURL,
//         'google-multilang-user-js-rapidAPIKey',
//         'width=1024,height=768'
//     );
//     await new Promise((r) => {
//         let id = setInterval(() => w.closed && (r(), clearInterval(id)));
//     });
//     return (
//         await rakSet(
//             prompt((msg + '\n' + introPrompt).trim(), await rakGet()) || await rakGet() || ''
//         ),
//         await rakGet()
//     );
// }

// async function rakGet() {
//     return (
//         (await globalThis.GM_getValue?.('rapidAPIKey', '')) ||
//         localStorage.getItem('rapidAPIKey')
//     )?.replace(/^null$/, '');
// }
// async function rakSet(v = '') {
//     return (
//         (await (globalThis.GM_setValue &&
//             ((await globalThis.GM_setValue('rapidAPIKey', v || '')) ||
//                 true))) ||
//         localStorage.setItem('rapidAPIKey', v || '') ||
//         true
//     );
// }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址