您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
[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,
当前为
// ==UserScript== // @name google multilang search view en/zh // @name:zh 谷歌多语言搜索 en/zh // @namespace [email protected] // @author [email protected] // @version 0.0.6 // @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. 2. Sign up or login 3. Click Subscribe to Test 4. Copy your API key 4. Come back and paste in prompt 5. 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 getMultiLangSearchLinks(); searchLinks.length && replacePageWIthMultiLang(searchLinks); })(); function replacePageWIthMultiLang(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 getMultiLangSearchLinks() { const url = new URL(location.href); const query = url.searchParams.get('q') || ''; if (!query) return []; const result = await bingTranslate(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 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 ak = await rakGet(); if (!ak) 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': ak, 'X-RapidAPI-Host': 'microsoft-translator-text.p.rapidapi.com', }, body, } ) .then((r) => r.json()) .catch(async (e) => await rapidAPIKeyLoadNew('error: ' + e.message)); return response; } 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() ); } 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 ); }