TransKit

专为油猴脚本 (Userscript) 打造的高性能单文件翻译 SDK。内置 7 大开箱即用稳定翻译源,提供两级缓存 (L1/L2)、并发去重、自动批处理、故障转移 (Failover) 与半开熔断防护等核心能力。

Ezt a szkriptet nem ajánlott közvetlenül telepíteni. Ez egy könyvtár más szkriptek számára, amik tartalmazzák a // @require https://update.greasyfork.org/scripts/591255/1902101/TransKit.js hivatkozást.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         TransKit
// @namespace    You Boy
// @version      1.0.1
// @description  专为油猴脚本 (Userscript) 打造的高性能单文件翻译 SDK。内置 7 大开箱即用稳定翻译源,提供两级缓存 (L1/L2)、并发去重、自动批处理、故障转移 (Failover) 与半开熔断防护等核心能力。
// @author       You Boy
// @license      MIT
// @grant        none
// @connect      translate.google.com
// @connect      translate.googleapis.com
// @connect      edge.microsoft.com
// @connect      transmart.qq.com
// @connect      www2.deepl.com
// @connect      api.interpreter.caiyunai.com
// @connect      m.youdao.com
// ==/UserScript==

(function (global) {
  "use strict";

  /* ============================================================================
   * TransKit v1 - Userscript Translation SDK (Final Release)
   * 单文件、面向 Userscript 环境的翻译 SDK 规范实现
   * ============================================================================ */

  var VERSION = "1.0.2";

  /* ============================================================================
   * 1. 基础工具类 (Utils)
   * ============================================================================ */

  var Utils = {
    getGlobal: function (name) {
      try {
        return global[name];
      } catch (e) {
        return undefined;
      }
    },

    isFunction: function (fn) {
      return typeof fn === "function";
    },

    isObject: function (obj) {
      return obj !== null && typeof obj === "object";
    },

    isString: function (value) {
      return typeof value === "string";
    },

    isArray: function (value) {
      return Array.isArray(value);
    },

    now: function () {
      return Date.now();
    },

    randomId: function (prefix) {
      return (
        (prefix || "tk") +
        "_" +
        Date.now().toString(36) +
        "_" +
        Math.random().toString(36).slice(2, 10)
      );
    },

    guid: function () {
      return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
        var r = (Math.random() * 16) | 0;
        var v = c === "x" ? r : (r & 0x3) | 0x8;
        return v.toString(16);
      });
    },

    clamp: function (value, min, max) {
      return Math.max(min, Math.min(max, value));
    },

    encodeForm: function (obj) {
      var result = [];
      Object.keys(obj).forEach(function (key) {
        var value = obj[key];
        if (value === undefined || value === null) value = "";
        result.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(value)));
      });
      return result.join("&");
    },

    safeJSON: function (text) {
      if (typeof text !== "string") return text;
      try {
        return JSON.parse(text);
      } catch (e) {
        return null;
      }
    },

    sleep: function (ms) {
      return new Promise(function (resolve) {
        setTimeout(resolve, ms);
      });
    },

    // 使用 Math.imul 的双 32-bit (DJB2 + FNV-1a) 组合哈希,显著降低短字符串缓存 Key 碰撞概率
    fastHash: function (str) {
      var h1 = 5381;
      var h2 = 2166136261;
      var i = str.length;
      while (i) {
        var code = str.charCodeAt(--i);
        h1 = (h1 * 33) ^ code;
        h2 = Math.imul(h2 ^ code, 16777619);
      }
      return (h1 >>> 0).toString(36) + (h2 >>> 0).toString(36);
    }
  };

  /* ============================================================================
   * 2. 统一异常体系 (Error Contract)
   * ============================================================================ */

  function TransKitError(message, code, details) {
    this.name = "TransKitError";
    this.message = message || "TransKit error";
    this.code = code || "TRANSKIT_ERROR";
    this.details = details || null;
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, this.constructor);
    }
  }
  TransKitError.prototype = Object.create(Error.prototype);
  TransKitError.prototype.constructor = TransKitError;

  function ProviderError(message, code, details) {
    TransKitError.call(this, message, code || "PROVIDER_ERROR", details);
    this.name = "ProviderError";
  }
  ProviderError.prototype = Object.create(TransKitError.prototype);
  ProviderError.prototype.constructor = ProviderError;

  function TranslationError(message, code, details) {
    TransKitError.call(this, message, code || "TRANSLATION_ERROR", details);
    this.name = "TranslationError";
  }
  TranslationError.prototype = Object.create(TransKitError.prototype);
  TranslationError.prototype.constructor = TranslationError;

  function NetworkError(message, code, details) {
    TransKitError.call(this, message, code || "NETWORK_ERROR", details);
    this.name = "NetworkError";
  }
  NetworkError.prototype = Object.create(TransKitError.prototype);
  NetworkError.prototype.constructor = NetworkError;

  function TimeoutError(message, code, details) {
    TransKitError.call(this, message, code || "REQUEST_TIMEOUT", details);
    this.name = "TimeoutError";
  }
  TimeoutError.prototype = Object.create(TransKitError.prototype);
  TimeoutError.prototype.constructor = TimeoutError;

  function CacheError(message, code, details) {
    TransKitError.call(this, message, code || "CACHE_ERROR", details);
    this.name = "CacheError";
  }
  CacheError.prototype = Object.create(TransKitError.prototype);
  CacheError.prototype.constructor = CacheError;

  function ConfigurationError(message, code, details) {
    TransKitError.call(this, message, code || "CONFIGURATION_ERROR", details);
    this.name = "ConfigurationError";
  }
  ConfigurationError.prototype = Object.create(TransKitError.prototype);
  ConfigurationError.prototype.constructor = ConfigurationError;

  function CircuitOpenError(message, code, details) {
    TransKitError.call(this, message, code || "CIRCUIT_OPEN", details);
    this.name = "CircuitOpenError";
  }
  CircuitOpenError.prototype = Object.create(TransKitError.prototype);
  CircuitOpenError.prototype.constructor = CircuitOpenError;

  /* ============================================================================
   * 3. 加密与哈希服务 (Crypto Service)
   * ============================================================================ */

  function utf8Bytes(str) {
    var bytes = [];
    str = String(str);
    for (var i = 0; i < str.length; i++) {
      var c = str.charCodeAt(i);
      if (c < 0x80) {
        bytes.push(c);
      } else if (c < 0x800) {
        bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
      } else if (c >= 0xd800 && c <= 0xdbff && i + 1 < str.length) {
        var c2 = str.charCodeAt(++i);
        var cp = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
        bytes.push(
          0xf0 | (cp >> 18),
          0x80 | ((cp >> 12) & 0x3f),
          0x80 | ((cp >> 6) & 0x3f),
          0x80 | (cp & 0x3f)
        );
      } else {
        bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
      }
    }
    return bytes;
  }

  function utf8Decode(bytes) {
    var result = "";
    for (var i = 0; i < bytes.length; ) {
      var c = bytes[i++];
      if (c < 0x80) {
        result += String.fromCharCode(c);
      } else if (c < 0xe0) {
        var c2 = bytes[i++];
        result += String.fromCharCode(((c & 0x1f) << 6) | (c2 & 0x3f));
      } else if (c < 0xf0) {
        var c3 = bytes[i++];
        var c4 = bytes[i++];
        result += String.fromCharCode(((c & 0x0f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f));
      } else {
        var c5 = bytes[i++];
        var c6 = bytes[i++];
        var c7 = bytes[i++];
        var cp = ((c & 0x07) << 18) | ((c5 & 0x3f) << 12) | ((c6 & 0x3f) << 6) | (c7 & 0x3f);
        cp -= 0x10000;
        result += String.fromCharCode(0xd800 + (cp >> 10), 0xdc00 + (cp & 0x3ff));
      }
    }
    return result;
  }

  function leftRotate(x, c) {
    return (x << c) | (x >>> (32 - c));
  }

  function md5WordsFromBytes(bytes) {
    var originalLength = bytes.length;
    var bitLength = originalLength * 8;
    var paddedBytes = bytes.slice();
    paddedBytes.push(0x80);
    while (paddedBytes.length % 64 !== 56) paddedBytes.push(0);
    for (var i = 0; i < 8; i++) {
      paddedBytes.push(bitLength % 256);
      bitLength = Math.floor(bitLength / 256);
    }
    var a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
    var k = [], s = [
      7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
      5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
      4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
      6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
    ];
    for (i = 0; i < 64; i++) k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 4294967296);

    for (var offset = 0; offset < paddedBytes.length; offset += 64) {
      var m = new Array(16);
      for (i = 0; i < 16; i++) {
        var p = offset + i * 4;
        m[i] = paddedBytes[p] | (paddedBytes[p + 1] << 8) | (paddedBytes[p + 2] << 16) | (paddedBytes[p + 3] << 24);
      }
      var a = a0, b = b0, c = c0, d = d0;
      for (i = 0; i < 64; i++) {
        var f, g;
        if (i < 16) { f = (b & c) | (~b & d); g = i; }
        else if (i < 32) { f = (d & b) | (~d & c); g = (5 * i + 1) % 16; }
        else if (i < 48) { f = b ^ c ^ d; g = (3 * i + 5) % 16; }
        else { f = c ^ (b | ~d); g = (7 * i) % 16; }
        var temp = d; d = c; c = b;
        b = (b + leftRotate((a + f + k[i] + m[g]) | 0, s[i])) | 0;
        a = temp;
      }
      a0 = (a0 + a) | 0; b0 = (b0 + b) | 0; c0 = (c0 + c) | 0; d0 = (d0 + d) | 0;
    }
    return [a0, b0, c0, d0];
  }

  function md5HexFromBytes(bytes) {
    var words = md5WordsFromBytes(bytes);
    var result = "";
    for (var i = 0; i < words.length; i++) {
      var word = words[i];
      for (var j = 0; j < 4; j++) {
        var b = (word >>> (j * 8)) & 0xff;
        result += (b < 16 ? "0" : "") + b.toString(16);
      }
    }
    return result;
  }

  function md5Hex(str) {
    return md5HexFromBytes(utf8Bytes(str));
  }

  function hexToBytes(hex) {
    var bytes = [];
    for (var c = 0; c < hex.length; c += 2) {
      bytes.push(parseInt(hex.substr(c, 2), 16));
    }
    return bytes;
  }

  var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  function base64EncodeBytes(bytes) {
    var result = "";
    for (var i = 0; i < bytes.length; i += 3) {
      var a = bytes[i], b = i + 1 < bytes.length ? bytes[i + 1] : 0, c = i + 2 < bytes.length ? bytes[i + 2] : 0;
      var triple = (a << 16) | (b << 8) | c;
      result += BASE64_CHARS[(triple >> 18) & 63];
      result += BASE64_CHARS[(triple >> 12) & 63];
      result += i + 1 < bytes.length ? BASE64_CHARS[(triple >> 6) & 63] : "=";
      result += i + 2 < bytes.length ? BASE64_CHARS[triple & 63] : "=";
    }
    return result;
  }

  function base64Encode(str) {
    return base64EncodeBytes(utf8Bytes(str));
  }

  function base64Decode(str) {
    var binary = "";
    try {
      if (typeof atob === "function") {
        binary = atob(str);
      } else if (global && typeof global.atob === "function") {
        binary = global.atob(str);
      } else {
        throw new Error("atob unavailable");
      }
    } catch (e) {
      throw new TransKitError("Base64 decode failed", "BASE64_DECODE_ERROR", e);
    }
    var bytes = [];
    for (var i = 0; i < binary.length; i++) bytes.push(binary.charCodeAt(i));
    return utf8Decode(bytes);
  }

  function hmacMd5(message, key) {
    var keyBytes = utf8Bytes(key);
    if (keyBytes.length > 64) keyBytes = hexToBytes(md5HexFromBytes(keyBytes));
    while (keyBytes.length < 64) keyBytes.push(0);

    var ipad = [], opad = [];
    for (var i = 0; i < 64; i++) {
      ipad.push(keyBytes[i] ^ 0x36);
      opad.push(keyBytes[i] ^ 0x5c);
    }

    var inner = ipad.concat(utf8Bytes(message));
    var innerHashHex = md5HexFromBytes(inner);
    var innerHashBytes = hexToBytes(innerHashHex);

    var outer = opad.concat(innerHashBytes);
    return md5HexFromBytes(outer);
  }

  function xorEncrypt(text, key) {
    var textBytes = utf8Bytes(text);
    var keyBytes = utf8Bytes(key);
    var out = [];
    for (var i = 0; i < textBytes.length; i++) {
      out.push(textBytes[i] ^ keyBytes[i % keyBytes.length]);
    }
    return base64EncodeBytes(out);
  }

  function xorDecrypt(ciphertext, key) {
    var bytes = [];
    var binary = atob(ciphertext);
    for (var i = 0; i < binary.length; i++) bytes.push(binary.charCodeAt(i));
    var keyBytes = utf8Bytes(key);
    var out = [];
    for (var j = 0; j < bytes.length; j++) {
      out.push(bytes[j] ^ keyBytes[j % keyBytes.length]);
    }
    return utf8Decode(out);
  }

  var CryptoService = {
    md5: function (value) {
      return md5Hex(String(value));
    },
    hmacMd5: function (value, key) {
      return hmacMd5(String(value), String(key));
    },
    hmacMD5Base64: function (value, key) {
      var hex = hmacMd5(String(value), String(key));
      return base64EncodeBytes(hexToBytes(hex));
    },
    base64Encode: function (value) {
      return base64Encode(String(value));
    },
    base64Decode: function (value) {
      return base64Decode(String(value));
    },
    xorEncrypt: xorEncrypt,
    xorDecrypt: xorDecrypt
  };

  /* ============================================================================
   * 4. Runtime 注册表管理与深度配置合并
   * ============================================================================ */

  var Runtime = {
    inflight: new Map(),
    breakers: new Map(),
    services: new Map(),
    providers: new Map(),
    strategies: new Map(),
    _config: {}
  };

  function mergeConfig(base, override) {
    base = base || {};
    override = override || {};
    var result = Object.assign({}, base, override);

    if (base.cache || override.cache) {
      result.cache = Object.assign({}, base.cache || {}, override.cache || {});
    }
    if (base.circuitBreaker || override.circuitBreaker) {
      result.circuitBreaker = Object.assign({}, base.circuitBreaker || {}, override.circuitBreaker || {});
    }
    if (base.retry || override.retry) {
      if (typeof override.retry === "number" || typeof base.retry === "number") {
        result.retry = override.retry !== undefined ? override.retry : base.retry;
      } else {
        result.retry = Object.assign({}, base.retry || {}, override.retry || {});
      }
    }
    if (base.services || override.services) {
      result.services = Object.assign({}, base.services || {}, override.services || {});
    }
    return result;
  }

  // Service Registry
  var ServiceRegistry = {
    register: function (id, Service, options) {
      options = options || {};
      if (Runtime.services.has(id) && !options.override) {
        throw new ConfigurationError("Service already exists: " + id, "SERVICE_EXISTS");
      }
      var instance = typeof Service === "function" ? new Service(options.options || {}) : Service;
      Runtime.services.set(id, instance);
      return instance;
    },
    get: function (id) {
      return Runtime.services.get(id);
    },
    list: function () {
      return Array.from(Runtime.services.keys());
    },
    unregister: function (id) {
      return Runtime.services.delete(id);
    }
  };

  // Provider Registry
  var ProviderRegistry = {
    register: function (Provider, options) {
      options = options || {};
      var instance = typeof Provider === "function" ? new Provider(options) : Provider;
      var id = options.id || instance.id || Provider.id;

      if (!id) {
        throw new ConfigurationError("Provider id is required", "PROVIDER_ID_REQUIRED");
      }
      if (Runtime.providers.has(id) && !options.override) {
        throw new ConfigurationError("Provider already exists: " + id, "PROVIDER_EXISTS");
      }

      instance.id = id;
      if (instance.priority === undefined) {
        instance.priority = Provider.priority !== undefined ? Provider.priority : 0;
      }
      if (!instance.capabilities) {
        instance.capabilities = {
          translate: true,
          detect: false,
          batch: false,
          streaming: false
        };
      }
      Runtime.providers.set(id, instance);
      return instance;
    },
    get: function (id) {
      return Runtime.providers.get(id);
    },
    list: function () {
      return Array.from(Runtime.providers.values()).map(function (provider) {
        return {
          id: provider.id,
          name: provider.name || provider.id,
          version: provider.version || VERSION,
          priority: provider.priority || 0,
          capabilities: Object.assign({}, provider.capabilities || {})
        };
      });
    },
    unregister: function (id) {
      return Runtime.providers.delete(id);
    }
  };

  // Strategy Registry
  var StrategyRegistry = {
    register: function (id, Strategy, options) {
      options = options || {};
      if (Runtime.strategies.has(id) && !options.override) {
        throw new ConfigurationError("Strategy already exists: " + id, "STRATEGY_EXISTS");
      }
      var instance = typeof Strategy === "function" ? new Strategy(options.options || {}) : Strategy;
      Runtime.strategies.set(id, instance);
      return instance;
    },
    get: function (id) {
      return Runtime.strategies.get(id);
    },
    list: function () {
      return Array.from(Runtime.strategies.keys());
    },
    unregister: function (id) {
      return Runtime.strategies.delete(id);
    }
  };

  var DefaultStrategy = {
    id: "default",
    resolve: function (providers) {
      return providers.slice().sort(function (a, b) {
        return (b.priority || 0) - (a.priority || 0);
      });
    }
  };

  // 动态解析 Service 优先级:请求级 -> 实例级 -> 全局级 -> Registry
  function resolveService(serviceId, requestOpts, instanceOpts) {
    if (requestOpts && requestOpts.services && requestOpts.services[serviceId]) {
      return requestOpts.services[serviceId];
    }
    if (instanceOpts && instanceOpts.services && instanceOpts.services[serviceId]) {
      return instanceOpts.services[serviceId];
    }
    if (Runtime._config && Runtime._config.services && Runtime._config.services[serviceId]) {
      return Runtime._config.services[serviceId];
    }
    return ServiceRegistry.get(serviceId);
  }

  /* ============================================================================
   * 5. 内置标准服务 (Request & Storage Services)
   * ============================================================================ */

  // 5.1 Request Service
  function RequestService(options) {
    this.options = options || {};
  }

  RequestService.prototype.request = function (options) {
    var gm = Utils.getGlobal("GM_xmlhttpRequest");
    var gmModern = global.GM && global.GM.xmlHttpRequest;

    if (Utils.isFunction(gmModern)) {
      return this._gmRequest(gmModern.bind(global.GM), options);
    }
    if (Utils.isFunction(gm)) {
      return this._gmRequest(gm, options);
    }
    return this._nativeRequest(options);
  };

  RequestService.prototype._gmRequest = function (fn, options) {
    var self = this;
    return new Promise(function (resolve, reject) {
      var timeout = options.timeout || self.options.timeout || 10000;
      var finished = false;

      var timer = setTimeout(function () {
        if (!finished) {
          finished = true;
          reject(new TimeoutError("Request timeout", "REQUEST_TIMEOUT"));
        }
      }, timeout);

      var config = Object.assign({}, options);
      config.timeout = timeout;

      config.onload = function (response) {
        if (finished) return;
        finished = true;
        clearTimeout(timer);
        if (response.status >= 200 && response.status < 300) {
          resolve({
            status: response.status,
            headers: response.responseHeaders || {},
            responseText: response.responseText,
            response: response.response
          });
        } else {
          reject(new NetworkError("HTTP " + response.status, "HTTP_ERROR", response));
        }
      };

      config.onerror = function (error) {
        if (finished) return;
        finished = true;
        clearTimeout(timer);
        reject(new NetworkError("Network request failed", "NETWORK_ERROR", error));
      };

      config.ontimeout = function () {
        if (finished) return;
        finished = true;
        clearTimeout(timer);
        reject(new TimeoutError("Request timeout", "REQUEST_TIMEOUT"));
      };

      try {
        fn(config);
      } catch (e) {
        if (!finished) {
          finished = true;
          clearTimeout(timer);
          reject(new NetworkError(e.message, "REQUEST_ERROR", e));
        }
      }
    });
  };

  RequestService.prototype._nativeRequest = function (options) {
    var timeout = options.timeout || this.options.timeout || 10000;

    if (typeof fetch === "function") {
      var controller = typeof AbortController !== "undefined" ? new AbortController() : null;
      var timer = controller ? setTimeout(function () { controller.abort(); }, timeout) : null;

      return fetch(options.url, {
        method: options.method || "GET",
        headers: options.headers || {},
        body: options.data,
        signal: controller ? controller.signal : options.signal
      })
        .then(function (res) {
          if (timer) clearTimeout(timer);
          if (!res.ok) throw new NetworkError("HTTP " + res.status, "HTTP_ERROR");
          return res.text().then(function (text) {
            return {
              status: res.status,
              headers: {},
              responseText: text,
              response: text
            };
          });
        })
        .catch(function (err) {
          if (timer) clearTimeout(timer);
          if (err && err.name === "AbortError") throw new TimeoutError("Request timeout", "REQUEST_TIMEOUT");
          throw err;
        });
    }

    return new Promise(function (resolve, reject) {
      var xhr = new XMLHttpRequest();
      xhr.open(options.method || "GET", options.url, true);
      if (options.headers) {
        Object.keys(options.headers).forEach(function (key) {
          xhr.setRequestHeader(key, options.headers[key]);
        });
      }
      xhr.timeout = timeout;
      xhr.onload = function () {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve({
            status: xhr.status,
            headers: {},
            responseText: xhr.responseText,
            response: xhr.response
          });
        } else {
          reject(new NetworkError("HTTP " + xhr.status, "HTTP_ERROR"));
        }
      };
      xhr.onerror = function () { reject(new NetworkError("Network request failed", "NETWORK_ERROR")); };
      xhr.ontimeout = function () { reject(new TimeoutError("Request timeout", "REQUEST_TIMEOUT")); };
      xhr.send(options.data || null);
    });
  };

  // 5.2 Storage Service
  function StorageService() {}

  StorageService.prototype.get = function (key) {
    var gm = Utils.getGlobal("GM_getValue");
    if (Utils.isFunction(gm)) return Promise.resolve(gm(key, null));
    if (global.GM && Utils.isFunction(global.GM.getValue)) return global.GM.getValue(key, null);
    try {
      if (global.localStorage) {
        var value = global.localStorage.getItem(key);
        return Promise.resolve(value === null ? null : JSON.parse(value));
      }
    } catch (e) {
      return Promise.reject(new CacheError(e.message, "STORAGE_GET_ERROR"));
    }
    return Promise.resolve(null);
  };

  StorageService.prototype.set = function (key, value) {
    var gm = Utils.getGlobal("GM_setValue");
    if (Utils.isFunction(gm)) { gm(key, value); return Promise.resolve(); }
    if (global.GM && Utils.isFunction(global.GM.setValue)) return global.GM.setValue(key, value);
    try {
      if (global.localStorage) global.localStorage.setItem(key, JSON.stringify(value));
      return Promise.resolve();
    } catch (e) {
      return Promise.reject(new CacheError(e.message, "STORAGE_SET_ERROR"));
    }
  };

  StorageService.prototype.delete = function (key) {
    var gm = Utils.getGlobal("GM_deleteValue");
    if (Utils.isFunction(gm)) { gm(key); return Promise.resolve(); }
    if (global.GM && Utils.isFunction(global.GM.deleteValue)) return global.GM.deleteValue(key);
    try {
      if (global.localStorage) global.localStorage.removeItem(key);
      return Promise.resolve();
    } catch (e) {
      return Promise.reject(new CacheError(e.message, "STORAGE_DELETE_ERROR"));
    }
  };
  StorageService.prototype.remove = StorageService.prototype.delete;

  StorageService.prototype.getMany = function (keys) {
    var self = this;
    if (global.GM && Utils.isFunction(global.GM.getValues)) {
      return global.GM.getValues(keys);
    }
    return Promise.all(keys.map(function (k) { return self.get(k); })).then(function (values) {
      var result = {};
      keys.forEach(function (k, i) { result[k] = values[i]; });
      return result;
    });
  };

  StorageService.prototype.setMany = function (entries) {
    var self = this;
    if (global.GM && Utils.isFunction(global.GM.setValues)) {
      return global.GM.setValues(entries);
    }
    var promises = Object.keys(entries).map(function (k) {
      return self.set(k, entries[k]);
    });
    return Promise.all(promises).then(function () {});
  };

  StorageService.prototype.deleteMany = function (keys) {
    var self = this;
    if (global.GM && Utils.isFunction(global.GM.deleteValues)) {
      return global.GM.deleteValues(keys);
    }
    var promises = keys.map(function (k) { return self.delete(k); });
    return Promise.all(promises).then(function () {});
  };

  StorageService.prototype.clearPrefix = function (prefix) {
    var self = this;
    var gmLive = Utils.getGlobal("GM_listValues");
    if (Utils.isFunction(gmLive)) {
      try {
        var keys = gmLive();
        var toDelete = keys.filter(function (k) { return k.indexOf(prefix) === 0; });
        return self.deleteMany(toDelete);
      } catch (e) {}
    }
    if (global.GM && Utils.isFunction(global.GM.listValues)) {
      return global.GM.listValues().then(function (keys) {
        var toDelete = keys.filter(function (k) { return k.indexOf(prefix) === 0; });
        return self.deleteMany(toDelete);
      });
    }
    try {
      if (global.localStorage) {
        var toDeleteLS = [];
        for (var i = 0; i < global.localStorage.length; i++) {
          var k = global.localStorage.key(i);
          if (k && k.indexOf(prefix) === 0) toDeleteLS.push(k);
        }
        toDeleteLS.forEach(function (k) { global.localStorage.removeItem(k); });
      }
    } catch (e) {}
    return Promise.resolve();
  };

  /* ============================================================================
   * 6. 缓存服务 (CacheService)
   * 支持 L1/L2 两级缓存、热刷新及容量上限淘汰 (maxEntries)
   * ============================================================================ */

  function CacheService(storage, options) {
    this.storage = storage;
    this.options = Object.assign(
      {
        enabled: true,
        ttl: 7 * 24 * 60 * 60 * 1000,
        refreshOnHit: true,
        refreshInterval: 6 * 60 * 60 * 1000,
        maxEntries: 10000,
        persistent: true
      },
      options || {}
    );
    this.memory = new Map();
  }

  // 动态 Storage 解析器:支持“请求级 -> 实例级 -> 全局级 -> 注册表”优先链
  CacheService.prototype._getStorage = function (translator, requestOpts) {
    if (requestOpts && requestOpts.services && requestOpts.services.storage) {
      return requestOpts.services.storage;
    }
    if (translator) {
      return resolveService("storage", requestOpts, translator.options);
    }
    return this.storage || ServiceRegistry.get("storage");
  };

  CacheService.prototype._memoryKey = function (text, source, target) {
    return String(text) + "\u0001" + String(source) + "\u0001" + String(target);
  };

  CacheService.prototype._persistentKey = function (text, source, target) {
    var raw = String(text) + "\u0001" + String(source) + "\u0001" + String(target);
    return "transkit:cache:" + Utils.fastHash(raw);
  };

  CacheService.prototype._checkCapacity = function () {
    var max = this.options.maxEntries || 10000;
    if (this.memory.size < max) return;

    var now = Date.now();
    this.memory.forEach(function (val, key, map) {
      if (val.expiresAt <= now) map.delete(key);
    });

    if (this.memory.size >= max) {
      var targetSize = Math.floor(max * 0.8);
      var entries = Array.from(this.memory.entries()).sort(function (a, b) {
        return (a[1].lastRefreshAt || a[1].lastRefresh || 0) - (b[1].lastRefreshAt || b[1].lastRefresh || 0);
      });
      for (var i = 0; i < entries.length && this.memory.size > targetSize; i++) {
        this.memory.delete(entries[i][0]);
      }
    }
  };

  CacheService.prototype.get = function (text, source, target, translator, requestOpts) {
    if (!this.options.enabled) return Promise.resolve(null);

    var memoryKey = this._memoryKey(text, source, target);
    var now = Date.now();
    var memory = this.memory.get(memoryKey);

    if (memory && memory.expiresAt > now) {
      memory.hitCount = (memory.hitCount || 0) + 1;
      memory.lastHitAt = now;

      if (
        this.options.refreshOnHit &&
        now - (memory.lastRefreshAt || memory.lastRefresh || 0) >= this.options.refreshInterval
      ) {
        var newExpiresAt = now + this.options.ttl;
        memory.expiresAt = newExpiresAt;
        memory.lastRefreshAt = now;
        memory.lastRefresh = now;
        if (translator) translator._log("CACHE", "Hot Cache Refreshed (L1)", { text: text });
        if (this.options.persistent) {
          this._persist(text, source, target, memory.value, newExpiresAt, memory, translator, requestOpts);
        }
      } else if (translator) {
        translator._log("CACHE", "Hit L1 Memory Cache", { text: text });
      }

      return Promise.resolve({
        value: memory.value || memory.translatedText,
        fromCache: true,
        level: "L1"
      });
    }

    if (memory) this.memory.delete(memoryKey);
    if (!this.options.persistent) return Promise.resolve(null);

    var key = this._persistentKey(text, source, target);
    var self = this;
    var storage = this._getStorage(translator, requestOpts);

    return storage.get(key).then(function (entry) {
      if (!entry) {
        if (translator) translator._log("CACHE", "Cache Miss", { text: text });
        return null;
      }
      if (entry.expiresAt && entry.expiresAt <= Date.now()) {
        storage.delete(key);
        if (translator) translator._log("CACHE", "L2 Cache Expired", { text: text });
        return null;
      }

      self._checkCapacity();
      var val = entry.value || entry.translatedText;
      self.memory.set(memoryKey, {
        originalText: text,
        translatedText: val,
        value: val,
        source: source,
        target: target,
        expiresAt: entry.expiresAt,
        createdAt: entry.createdAt || Date.now(),
        hitCount: (entry.hitCount || 0) + 1,
        lastHitAt: Date.now(),
        lastRefreshAt: Date.now(),
        lastRefresh: Date.now()
      });

      if (translator) translator._log("CACHE", "Hit L2 Storage Cache", { text: text });

      return {
        value: val,
        fromCache: true,
        level: "L2"
      };
    });
  };

  CacheService.prototype.set = function (text, source, target, value, translator, requestOpts) {
    if (!this.options.enabled) return Promise.resolve();

    var now = Date.now();
    var expiresAt = now + this.options.ttl;
    var memoryKey = this._memoryKey(text, source, target);

    var record = {
      originalText: text,
      translatedText: value,
      value: value,
      source: source,
      target: target,
      createdAt: now,
      expiresAt: expiresAt,
      hitCount: 0,
      lastHitAt: now,
      lastRefreshAt: now,
      lastRefresh: now
    };

    this._checkCapacity();
    this.memory.set(memoryKey, record);

    if (!this.options.persistent) return Promise.resolve();
    return this._persist(text, source, target, value, expiresAt, record, translator, requestOpts);
  };

  CacheService.prototype._persist = function (text, source, target, value, expiresAt, record, translator, requestOpts) {
    var key = this._persistentKey(text, source, target);
    var storage = this._getStorage(translator, requestOpts);
    return storage.set(key, Object.assign({
      originalText: text,
      translatedText: value,
      value: value,
      source: source,
      target: target,
      createdAt: Date.now(),
      expiresAt: expiresAt
    }, record || {}));
  };

  CacheService.prototype.clear = function (translator, requestOpts) {
    this.memory.clear();
    var storage = this._getStorage(translator, requestOpts);
    if (storage && Utils.isFunction(storage.clearPrefix)) {
      return storage.clearPrefix("transkit:cache:");
    }
    return Promise.resolve();
  };

  /* ============================================================================
   * 7. 全局熔断器 (Circuit Breaker - 带半开探针锁)
   * ============================================================================ */

  function CircuitBreaker(providerId, options) {
    this.providerId = providerId;
    this.options = Object.assign(
      {
        enabled: false,
        failureThreshold: 10,
        cooldown: 6 * 60 * 60 * 1000
      },
      options || {}
    );
    this.failures = 0;
    this.state = "closed";
    this.openedAt = 0;
    this.halfOpenProbeCount = 0;
  }

  CircuitBreaker.prototype.allow = function () {
    if (!this.options.enabled) return true;
    if (this.state === "closed") return true;

    var cooldown = this.options.cooldown || this.options.resetTimeout || 21600000;
    if (this.state === "open" && Date.now() - this.openedAt >= cooldown) {
      this.state = "half-open";
      this.halfOpenProbeCount = 0;
    }

    if (this.state === "half-open") {
      if (this.halfOpenProbeCount > 0) {
        return false; // 探针锁:半开恢复期严格限制只放行 1 个试探请求
      }
      this.halfOpenProbeCount++;
      return true;
    }
    return false;
  };

  CircuitBreaker.prototype.success = function () {
    if (!this.options.enabled) return;
    this.failures = 0;
    this.state = "closed";
    this.openedAt = 0;
    this.halfOpenProbeCount = 0;
  };

  CircuitBreaker.prototype.failure = function () {
    if (!this.options.enabled) return;
    this.failures++;
    this.halfOpenProbeCount = 0;
    if (this.failures >= this.options.failureThreshold) {
      this.state = "open";
      this.openedAt = Date.now();
    }
  };

  /* ============================================================================
   * 8. Provider 统一上下文创建 (自动包装及动态注入 Timeout 与 Services)
   * ============================================================================ */

  function responseText(response) {
    return response && response.responseText !== undefined ? response.responseText : response;
  }

  function jsonResponse(response) {
    return JSON.parse(responseText(response));
  }

  function createProviderContext(translator, request, requestOpts) {
    var effectiveTimeout = (requestOpts && requestOpts.timeout !== undefined)
      ? requestOpts.timeout
      : (translator && translator.options && translator.options.timeout !== undefined)
        ? translator.options.timeout
        : 10000;

    var rawReqService = resolveService("request", requestOpts, translator ? translator.options : null);

    // 包装 Request Service,保证请求级/实例级 timeout 自动继承
    var wrappedReqService = {
      request: function (opts) {
        var reqOpts = Object.assign({ timeout: effectiveTimeout }, opts || {});
        return rawReqService.request(reqOpts);
      }
    };

    var resolvedCache = (requestOpts && requestOpts.services && requestOpts.services.cache)
      ? requestOpts.services.cache
      : (Runtime.services.has("cache") && ServiceRegistry.get("cache"))
        ? ServiceRegistry.get("cache")
        : (translator ? translator.cache : null);

    return {
      request: request,
      services: {
        request: wrappedReqService,
        storage: resolveService("storage", requestOpts, translator ? translator.options : null),
        cache: resolvedCache,
        crypto: resolveService("crypto", requestOpts, translator ? translator.options : null) || CryptoService
      },
      utils: Utils
    };
  }

  /* ============================================================================
   * 9. 保留实测稳定可用的 7 大内置 Provider 实现
   * ============================================================================ */

  // 9.1 Google Provider
  function GoogleProvider() {
    this.id = "google";
    this.name = "Google Translate";
    this.version = VERSION;
    this.priority = 100;
    this.capabilities = { translate: true, detect: false, batch: false, streaming: false };
  }
  GoogleProvider.prototype.translate = function (request, context) {
    var url =
      "https://translate.google.com/translate_a/t?client=gtx" +
      "&sl=" + encodeURIComponent(request.source || "auto") +
      "&tl=" + encodeURIComponent(request.target || "zh-CN") +
      "&q=" + encodeURIComponent(request.text);

    return context.services.request.request({ method: "GET", url: url }).then(function (res) {
      var data = JSON.parse(responseText(res));
      if (!data || !data[0] || !data[0][0]) {
        throw new ProviderError("Invalid Google response", "INVALID_RESPONSE");
      }
      return { text: data[0][0] };
    });
  };

  // 9.2 Bing Provider
  function BingProvider() {
    this.id = "bing";
    this.name = "Bing Translator";
    this.version = VERSION;
    this.priority = 90;
    this.capabilities = { translate: true, detect: false, batch: false, streaming: false };
  }
  BingProvider.prototype.translate = function (request, context) {
    var target = request.target === "zh-CN" ? "zh-Hans" : request.target === "zh-TW" ? "zh-Hant" : request.target || "zh-Hans";
    var source = request.source && request.source !== "auto" ? request.source : "";
    var url = "https://edge.microsoft.com/translate/translatetext?from=" + encodeURIComponent(source) + "&to=" + encodeURIComponent(target);

    return context.services.request.request({
      method: "POST",
      url: url,
      headers: { "Content-Type": "application/json" },
      data: JSON.stringify([request.text])
    }).then(function (res) {
      var data = jsonResponse(res);
      var text = data && data[0] && data[0].translations && data[0].translations[0] && data[0].translations[0].text;
      if (!text) throw new ProviderError("Invalid Bing response", "INVALID_RESPONSE");
      return { text: text };
    });
  };

  // 9.3 Tencent AI Provider
  function TencentAIProvider() {
    this.id = "tencent-ai";
    this.name = "Tencent AI Translate";
    this.version = VERSION;
    this.priority = 82;
    this.capabilities = { translate: true, detect: false, batch: false, streaming: false };
  }
  TencentAIProvider.prototype.translate = function (request, context) {
    var timestamp = Date.now();
    var body = {
      header: {
        fn: "auto_translation",
        client_key: "browser-chrome-121.0.0-Windows_10-" + Utils.guid() + "-" + timestamp,
        session: "", user: ""
      },
      type: "plain",
      model_category: "normal",
      text_domain: "",
      source: { lang: request.source || "auto", text_list: [request.text] },
      target: { lang: request.target === "zh-CN" ? "zh" : request.target || "zh" }
    };

    return context.services.request.request({
      method: "POST",
      url: "https://transmart.qq.com/api/imt",
      headers: {
        "Content-Type": "application/json",
        Origin: "https://transmart.qq.com",
        Referer: "https://transmart.qq.com/"
      },
      data: JSON.stringify(body)
    }).then(function (res) {
      var data = jsonResponse(res);
      var text = data && data.auto_translation && data.auto_translation[0];
      if (!text) throw new ProviderError("Invalid Tencent AI response", "INVALID_RESPONSE");
      return { text: text };
    });
  };

  // 9.4 DeepL Provider
  function DeepLProvider() {
    this.id = "deepl";
    this.name = "DeepL";
    this.version = VERSION;
    this.priority = 70;
    this.capabilities = { translate: true, detect: false, batch: false, streaming: false };
  }
  DeepLProvider.prototype._timestamp = function (text) {
    var count = (text.match(/i/g) || []).length;
    var ts = Date.now();
    if (count !== 0) return ts - (ts % (count + 1)) + (count + 1);
    return ts;
  };
  DeepLProvider.prototype.translate = function (request, context) {
    var id = (Math.floor(Math.random() * 99999) + 100000) * 1000;
    var postData = JSON.stringify({
      jsonrpc: "2.0",
      method: "LMT_handle_texts",
      id: id,
      params: {
        splitting: "newlines",
        lang: {
          source_lang_user_selected: (request.source && request.source !== "auto") ? request.source.toUpperCase() : "auto",
          target_lang: request.target === "zh-CN" ? "ZH" : String(request.target || "ZH").split("-")[0].toUpperCase()
        },
        texts: [{ text: request.text, requestAlternatives: 3 }],
        timestamp: this._timestamp(request.text)
      }
    });

    if ((id + 5) % 29 === 0 || (id + 3) % 13 === 0) {
      postData = postData.replace('"method":"', '"method" : "');
    } else {
      postData = postData.replace('"method":"', '"method": "');
    }

    return context.services.request.request({
      method: "POST",
      url: "https://www2.deepl.com/jsonrpc",
      headers: {
        "Content-Type": "application/json",
        Origin: "https://www.deepl.com",
        Referer: "https://www.deepl.com/"
      },
      data: postData
    }).then(function (res) {
      var data = jsonResponse(res);
      var text = data && data.result && data.result.texts && data.result.texts[0] && data.result.texts[0].text;
      if (!text) throw new ProviderError("Invalid DeepL response", "INVALID_RESPONSE");
      return { text: text };
    });
  };

  // 9.5 Caiyun Provider
  function CaiyunProvider() {
    this.id = "caiyun";
    this.name = "Caiyun Translate";
    this.version = VERSION;
    this.priority = 65;
    this.capabilities = { translate: true, detect: false, batch: false, streaming: false };
    this.browserId = null;
    this.jwt = null;
  }
  CaiyunProvider.prototype._decode = function (encoded) {
    var source = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    var target = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm";
    var dict = {};
    for (var i = 0; i < source.length; i++) dict[source[i]] = target[i];
    var replaced = String(encoded).split("").map(function (ch) { return dict[ch] || ch; }).join("");
    return base64Decode(replaced);
  };
  CaiyunProvider.prototype._ensureAuth = function (context) {
    var self = this;
    if (self.browserId && self.jwt) return Promise.resolve();
    self.browserId = md5Hex(Math.random().toString());

    return context.services.request.request({
      method: "POST",
      url: "https://api.interpreter.caiyunai.com/v1/user/jwt/generate",
      headers: {
        "Content-Type": "application/json",
        "X-Authorization": "token:qgemv4jr1y38jyq6vhvi",
        Origin: "https://fanyi.caiyunapp.com"
      },
      data: JSON.stringify({ browser_id: self.browserId })
    }).then(function (res) {
      var data = jsonResponse(res);
      if (!data.jwt) throw new ProviderError("Caiyun JWT unavailable", "AUTH_INIT_ERROR");
      self.jwt = data.jwt;
    });
  };
  CaiyunProvider.prototype.translate = function (request, context) {
    var self = this;
    var src = (request.source && request.source !== "auto") ? request.source : "auto";
    var tgt = request.target === "zh-TW" ? "zh" : (request.target || "zh");
    var transType = src + "2" + (tgt === "zh-CN" ? "zh" : tgt);

    return self._ensureAuth(context).then(function () {
      return context.services.request.request({
        method: "POST",
        url: "https://api.interpreter.caiyunai.com/v1/translator",
        headers: {
          "Content-Type": "application/json",
          "X-Authorization": "token:qgemv4jr1y38jyq6vhvi",
          "T-Authorization": self.jwt
        },
        data: JSON.stringify({
          source: [request.text],
          trans_type: transType,
          detect: true,
          browser_id: self.browserId
        })
      });
    }).then(function (res) {
      var data = jsonResponse(res);
      if (!data || !data.target) throw new ProviderError("Invalid Caiyun response", "INVALID_RESPONSE");
      var text = data.target.map(function (item) { return self._decode(item); }).join("\n");
      if (!text) throw new ProviderError("Empty Caiyun result", "EMPTY_RESULT");
      return { text: text };
    });
  };

  // 9.6 Youdao Mobile Provider
  function YoudaoMobileProvider() {
    this.id = "youdao-mobile";
    this.name = "Youdao Mobile";
    this.version = VERSION;
    this.priority = 40;
    this.capabilities = { translate: true, detect: false, batch: false, streaming: false };
  }
  YoudaoMobileProvider.prototype.translate = function (request, context) {
    var body = Utils.encodeForm({ inputtext: request.text, type: "AUTO" });
    return context.services.request.request({
      method: "POST",
      url: "http://m.youdao.com/translate",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      data: body
    }).then(function (res) {
      var html = responseText(res);
      var match = /id="translateResult">\s*?<li>([\s\S]*?)<\/li>\s*?<\/ul/.exec(html);
      if (!match) throw new ProviderError("Invalid Youdao mobile response", "INVALID_RESPONSE");
      return { text: match[1].replace(/<[^>]+>/g, "").trim() };
    });
  };

  // 9.7 Google Mobile Provider
  function GoogleMobileProvider() {
    this.id = "google-mobile";
    this.name = "Google Mobile Translate";
    this.version = VERSION;
    this.priority = 35;
    this.capabilities = { translate: true, detect: false, batch: false, streaming: false };
  }
  GoogleMobileProvider.prototype.translate = function (request, context) {
    var url = "https://translate.google.com/m?tl=" + encodeURIComponent(request.target || "zh-CN") + "&q=" + encodeURIComponent(request.text);
    return context.services.request.request({ method: "GET", url: url }).then(function (res) {
      var html = responseText(res);
      var match = /class="result-container">((?:.|\n)*?)<\/div/.exec(html);
      if (!match) throw new ProviderError("Invalid Google mobile response", "INVALID_RESPONSE");
      return { text: match[1].replace(/<[^>]+>/g, "").trim() };
    });
  };

  /* ============================================================================
   * 10. 语种检测 (Language Detection Helper)
   * ============================================================================ */

  function detectLanguage(text, context) {
    var query = String(text || "").trim().slice(0, 100);
    if (!query) return Promise.resolve("auto");

    function localDetect(str) {
      if (/[\u3040-\u30ff\u31f0-\u31ff]/.test(str)) return "ja";
      if (/[\uac00-\ud7af\u1100-\u11ff]/.test(str)) return "ko";
      if (/[\u4e00-\u9fa5]/.test(str)) return "zh";
      if (/[\u0400-\u04ff]/.test(str)) return "ru";
      return "en";
    }

    function tryBing() {
      var bingUrl = "https://edge.microsoft.com/translate/translatetext?from=&to=zh-Hans";
      return context.services.request.request({
        method: "POST",
        url: bingUrl,
        headers: { "Content-Type": "application/json" },
        data: JSON.stringify([query]),
        timeout: 4000
      }).then(function (res) {
        var data = jsonResponse(res);
        var lang = data && data[0] && data[0].detectedLanguage && data[0].detectedLanguage.language;
        if (!lang) throw new Error("Bing detect empty");
        return lang === "zh-Hans" ? "zh" : lang;
      });
    }

    var googleUrl = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=zh-CN&dt=t&q=" + encodeURIComponent(query);
    return context.services.request.request({
      method: "GET",
      url: googleUrl,
      timeout: 4000
    }).then(function (res) {
      var data = jsonResponse(res);
      var lang = data && data[2];
      if (!lang) throw new Error("Google detect empty");
      return lang;
    }).catch(function () {
      return tryBing();
    }).catch(function () {
      return localDetect(query);
    });
  }

  /* ============================================================================
   * 11. 核心 Translator 类定义
   * 符合 Spec 34 规范要求的全部默认配置
   * ============================================================================ */

  function Translator(options) {
    var DEFAULTS = {
      source: "auto",
      target: "zh-CN",
      providers: "auto",
      batchSize: 30,
      timeout: 10000,
      retry: 0,
      debug: false,
      cache: {
        enabled: true,
        ttl: 7 * 24 * 60 * 60 * 1000, // 7 天
        refreshOnHit: true,
        refreshInterval: 6 * 60 * 60 * 1000, // 6 小时
        maxEntries: 10000,
        persistent: true
      },
      circuitBreaker: {
        enabled: false,
        failureThreshold: 10,
        cooldown: 6 * 60 * 60 * 1000 // 6 小时
      }
    };

    this.options = mergeConfig(DEFAULTS, options || {});

    var storageService = resolveService("storage", null, this.options);
    this.cache = new CacheService(storageService, this.options.cache);
  }

  // Debug 日志打印实现
  Translator.prototype._log = function (type, message, details) {
    if (!this.options.debug) return;
    if (global.console && console.log) {
      var prefix = "[TransKit:" + type + "]";
      if (details !== undefined) {
        console.log(prefix, message, details);
      } else {
        console.log(prefix, message);
      }
    }
  };

  Translator.prototype._resolveProviders = function (requestOpts) {
    var configured = (requestOpts && requestOpts.providers) || this.options.providers;
    var list = Array.from(Runtime.providers.values());

    if (configured === "auto" || configured === undefined) {
      var defaultStrat = StrategyRegistry.get("default");
      if (defaultStrat && Utils.isFunction(defaultStrat.resolve)) {
        return defaultStrat.resolve(list);
      }
      return DefaultStrategy.resolve(list);
    }
    if (typeof configured === "string") {
      var strat = StrategyRegistry.get(configured);
      if (strat && Utils.isFunction(strat.resolve)) {
        return strat.resolve(list);
      }
      var provider = Runtime.providers.get(configured);
      if (!provider) {
        throw new ConfigurationError("Provider or Strategy not found: " + configured, "PROVIDER_NOT_FOUND");
      }
      return [provider];
    }
    if (Array.isArray(configured)) {
      var result = [];
      configured.forEach(function (id) {
        var p = typeof id === "string" ? Runtime.providers.get(id) : id;
        if (!p) {
          throw new ConfigurationError("Provider not found: " + id, "PROVIDER_NOT_FOUND");
        }
        result.push(p);
      });
      return result;
    }
    throw new ConfigurationError("Invalid providers configuration", "INVALID_PROVIDERS");
  };

  Translator.prototype._getBreaker = function (provider) {
    var id = provider.id;
    var breaker = Runtime.breakers.get(id);
    if (!breaker) {
      breaker = new CircuitBreaker(id, this.options.circuitBreaker);
      Runtime.breakers.set(id, breaker);
    }
    breaker.options = Object.assign({}, breaker.options, this.options.circuitBreaker);
    return breaker;
  };

  Translator.prototype._callProvider = function (provider, request, context) {
    var breaker = this._getBreaker(provider);
    if (!breaker.allow()) {
      this._log("CIRCUIT", "Circuit open, skipping provider: " + provider.id);
      throw new CircuitOpenError("Circuit is open: " + provider.id, "CIRCUIT_OPEN", { provider: provider.id });
    }

    var started = Date.now();
    var self = this;
    self._log("PROVIDER", "Calling provider: " + provider.id, { text: request.text });

    var initPromise;
    if (Utils.isFunction(provider.initialize)) {
      if (!provider._initPromise) {
        provider._initPromise = Promise.resolve(provider.initialize(context)).catch(function (err) {
          provider._initPromise = null;
          throw err;
        });
      }
      initPromise = provider._initPromise;
    } else {
      initPromise = Promise.resolve();
    }

    return initPromise
      .then(function () {
        return provider.translate(request, context);
      })
      .then(function (result) {
        var duration = Date.now() - started;
        if (!result || typeof result.text !== "string") {
          throw new ProviderError("Provider returned invalid result", "INVALID_PROVIDER_RESULT", { provider: provider.id });
        }
        breaker.success();
        self._log("PROVIDER", "Provider success: " + provider.id, { duration: duration });
        return {
          text: result.text,
          provider: provider.id,
          success: true,
          duration: duration
        };
      })
      .catch(function (error) {
        breaker.failure();
        self._log("PROVIDER", "Provider failed: " + provider.id, { error: error ? error.message : error });
        throw error;
      });
  };

  Translator.prototype._executeTranslation = function (request, requestOpts) {
    var providers = this._resolveProviders(requestOpts);
    var attempts = [];

    var retryConfig = (requestOpts && requestOpts.retry !== undefined)
      ? requestOpts.retry
      : this.options.retry;

    var retryTimes = typeof retryConfig === "number"
      ? retryConfig
      : Math.max(0, (retryConfig && retryConfig.times) || 0);

    var retryDelay = (retryConfig && typeof retryConfig === "object")
      ? (retryConfig.delay || 0)
      : 0;

    var context = createProviderContext(this, request, requestOpts);
    var self = this;

    function attemptProvider(provider) {
      var currentTry = 0;
      function run() {
        currentTry++;
        return self._callProvider(provider, request, context)
          .then(function (result) {
            attempts.push({ provider: provider.id, success: true, duration: result.duration });
            return result;
          })
          .catch(function (error) {
            attempts.push({
              provider: provider.id,
              success: false,
              duration: 0,
              error: error ? error.code || error.message : "UNKNOWN_ERROR"
            });

            if (currentTry <= retryTimes) {
              self._log("RETRY", "Retrying provider: " + provider.id, { try: currentTry });
              return Utils.sleep(retryDelay).then(run);
            }
            throw error;
          });
      }
      return run();
    }

    function next(index, lastError) {
      if (index >= providers.length) {
        throw new TranslationError("All providers failed", "ALL_PROVIDERS_FAILED", {
          attempts: attempts,
          lastError: lastError
        });
      }
      var provider = providers[index];
      self._log("FAILOVER", "Trying provider " + (index + 1) + "/" + providers.length + ": " + provider.id);
      return attemptProvider(provider).catch(function (error) {
        return next(index + 1, error);
      });
    }

    return Promise.resolve()
      .then(function () { return next(0, null); })
      .then(function (result) {
        result.attempts = attempts;
        return result;
      })
      .catch(function (error) {
        if (error instanceof TranslationError) {
          error.details = Object.assign({}, error.details || {}, { attempts: attempts });
        }
        throw error;
      });
  };

  /* ============================================================================
   * 12. 单项目翻译流程与 Inflight 去重
   * ============================================================================ */

  Translator.prototype._translateOne = function (text, index, requestOpts) {
    if (text === "") {
      return Promise.resolve({
        originalText: "",
        text: "",
        success: true,
        provider: null,
        fromCache: false,
        duration: 0,
        attempts: []
      });
    }

    var source = (requestOpts && requestOpts.source) || this.options.source || "auto";
    var target = (requestOpts && requestOpts.target) || this.options.target || "zh-CN";

    var configuredProviders = (requestOpts && requestOpts.providers) || this.options.providers;
    var providersKey = Utils.isArray(configuredProviders)
      ? configuredProviders.join(",")
      : String(configuredProviders || "auto");

    var request = {
      text: text,
      source: source,
      target: target,
      requestId: Utils.randomId("req"),
      signal: requestOpts ? requestOpts.signal : null
    };

    var inflightKey =
      String(text) + "\u0001" +
      String(source) + "\u0001" +
      String(target) + "\u0001" +
      providersKey;

    var existing = Runtime.inflight.get(inflightKey);

    if (existing) {
      this._log("INFLIGHT", "Merging into existing inflight request", { text: text, providers: providersKey });
      return existing.then(function (res) { return Object.assign({}, res); });
    }

    var started = Date.now();
    var self = this;

    var promise = this.cache.get(text, source, target, self, requestOpts)
      .then(function (cached) {
        if (cached) {
          return {
            originalText: text,
            text: cached.value,
            success: true,
            provider: null,
            fromCache: true,
            duration: Date.now() - started,
            attempts: []
          };
        }

        return self._executeTranslation(request, requestOpts).then(function (result) {
          return self.cache.set(text, source, target, result.text, self, requestOpts)
            .catch(function (e) {
              self._log("CACHE", "Cache write failed non-fatally", { error: e.message });
            })
            .then(function () {
              return {
                originalText: text,
                text: result.text,
                success: true,
                provider: result.provider,
                fromCache: false,
                duration: Date.now() - started,
                attempts: result.attempts
              };
            });
        });
      })
      .catch(function (error) {
        return {
          originalText: text,
          text: null,
          success: false,
          provider: null,
          fromCache: false,
          duration: Date.now() - started,
          attempts: error && error.details && error.details.attempts ? error.details.attempts : [],
          error: error
        };
      });

    Runtime.inflight.set(inflightKey, promise);
    promise.finally(function () {
      if (Runtime.inflight.get(inflightKey) === promise) {
        Runtime.inflight.delete(inflightKey);
      }
    });

    return promise;
  };

  /* ============================================================================
   * 13. Batch 批量处理与顺序组队去重
   * ============================================================================ */

  Translator.prototype._batch = function (texts, requestOpts) {
    var self = this;

    var unique = [];
    var indexMap = new Map();

    texts.forEach(function (text) {
      if (!indexMap.has(text)) {
        indexMap.set(text, unique.length);
        unique.push(text);
      }
    });

    var rawBatchSize = (requestOpts && requestOpts.batchSize !== undefined)
      ? requestOpts.batchSize
      : this.options.batchSize;

    var batchSize = Math.max(1, rawBatchSize || 30);
    var results = new Array(unique.length);

    function processGroup(start) {
      if (start >= unique.length) return Promise.resolve();

      var group = unique.slice(start, start + batchSize);
      self._log("BATCH", "Processing batch group " + (start / batchSize + 1), { count: group.length });

      return Promise.all(
        group.map(function (text) {
          return self._translateOne(text, start, requestOpts);
        })
      ).then(function (groupResults) {
        groupResults.forEach(function (res, offset) {
          results[start + offset] = res;
        });
        return processGroup(start + batchSize);
      });
    }

    return processGroup(0).then(function () {
      return texts.map(function (text) {
        var res = results[indexMap.get(text)];
        return Object.assign({}, res);
      });
    });
  };

  /* ============================================================================
   * 14. 公开 API 实现 (translate / translateWithDetails / detect)
   * ============================================================================ */

  Translator.prototype.translateWithDetails = function (input, options) {
    if (typeof input === "string") {
      return this._translateOne(input, 0, options).then(function (res) {
        return [res];
      });
    }

    if (Array.isArray(input)) {
      return this._batch(input, options);
    }

    return Promise.reject(
      new ConfigurationError("translate input must be string or string[]", "INVALID_TRANSLATE_INPUT")
    );
  };

  Translator.prototype.translate = function (input, options) {
    if (typeof input === "string") {
      if (input === "") return Promise.resolve("");
      return this._translateOne(input, 0, options).then(function (res) {
        if (!res.success) {
          throw res.error || new TranslationError("Translation failed", "TRANSLATION_FAILED");
        }
        return res.text;
      });
    }

    if (Array.isArray(input)) {
      return this._batch(input, options).then(function (results) {
        var failed = results.find(function (item) { return !item.success; });
        if (failed) {
          throw failed.error || new TranslationError("One or more translations failed", "TRANSLATION_FAILED", { results: results });
        }
        return results.map(function (item) { return item.text; });
      });
    }

    return Promise.reject(
      new ConfigurationError("translate input must be string or string[]", "INVALID_TRANSLATE_INPUT")
    );
  };

  Translator.prototype.detect = function (text) {
    if (text === "") return Promise.resolve("");
    var context = createProviderContext(this, { text: text }, null);
    return detectLanguage(text, context);
  };

  Translator.prototype.getInflightCount = function () {
    return Runtime.inflight.size;
  };

  Translator.prototype.getProviderStatus = function () {
    var result = {};
    Runtime.providers.forEach(function (provider, id) {
      var breaker = Runtime.breakers.get(id);
      result[id] = {
        state: breaker ? breaker.state : "closed",
        failures: breaker ? breaker.failures : 0
      };
    });
    return result;
  };

  /* ============================================================================
   * 15. TransKit 全局入口与单例 / 诊断 API
   * ============================================================================ */

  var defaultTranslator = null;

  function getDefaultTranslator() {
    if (!defaultTranslator) {
      defaultTranslator = new Translator(Runtime._config);
    }
    return defaultTranslator;
  }

  var TransKit = {
    version: VERSION,
    Utils: Utils,

    errors: {
      TransKitError: TransKitError,
      ProviderError: ProviderError,
      TranslationError: TranslationError,
      NetworkError: NetworkError,
      TimeoutError: TimeoutError,
      CacheError: CacheError,
      ConfigurationError: ConfigurationError,
      CircuitOpenError: CircuitOpenError
    },

    crypto: CryptoService,
    providers: ProviderRegistry,
    services: ServiceRegistry,
    strategies: StrategyRegistry,

    configure: function (options) {
      Runtime._config = mergeConfig(Runtime._config, options || {});
      defaultTranslator = null;
      return TransKit;
    },

    create: function (options) {
      var merged = mergeConfig(Runtime._config, options || {});
      return new Translator(merged);
    },

    translate: function (input, options) {
      if (options) {
        return TransKit.create(options).translate(input, options);
      }
      return getDefaultTranslator().translate(input);
    },

    translateWithDetails: function (input, options) {
      if (options) {
        return TransKit.create(options).translateWithDetails(input, options);
      }
      return getDefaultTranslator().translateWithDetails(input);
    },

    detect: function (text) {
      return TransKit.create().detect(text);
    },

    clearCache: function () {
      return getDefaultTranslator().cache.clear(getDefaultTranslator());
    },

    runtime: {
      getInflightCount: function () {
        return Runtime.inflight.size;
      },
      getInflightKeys: function () {
        return Array.from(Runtime.inflight.keys());
      },
      getCircuitState: function (id) {
        if (id) {
          var breaker = Runtime.breakers.get(id);
          return {
            state: breaker ? breaker.state : "closed",
            failures: breaker ? breaker.failures : 0,
            openedAt: breaker ? breaker.openedAt : 0
          };
        }
        var result = {};
        ProviderRegistry.list().forEach(function (item) {
          var breaker = Runtime.breakers.get(item.id);
          result[item.id] = {
            state: breaker ? breaker.state : "closed",
            failures: breaker ? breaker.failures : 0,
            openedAt: breaker ? breaker.openedAt : 0
          };
        });
        return result;
      },
      resetCircuit: function (id) {
        if (id) {
          var breaker = Runtime.breakers.get(id);
          if (breaker) breaker.success();
        } else {
          Runtime.breakers.clear();
        }
      },
      clearCache: function () {
        return getDefaultTranslator().cache.clear(getDefaultTranslator());
      }
    }
  };

  /* ============================================================================
   * 16. 注册内置 Service, Strategy 及 7 大稳定 Provider
   * ============================================================================ */

  ServiceRegistry.register("request", RequestService, { override: true });
  ServiceRegistry.register("storage", StorageService, { override: true });
  ServiceRegistry.register("crypto", CryptoService, { override: true });

  StrategyRegistry.register("default", DefaultStrategy, { override: true });

  ProviderRegistry.register(GoogleProvider, { override: true });
  ProviderRegistry.register(BingProvider, { override: true });
  ProviderRegistry.register(TencentAIProvider, { override: true });
  ProviderRegistry.register(DeepLProvider, { override: true });
  ProviderRegistry.register(CaiyunProvider, { override: true });
  ProviderRegistry.register(YoudaoMobileProvider, { override: true });
  ProviderRegistry.register(GoogleMobileProvider, { override: true });

  TransKit.builtinProviders = {
    GoogleProvider: GoogleProvider,
    BingProvider: BingProvider,
    TencentAIProvider: TencentAIProvider,
    DeepLProvider: DeepLProvider,
    CaiyunProvider: CaiyunProvider,
    YoudaoMobileProvider: YoudaoMobileProvider,
    GoogleMobileProvider: GoogleMobileProvider
  };

  /* ============================================================================
   * 17. 宿主环境导出
   * ============================================================================ */

  global.TransKit = TransKit;

})(
  typeof globalThis !== "undefined"
    ? globalThis
    : typeof unsafeWindow !== "undefined"
    ? unsafeWindow
    : window
);