Dual-engine translation (Google + Yandex). Anti-crash.
目前為
// ==UserScript==
// @name OptiTranslate
// @namespace com.vonkleist.optitranslate
// @version 3.0
// @description Dual-engine translation (Google + Yandex). Anti-crash.
// @author VonKleistL
// @match *://*/*
// @run-at document-end
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// --- CONFIGURATION ---
const TARGET_LANG = 'en';
// --- SAFETY CHECKS ---
// Prevent recursive loading on translation sites
if (window.location.hostname.match(/(translate|googleusercontent|yandex)/)) return;
// Language & Content Check
const pageLang = document.documentElement.lang || navigator.language;
if (pageLang && pageLang.toLowerCase().startsWith(TARGET_LANG)) return;
if (document.body.innerText.length < 50) return;
// --- THE HOST ---
const host = document.createElement('div');
host.id = 'opti-translate-host';
document.documentElement.appendChild(host);
const shadow = host.attachShadow({mode: 'open'});
// --- THE AESTHETIC ---
const style = document.createElement('style');
style.textContent = `
:host {
all: initial;
z-index: 2147483647;
position: fixed;
bottom: 30px;
right: 20px;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
pointer-events: none;
}
.opti-panel {
background: rgba(22, 22, 24, 0.92);
backdrop-filter: blur(25px) saturate(180%);
-webkit-backdrop-filter: blur(25px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
border-radius: 22px;
padding: 10px 12px;
display: flex;
align-items: center;
gap: 10px;
transform: translateY(120%);
opacity: 0;
transition: transform 0.5s cubic-bezier(0.19, 1, 0.22, 1), opacity 0.4s ease-out;
pointer-events: auto;
}
.opti-panel.visible {
transform: translateY(0);
opacity: 1;
}
/* The Engines Container */
.opti-engines {
display: flex;
gap: 6px;
}
.opti-btn {
border: none;
padding: 0 14px;
height: 34px;
border-radius: 10px;
color: #fff;
font-size: 13px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.1s, opacity 0.2s;
}
.opti-btn:active {
transform: scale(0.96);
}
/* Google Brand Color */
#btn-google {
background: rgba(66, 133, 244, 0.2);
color: #8ab4f8;
border: 1px solid rgba(66, 133, 244, 0.3);
}
#btn-google:active { background: rgba(66, 133, 244, 0.4); }
/* Yandex Brand Color (Red/Orange) */
#btn-yandex {
background: rgba(252, 63, 29, 0.2);
color: #ff8a80;
border: 1px solid rgba(252, 63, 29, 0.3);
}
#btn-yandex:active { background: rgba(252, 63, 29, 0.4); }
.opti-divider {
width: 1px;
height: 20px;
background: rgba(255,255,255,0.1);
}
.opti-close {
background: transparent;
border: none;
color: rgba(255, 255, 255, 0.3);
font-size: 20px;
width: 28px;
height: 34px;
display: flex;
align-items: center;
justify-content: center;
margin-left: 2px;
cursor: pointer;
}
.opti-close:active { color: white; }
`;
shadow.appendChild(style);
// --- THE BODY ---
const panel = document.createElement('div');
panel.className = 'opti-panel';
const detected = pageLang.toUpperCase().split('-')[0];
// Minimalist UI: [ Google ] [ Yandex ] | [ x ]
panel.innerHTML = `
<div class="opti-engines">
<button class="opti-btn" id="btn-google">Google</button>
<button class="opti-btn" id="btn-yandex">Yandex</button>
</div>
<div class="opti-divider"></div>
<button class="opti-close" id="do-close">×</button>
`;
shadow.appendChild(panel);
// --- THE NERVOUS SYSTEM ---
// 1. Google (Mobile Endpoint - Anti-Captcha)
shadow.getElementById('btn-google').addEventListener('click', (e) => {
e.stopPropagation();
const url = encodeURIComponent(window.location.href);
// Using /m? endpoint for lighter, safer translation
window.open(`https://translate.google.com/m?sl=auto&tl=${TARGET_LANG}&u=${url}`, '_blank');
closePanel();
});
// 2. Yandex (The Heavy Hitter)
shadow.getElementById('btn-yandex').addEventListener('click', (e) => {
e.stopPropagation();
const url = encodeURIComponent(window.location.href);
window.open(`https://translate.yandex.com/translate?lang=auto-${TARGET_LANG}&url=${url}`, '_blank');
closePanel();
});
// 3. Dismiss
shadow.getElementById('do-close').addEventListener('click', (e) => {
e.stopPropagation();
closePanel();
});
function closePanel() {
panel.classList.remove('visible');
setTimeout(() => host.remove(), 600);
}
// 4. Entrance
setTimeout(() => {
panel.classList.add('visible');
}, 1000);
})();