diff --git a/package.json b/package.json
index 317482f..4aaa22e 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"react-markdown": "^8.0.7",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
+ "sval": "^0.5.2",
"webdav": "^5.3.0",
"webextension-polyfill": "^0.10.0"
},
@@ -31,7 +32,7 @@
"build:userscript": "rm -rf build/userscript && mkdir build/userscript && cp build/web/*.user.js build/userscript/",
"build:rules": "babel-node src/rules.js",
"build": "pnpm build:chrome && pnpm build:edge && pnpm build:firefox && pnpm build:web && pnpm build:userscript-ios && pnpm build:userscript && pnpm build:rules",
- "pack": "cd build && zip -r chrome.zip chrome && zip -r edge.zip edge && cd firefox && zip -r ../firefox.zip .",
+ "zip": "cd build && zip -r chrome.zip chrome && zip -r edge.zip edge && cd firefox && zip -r ../firefox.zip .",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0926f89..360e4a0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -41,6 +41,9 @@ dependencies:
react-scripts:
specifier: 5.0.1
version: 5.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(react@18.2.0)(typescript@5.4.5)
+ sval:
+ specifier: ^0.5.2
+ version: 0.5.2
webdav:
specifier: ^5.3.0
version: 5.3.0
@@ -6504,7 +6507,7 @@ packages:
optional: true
dependencies:
abab: 2.0.6
- acorn: 8.10.0
+ acorn: 8.11.3
acorn-globals: 6.0.0
cssom: 0.4.4
cssstyle: 2.3.0
@@ -9348,6 +9351,12 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ /sval@0.5.2:
+ resolution: {integrity: sha512-cMN4SWqQ8K2DypYVZ1DVsicvXsr4gQmAYR2faKwHttJFJAqjfc4+taG9esMIP0hMP5+4Caun99n6y+4T6nCPEA==}
+ dependencies:
+ acorn: 8.11.3
+ dev: false
+
/svg-parser@2.0.4:
resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==}
diff --git a/src/apis/index.js b/src/apis/index.js
index e41baf8..c0b842f 100644
--- a/src/apis/index.js
+++ b/src/apis/index.js
@@ -33,6 +33,7 @@ import {
OPT_LANGS_SPECIAL,
} from "../config";
import { sha256 } from "../libs/utils";
+import interpreter from "../libs/interpreter";
/**
* 同步数据
@@ -275,27 +276,14 @@ export const apiTranslate = async ({
case OPT_TRANS_CUSTOMIZE_3:
case OPT_TRANS_CUSTOMIZE_4:
case OPT_TRANS_CUSTOMIZE_5:
- trText = res.text;
- isSame = to === res.from;
-
- const { customOption } = apiSetting;
- if (customOption?.trim()) {
- try {
- const opt = JSON.parse(customOption);
- const textPattern = opt.resPattern?.text;
- const fromPattern = opt.resPattern?.from;
- if (textPattern) {
- trText = textPattern.split(".").reduce((pre, cur) => pre[cur], res);
- }
- if (fromPattern) {
- isSame =
- to === fromPattern.split(".").reduce((pre, cur) => pre[cur], res);
- }
- } catch (err) {
- throw new Error(`custom option parse err: ${err}`);
- }
+ const { resHook } = apiSetting;
+ if (resHook?.trim()) {
+ interpreter.run(`exports.resHook = ${resHook}`);
+ [trText, isSame] = interpreter.exports.resHook(res, text, from, to);
+ } else {
+ trText = res.text;
+ isSame = to === res.from;
}
-
break;
default:
}
diff --git a/src/apis/trans.js b/src/apis/trans.js
index 331983e..b332e4b 100644
--- a/src/apis/trans.js
+++ b/src/apis/trans.js
@@ -32,6 +32,7 @@ import {
import { msAuth } from "../libs/auth";
import { genDeeplFree } from "./deepl";
import { genBaidu } from "./baidu";
+import interpreter from "../libs/interpreter";
const keyMap = new Map();
const urlMap = new Map();
@@ -293,20 +294,27 @@ const genCloudflareAI = ({ text, from, to, url, key }) => {
return [url, init];
};
-const genCustom = ({ text, from, to, url, key, customOption }) => {
- const replaceInput = (str) =>
- str
- .replaceAll(INPUT_PLACE_URL, url)
- .replaceAll(INPUT_PLACE_FROM, from)
- .replaceAll(INPUT_PLACE_TO, to)
- .replaceAll(INPUT_PLACE_TEXT, text.replaceAll(`"`, `\n`))
- .replaceAll(INPUT_PLACE_KEY, key);
+const genCustom = ({ text, from, to, url, key, reqHook }) => {
+ url = url
+ .replaceAll(INPUT_PLACE_URL, url)
+ .replaceAll(INPUT_PLACE_FROM, from)
+ .replaceAll(INPUT_PLACE_TO, to)
+ .replaceAll(INPUT_PLACE_TEXT, text)
+ .replaceAll(INPUT_PLACE_KEY, key);
+ let init = {};
+
+ if (reqHook?.trim()) {
+ interpreter.run(`exports.reqHook = ${reqHook}`);
+ [url, init] = interpreter.exports.reqHook(text, from, to, url, key);
+ return [url, init];
+ }
+
const data = {
text,
from,
to,
};
- const init = {
+ init = {
headers: {
"Content-type": "application/json",
},
@@ -316,23 +324,6 @@ const genCustom = ({ text, from, to, url, key, customOption }) => {
if (key) {
init.headers.Authorization = `Bearer ${key}`;
}
- url = replaceInput(url);
-
- if (customOption?.trim()) {
- try {
- const opt = JSON.parse(replaceInput(customOption));
- opt.url && (url = opt.url);
- opt.headers && (init.headers = opt.headers);
- opt.method && (init.method = opt.method);
- if (init.method === "GET") {
- delete init.body;
- } else {
- opt.body && (init.body = JSON.stringify(opt.body));
- }
- } catch (err) {
- throw new Error(`custom option parse err: ${err}`);
- }
- }
return [url, init];
};
diff --git a/src/config/i18n.js b/src/config/i18n.js
index b74dfbe..d4dc2d5 100644
--- a/src/config/i18n.js
+++ b/src/config/i18n.js
@@ -42,7 +42,23 @@ const customApiLangs = `["en", "English - English"],
["vi", "Vietnamese - Tiếng Việt"],
`;
-const customDefaultOption = `{
+const hookExample = `// URL
+https://translate.googleapis.com/translate_a/single?client=gtx&dj=1&dt=t&ie=UTF-8&q={{text}}&sl=en&tl=zh-CN
+
+// Request Hook
+(text, from, to, url, key) => [url, {
+ headers: {
+ "Content-type": "application/json",
+ },
+ method: "GET",
+ body: null,
+}]
+
+// Response Hook
+(res, text, from, to) => [res.sentences.map((item) => item.trans).join(" "), to === res.src]`;
+
+const customApiHelpZH = `// 请求数据默认格式
+{
"url": "{{url}}",
"method": "POST",
"headers": {
@@ -50,18 +66,12 @@ const customDefaultOption = `{
"Authorization": "Bearer {{key}}"
},
"body": {
- "text": "{{text}}",
- "from": "{{from}}",
- "to": "{{to}}"
+ "text": "{{text}}", // 待翻译文字
+ "from": "{{from}}", // 文字的语言(可能为空)
+ "to": "{{to}}", // 目标语言
},
- "resPattern": {
- "text": "text",
- "from": "from"
- }
-}`;
+}
-const customApiHelpZH = `// 自定义选项范例
-${customDefaultOption}
// 返回数据默认格式
{
@@ -70,20 +80,43 @@ ${customDefaultOption}
to: "", // 目标语言(可选)
}
+
+// Hook 范例
+${hookExample}
+
+
// 支持的语言代码如下
${customApiLangs}
`;
-const customApiHelpEN = `// Example of custom options
-${customDefaultOption}
+const customApiHelpEN = `// Default request
+{
+ "url": "{{url}}",
+ "method": "POST",
+ "headers": {
+ "Content-type": "application/json",
+ "Authorization": "Bearer {{key}}"
+ },
+ "body": {
+ "text": "{{text}}", // Text to be translated
+ "from": "{{from}}", // The language of the text (may be empty)
+ "to": "{{to}}", // Target language
+ },
+}
-// Return data default format
+
+// Default response
{
text: "", // translated text
from: "", // Recognized source language
to: "", // Target language (optional)
}
+
+/// Hook Example
+${hookExample}
+
+
// The supported language codes are as follows
${customApiLangs}
`;
diff --git a/src/config/index.js b/src/config/index.js
index a18dea7..02d7388 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -485,7 +485,9 @@ export const DEFAULT_SUBRULES_LIST = [
const defaultCustomApi = {
url: "",
key: "",
- customOption: "",
+ customOption: "", // (作废)
+ reqHook: "", // request 钩子函数
+ resHook: "", // response 钩子函数
fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL,
};
diff --git a/src/libs/interpreter.js b/src/libs/interpreter.js
new file mode 100644
index 0000000..d6b90a8
--- /dev/null
+++ b/src/libs/interpreter.js
@@ -0,0 +1,16 @@
+import Sval from "sval";
+
+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
+ // or "latest"
+ ecmaVer: "latest",
+ // Code source type
+ // "script" or "module"
+ sourceType: "script",
+ // Whether the code runs in a sandbox
+ sandBox: true,
+});
+
+export default interpreter;
diff --git a/src/views/Options/Apis.js b/src/views/Options/Apis.js
index 385db2d..b762a60 100644
--- a/src/views/Options/Apis.js
+++ b/src/views/Options/Apis.js
@@ -119,7 +119,8 @@ function ApiFields({ translator }) {
fetchInterval = DEFAULT_FETCH_INTERVAL,
dictNo = "",
memoryNo = "",
- customOption = "",
+ reqHook = "",
+ resHook = "",
} = api;
const handleChange = (e) => {
@@ -244,15 +245,26 @@ function ApiFields({ translator }) {
)}
{translator.startsWith(OPT_TRANS_CUSTOMIZE) && (
-
+ <>
+
+
+ >
)}