🌐 Telegram Translator Pro — Matrix Edition v4.5

Advanced Telegram Web translator with Matrix-style UI, instant language switching & smart cache. Developed by Fer3on_Mod

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         🌐 Telegram Translator Pro — Matrix Edition v4.5
// @namespace    Fer3on_Mod
// @version      4.5
// @description  Advanced Telegram Web translator with Matrix-style UI, instant language switching & smart cache. Developed by Fer3on_Mod
// @author       Fer3on_Mod
// @match        https://web.telegram.org/k/*
// @license MIT
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      translate.googleapis.com
// ==/UserScript==

(function () {
  "use strict";

  // 💠 Styles
  GM_addStyle(`
    #tg-translator-panel {
      position: fixed;
      right: 18px;
      bottom: 18px;
      z-index: 2147483647;
      font-family: "Segoe UI", system-ui, sans-serif;
      user-select: none;
      color: #dfffe6;
    }

    #tg-panel-toggle {
      width: 60px;
      height: 60px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 26px;
      background: radial-gradient(circle at 30% 30%, #00ff99 0%, #0066ff 100%);
      color: white;
      box-shadow: 0 0 25px rgba(0, 255, 136, 0.7);
      cursor: pointer;
      transition: all 0.2s ease;
    }
    #tg-panel-toggle:hover { transform: scale(1.1); filter: brightness(1.2); }

    #tg-panel {
      position: fixed;
      right: 18px;
      bottom: 18px;
      width: 280px;
      background: linear-gradient(180deg, #060a0d, #0a1418);
      border-radius: 14px;
      color: #dfffe6;
      box-shadow: 0 0 25px rgba(0, 255, 136, 0.2);
      padding: 16px;
      display: none;
      backdrop-filter: blur(8px);
      border: 1px solid rgba(0, 255, 136, 0.08);
      animation: slideIn 0.25s ease forwards;
    }

    @keyframes slideIn {
      from { opacity: 0; transform: translateY(10px); }
      to { opacity: 1; transform: translateY(0); }
    }

    #tg-close-btn {
      position: absolute;
      top: -10px;
      left: -10px;
      width: 28px;
      height: 28px;
      border-radius: 50%;
      background: radial-gradient(circle at 30% 30%, #00ff99, #0066ff);
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 14px;
      color: #000;
      cursor: pointer;
      box-shadow: 0 0 15px rgba(0,255,136,0.6);
      transition: transform 0.15s ease;
    }
    #tg-close-btn:hover { transform: scale(1.1); }

    #tg-panel h3 {
      margin-top: 0;
      text-align: center;
      color: #00ff88;
      font-weight: 600;
      letter-spacing: 0.5px;
      margin-bottom: 10px;
    }

    .tg-row { display: flex; flex-direction: column; gap: 5px; margin: 10px 0; }

    .tg-select, .tg-input {
      width: 100%;
      padding: 8px;
      border-radius: 8px;
      background: rgba(0,255,136,0.05);
      border: 1px solid rgba(0,255,136,0.15);
      color: #eaffea;
      font-size: 13px;
      outline: none;
    }
    .tg-select option { background: #071018; color: #eaffea; }

    .tg-btn {
      width: 100%;
      padding: 8px;
      margin-top: 8px;
      border-radius: 10px;
      background: linear-gradient(90deg,#0a1f1f,#002244);
      border: 1px solid rgba(0,255,136,0.2);
      cursor: pointer;
      color: #dfffe6;
      font-size: 13px;
      transition: all 0.15s ease;
    }
    .tg-btn:hover { filter: brightness(1.3); }

    .tg-btn.positive { background: linear-gradient(90deg,#00c853,#0091ea); box-shadow: 0 0 10px rgba(0,255,136,0.3); }

    /* Switch style */
    .switch {
      position: relative;
      display: inline-block;
      width: 50px;
      height: 26px;
    }
    .switch input { opacity: 0; width: 0; height: 0; }
    .slider {
      position: absolute;
      cursor: pointer;
      top: 0; left: 0; right: 0; bottom: 0;
      background-color: #555;
      transition: .3s;
      border-radius: 26px;
    }
    .slider:before {
      position: absolute;
      content: "";
      height: 20px; width: 20px;
      left: 3px; bottom: 3px;
      background-color: white;
      transition: .3s;
      border-radius: 50%;
    }
    input:checked + .slider {
      background-color: #00ff88;
      box-shadow: 0 0 10px rgba(0,255,136,0.4);
    }
    input:checked + .slider:before {
      transform: translateX(24px);
    }

    .tg-footer {
      margin-top: 12px;
      text-align: center;
      font-size: 12px;
      opacity: 0.8;
    }
    .tg-footer a { color: #00ff88; text-decoration: none; font-weight: 600; }
  `);

  // 🌐 Panel HTML
  const panelWrap = document.createElement("div");
  panelWrap.id = "tg-translator-panel";
  panelWrap.innerHTML = `
    <div id="tg-panel-toggle" title="Open Translator Menu">🌐</div>
    <div id="tg-panel" role="dialog" aria-label="Translator Pro Settings">
      <div id="tg-close-btn">✕</div>
      <h3>Translator Pro Settings</h3>

      <div class="tg-row">
        <label>Language:</label>
        <select id="tg-lang-select" class="tg-select">
          <option value="ar">Arabic</option>
          <option value="en">English</option>
          <option value="fr">French</option>
          <option value="es">Spanish</option>
          <option value="de">German</option>
          <option value="ru">Russian</option>
          <option value="zh-CN">Chinese (Simplified)</option>
          <option value="ja">Japanese</option>
          <option value="it">Italian</option>
          <option value="pt">Portuguese</option>
          <option value="tr">Turkish</option>
          <option value="ko">Korean</option>
          <option value="hi">Hindi</option>
          <option value="nl">Dutch</option>
          <option value="sv">Swedish</option>
          <option value="pl">Polish</option>
          <option value="uk">Ukrainian</option>
          <option value="id">Indonesian</option>
          <option value="th">Thai</option>
          <option value="fa">Persian</option>
          <option value="he">Hebrew</option>
          <option value="vi">Vietnamese</option>
          <option value="ro">Romanian</option>
          <option value="cs">Czech</option>
          <option value="fi">Finnish</option>
          <option value="el">Greek</option>
        </select>
      </div>

      <div class="tg-row">
        <label><input type="checkbox" id="tg-auto-detect"> Auto-detect Source Language</label>
        <label>Enable Translation:
          <label class="switch">
            <input type="checkbox" id="tg-enabled">
            <span class="slider"></span>
          </label>
        </label>
      </div>

      <button id="tg-clear-btn" class="tg-btn">🧹 Clear Cache</button>

      <div class="tg-footer">
        <a href="https://t.me/Fer3on_Mod" target="_blank">Developer: Fer3on_Mod</a>
        <div>v4.4 — Matrix Edition</div>
      </div>
    </div>
  `;
  document.body.appendChild(panelWrap);

  // 🎛️ DOM refs
  const toggleBtn = document.getElementById("tg-panel-toggle");
  const panel = document.getElementById("tg-panel");
  const closeBtn = document.getElementById("tg-close-btn");
  const langSelect = document.getElementById("tg-lang-select");
  const autoDetectInput = document.getElementById("tg-auto-detect");
  const enableInput = document.getElementById("tg-enabled");
  const clearBtn = document.getElementById("tg-clear-btn");

  // ⚙️ Load Settings
  let state = {
    targetLang: localStorage.getItem("tg_target_lang") || "ar",
    autoDetect: localStorage.getItem("tg_auto_detect") === "true",
    enabled: localStorage.getItem("tg_enabled") !== "false"
  };

  langSelect.value = state.targetLang;
  autoDetectInput.checked = state.autoDetect;
  enableInput.checked = state.enabled;

  // 🧩 Panel toggle behavior
  toggleBtn.onclick = () => {
    panel.style.display = "block";
    toggleBtn.style.display = "none";
  };
  closeBtn.onclick = () => {
    panel.style.display = "none";
    toggleBtn.style.display = "flex";
  };

  // ⚡ Instant apply language change
  langSelect.onchange = () => {
    state.targetLang = langSelect.value;
    localStorage.setItem("tg_target_lang", state.targetLang);
  };

  // ⚡ Instant toggle
  enableInput.onchange = () => {
    state.enabled = enableInput.checked;
    localStorage.setItem("tg_enabled", state.enabled);
    if (!state.enabled) removeAllTranslations();
  };

  autoDetectInput.onchange = () => {
    state.autoDetect = autoDetectInput.checked;
    localStorage.setItem("tg_auto_detect", state.autoDetect);
  };

  clearBtn.onclick = () => {
    translateCache.clear();
    alert("🧹 Cache cleared");
  };

  // 💾 Cache
  const translateCache = new Map();

  // 🧠 Translation Engine
  function translateText(text) {
    return new Promise((resolve) => {
      if (!state.enabled) return resolve(null);
      if (!text.trim() || text.length < 3) return resolve(null);
      if (/^[0-9:\s]+$/.test(text)) return resolve(null);
      if (translateCache.has(text)) return resolve(translateCache.get(text));

      const sl = state.autoDetect ? "auto" : "en";
      const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sl}&tl=${state.targetLang}&dt=t&q=${encodeURIComponent(text)}`;

      GM_xmlhttpRequest({
        method: "GET",
        url,
        onload: (res) => {
          try {
            const result = JSON.parse(res.responseText);
            const translated = result[0].map(t => t[0]).join("");
            translateCache.set(text, translated);
            resolve(translated);
          } catch {
            resolve(null);
          }
        },
        onerror: () => resolve(null)
      });
    });
  }

  // 🧩 Render translation
  function renderTranslation(el, translated) {
    if (!el || !translated) return;
    if (el.querySelector(".tg-translated-text")) return;
    const div = document.createElement("div");
    div.className = "tg-translated-text";
    div.textContent = translated;
    div.style.marginTop = "6px";
    div.style.color = "#00ff88";
    div.style.fontSize = "0.88em";
    el.appendChild(div);
  }

  // ❌ Remove all translations
  function removeAllTranslations() {
    document.querySelectorAll(".tg-translated-text").forEach(el => el.remove());
  }

  // 🔁 Observe messages
  const observer = new MutationObserver((mutations) => {
    if (!state.enabled) return;
    mutations.forEach((m) => {
      m.addedNodes.forEach(async (node) => {
        if (node.nodeType === 1 && node.querySelector) {
          const msg = node.querySelector(".text-content, .message, .message_text, .text-content-inner");
          if (msg && !msg.querySelector(".tg-translated-text")) {
            const text = msg.innerText.trim();
            const translated = await translateText(text);
            if (translated && translated !== text) renderTranslation(msg, translated);
          }
        }
      });
    });
  });

  const container = document.querySelector("#column-center, .messages-container, body");
  if (container) observer.observe(container, { childList: true, subtree: true });
})();