TransKit

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

이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/591255/1902101/TransKit.js을(를) 사용하여 포함하는 라이브러리입니다.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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
);