diff --git a/src/apis/index.js b/src/apis/index.js index fdcf209..2cb301e 100644 --- a/src/apis/index.js +++ b/src/apis/index.js @@ -14,9 +14,9 @@ import { MSG_BUILTINAI_TRANSLATE, OPT_TRANS_BUILTINAI, URL_CACHE_SUBTITLE, + OPT_LANGS_TO_CODE, } from "../config"; import { sha256, withTimeout } from "../libs/utils"; -import { kissLog } from "../libs/log"; import { handleTranslate, handleSubtitle, @@ -424,7 +424,7 @@ export const apiTranslate = async ({ usePool = true, }) => { if (!text) { - return ["", false]; + throw new Error("The text cannot be empty."); } const { apiType, apiSlug, useBatchFetch } = apiSetting; @@ -432,8 +432,7 @@ export const apiTranslate = async ({ const from = langMap.get(fromLang); const to = langMap.get(toLang); if (!to) { - kissLog(`target lang: ${toLang} not support`); - return ["", false]; + throw new Error(`The target lang: ${toLang} not support`); } // todo: 优化缓存失效因素 @@ -451,7 +450,7 @@ export const apiTranslate = async ({ if (useCache) { const cache = await getHttpCachePolyfill(cacheInput); if (cache?.trText) { - return [cache.trText, cache.isSame]; + return cache; } } @@ -499,8 +498,12 @@ export const apiTranslate = async ({ let trText = ""; let srLang = ""; + let srCode = ""; if (Array.isArray(tranlation)) { [trText, srLang = ""] = tranlation; + if (srLang) { + srCode = OPT_LANGS_TO_CODE[apiType].get(srLang) || ""; + } } else if (typeof tranlation === "string") { trText = tranlation; } @@ -513,10 +516,10 @@ export const apiTranslate = async ({ // 插入缓存 if (useCache) { - putHttpCachePolyfill(cacheInput, null, { trText, isSame, srLang }); + putHttpCachePolyfill(cacheInput, null, { trText, isSame, srLang, srCode }); } - return [trText, isSame]; + return { trText, srLang, srCode, isSame }; }; // 字幕处理/翻译 diff --git a/src/apis/trans.js b/src/apis/trans.js index cbb6297..a989881 100644 --- a/src/apis/trans.js +++ b/src/apis/trans.js @@ -32,7 +32,7 @@ import { import { msAuth } from "../libs/auth"; import { genDeeplFree } from "./deepl"; import { genBaidu } from "./baidu"; -import interpreter from "../libs/interpreter"; +import { interpreter } from "../libs/interpreter"; import { parseJsonObj, extractJson } from "../libs/utils"; import { kissLog } from "../libs/log"; import { fetchData } from "../libs/fetch"; diff --git a/src/background.js b/src/background.js index 0d4ee60..757d2ed 100644 --- a/src/background.js +++ b/src/background.js @@ -33,7 +33,7 @@ import { sendTabMsg } from "./libs/msg"; import { trySyncAllSubRules } from "./libs/subRules"; import { saveRule } from "./libs/rules"; import { getCurTabId } from "./libs/msg"; -import { injectInlineJs, injectInternalCss } from "./libs/injector"; +import { injectInlineJsBg, injectInternalCss } from "./libs/injector"; import { kissLog, logger } from "./libs/log"; import { chromeDetect, chromeTranslate } from "./libs/builtinAI"; @@ -268,7 +268,7 @@ const messageHandlers = { [MSG_PUT_HTTPCACHE]: (args) => putHttpCache(args), [MSG_OPEN_OPTIONS]: () => browser.runtime.openOptionsPage(), [MSG_SAVE_RULE]: (args) => saveRule(args), - [MSG_INJECT_JS]: (args) => injectToCurrentTab(injectInlineJs, args), + [MSG_INJECT_JS]: (args) => injectToCurrentTab(injectInlineJsBg, args), [MSG_INJECT_CSS]: (args) => injectToCurrentTab(injectInternalCss, args), [MSG_UPDATE_CSP]: (args) => updateCspRules(args), [MSG_CONTEXT_MENUS]: (args) => addContextMenus(args), diff --git a/src/config/api.js b/src/config/api.js index 1656fcf..91fa130 100644 --- a/src/config/api.js +++ b/src/config/api.js @@ -46,7 +46,7 @@ export const OPT_TRANS_OPENROUTER = "OpenRouter"; export const OPT_TRANS_CUSTOMIZE = "Custom"; // 内置支持的翻译引擎 -export const OPT_ALL_TYPES = [ +export const OPT_ALL_TRANS_TYPES = [ OPT_TRANS_BUILTINAI, OPT_TRANS_GOOGLE, OPT_TRANS_GOOGLE_2, @@ -82,7 +82,7 @@ export const OPT_LANGDETECTOR_MAP = new Set(OPT_LANGDETECTOR_ALL); // 翻译引擎特殊集合 export const API_SPE_TYPES = { // 内置翻译 - builtin: new Set(OPT_ALL_TYPES), + builtin: new Set(OPT_ALL_TRANS_TYPES), // 机器翻译 machine: new Set([ OPT_TRANS_MICROSOFT, @@ -557,7 +557,7 @@ const defaultApiOpts = { }; // 内置翻译接口列表(带参数) -export const DEFAULT_API_LIST = OPT_ALL_TYPES.map((apiType) => ({ +export const DEFAULT_API_LIST = OPT_ALL_TRANS_TYPES.map((apiType) => ({ ...defaultApiOpts[apiType], apiSlug: apiType, apiName: apiType, @@ -565,4 +565,6 @@ export const DEFAULT_API_LIST = OPT_ALL_TYPES.map((apiType) => ({ })); export const DEFAULT_API_TYPE = OPT_TRANS_MICROSOFT; -export const DEFAULT_API_SETTING = DEFAULT_API_LIST[DEFAULT_API_TYPE]; +export const DEFAULT_API_SETTING = DEFAULT_API_LIST.find( + (a) => a.apiType === DEFAULT_API_TYPE +); diff --git a/src/config/i18n.js b/src/config/i18n.js index 8e31a11..2ea0b9d 100644 --- a/src/config/i18n.js +++ b/src/config/i18n.js @@ -745,9 +745,33 @@ export const I18N = { zh_TW: `注入 JS`, }, inject_js_helper: { - zh: `初始化时注入运行,一个页面仅运行一次。`, - en: `Injected and run at initialization, and only run once per page.`, - zh_TW: `初始化時注入運行,一個頁面僅運行一次。`, + zh: `预加载时注入,一个页面仅运行一次。内置全局对象 KT: { + apiTranslate, + apiDectect, + apiSetting, + apisMap, + toLang, + docInfo, + glossary, + }`, + en: `Injected during preload, runs only once per page. Built-in global object KT: { + apiTranslate, + apiDectect, + apiSetting, + apisMap, + toLang, + docInfo, + glossary, + }`, + zh_TW: `預先載入時注入,一個頁面僅運行一次。內建全域物件 KT: { + apiTranslate, + apiDectect, + apiSetting, + apisMap, + toLang, + docInfo, + glossary, + }`, }, inject_css: { zh: `注入CSS`, @@ -1360,9 +1384,24 @@ export const I18N = { zh_TW: `翻譯開始 Hook`, }, translate_start_hook_helper: { - zh: `翻译前时运行,入参为: ({hostNode, parentNode, nodes})`, - en: `Run before translation, input parameters are: ({hostNode, parentNode, nodes})`, - zh_TW: `翻譯前時運行,入參為: ({hostNode, parentNode, nodes})`, + zh: `翻译前时运行,入参为: {text, + fromLang, + toLang, + apiSetting, + docInfo, + glossary,}`, + en: `Run before translation, input parameters are: {text, + fromLang, + toLang, + apiSetting, + docInfo, + glossary,}`, + zh_TW: `翻譯前時運行,入參為: {text, + fromLang, + toLang, + apiSetting, + docInfo, + glossary,}`, }, translate_end_hook: { zh: `翻译完成钩子函数`, diff --git a/src/config/rules.js b/src/config/rules.js index 7853840..02fce65 100644 --- a/src/config/rules.js +++ b/src/config/rules.js @@ -117,7 +117,7 @@ export const DEFAULT_RULE = { parentStyle: "", // 选择器父节点样式 grandStyle: "", // 选择器父节点样式 injectJs: "", // 注入JS - injectCss: "", // 注入CSS + // injectCss: "", // 注入CSS (作废) transOnly: GLOBAL_KEY, // 是否仅显示译文 // transTiming: GLOBAL_KEY, // 翻译时机/鼠标悬停翻译 (暂时作废) transTag: GLOBAL_KEY, // 译文元素标签 @@ -160,7 +160,7 @@ export const GLOBLA_RULE = { parentStyle: DEFAULT_SELECT_STYLE, // 选择器父节点样式 grandStyle: DEFAULT_SELECT_STYLE, // 选择器祖节点样式 injectJs: "", // 注入JS - injectCss: "", // 注入CSS + // injectCss: "", // 注入CSS(作废) transOnly: "false", // 是否仅显示译文 // transTiming: OPT_TIMING_PAGESCROLL, // 翻译时机/鼠标悬停翻译 (暂时作废) transTag: DEFAULT_TRANS_TAG, // 译文元素标签 diff --git a/src/libs/injector.js b/src/libs/injector.js index 7a88efa..5d7b75d 100644 --- a/src/libs/injector.js +++ b/src/libs/injector.js @@ -13,6 +13,18 @@ export const injectInlineJs = (code, id = "kiss-translator-inline-js") => { (document.head || document.documentElement).appendChild(el); }; +export const injectInlineJsBg = (code, id = "kiss-translator-inline-js") => { + if (document.getElementById(id)) { + return; + } + + const el = document.createElement("script"); + el.type = "text/javascript"; + el.id = id; + el.textContent = code; + (document.head || document.documentElement).appendChild(el); +}; + // Function to inject external JavaScript file export const injectExternalJs = (src, id = "kiss-translator-external-js") => { if (document.getElementById(id)) { diff --git a/src/libs/inputTranslate.js b/src/libs/inputTranslate.js index 1489ff9..d96aadc 100644 --- a/src/libs/inputTranslate.js +++ b/src/libs/inputTranslate.js @@ -193,7 +193,7 @@ export class InputTranslator { try { addLoading(node, loadingId); - const [trText, isSame] = await apiTranslate({ + const { trText, isSame } = await apiTranslate({ text, fromLang, toLang, diff --git a/src/libs/interpreter.js b/src/libs/interpreter.js index d6b90a8..76f1e3a 100644 --- a/src/libs/interpreter.js +++ b/src/libs/interpreter.js @@ -1,6 +1,6 @@ import Sval from "sval"; -const interpreter = new Sval({ +export const interpreter = new Sval({ // ECMA Version of the code // 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 // or 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 @@ -12,5 +12,3 @@ const interpreter = new Sval({ // Whether the code runs in a sandbox sandBox: true, }); - -export default interpreter; diff --git a/src/libs/rules.js b/src/libs/rules.js index 9ccce74..7694098 100644 --- a/src/libs/rules.js +++ b/src/libs/rules.js @@ -60,7 +60,7 @@ export const matchRule = async (href, { injectRules, subrulesList }) => { "parentStyle", "grandStyle", "injectJs", - "injectCss", + // "injectCss", // "fixerSelector", "transStartHook", "transEndHook", @@ -154,7 +154,7 @@ export const checkRules = (rules) => { parentStyle, grandStyle, injectJs, - injectCss, + // injectCss, apiSlug, fromLang, toLang, @@ -193,7 +193,7 @@ export const checkRules = (rules) => { parentStyle: type(parentStyle) === "string" ? parentStyle : "", grandStyle: type(grandStyle) === "string" ? grandStyle : "", injectJs: type(injectJs) === "string" ? injectJs : "", - injectCss: type(injectCss) === "string" ? injectCss : "", + // injectCss: type(injectCss) === "string" ? injectCss : "", bgColor: type(bgColor) === "string" ? bgColor : "", textDiyStyle: type(textDiyStyle) === "string" ? textDiyStyle : "", apiSlug: diff --git a/src/libs/translator.js b/src/libs/translator.js index adebf80..d3891ce 100644 --- a/src/libs/translator.js +++ b/src/libs/translator.js @@ -2,8 +2,6 @@ import { APP_UPNAME, APP_LCNAME, APP_CONSTS, - MSG_INJECT_JS, - MSG_INJECT_CSS, OPT_STYLE_FUZZY, GLOBLA_RULE, DEFAULT_SETTING, @@ -16,13 +14,10 @@ import { OPT_SPLIT_PARAGRAPH_DISABLE, OPT_SPLIT_PARAGRAPH_TEXTLENGTH, } from "../config"; -import interpreter from "./interpreter"; +import { interpreter } from "./interpreter"; import { clearFetchPool } from "./pool"; import { debounce, scheduleIdle, genEventName, truncateWords } from "./utils"; import { apiTranslate } from "../apis"; -import { sendBgMsg } from "./msg"; -import { isExt } from "./client"; -import { injectInlineJs, injectInternalCss } from "./injector"; import { kissLog } from "./log"; import { clearAllBatchQueue } from "./batchQueue"; import { genTextClass } from "./style"; @@ -1145,7 +1140,6 @@ export class Translator { const { transTag, textStyle, - transStartHook, transEndHook, transOnly, termsStyle, @@ -1164,20 +1158,6 @@ export class Translator { const parentNode = hostNode.parentElement; const hideOrigin = transOnly === "true"; - // 翻译开始钩子函数 - if (transStartHook?.trim()) { - try { - interpreter.run(`exports.transStartHook = ${transStartHook}`); - interpreter.exports.transStartHook({ - hostNode, - parentNode, - nodes, - }); - } catch (err) { - kissLog("transStartHook", err); - } - } - try { const [processedString, placeholderMap] = this.#serializeForTranslation( nodes, @@ -1202,10 +1182,8 @@ export class Translator { nodes[nodes.length - 1].after(wrapper); const currentRunId = this.#runId; - const [translatedText, isSameLang] = await this.#translateFetch( - processedString, - deLang - ); + const { trText: translatedText, isSame: isSameLang } = + await this.#translateFetch(processedString, deLang); if (this.#runId !== currentRunId) { throw new Error("Request terminated"); } @@ -1391,16 +1369,39 @@ export class Translator { // 发起翻译请求 #translateFetch(text, deLang = "") { - const { fromLang, toLang } = this.#rule; + const { toLang, transStartHook } = this.#rule; + const fromLang = deLang || this.#rule.fromLang; + const apiSetting = { ...this.#apiSetting }; + const docInfo = { ...this.#docInfo }; + const glossary = { ...this.#glossary }; + const apisMap = this.#apisMap; - return apiTranslate({ + const args = { text, - fromLang: deLang || fromLang, + fromLang, toLang, - apiSetting: this.#apiSetting, - docInfo: this.#docInfo, - glossary: this.#glossary, - }); + apiSetting, + docInfo, + glossary, + }; + + // 翻译开始钩子函数 + if (transStartHook?.trim()) { + try { + interpreter.run(`exports.transStartHook = ${transStartHook}`); + const hookResult = interpreter.exports.transStartHook({ + ...args, + apisMap, + }); + if (hookResult) { + Object.assign(args, ...hookResult); + } + } catch (err) { + kissLog("transStartHook", err); + } + } + + return apiTranslate(args); } // 查找指定节点下所有译文节点 @@ -1596,14 +1597,35 @@ export class Translator { this.#isJsInjected = true; try { - const { injectJs, injectCss } = this.#rule; - if (isExt) { - injectJs && sendBgMsg(MSG_INJECT_JS, injectJs); - injectCss && sendBgMsg(MSG_INJECT_CSS, injectCss); - } else { - injectJs && - injectInlineJs(injectJs, "kiss-translator-userinit-injector"); - injectCss && injectInternalCss(injectCss); + // const { injectJs, injectCss } = this.#rule; + // if (isExt) { + // injectJs && sendBgMsg(MSG_INJECT_JS, injectJs); + // injectCss && sendBgMsg(MSG_INJECT_CSS, injectCss); + // } else { + // injectJs && + // injectInlineJs(injectJs, "kiss-translator-userinit-injector"); + // injectCss && injectInternalCss(injectCss); + // } + + const { injectJs, toLang } = this.#rule; + if (injectJs?.trim()) { + const apiSetting = { ...this.#apiSetting }; + const docInfo = { ...this.#docInfo }; + const glossary = { ...this.#glossary }; + const apisMap = this.#apisMap; + const apiDectect = tryDetectLang; + interpreter.import({ + KT: { + apiTranslate, + apiDectect, + apiSetting, + apisMap, + toLang, + docInfo, + glossary, + }, + }); + interpreter.run(injectJs); } } catch (err) { kissLog("inject js", err); @@ -1654,8 +1676,8 @@ export class Translator { try { const deLang = await tryDetectLang(title); - const [translatedTitle] = await this.#translateFetch(title, deLang); - document.title = translatedTitle || title; + const { trText } = await this.#translateFetch(title, deLang); + document.title = trText || title; } catch (err) { kissLog("tanslate title", err); } diff --git a/src/views/Options/Apis.js b/src/views/Options/Apis.js index 4d0cd66..8d50e15 100644 --- a/src/views/Options/Apis.js +++ b/src/views/Options/Apis.js @@ -38,7 +38,7 @@ import { DEFAULT_BATCH_SIZE, DEFAULT_BATCH_LENGTH, DEFAULT_CONTEXT_SIZE, - OPT_ALL_TYPES, + OPT_ALL_TRANS_TYPES, API_SPE_TYPES, BUILTIN_STONES, BUILTIN_PLACEHOLDERS, @@ -54,7 +54,7 @@ function TestButton({ api }) { const handleApiTest = async () => { try { setLoading(true); - const [text] = await apiTranslate({ + const { trText } = await apiTranslate({ text: "hello world", fromLang: "en", toLang: "zh-CN", @@ -62,7 +62,7 @@ function TestButton({ api }) { useCache: false, usePool: false, }); - if (!text) { + if (!trText) { throw new Error("empty result"); } alert.success(i18n("test_success")); @@ -775,7 +775,7 @@ export default function Apis() { const apiTypes = useMemo( () => - OPT_ALL_TYPES.map((type) => ({ + OPT_ALL_TRANS_TYPES.map((type) => ({ type, label: type, })), diff --git a/src/views/Options/Rules.js b/src/views/Options/Rules.js index 12a905b..42380d2 100644 --- a/src/views/Options/Rules.js +++ b/src/views/Options/Rules.js @@ -108,7 +108,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) { parentStyle = "", grandStyle = "", injectJs = "", - injectCss = "", + // injectCss = "", apiSlug, fromLang, toLang, @@ -695,7 +695,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) { maxRows={10} /> */} - + /> */}