From 3c5ffc045fdbbee6ffb07d159e849e5e0e5469ee Mon Sep 17 00:00:00 2001 From: Gabe Date: Wed, 1 Oct 2025 16:18:19 +0800 Subject: [PATCH] feat: support AI terms --- src/apis/index.js | 3 +++ src/apis/trans.js | 23 ++++++++++++++-- src/config/i18n.js | 15 +++++++---- src/config/rules.js | 2 ++ src/libs/rules.js | 3 +++ src/libs/translator.js | 21 +++++++++++++++ src/views/Options/Apis.js | 43 ----------------------------- src/views/Options/Rules.js | 55 +++++++++++++++++++++----------------- 8 files changed, 91 insertions(+), 74 deletions(-) diff --git a/src/apis/index.js b/src/apis/index.js index aef4664..3e677d1 100644 --- a/src/apis/index.js +++ b/src/apis/index.js @@ -217,6 +217,7 @@ export const apiTranslate = async ({ toLang, apiSetting = DEFAULT_API_SETTING, docInfo = {}, + glossary = {}, useCache = true, usePool = true, }) => { @@ -265,6 +266,7 @@ export const apiTranslate = async ({ toLang, langMap, docInfo, + glossary, apiSetting, usePool, batchInterval, @@ -285,6 +287,7 @@ export const apiTranslate = async ({ toLang, langMap, docInfo, + glossary, apiSetting, usePool, }); diff --git a/src/apis/trans.js b/src/apis/trans.js index 76526fb..341dc79 100644 --- a/src/apis/trans.js +++ b/src/apis/trans.js @@ -573,6 +573,7 @@ export const genTransReq = async ({ reqHook, resHook, ...args }) => { to, texts, docInfo, + glossary, customHeader, customBody, } = args; @@ -587,7 +588,14 @@ export const genTransReq = async ({ reqHook, resHook, ...args }) => { if (API_SPE_TYPES.ai.has(apiType)) { args.systemPrompt = genSystemPrompt({ systemPrompt, from, to }); - args.userPrompt = genUserPrompt({ userPrompt, from, to, texts, docInfo }); + args.userPrompt = genUserPrompt({ + userPrompt, + from, + to, + texts, + docInfo, + glossary, + }); } const { @@ -784,7 +792,17 @@ export const parseTransRes = async ( */ export const handleTranslate = async ( texts = [], - { from, to, fromLang, toLang, langMap, docInfo, apiSetting, usePool } + { + from, + to, + fromLang, + toLang, + langMap, + docInfo, + glossary, + apiSetting, + usePool, + } ) => { let history = null; let hisMsgs = []; @@ -815,6 +833,7 @@ export const handleTranslate = async ( toLang, langMap, docInfo, + glossary, hisMsgs, token, ...apiSetting, diff --git a/src/config/i18n.js b/src/config/i18n.js index 47bbac6..a406844 100644 --- a/src/config/i18n.js +++ b/src/config/i18n.js @@ -668,6 +668,16 @@ export const I18N = { en: `1. Supports regular expression matching, no slash required, and no modifiers are supported. 2. Separate multiple terms with newlines or semicolons ";". 3. Terms and translations are separated by English commas ",". 4. If there is no translation, the term will be deemed not to be translated.`, zh_TW: `1. 支援正則表達式比對,無需斜線,且不支援修飾符。2. 多條術語以換行或分號「;」分隔。3. 術語與譯文以英文逗號「,」分隔。4. 無譯文者視為不翻譯該術語。`, }, + ai_terms: { + zh: `AI专业术语`, + en: `AI Terms`, + zh_TW: `AI專業術語`, + }, + ai_terms_helper: { + zh: `1、AI智能替换,不支持正则表达式。2、多条术语用换行或分号“;”隔开。3、术语和译文用英文逗号“,”隔开。4、没有译文视为不翻译术语。`, + en: `1. AI intelligent replacement does not support regular expressions.2. Separate multiple terms with newlines or semicolons ";". 3. Terms and translations are separated by English commas ",". 4. If there is no translation, the term will be deemed not to be translated.`, + zh_TW: `1.AI智能替換,不支援正規表示式。2. 多條術語以換行或分號「;」分隔。3. 術語與譯文以英文逗號「,」分隔。4. 無譯文者視為不翻譯該術語。`, + }, selector_style: { zh: `选择器节点样式`, en: `Selector Style`, @@ -1453,11 +1463,6 @@ export const I18N = { en: `Placeholder tag name`, zh_TW: `佔位標名`, }, - ai_terms: { - zh: `AI识别术语表`, - en: `AI Identification Glossary`, - zh_TW: `AI辨識術語表`, - }, system_prompt_helper: { zh: `在未完全理解默认Prompt的情况下,请勿随意修改,否则可能翻译失败。`, en: `If you do not fully understand the default prompt, please do not modify it at will, otherwise the translation may fail.`, diff --git a/src/config/rules.js b/src/config/rules.js index 82cae71..8b2f0e1 100644 --- a/src/config/rules.js +++ b/src/config/rules.js @@ -80,6 +80,7 @@ export const DEFAULT_RULE = { selector: "", // 选择器 keepSelector: "", // 保留元素选择器 terms: "", // 专业术语 + aiTerms: "", // AI专业术语 apiSlug: GLOBAL_KEY, // 翻译服务 fromLang: GLOBAL_KEY, // 源语言 toLang: GLOBAL_KEY, // 目标语言 @@ -116,6 +117,7 @@ export const GLOBLA_RULE = { selector: DEFAULT_SELECTOR, // 选择器 keepSelector: DEFAULT_KEEP_SELECTOR, // 保留元素选择器 terms: "", // 专业术语 + aiTerms: "", // AI专业术语 apiSlug: OPT_TRANS_MICROSOFT, // 翻译服务 fromLang: "auto", // 源语言 toLang: "zh-CN", // 目标语言 diff --git a/src/libs/rules.js b/src/libs/rules.js index a334564..6b937fb 100644 --- a/src/libs/rules.js +++ b/src/libs/rules.js @@ -52,6 +52,7 @@ export const matchRule = async (href, { injectRules, subrulesList }) => { "rootsSelector", "ignoreSelector", "terms", + "aiTerms", "selectStyle", "parentStyle", "injectJs", @@ -134,6 +135,7 @@ export const checkRules = (rules) => { rootsSelector, ignoreSelector, terms, + aiTerms, selectStyle, parentStyle, injectJs, @@ -166,6 +168,7 @@ export const checkRules = (rules) => { rootsSelector: type(rootsSelector) === "string" ? rootsSelector : "", ignoreSelector: type(ignoreSelector) === "string" ? ignoreSelector : "", terms: type(terms) === "string" ? terms : "", + aiTerms: type(aiTerms) === "string" ? aiTerms : "", selectStyle: type(selectStyle) === "string" ? selectStyle : "", parentStyle: type(parentStyle) === "string" ? parentStyle : "", injectJs: type(injectJs) === "string" ? injectJs : "", diff --git a/src/libs/translator.js b/src/libs/translator.js index a87d552..e0eec53 100644 --- a/src/libs/translator.js +++ b/src/libs/translator.js @@ -271,6 +271,7 @@ export class Translator { #translationTagName = APP_NAME; // 翻译容器的标签名 #eventName = ""; // 通信事件名称 #docInfo = {}; // 网页信息 + #glossary = {}; // AI词典 #textClass = {}; // 译文样式class #textSheet = ""; // 译文样式字典 #apiSetting = null; @@ -334,6 +335,7 @@ export class Translator { ); this.#placeholderRegex = this.#createPlaceholderRegex(); this.#parseTerms(this.#rule.terms); + this.#parseAITerms(this.#rule.aiTerms); this.#createTextStyles(); this.#boundMouseMoveHandler = this.#handleMouseMove.bind(this); @@ -512,6 +514,24 @@ export class Translator { } } + #parseAITerms(termsString) { + if (!termsString || typeof termsString !== "string") return; + + try { + this.#glossary = Object.fromEntries( + termsString + .split(/\n|;/) + .map((line) => { + const [k = "", v = ""] = line.split(",").map((s) => s.trim()); + return [k, v]; + }) + .filter(([k]) => k) + ); + } catch (err) { + kissLog("parse aiterms", err); + } + } + // todo: 利用AI总结 #getDocDescription() { try { @@ -1157,6 +1177,7 @@ export class Translator { toLang, apiSetting: this.#apiSetting, docInfo: this.#docInfo, + glossary: this.#glossary, }); } diff --git a/src/views/Options/Apis.js b/src/views/Options/Apis.js index e4540ab..0c08e2c 100644 --- a/src/views/Options/Apis.js +++ b/src/views/Options/Apis.js @@ -352,49 +352,6 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) { multiline maxRows={10} /> */} - - {/* - - - - - - - - - - {i18n("enable")} - {i18n("disable")} - - - - */} )} diff --git a/src/views/Options/Rules.js b/src/views/Options/Rules.js index cee4ff6..110c632 100644 --- a/src/views/Options/Rules.js +++ b/src/views/Options/Rules.js @@ -95,6 +95,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) { rootsSelector = "", ignoreSelector = "", terms = "", + aiTerms = "", selectStyle = "", parentStyle = "", injectJs = "", @@ -443,30 +444,6 @@ function RuleFields({ rule, rules, setShow, setKeyword }) { ))} - {/* - - {GlobalItem} - {OPT_TIMING_ALL.map((item) => ( - - {i18n(item)} - - ))} - - */} - - - - - + {/* + + {GlobalItem} + {OPT_TIMING_ALL.map((item) => ( + + {i18n(item)} + + ))} + + */} @@ -527,6 +523,17 @@ function RuleFields({ rule, rules, setShow, setKeyword }) { multiline maxRows={10} /> +