Greasy Fork

Greasy Fork is available in English.

Show account names (all locales) + Force English + Auto-press Translate + De-locale redirect

Forces English on esologs.com, redirects locale subdomains (e.g., ru.esologs.com) to esologs.com, auto-presses "Translate" button, and replaces character names with account names

当前为 2025-10-15 提交的版本,查看 最新版本

// ==UserScript==
// @name        Show account names (all locales) + Force English + Auto-press Translate + De-locale redirect
// @namespace   io.inp
// @match       https://*.esologs.com/*
// @grant       none
// @version     1.6
// @author      Xandaros (tweaked by Kwiebe-Kwibus)
// @license     BSD-2-Clause
// @run-at      document-start
// @description Forces English on esologs.com, redirects locale subdomains (e.g., ru.esologs.com) to esologs.com, auto-presses "Translate" button, and replaces character names with account names
// ==/UserScript==

(function () {
  "use strict";

  // --- 0) If on a locale subdomain (e.g., ru.esologs.com), redirect to the root domain (esologs.com) ---
  (function redirectLocaleSubdomain() {
    try {
      const host = location.hostname; // e.g., ru.esologs.com
      const m = host.match(/^([a-z]{2})\.esologs\.com$/i);
      // Redirect any 2-letter locale (ru, de, fr, etc.) EXCEPT "en" (just in case)
      if (m && m[1].toLowerCase() !== "en") {
        const targetHost = "esologs.com"; // user explicitly asked for root, not www
        const newUrl = location.protocol + "//" + targetHost + location.pathname + location.search + location.hash;
        // Use replace() to avoid back button ping-pong
        location.replace(newUrl);
        return; // stop running rest on this page; the new page will run the script again
      }
    } catch (_) {}
  })();

  // --- 1) Force site language to English (works on esologs.com & subdomains) ---
  (function ensureEnglish() {
    try {
      const desired = "en";
      const hasEnCookie = (name) =>
        document.cookie.split(";").some((c) => c.trim().startsWith(name + "=en"));

      const setCookie = (name, value, domain) => {
        const maxAge = 60 * 60 * 24 * 365; // 1 year
        const parts = [
          `${name}=${value}`,
          "path=/",
          `max-age=${maxAge}`,
          "samesite=lax",
          "secure",
        ];
        if (domain) parts.push(`domain=${domain}`);
        document.cookie = parts.join("; ");
      };

      const needSet =
        !hasEnCookie("NEXT_LOCALE") ||
        (typeof localStorage !== "undefined" &&
          (localStorage.getItem("NEXT_LOCALE") !== desired ||
            localStorage.getItem("locale") !== desired ||
            localStorage.getItem("language") !== desired));

      if (needSet) {
        const host = location.hostname;
        // Root cookie so it applies on all subdomains too
        setCookie("NEXT_LOCALE", desired, ".esologs.com");
        // Also set on current host in case root is blocked for any reason
        setCookie("NEXT_LOCALE", desired, undefined);

        try {
          localStorage.setItem("NEXT_LOCALE", desired);
          localStorage.setItem("locale", desired);
          localStorage.setItem("language", desired);
        } catch (_) {}
        try {
          sessionStorage.setItem("NEXT_LOCALE", desired);
        } catch (_) {}

        if (!sessionStorage.getItem("esologs_forced_en_reloaded")) {
          sessionStorage.setItem("esologs_forced_en_reloaded", "1");
          location.reload();
        }
      }
    } catch (_) {}
  })();

  // --- 2) Auto-press the "Translate"/"Show Original" button ---
  function autoPressTranslate() {
    function clickButton() {
      const btn = document.querySelector("input.translator-button");
      if (btn && btn.value && /Show Original|Translate/i.test(btn.value)) {
        btn.click();
        return true;
      }
      return false;
    }

    // Try a few times (button appears late)
    let tries = 0;
    const maxTries = 20;
    const timer = setInterval(() => {
      tries++;
      if (clickButton() || tries >= maxTries) clearInterval(timer);
    }, 500);

    // Watch for future appearances
    const mo = new MutationObserver(() => clickButton());
    const startObs = () => {
      if (document.body) {
        mo.observe(document.body, { childList: true, subtree: true });
      } else {
        requestAnimationFrame(startObs);
      }
    };
    startObs();
  }

  // --- 3) Replace character names with account display names ---
  function replaceNames(nodeOr$) {
    const $root = window.jQuery ? window.jQuery(nodeOr$) : null;
    const nodeList = $root
      ? $root.contents().toArray()
      : nodeOr$?.childNodes
      ? Array.from(nodeOr$.childNodes)
      : [];

    for (const inner of nodeList) {
      if (!inner) continue;

      if (inner.nodeType === Node.TEXT_NODE && inner.parentElement) {
        const tag = inner.parentElement.tagName;
        if (["SCRIPT", "STYLE", "TEXTAREA", "INPUT"].includes(tag)) continue;

        const txt = inner.textContent;
        if (!txt || !Array.isArray(window.players)) continue;

        let out = txt;
        for (const player of window.players) {
          if (!player) continue;
          if (player.type === "NPC" || player.anonymous) continue;
          if (!player.name || !player.displayName) continue;

          if (out.includes(player.displayName)) continue;
          out = out.replaceAll(player.name, player.displayName);
        }

        if (out !== txt) inner.textContent = out;
      }

      if (inner.childNodes && inner.childNodes.length) replaceNames(inner);
    }
  }

  function initWhenReady() {
    if (Array.isArray(window.players) && window.players.length > 0) {
      replaceNames(document.documentElement);

      const obs = new MutationObserver((mutations) => {
        for (const m of mutations) {
          if (m.type === "childList") {
            m.addedNodes.forEach((n) => replaceNames(n));
          } else if (m.type === "characterData" && m.target?.parentElement) {
            replaceNames(m.target.parentElement);
          }
        }
      });

      obs.observe(document.documentElement, {
        childList: true,
        characterData: true,
        subtree: true,
      });
      return true;
    }
    return false;
  }

  function startNameReplacement() {
    if (!initWhenReady()) {
      const maxTries = 30;
      let tries = 0;
      const timer = setInterval(() => {
        tries++;
        if (initWhenReady() || tries >= maxTries) clearInterval(timer);
      }, 500);
    }
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", () => {
      autoPressTranslate();
      startNameReplacement();
    }, { once: true });
  } else {
    autoPressTranslate();
    startNameReplacement();
  }
})();