// ==UserScript== // @name Google 搜索 AI 翻译增强 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 自动将中文搜索翻译成英文搜索,修复重复翻译/流程错误,支持通义千问 DashScope API。 // @author Gemini // @match https://www.google.com/search?* // @match https://www.google.com.hk/search?* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_xmlhttpRequest // @connect dashscope.aliyuncs.com // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // --- 默认配置 --- const DEFAULT_CONFIG = { apiKey: 'sk-9fd4a1a4fefc42048174f9168c7bdd6c', apiEndpoint: 'https://dashscope.aliyuncs.com/compatible-mode/v1', model: 'qwen-mt-flash', }; let CURRENT_CONFIG = loadConfig(); function loadConfig() { return { apiKey: GM_getValue('aiApiKey', DEFAULT_CONFIG.apiKey), apiEndpoint: GM_getValue('aiApiEndpoint', DEFAULT_CONFIG.apiEndpoint), model: GM_getValue('aiModel', DEFAULT_CONFIG.model), }; } function saveConfig(config) { GM_setValue('aiApiKey', config.apiKey); GM_setValue('aiApiEndpoint', config.apiEndpoint); GM_setValue('aiModel', config.model); CURRENT_CONFIG = config; } // --- 设置界面 --- function createSettingsUI() { GM_addStyle(` #ai-config-button-container { position: absolute; top: 0; right: 50px; z-index: 9999; height: 48px; display: flex; align-items: center; justify-content: center; margin-top: 5px; } #ai-config-button { background: #4285F4; color: white; border: none; border-radius: 6px; padding: 5px 10px; cursor: pointer; font-size: 14px; line-height: 1.2; font-weight: bold; display: flex; flex-direction: column; align-items: center; width: 75px; height: 38px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } #ai-config-button .main-text { font-size: 16px; white-space: nowrap; } #ai-config-button .sub-text { font-size: 10px; margin-top: 2px; } #translation-status { position: fixed; top: 5px; right: 150px; padding: 5px 10px; background: #fff3cd; border: 1px solid #ffeeba; color: #856404; border-radius: 4px; z-index: 10000; } #ai-config-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border: 1px solid #ccc; padding: 20px; border-radius: 8px; display: none; width: 400px; z-index: 10001; box-shadow: 0 4px 12px rgba(0,0,0,0.3); } #ai-config-modal input, #ai-config-modal select { width: 100%; padding: 8px; margin-top: 5px; border: 1px solid #ccc; border-radius: 4px; } #ai-config-modal .save { background: #34A853; color: white; } #ai-config-modal .close { background: #EA4335; color: white; margin-left: 10px; } `); const configContainer = document.createElement('div'); configContainer.id = 'ai-config-button-container'; const configButton = document.createElement('button'); configButton.id = 'ai-config-button'; configButton.innerHTML = `🤖 AI 配`; configContainer.appendChild(configButton); const topBar = document.querySelector('#gb, [role="banner"]'); (topBar || document.body).appendChild(configContainer); const modal = document.createElement('div'); modal.id = 'ai-config-modal'; modal.innerHTML = `

AI 翻译配置

`; document.body.appendChild(modal); document.getElementById("api-endpoint").value = CURRENT_CONFIG.apiEndpoint; document.getElementById("api-key").value = CURRENT_CONFIG.apiKey; document.getElementById("ai-model").value = CURRENT_CONFIG.model; configButton.onclick = () => modal.style.display = "block"; modal.querySelector('.close').onclick = () => modal.style.display = "none"; modal.querySelector('.save').onclick = () => { saveConfig({ apiEndpoint: document.getElementById("api-endpoint").value.trim(), apiKey: document.getElementById("api-key").value.trim(), model: document.getElementById("ai-model").value.trim() }); alert("配置已保存!"); modal.style.display = "none"; }; } // --- AI 翻译 --- function translateText(text) { const body = { model: CURRENT_CONFIG.model, input: { messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: `请将下面文本翻译成准确英文:\n${text}` } ] }, parameters: { result_format: "message" } }; return new Promise(resolve => { GM_xmlhttpRequest({ method: "POST", url: CURRENT_CONFIG.apiEndpoint, headers: { "Content-Type": "application/json", "Authorization": `Bearer ${CURRENT_CONFIG.apiKey}` }, data: JSON.stringify(body), onload(res) { try { const json = JSON.parse(res.responseText); if (!json.output) return resolve(null); resolve(json.output.choices[0].message.content.trim()); } catch { resolve(null); } }, onerror() { resolve(null); } }); }); } // --- 主逻辑(核心修复点) --- async function main() { createSettingsUI(); const params = new URLSearchParams(location.search); let query = params.get("q"); if (!query) return; // 关键修复:只在第一次中文时触发翻译 if (params.get("ai_translated") === "1") return; const decoded = decodeURIComponent(query); // 检测中文 if (!/[\u4e00-\u9fa5]/.test(decoded)) return; const status = document.createElement("div"); status.id = "translation-status"; status.textContent = `🤖 正在翻译 "${decoded}"...`; document.body.appendChild(status); const english = await translateText(decoded); status.remove(); if (!english) return; // 构造新 URL const newParams = new URLSearchParams(); newParams.set("q", english); newParams.set("ai_translated", "1"); for (const [k, v] of params.entries()) { if (k !== "q" && k !== "ai_translated") newParams.set(k, v); } location.replace(`${location.pathname}?${newParams.toString()}`); } main(); })();