Compare commits

...

91 Commits

Author SHA1 Message Date
Gabe
2a5e9db079 v1.9.1 2025-07-23 21:03:02 +08:00
Gabe
650d6e8b41 fix: workflows 2025-07-23 20:58:50 +08:00
Gabe
1daf134b31 fix: gemini api 2025-07-23 20:03:54 +08:00
Gabe
e1dfa35c6c v1.9.0 2025-07-23 00:38:33 +08:00
Gabe
73f80692d3 feat: add format script (prettier) 2025-07-03 19:08:33 +08:00
Gabe
42c7dae495 fix: log 2025-07-03 18:08:49 +08:00
Gabe
b2a1309caa feat: add gemini2 api 2025-07-02 21:54:18 +08:00
Gabe
94bf5f9580 fix: gemini api 2025-07-02 16:37:57 +08:00
Gabe
704ebdc9d7 fix: variable name 2025-07-02 13:38:30 +08:00
Gabe
165da4e559 fix: rule page: show more button 2025-07-01 23:34:16 +08:00
Gabe
d3e3b484bf fix: remove excess code 2025-07-01 22:47:04 +08:00
Gabe
192f8faa5b feat: support keypick for customize API 2025-07-01 22:42:57 +08:00
Gabe
579d5cb0a3 fix: update userscript 2025-07-01 17:55:57 +08:00
Gabe
07fca5b9af fix: #209 2025-07-01 17:03:52 +08:00
Gabe
866a63ab6c feat: move selected translation switch from setting to rule 2025-07-01 16:44:46 +08:00
Gabe
97b4935bc4 feat: api fetch timeout 2025-07-01 12:38:06 +08:00
Gabe
30129abef3 feat: custom API name 2025-07-01 10:54:30 +08:00
Gabe
24d904b32c feat: support volcengine api 2025-06-30 21:34:37 +08:00
Gabe
5f0ce57ead feat: qq transmart 2025-06-27 20:03:58 +08:00
Gabe
51f58d095a feat: add new google translate api (issue: 225, by: Bush2021) 2025-06-27 19:29:00 +08:00
Gabe
d22e3838c4 refactor: deepModels -> tinkIgnore 2025-06-27 16:33:30 +08:00
Gabe
adbb421b7b refactor: fetch timeout 2025-06-27 12:31:32 +08:00
Gabe
eaa47af269 fix: revert old google translate api 2025-06-26 11:13:51 +08:00
Gabe
a6cb5544f8 fix: browser.menus -> browser.contextMenus 2025-06-26 10:01:43 +08:00
Gabe
9e91faa660 Merge pull request #240 from unclemcz/dev
feat:ollama接口设置新增是否禁用深度思考参数
2025-06-25 20:42:31 +08:00
mcz
8636fadc72 接口设置ollama新增是否禁用深度思考参数 2025-06-03 23:07:10 +08:00
Gabe
0621957592 chore: thunderbird 2025-05-18 00:34:20 +08:00
Gabe
8ec06b0c84 chore: defined messenger in package.json 2025-05-18 00:29:06 +08:00
Gabe
d47f8d7ee9 Merge remote-tracking branch 'origin/dev' into dev 2025-05-17 23:22:09 +08:00
Gabe
24f8959525 fix: Ignore html comment elements 2025-05-17 23:19:38 +08:00
Gabe
983740578b Merge pull request #235 from unclemcz/dev
feat:基本设置增加请求超时参数&ollama接口配置增加<think>块忽略参数
2025-05-09 14:09:18 +08:00
Gabe
b5f79ed7cd Merge pull request #232 from Bush2021/fix-workflow
build: fix build errors caused by deprecated actions
2025-05-09 14:08:47 +08:00
Gabe
bbb0e79d4e Merge pull request #231 from Bush2021/fix-google-translate
fix: update API for Google Translate
2025-05-09 14:08:16 +08:00
mcz
471dc05897 ollama接口设置增加<think>块忽略参数 2025-05-01 23:41:08 +08:00
mcz
7a772d2459 在基本设置页面增加接口请求超时时间设置 2025-05-01 20:04:58 +08:00
mcz
1d92421960 Remove the <think></think> tags in qwen3 too. 2025-04-29 19:49:14 +08:00
Bush2021
aeaaf429d7 build: fix build errors caused by deprecated actions 2025-04-16 18:45:41 -04:00
Bush2021
84432e98ae fix: update API for Google Translate 2025-04-16 18:00:54 -04:00
Gabe
77c6102de7 Merge pull request #219 from unclemcz/dev
fix:(Ollama)Remove the <think></think> tags in deepseek-r1.
2025-03-12 17:18:26 +08:00
mcz
ab5dd82169 When using the deepseek-r1 model in Ollama, remove the content between the <think></think> tags. 2025-02-23 11:54:48 +08:00
Gabe
23e7b69dc5 Merge pull request #213 from htyxyt/master
采用更好的方式支持thunderbird
2025-02-19 18:09:01 +08:00
htyxyt
3dc8f393f2 Update package.json 2025-02-18 12:56:29 +08:00
htyxyt
8a2144f263 Update background.js 2025-02-18 12:44:06 +08:00
htyxyt
c1c59caa10 Update package.json 2025-02-18 12:29:43 +08:00
htyxyt
d27ebd90b6 Update package.json 2025-02-18 12:14:02 +08:00
htyxyt
467745c1e9 Update package.json 2025-02-18 11:58:39 +08:00
htyxyt
537378a038 Update background.js 2025-02-18 11:45:57 +08:00
htyxyt
4b5ed30e5b Delete src/background.thunderbird.js 2025-02-18 11:42:57 +08:00
htyxyt
52b7f6a225 Update background.js 2025-02-18 11:42:47 +08:00
htyxyt
f31675d8a2 Update index.js 2025-02-18 11:42:05 +08:00
htyxyt
dd46a8450c Update config-overrides.js 2025-02-18 11:41:28 +08:00
htyxyt
b0843f7d66 Update package.json 2025-02-18 11:39:58 +08:00
htyxyt
daadc0195c Update background.thunderbird.js 2025-02-18 10:25:12 +08:00
Gabe
298dec6957 Merge pull request #210 from htyxyt/master
添加对Thunderbird的支持
2025-02-17 23:48:40 +08:00
Gabe
bf39d85dfa Merge pull request #212 from qonmnop/patch-1
允许用户自定义 hooks 中重写signal 参数,自定义超时时间
2025-02-17 23:30:19 +08:00
htyxyt
30a9de25a8 Update config-overrides.js 2025-02-17 14:01:17 +08:00
htyxyt
af1ecf0bd4 Update package.json 2025-02-17 14:00:35 +08:00
qonmnop
fe55a2cd3c 允许用户自定义 hooks 中重写signal 参数,自定义超时时间 2025-02-17 12:14:10 +08:00
htyxyt
5a33d4e57e Add files via upload 2025-02-17 11:57:40 +08:00
htyxyt
7f46a9023c Delete public/background.thunderbird.js 2025-02-17 11:57:12 +08:00
htyxyt
dfd943b621 Rename manifest.thunderfird.json to manifest.thunderbird.json 2025-02-17 11:10:29 +08:00
htyxyt
7007d0d922 Update package.json 2025-02-17 10:39:08 +08:00
htyxyt
601678500d Update package.json 2025-02-14 15:23:03 +08:00
htyxyt
9bfb504381 Add files via upload 2025-02-14 15:13:40 +08:00
htyxyt
8a03b0cf15 Delete src/background.thunderfird.js 2025-02-14 15:12:57 +08:00
htyxyt
11ba89de0a Update manifest.thunderfird.json 2025-02-14 14:57:38 +08:00
htyxyt
bac7f62eea Update background.thunderfird.js 2025-02-14 14:56:58 +08:00
htyxyt
eef90ea02b Delete src/popup.thunderfird.js 2025-02-14 14:56:10 +08:00
htyxyt
0650df534a Delete src/content.thunderfird.js 2025-02-14 14:56:00 +08:00
htyxyt
9ef8c8b823 Delete src/options.thunderfird.js 2025-02-14 14:55:49 +08:00
htyxyt
d7e08da0b2 Add files via upload 2025-02-13 14:53:06 +08:00
htyxyt
ef361e0798 Add files via upload 2025-02-13 14:50:33 +08:00
Gabe
6855332092 fix: modify systemPrompt & userPrompt 2024-11-30 00:41:29 +08:00
Gabe
121d523e02 Merge remote-tracking branch 'origin/dev' into dev 2024-11-30 00:09:48 +08:00
Gabe
42a375c4c7 Merge pull request #188 from unclemcz/dev
给ollama增加system message
2024-11-30 00:07:14 +08:00
Gabe
a1dd705d97 fix: update pnpm-lock file 2024-11-29 21:14:30 +08:00
mcz
71f90b36ca 给ollama增加system message 2024-09-30 16:41:58 +08:00
Gabe
37facdc3c1 Merge pull request #186 from hoilc/dev
feat: enhance openai prompt
2024-09-26 23:38:34 +08:00
hoilc
66b4f547ff feat: enhance openai prompt 2024-09-25 14:03:12 +08:00
Gabe
d27b9c7f2d Merge pull request #185 from hoilc/dev
feat: support claude api
2024-09-24 23:30:00 +08:00
hoilc
278ff9c6bc feat: support claude api 2024-09-23 18:22:19 +08:00
Gabe Yuan
d6fe1ce9d7 fix: try detect language only when fromLang is auto 2024-05-30 21:05:05 +08:00
Gabe Yuan
0bfa5256b8 fix: simplify keepSelector logic 2024-05-30 17:18:39 +08:00
Gabe Yuan
72ccfc8aec v1.8.11 2024-05-23 20:06:48 +08:00
Gabe Yuan
d117c5dc10 feat: baidu dict can be disabled 2024-05-23 00:08:10 +08:00
Gabe Yuan
9312783f44 feat: lang detector can be selected 2024-05-22 23:33:30 +08:00
Gabe Yuan
e5b16ebfd3 Merge remote-tracking branch 'origin/master' into dev 2024-05-22 10:19:04 +08:00
Gabe Yuan
5d1d65c2d3 feat: the temperature and maxTokens of the openai can be configured 2024-05-21 23:15:46 +08:00
Gabe
9ca1309cec Merge pull request #126 from kebyn/master
fix: option translation
2024-05-21 20:02:53 +08:00
kebyn
a03afc05f5 fix: option translation 2024-05-21 08:56:09 +00:00
Gabe Yuan
0198963584 fix: deeplx: replace auto to blank string 2024-05-21 11:55:17 +08:00
35 changed files with 8669 additions and 5167 deletions

2
.env
View File

@@ -2,7 +2,7 @@ GENERATE_SOURCEMAP=false
REACT_APP_NAME=KISS Translator REACT_APP_NAME=KISS Translator
REACT_APP_NAME_CN=简约翻译 REACT_APP_NAME_CN=简约翻译
REACT_APP_VERSION=1.8.10 REACT_APP_VERSION=1.9.1
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator

View File

@@ -7,28 +7,28 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v4
with: with:
version: 8.7.6 version: latest
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: "18.17.0" node-version: latest
cache: "pnpm" cache: "pnpm"
- run: pnpm install - run: pnpm install
- run: pnpm build - run: pnpm build+zip
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: build-artifacts name: build-artifacts
path: build path: build
deploy-web: deploy-web:
needs: build needs: build
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: build-artifacts name: build-artifacts
path: build path: build
@@ -37,7 +37,8 @@ jobs:
with: with:
folder: build/web folder: build/web
create-release: create-release:
runs-on: ubuntu-22.04 needs: build
runs-on: ubuntu-latest
outputs: outputs:
upload_url: ${{ steps.create-release.outputs.upload_url }} upload_url: ${{ steps.create-release.outputs.upload_url }}
steps: steps:
@@ -54,18 +55,14 @@ jobs:
needs: [build, create-release] needs: [build, create-release]
strategy: strategy:
matrix: matrix:
client: ["chrome", "edge", "firefox", "userscript"] client: ["chrome", "edge", "firefox", "userscript", "thunderbird"]
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: build-artifacts name: build-artifacts
path: build path: build
- name: Zip Release
run: |
cd build
zip -r ${{ matrix.client }}.zip ${{ matrix.client }}
- uses: actions/upload-release-asset@v1 - uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules
build
public
package.json

24
.prettierrc Normal file
View File

@@ -0,0 +1,24 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"singleAttributePerLine": false,
"bracketSameLine": false,
"jsxBracketSameLine": false,
"jsxSingleQuote": false,
"printWidth": 80,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"embeddedLanguageFormatting": "auto",
"vueIndentScriptAndStyle": false,
"experimentalTernaries": false,
"parser": "babel"
}

View File

@@ -92,6 +92,7 @@ const userscriptWebpack = (config, env) => {
// @grant GM.info // @grant GM.info
// @grant unsafeWindow // @grant unsafeWindow
// @connect translate.googleapis.com // @connect translate.googleapis.com
// @connect translate-pa.googleapis.com
// @connect api-edge.cognitive.microsofttranslator.com // @connect api-edge.cognitive.microsofttranslator.com
// @connect edge.microsoft.com // @connect edge.microsoft.com
// @connect api-free.deepl.com // @connect api-free.deepl.com
@@ -108,12 +109,10 @@ const userscriptWebpack = (config, env) => {
// @connect dav.jianguoyun.com // @connect dav.jianguoyun.com
// @connect fanyi.baidu.com // @connect fanyi.baidu.com
// @connect transmart.qq.com // @connect transmart.qq.com
// @connect localhost:3000 // @connect niutrans.com
// @connect 127.0.0.1:3000 // @connect translate.volcengine.com
// @connect localhost:1188 // @connect localhost
// @connect 127.0.0.1:1188 // @connect 127.0.0.1
// @connect localhost:11434
// @connect 127.0.0.1:11434
// @run-at document-end // @run-at document-end
// ==/UserScript== // ==/UserScript==

View File

@@ -1,7 +1,7 @@
{ {
"name": "kiss-translator", "name": "kiss-translator",
"description": "A minimalist bilingual translation Extension & Greasemonkey Script", "description": "A minimalist bilingual translation Extension & Greasemonkey Script",
"version": "1.8.10", "version": "1.9.1",
"author": "Gabe<yugang2002@gmail.com>", "author": "Gabe<yugang2002@gmail.com>",
"private": true, "private": true,
"dependencies": { "dependencies": {
@@ -26,13 +26,16 @@
"start:userscript": "REACT_APP_CLIENT=userscript react-app-rewired start", "start:userscript": "REACT_APP_CLIENT=userscript react-app-rewired start",
"build:chrome": "rm -rf build/chrome && BUILD_PATH=./build/chrome REACT_APP_CLIENT=chrome react-app-rewired build && rm build/chrome/content.html", "build:chrome": "rm -rf build/chrome && BUILD_PATH=./build/chrome REACT_APP_CLIENT=chrome react-app-rewired build && rm build/chrome/content.html",
"build:edge": "rm -rf build/edge && cp -r build/chrome build/edge", "build:edge": "rm -rf build/edge && cp -r build/chrome build/edge",
"build:thunderbird": "rm -rf build/thunderbird && BUILD_PATH=./build/thunderbird REACT_APP_CLIENT=thunderbird react-app-rewired build && rm build/thunderbird/content.html && cp ./build/thunderbird/manifest.thunderbird.json ./build/thunderbird/manifest.json && rm build/*/manifest.thunderbird.json",
"build:firefox": "rm -rf build/firefox && cp -r build/chrome build/firefox && cat ./build/firefox/manifest.firefox.json > ./build/firefox/manifest.json && rm build/*/manifest.firefox.json", "build:firefox": "rm -rf build/firefox && cp -r build/chrome build/firefox && cat ./build/firefox/manifest.firefox.json > ./build/firefox/manifest.json && rm build/*/manifest.firefox.json",
"build:web": "rm -rf build/web && BUILD_PATH=./build/web REACT_APP_CLIENT=userscript react-app-rewired build", "build:web": "rm -rf build/web && BUILD_PATH=./build/web REACT_APP_CLIENT=userscript react-app-rewired build",
"build:userscript-ios": "file1=build/web/kiss-translator.user.js file2=build/web/kiss-translator-ios-safari.user.js && cp $file1 $file2 && sed -i 's|// @grant unsafeWindow|// @inject-into content|g' $file2", "build:userscript-ios": "file1=build/web/kiss-translator.user.js file2=build/web/kiss-translator-ios-safari.user.js && cp $file1 $file2 && sed -i 's|// @grant unsafeWindow|// @inject-into content|g' $file2",
"build:userscript": "rm -rf build/userscript && mkdir build/userscript && cp build/web/*.user.js build/userscript/", "build:userscript": "rm -rf build/userscript && mkdir build/userscript && cp build/web/*.user.js build/userscript/",
"build:rules": "babel-node src/rules.js", "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", "build": "pnpm format && pnpm build:chrome && pnpm build:edge && pnpm build:thunderbird && pnpm build:firefox && pnpm build:web && pnpm build:userscript-ios && pnpm build:userscript && pnpm build:rules",
"zip": "cd build && zip -r chrome.zip chrome && zip -r edge.zip edge && cd firefox && zip -r ../firefox.zip .", "zip": "cd build && rm -f *.zip && zip -r chrome.zip chrome && zip -r edge.zip edge && (cd firefox && zip -r ../firefox.zip .) && (cd thunderbird && zip -r ../thunderbird.zip .)",
"build+zip": "pnpm build && pnpm zip",
"format": "prettier --write \"**/*.{js,json,html}\"",
"test": "react-app-rewired test", "test": "react-app-rewired test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
@@ -44,7 +47,8 @@
"globals": { "globals": {
"GM": true, "GM": true,
"unsafeWindow": true, "unsafeWindow": true,
"globalThis": true "globalThis": true,
"messenger": true
} }
}, },
"browserslist": { "browserslist": {
@@ -64,6 +68,7 @@
"@babel/node": "^7.22.19", "@babel/node": "^7.22.19",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.22.20", "@babel/preset-env": "^7.22.20",
"prettier": "3.6.2",
"react-app-rewired": "^2.2.1" "react-app-rewired": "^2.2.1"
} }
} }

12139
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@@ -16,7 +16,7 @@
} }
</style> </style>
<script> <script>
document.addEventListener("DOMContentLoaded", function () { document.addEventListener('DOMContentLoaded', function () {
// (() => { // (() => {
// var shadow = document.querySelector("#shadow1"); // var shadow = document.querySelector("#shadow1");
// var root = shadow.attachShadow({ mode: "open" }); // var root = shadow.attachShadow({ mode: "open" });
@@ -54,8 +54,8 @@
// }, 1000); // }, 1000);
setTimeout(function () { setTimeout(function () {
var el = document.querySelector("h2>p>span"); var el = document.querySelector('h2>p>span');
el.innerText = "hello world"; el.innerText = 'hello world';
}, 1000); }, 1000);
}); });
</script> </script>
@@ -105,28 +105,26 @@
<br /> <br />
<div id="content"> <div id="content">
<p>You need to enable JavaScript to run <span>this app.</span></p> <p>You need to enable JavaScript to run <span>this app.</span></p>
The <span>embargo</span> has just lifted to confirm that AmpereOne is The <span>embargo</span> has just lifted to confirm that AmpereOne is coming to
coming to Google Cloud with the C3A instances. Google Cloud with the C3A instances.
<br /> <br />
But these upcoming instances for now are only in private preview form. But these upcoming instances for now are only in private preview form.
<br /> <br />
<br /> <br />
Needless to say I also haven't had any AmpereOne access to check out the Needless to say I also haven't had any AmpereOne access to check out the
performance and power efficiency of these new Arm server processors from performance and power efficiency of these new Arm server processors from Ampere
Ampere Computing. Computing.
<br /> <br />
</div> </div>
<h2> <h2>
<p> <p>
<span <span>React is a JavaScript library for building user interfaces.</span>
>React is a JavaScript library for building user interfaces.</span
>
</p> </p>
</h2> </h2>
<hr /> <hr />
<input id="input1" style="width: 80%;" /> <input id="input1" style="width: 80%" />
<hr /> <hr />
<textarea id="textarea1" style="width: 80%;">test</textarea> <textarea id="textarea1" style="width: 80%">test</textarea>
<hr /> <hr />
<div id="addtitle"></div> <div id="addtitle"></div>
<h2>Shadow 1</h2> <h2>Shadow 1</h2>
@@ -166,15 +164,47 @@
<br /> <br />
<br /> <br />
<h2> <h2>
React Server Components (or RSC) is a new application architecture React Server Components (or RSC) is a new application architecture designed by the
designed by the React team. React team.
</h2> </h2>
<iframe <iframe
id="iframe1" id="iframe1"
width="800px" width="800px"
height="600px" height="600px"
src="http://localhost:3000/index.html" src="http://localhost:3000/index.html"></iframe>
></iframe> <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<h2>Weve first shared our research on RSC in an introductory talk and an RFC.</h2>
<br /> <br />
<br /> <br />
<br /> <br />
@@ -208,52 +238,10 @@
<br /> <br />
<br /> <br />
<h2> <h2>
Weve first shared our research on RSC in an introductory talk and an To recap them, we are introducing a new kind of component—Server Components—that
RFC. run ahead of time and are excluded from your JavaScript bundle.
</h2> </h2>
<br /> <iframe id="iframe2" width="800px" height="600px" src="https://react.dev/"></iframe>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<h2>
To recap them, we are introducing a new kind of component—Server
Components—that run ahead of time and are excluded from your JavaScript
bundle.
</h2>
<iframe
id="iframe2"
width="800px"
height="600px"
src="https://react.dev/"
></iframe>
<br /> <br />
<br /> <br />
<br /> <br />
@@ -288,15 +276,14 @@
<br /> <br />
<div class="cont cont1"> <div class="cont cont1">
<h2> <h2>
Server Components can run during the build, letting you read from the Server Components can run during the build, letting you read from the filesystem
filesystem or fetch static content. or fetch static content.
</h2> </h2>
<ul> <ul>
<li> <li>
They can also run on the server, letting you access your data layer They can also run on the server, letting you access your data layer without
without having to build an API. You can pass data by props from having to build an API. You can pass data by props from Server Components to
Server Components to the interactive Client Components in the the interactive Client Components in the browser.
browser.
</li> </li>
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li> <li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li>
</ul> </ul>
@@ -315,14 +302,14 @@
<br /> <br />
<div class="cont cont2"> <div class="cont cont2">
<h2> <h2>
Since our last update, we have merged the React Server Components RFC Since our last update, we have merged the React Server Components RFC to ratify
to ratify the proposal. the proposal.
</h2> </h2>
<ul> <ul>
<li> <li>
RSC combines the simple “request/response” mental model of RSC combines the simple “request/response” mental model of server-centric
server-centric Multi-Page Apps with the seamless interactivity of Multi-Page Apps with the seamless interactivity of client-centric Single-Page
client-centric Single-Page Apps, giving you the best of both worlds. Apps, giving you the best of both worlds.
</li> </li>
<li> <li>
React 使创建交互式 UI React 使创建交互式 UI

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />

View File

@@ -2,7 +2,7 @@
"manifest_version": 2, "manifest_version": 2,
"name": "__MSG_app_name__", "name": "__MSG_app_name__",
"description": "__MSG_app_description__", "description": "__MSG_app_description__",
"version": "1.8.10", "version": "1.9.1",
"default_locale": "en", "default_locale": "en",
"author": "Gabe<yugang2002@gmail.com>", "author": "Gabe<yugang2002@gmail.com>",
"homepage_url": "https://github.com/fishjar/kiss-translator", "homepage_url": "https://github.com/fishjar/kiss-translator",

View File

@@ -2,7 +2,7 @@
"manifest_version": 3, "manifest_version": 3,
"name": "__MSG_app_name__", "name": "__MSG_app_name__",
"description": "__MSG_app_description__", "description": "__MSG_app_description__",
"version": "1.8.10", "version": "1.9.1",
"default_locale": "en", "default_locale": "en",
"author": "Gabe<yugang2002@gmail.com>", "author": "Gabe<yugang2002@gmail.com>",
"homepage_url": "https://github.com/fishjar/kiss-translator", "homepage_url": "https://github.com/fishjar/kiss-translator",
@@ -45,12 +45,7 @@
"description": "__MSG_open_options__" "description": "__MSG_open_options__"
} }
}, },
"permissions": [ "permissions": ["storage", "contextMenus", "scripting", "declarativeNetRequest"],
"storage",
"contextMenus",
"scripting",
"declarativeNetRequest"
],
"host_permissions": ["<all_urls>"], "host_permissions": ["<all_urls>"],
"icons": { "icons": {
"16": "images/logo16.png", "16": "images/logo16.png",

View File

@@ -0,0 +1,78 @@
{
"manifest_version": 2,
"name": "__MSG_app_name__",
"description": "__MSG_app_description__",
"version": "1.9.1",
"default_locale": "en",
"author": "Gabe<yugang2002@gmail.com>",
"homepage_url": "https://github.com/fishjar/kiss-translator",
"browser_specific_settings": {
"gecko": {
"id": "yugang2002@gmail.com",
"strict_min_version": "78.0"
}
},
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"js": ["content.js"],
"matches": ["<all_urls>"],
"all_frames": true
}
],
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Alt+K"
}
},
"toggleTranslate": {
"suggested_key": {
"default": "Alt+Q"
},
"description": "__MSG_toggle_translate__"
},
"openTranbox": {
"suggested_key": {
"default": "Alt+S"
},
"description": "__MSG_open_tranbox__"
},
"toggleStyle": {
"suggested_key": {
"default": "Alt+C"
},
"description": "__MSG_toggle_style__"
},
"openOptions": {
"description": "__MSG_open_options__"
}
},
"permissions": [
"<all_urls>",
"storage",
"menus",
"messagesModify",
"scripting",
"declarativeNetRequest"
],
"icons": {
"16": "images/logo16.png",
"32": "images/logo32.png",
"48": "images/logo48.png",
"128": "images/logo128.png"
},
"browser_action": {
"default_icon": {
"128": "images/logo128.png"
},
"default_title": "__MSG_app_name__",
"default_popup": "popup.html"
},
"options_ui": {
"page": "options.html",
"open_in_tab": true
}
}

View File

@@ -2,6 +2,7 @@ import queryString from "query-string";
import { fetchData } from "../libs/fetch"; import { fetchData } from "../libs/fetch";
import { import {
OPT_TRANS_GOOGLE, OPT_TRANS_GOOGLE,
OPT_TRANS_GOOGLE_2,
OPT_TRANS_MICROSOFT, OPT_TRANS_MICROSOFT,
OPT_TRANS_DEEPL, OPT_TRANS_DEEPL,
OPT_TRANS_DEEPLFREE, OPT_TRANS_DEEPLFREE,
@@ -9,10 +10,13 @@ import {
OPT_TRANS_NIUTRANS, OPT_TRANS_NIUTRANS,
OPT_TRANS_BAIDU, OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT, OPT_TRANS_TENCENT,
OPT_TRANS_VOLCENGINE,
OPT_TRANS_OPENAI, OPT_TRANS_OPENAI,
OPT_TRANS_OPENAI_2, OPT_TRANS_OPENAI_2,
OPT_TRANS_OPENAI_3, OPT_TRANS_OPENAI_3,
OPT_TRANS_GEMINI, OPT_TRANS_GEMINI,
OPT_TRANS_GEMINI_2,
OPT_TRANS_CLAUDE,
OPT_TRANS_CLOUDFLAREAI, OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_OLLAMA, OPT_TRANS_OLLAMA,
OPT_TRANS_OLLAMA_2, OPT_TRANS_OLLAMA_2,
@@ -24,6 +28,8 @@ import {
OPT_TRANS_CUSTOMIZE_5, OPT_TRANS_CUSTOMIZE_5,
URL_CACHE_TRAN, URL_CACHE_TRAN,
KV_SALT_SYNC, KV_SALT_SYNC,
URL_GOOGLE_TRAN,
URL_MICROSOFT_LANGDETECT,
URL_BAIDU_LANGDETECT, URL_BAIDU_LANGDETECT,
URL_BAIDU_SUGGEST, URL_BAIDU_SUGGEST,
URL_BAIDU_TTS, URL_BAIDU_TTS,
@@ -31,9 +37,12 @@ import {
URL_TENCENT_TRANSMART, URL_TENCENT_TRANSMART,
OPT_LANGS_TENCENT, OPT_LANGS_TENCENT,
OPT_LANGS_SPECIAL, OPT_LANGS_SPECIAL,
OPT_LANGS_MICROSOFT,
} from "../config"; } from "../config";
import { sha256 } from "../libs/utils"; import { sha256 } from "../libs/utils";
import interpreter from "../libs/interpreter"; import interpreter from "../libs/interpreter";
import { msAuth } from "../libs/auth";
import { kissLog } from "../libs/log";
/** /**
* 同步数据 * 同步数据
@@ -59,6 +68,52 @@ export const apiSyncData = async (url, key, data) =>
*/ */
export const apiFetch = (url) => fetchData(url); export const apiFetch = (url) => fetchData(url);
/**
* Google语言识别
* @param {*} text
* @returns
*/
export const apiGoogleLangdetect = async (text) => {
const params = {
client: "gtx",
dt: "t",
dj: 1,
ie: "UTF-8",
sl: "auto",
tl: "zh-CN",
q: text,
};
const input = `${URL_GOOGLE_TRAN}?${queryString.stringify(params)}`;
const res = await fetchData(input, {
headers: {
"Content-type": "application/json",
},
useCache: true,
});
return res.src;
};
/**
* Microsoft语言识别
* @param {*} text
* @returns
*/
export const apiMicrosoftLangdetect = async (text) => {
const [token] = await msAuth();
const res = await fetchData(URL_MICROSOFT_LANGDETECT, {
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${token}`,
},
method: "POST",
body: JSON.stringify([{ Text: text }]),
useCache: true,
});
return OPT_LANGS_MICROSOFT.get(res[0].language) ?? res[0].language;
};
/** /**
* 百度语言识别 * 百度语言识别
* @param {*} text * @param {*} text
@@ -172,7 +227,7 @@ export const apiTranslate = async ({
OPT_LANGS_SPECIAL[translator].get("auto"); OPT_LANGS_SPECIAL[translator].get("auto");
const to = OPT_LANGS_SPECIAL[translator].get(toLang); const to = OPT_LANGS_SPECIAL[translator].get(toLang);
if (!to) { if (!to) {
console.log(`[trans] target lang: ${toLang} not support`); kissLog(`target lang: ${toLang} not support`, "translate");
return [trText, isSame]; return [trText, isSame];
} }
@@ -208,6 +263,10 @@ export const apiTranslate = async ({
trText = res.sentences.map((item) => item.trans).join(" "); trText = res.sentences.map((item) => item.trans).join(" ");
isSame = to === res.src; isSame = to === res.src;
break; break;
case OPT_TRANS_GOOGLE_2:
trText = res?.[0]?.[0] || "";
isSame = to === res.src;
break;
case OPT_TRANS_MICROSOFT: case OPT_TRANS_MICROSOFT:
trText = res trText = res
.map((item) => item.translations.map((item) => item.text).join(" ")) .map((item) => item.translations.map((item) => item.text).join(" "))
@@ -246,12 +305,17 @@ export const apiTranslate = async ({
} }
break; break;
case OPT_TRANS_TENCENT: case OPT_TRANS_TENCENT:
trText = res.auto_translation; trText = res?.auto_translation?.[0];
isSame = text === trText; isSame = text === trText;
break; break;
case OPT_TRANS_VOLCENGINE:
trText = res?.translation || "";
isSame = to === res?.detected_language;
break;
case OPT_TRANS_OPENAI: case OPT_TRANS_OPENAI:
case OPT_TRANS_OPENAI_2: case OPT_TRANS_OPENAI_2:
case OPT_TRANS_OPENAI_3: case OPT_TRANS_OPENAI_3:
case OPT_TRANS_GEMINI_2:
trText = res?.choices?.map((item) => item.message.content).join(" "); trText = res?.choices?.map((item) => item.message.content).join(" ");
isSame = text === trText; isSame = text === trText;
break; break;
@@ -261,6 +325,10 @@ export const apiTranslate = async ({
.join(" "); .join(" ");
isSame = text === trText; isSame = text === trText;
break; break;
case OPT_TRANS_CLAUDE:
trText = res?.content?.map((item) => item.text).join(" ");
isSame = text === trText;
break;
case OPT_TRANS_CLOUDFLAREAI: case OPT_TRANS_CLOUDFLAREAI:
trText = res?.result?.translated_text; trText = res?.result?.translated_text;
isSame = text === trText; isSame = text === trText;
@@ -268,7 +336,13 @@ export const apiTranslate = async ({
case OPT_TRANS_OLLAMA: case OPT_TRANS_OLLAMA:
case OPT_TRANS_OLLAMA_2: case OPT_TRANS_OLLAMA_2:
case OPT_TRANS_OLLAMA_3: case OPT_TRANS_OLLAMA_3:
trText = res?.response; const { thinkIgnore = "" } = apiSetting;
const deepModels = thinkIgnore.split(",").filter((model) => model.trim());
if (deepModels.some((model) => res?.model?.startsWith(model))) {
trText = res?.response.replace(/<think>[\s\S]*<\/think>/i, "");
} else {
trText = res?.response;
}
isSame = text === trText; isSame = text === trText;
break; break;
case OPT_TRANS_CUSTOMIZE: case OPT_TRANS_CUSTOMIZE:

View File

@@ -1,6 +1,7 @@
import queryString from "query-string"; import queryString from "query-string";
import { import {
OPT_TRANS_GOOGLE, OPT_TRANS_GOOGLE,
OPT_TRANS_GOOGLE_2,
OPT_TRANS_MICROSOFT, OPT_TRANS_MICROSOFT,
OPT_TRANS_DEEPL, OPT_TRANS_DEEPL,
OPT_TRANS_DEEPLFREE, OPT_TRANS_DEEPLFREE,
@@ -8,10 +9,13 @@ import {
OPT_TRANS_NIUTRANS, OPT_TRANS_NIUTRANS,
OPT_TRANS_BAIDU, OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT, OPT_TRANS_TENCENT,
OPT_TRANS_VOLCENGINE,
OPT_TRANS_OPENAI, OPT_TRANS_OPENAI,
OPT_TRANS_OPENAI_2, OPT_TRANS_OPENAI_2,
OPT_TRANS_OPENAI_3, OPT_TRANS_OPENAI_3,
OPT_TRANS_GEMINI, OPT_TRANS_GEMINI,
OPT_TRANS_GEMINI_2,
OPT_TRANS_CLAUDE,
OPT_TRANS_CLOUDFLAREAI, OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_OLLAMA, OPT_TRANS_OLLAMA,
OPT_TRANS_OLLAMA_2, OPT_TRANS_OLLAMA_2,
@@ -23,6 +27,7 @@ import {
OPT_TRANS_CUSTOMIZE_5, OPT_TRANS_CUSTOMIZE_5,
URL_MICROSOFT_TRAN, URL_MICROSOFT_TRAN,
URL_TENCENT_TRANSMART, URL_TENCENT_TRANSMART,
URL_VOLCENGINE_TRAN,
INPUT_PLACE_URL, INPUT_PLACE_URL,
INPUT_PLACE_FROM, INPUT_PLACE_FROM,
INPUT_PLACE_TO, INPUT_PLACE_TO,
@@ -79,6 +84,20 @@ const genGoogle = ({ text, from, to, url, key }) => {
return [input, init]; return [input, init];
}; };
const genGoogle2 = ({ text, from, to, url, key }) => {
const body = JSON.stringify([[[text], from, to], "wt_lib"]);
const init = {
method: "POST",
headers: {
"Content-Type": "application/json+protobuf",
"X-Goog-API-Key": key,
},
body,
};
return [url, init];
};
const genMicrosoft = async ({ text, from, to }) => { const genMicrosoft = async ({ text, from, to }) => {
const [token] = await msAuth(); const [token] = await msAuth();
const params = { const params = {
@@ -163,10 +182,14 @@ const genNiuTrans = ({ text, from, to, url, key, dictNo, memoryNo }) => {
const genTencent = ({ text, from, to }) => { const genTencent = ({ text, from, to }) => {
const data = { const data = {
header: { header: {
fn: "auto_translation_block", fn: "auto_translation",
client_key:
"browser-chrome-110.0.0-Mac OS-df4bd4c5-a65d-44b2-a40f-42f34f3535f2-1677486696487",
}, },
type: "plain",
model_category: "normal",
source: { source: {
text_block: text, text_list: [text],
lang: from, lang: from,
}, },
target: { target: {
@@ -177,6 +200,9 @@ const genTencent = ({ text, from, to }) => {
const init = { const init = {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"user-agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
referer: "https://transmart.qq.com/zh-CN/index",
}, },
method: "POST", method: "POST",
body: JSON.stringify(data), body: JSON.stringify(data),
@@ -185,25 +211,63 @@ const genTencent = ({ text, from, to }) => {
return [URL_TENCENT_TRANSMART, init]; return [URL_TENCENT_TRANSMART, init];
}; };
const genOpenAI = ({ text, from, to, url, key, prompt, model }) => { const genVolcengine = ({ text, from, to }) => {
prompt = prompt const data = {
source_language: from,
target_language: to,
text: text,
};
const init = {
headers: {
"Content-type": "application/json",
},
method: "POST",
body: JSON.stringify(data),
};
return [URL_VOLCENGINE_TRAN, init];
};
const genOpenAI = ({
text,
from,
to,
url,
key,
systemPrompt,
userPrompt,
model,
temperature,
maxTokens,
}) => {
// 兼容历史上作为systemPrompt的prompt如果prompt中不包含带翻译文本则添加文本到prompt末尾
// if (!prompt.includes(INPUT_PLACE_TEXT)) {
// prompt += `\nSource Text: ${INPUT_PLACE_TEXT}`;
// }
systemPrompt = systemPrompt
.replaceAll(INPUT_PLACE_FROM, from) .replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to); .replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
userPrompt = userPrompt
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
const data = { const data = {
model, model,
messages: [ messages: [
{ {
role: "system", role: "system",
content: prompt, content: systemPrompt,
}, },
{ {
role: "user", role: "user",
content: text, content: userPrompt,
}, },
], ],
temperature: 0, temperature,
max_tokens: 256, max_tokens: maxTokens,
}; };
const init = { const init = {
@@ -219,26 +283,48 @@ const genOpenAI = ({ text, from, to, url, key, prompt, model }) => {
return [url, init]; return [url, init];
}; };
const genGemini = ({ text, from, to, url, key, prompt, model }) => { const genGemini = ({
text,
from,
to,
url,
key,
systemPrompt,
userPrompt,
model,
temperature,
maxTokens,
}) => {
url = url url = url
.replaceAll(INPUT_PLACE_MODEL, model) .replaceAll(INPUT_PLACE_MODEL, model)
.replaceAll(INPUT_PLACE_KEY, key); .replaceAll(INPUT_PLACE_KEY, key);
prompt = prompt systemPrompt = systemPrompt
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
userPrompt = userPrompt
.replaceAll(INPUT_PLACE_FROM, from) .replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to) .replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text); .replaceAll(INPUT_PLACE_TEXT, text);
const data = { const data = {
contents: [ system_instruction: {
{ parts: {
// role: "user", text: systemPrompt,
parts: [
{
text: prompt,
},
],
}, },
], },
contents: {
role: "user",
parts: {
text: userPrompt,
},
},
generationConfig: {
maxOutputTokens: maxTokens,
temperature,
// topP: 0.8,
// topK: 10,
},
}; };
const init = { const init = {
@@ -252,15 +338,127 @@ const genGemini = ({ text, from, to, url, key, prompt, model }) => {
return [url, init]; return [url, init];
}; };
const genOllama = ({ text, from, to, url, key, prompt, model }) => { const genGemini2 = ({
prompt = prompt text,
from,
to,
url,
key,
systemPrompt,
userPrompt,
model,
temperature,
maxTokens,
}) => {
systemPrompt = systemPrompt
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
userPrompt = userPrompt
.replaceAll(INPUT_PLACE_FROM, from) .replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to) .replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text); .replaceAll(INPUT_PLACE_TEXT, text);
const data = { const data = {
model, model,
prompt, messages: [
{
role: "system",
content: systemPrompt,
},
{
role: "user",
content: userPrompt,
},
],
temperature,
max_tokens: maxTokens,
};
const init = {
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${key}`,
},
method: "POST",
body: JSON.stringify(data),
};
return [url, init];
};
const genClaude = ({
text,
from,
to,
url,
key,
systemPrompt,
userPrompt,
model,
temperature,
maxTokens,
}) => {
systemPrompt = systemPrompt
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
userPrompt = userPrompt
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
const data = {
model,
system: systemPrompt,
messages: [
{
role: "user",
content: userPrompt,
},
],
temperature,
max_tokens: maxTokens,
};
const init = {
headers: {
"Content-type": "application/json",
"anthropic-version": "2023-06-01",
"x-api-key": key,
},
method: "POST",
body: JSON.stringify(data),
};
return [url, init];
};
const genOllama = ({
text,
from,
to,
think,
url,
key,
systemPrompt,
userPrompt,
model,
}) => {
systemPrompt = systemPrompt
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
userPrompt = userPrompt
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
const data = {
model,
system: systemPrompt,
prompt: userPrompt,
think: think,
stream: false, stream: false,
}; };
@@ -345,11 +543,18 @@ export const genTransReq = ({ translator, text, from, to }, apiSetting) => {
case OPT_TRANS_OPENAI_2: case OPT_TRANS_OPENAI_2:
case OPT_TRANS_OPENAI_3: case OPT_TRANS_OPENAI_3:
case OPT_TRANS_GEMINI: case OPT_TRANS_GEMINI:
case OPT_TRANS_GEMINI_2:
case OPT_TRANS_CLAUDE:
case OPT_TRANS_CLOUDFLAREAI: case OPT_TRANS_CLOUDFLAREAI:
case OPT_TRANS_OLLAMA: case OPT_TRANS_OLLAMA:
case OPT_TRANS_OLLAMA_2: case OPT_TRANS_OLLAMA_2:
case OPT_TRANS_OLLAMA_3: case OPT_TRANS_OLLAMA_3:
case OPT_TRANS_NIUTRANS: case OPT_TRANS_NIUTRANS:
case OPT_TRANS_CUSTOMIZE:
case OPT_TRANS_CUSTOMIZE_2:
case OPT_TRANS_CUSTOMIZE_3:
case OPT_TRANS_CUSTOMIZE_4:
case OPT_TRANS_CUSTOMIZE_5:
args.key = keyPick(translator, args.key, keyMap); args.key = keyPick(translator, args.key, keyMap);
break; break;
case OPT_TRANS_DEEPLX: case OPT_TRANS_DEEPLX:
@@ -361,6 +566,8 @@ export const genTransReq = ({ translator, text, from, to }, apiSetting) => {
switch (translator) { switch (translator) {
case OPT_TRANS_GOOGLE: case OPT_TRANS_GOOGLE:
return genGoogle(args); return genGoogle(args);
case OPT_TRANS_GOOGLE_2:
return genGoogle2(args);
case OPT_TRANS_MICROSOFT: case OPT_TRANS_MICROSOFT:
return genMicrosoft(args); return genMicrosoft(args);
case OPT_TRANS_DEEPL: case OPT_TRANS_DEEPL:
@@ -375,12 +582,18 @@ export const genTransReq = ({ translator, text, from, to }, apiSetting) => {
return genBaidu(args); return genBaidu(args);
case OPT_TRANS_TENCENT: case OPT_TRANS_TENCENT:
return genTencent(args); return genTencent(args);
case OPT_TRANS_VOLCENGINE:
return genVolcengine(args);
case OPT_TRANS_OPENAI: case OPT_TRANS_OPENAI:
case OPT_TRANS_OPENAI_2: case OPT_TRANS_OPENAI_2:
case OPT_TRANS_OPENAI_3: case OPT_TRANS_OPENAI_3:
return genOpenAI(args); return genOpenAI(args);
case OPT_TRANS_GEMINI: case OPT_TRANS_GEMINI:
return genGemini(args); return genGemini(args);
case OPT_TRANS_GEMINI_2:
return genGemini2(args);
case OPT_TRANS_CLAUDE:
return genClaude(args);
case OPT_TRANS_CLOUDFLAREAI: case OPT_TRANS_CLOUDFLAREAI:
return genCloudflareAI(args); return genCloudflareAI(args);
case OPT_TRANS_OLLAMA: case OPT_TRANS_OLLAMA:

View File

@@ -17,6 +17,7 @@ import {
CMD_TOGGLE_STYLE, CMD_TOGGLE_STYLE,
CMD_OPEN_OPTIONS, CMD_OPEN_OPTIONS,
CMD_OPEN_TRANBOX, CMD_OPEN_TRANBOX,
CLIENT_THUNDERBIRD,
} from "./config"; } from "./config";
import { getSettingWithDefault, tryInitDefaultData } from "./libs/storage"; import { getSettingWithDefault, tryInitDefaultData } from "./libs/storage";
import { trySyncSettingAndRules } from "./libs/sync"; import { trySyncSettingAndRules } from "./libs/sync";
@@ -44,7 +45,7 @@ const REMOVE_HEADERS = [
async function addContextMenus(contextMenuType = 1) { async function addContextMenus(contextMenuType = 1) {
// 添加前先删除,避免重复ID的错误 // 添加前先删除,避免重复ID的错误
try { try {
await browser.contextMenus.removeAll(); await browser.menus.removeAll();
} catch (err) { } catch (err) {
// //
} }
@@ -123,12 +124,26 @@ async function updateCspRules(csplist = DEFAULT_CSPLIST.join(",\n")) {
} }
} }
/**
* 注册邮件显示脚本
*/
async function registerMsgDisplayScript() {
await messenger.messageDisplayScripts.register({
js: [{ file: "/content.js" }],
});
}
/** /**
* 插件安装 * 插件安装
*/ */
browser.runtime.onInstalled.addListener(() => { browser.runtime.onInstalled.addListener(() => {
tryInitDefaultData(); tryInitDefaultData();
//在thunderbird中注册脚本
if (process.env.REACT_APP_CLIENT === CLIENT_THUNDERBIRD) {
registerMsgDisplayScript();
}
// 右键菜单 // 右键菜单
addContextMenus(); addContextMenus();
@@ -151,6 +166,11 @@ browser.runtime.onStartup.addListener(async () => {
tryClearCaches(); tryClearCaches();
} }
//在thunderbird中注册脚本
if (process.env.REACT_APP_CLIENT === CLIENT_THUNDERBIRD) {
registerMsgDisplayScript();
}
// 右键菜单 // 右键菜单
// firefox重启后菜单会消失,故重复添加 // firefox重启后菜单会消失,故重复添加
addContextMenus(contextMenuType); addContextMenus(contextMenuType);

View File

@@ -72,7 +72,7 @@ function runtimeListener(translator) {
default: default:
return { error: `message action is unavailable: ${action}` }; return { error: `message action is unavailable: ${action}` };
} }
return { data: translator.rule }; return { rule: translator.rule, setting: translator.setting };
}); });
} }
@@ -135,14 +135,18 @@ async function showFab(translator) {
* @param {*} param0 * @param {*} param0
* @returns * @returns
*/ */
function showTransbox({ function showTransbox(
contextMenuType, {
tranboxSetting = DEFAULT_TRANBOX_SETTING, contextMenuType,
transApis, tranboxSetting = DEFAULT_TRANBOX_SETTING,
darkMode, transApis,
uiLang, darkMode,
}) { uiLang,
if (!tranboxSetting?.transOpen) { langDetector,
},
{ transSelected }
) {
if (transSelected === "false") {
return; return;
} }
@@ -172,6 +176,7 @@ function showTransbox({
tranboxSetting={tranboxSetting} tranboxSetting={tranboxSetting}
transApis={transApis} transApis={transApis}
uiLang={uiLang} uiLang={uiLang}
langDetector={langDetector}
/> />
</CacheProvider> </CacheProvider>
</React.StrictMode> </React.StrictMode>
@@ -249,7 +254,7 @@ export async function run(isUserscript = false) {
inputTranslate(setting); inputTranslate(setting);
// 划词翻译 // 划词翻译
showTransbox(setting); showTransbox(setting, rule);
// 浮球按钮 // 浮球按钮
await showFab(translator); await showFab(translator);

View File

@@ -186,6 +186,22 @@ export const I18N = {
zh: `最大并发请求数量 (1-100)`, zh: `最大并发请求数量 (1-100)`,
en: `Maximum Number Of Concurrent Requests (1-100)`, en: `Maximum Number Of Concurrent Requests (1-100)`,
}, },
if_think: {
zh: `启用或禁用模型的深度思考能力`,
en: `Enable or disable the models thinking behavior `,
},
think: {
zh: `启用深度思考`,
en: `enable thinking`,
},
nothink: {
zh: `禁用深度思考`,
en: `disable thinking`,
},
think_ignore: {
zh: `忽略以下模型的<think>输出,逗号(,)分割,当模型支持思考但ollama不支持时需要填写本参数`,
en: `Ignore the <think> block for the following models, comma (,) separated`,
},
fetch_interval: { fetch_interval: {
zh: `每次请求间隔时间 (0-5000ms)`, zh: `每次请求间隔时间 (0-5000ms)`,
en: `Time Between Requests (0-5000ms)`, en: `Time Between Requests (0-5000ms)`,
@@ -194,6 +210,10 @@ export const I18N = {
zh: `重新翻译间隔时间 (100-5000ms)`, zh: `重新翻译间隔时间 (100-5000ms)`,
en: `Retranslation Interval (100-5000ms)`, en: `Retranslation Interval (100-5000ms)`,
}, },
http_timeout: {
zh: `请求超时时间 (5000-30000ms)`,
en: `Request Timeout Time (5000-30000ms)`,
},
min_translate_length: { min_translate_length: {
zh: `最小翻译字符数 (1-100)`, zh: `最小翻译字符数 (1-100)`,
en: `Minimum number Of Translated Characters (1-100)`, en: `Minimum number Of Translated Characters (1-100)`,
@@ -427,8 +447,8 @@ export const I18N = {
en: `Keep unchanged selector`, en: `Keep unchanged selector`,
}, },
keep_selector_helper: { keep_selector_helper: {
zh: `1、遵循CSS选择器语法。2、子元素选择器用“>>>”隔开。`, zh: `1、遵循CSS选择器语法。`,
en: `1. Follow CSS selector syntax. 2. Sub-element selectors are separated by ">>>".`, en: `1. Follow CSS selector syntax.`,
}, },
terms: { terms: {
zh: `专业术语`, zh: `专业术语`,
@@ -656,7 +676,7 @@ export const I18N = {
}, },
use_simple_style: { use_simple_style: {
zh: `使用简洁界面`, zh: `使用简洁界面`,
en: `Click outside to close the pop-up window`, en: `Use a simple interface`,
}, },
show: { show: {
zh: `显示`, zh: `显示`,
@@ -854,6 +874,10 @@ export const I18N = {
zh: `更多`, zh: `更多`,
en: `More`, en: `More`,
}, },
less: {
zh: `更少`,
en: `Less`,
},
fixer_selector: { fixer_selector: {
zh: `网页修复选择器`, zh: `网页修复选择器`,
en: `Fixer Selector`, en: `Fixer Selector`,
@@ -930,4 +954,20 @@ export const I18N = {
zh: `翻译移除时运行,入参为: 翻译节点。`, zh: `翻译移除时运行,入参为: 翻译节点。`,
en: `Run when translation is removed, the input parameters are: translation node.`, en: `Run when translation is removed, the input parameters are: translation node.`,
}, },
english_dict: {
zh: `英文词典`,
en: `English Dictionary`,
},
api_name: {
zh: `接口名称`,
en: `API Name`,
},
is_disabled: {
zh: `是否禁用`,
en: `Is Disabled`,
},
translate_selected: {
zh: `是否启用划词翻译`,
en: `If translate selected`,
},
}; };

View File

@@ -39,7 +39,13 @@ export const CLIENT_CHROME = "chrome";
export const CLIENT_EDGE = "edge"; export const CLIENT_EDGE = "edge";
export const CLIENT_FIREFOX = "firefox"; export const CLIENT_FIREFOX = "firefox";
export const CLIENT_USERSCRIPT = "userscript"; export const CLIENT_USERSCRIPT = "userscript";
export const CLIENT_EXTS = [CLIENT_CHROME, CLIENT_EDGE, CLIENT_FIREFOX]; export const CLIENT_THUNDERBIRD = "thunderbird";
export const CLIENT_EXTS = [
CLIENT_CHROME,
CLIENT_EDGE,
CLIENT_FIREFOX,
CLIENT_THUNDERBIRD,
];
export const KV_RULES_KEY = "kiss-rules.json"; export const KV_RULES_KEY = "kiss-rules.json";
export const KV_WORDS_KEY = "kiss-words.json"; export const KV_WORDS_KEY = "kiss-words.json";
@@ -78,9 +84,20 @@ export const URL_RAW_PREFIX =
"https://raw.githubusercontent.com/fishjar/kiss-translator/master"; "https://raw.githubusercontent.com/fishjar/kiss-translator/master";
export const URL_CACHE_TRAN = `https://${APP_LCNAME}/translate`; export const URL_CACHE_TRAN = `https://${APP_LCNAME}/translate`;
// api.cognitive.microsofttranslator.com
export const URL_MICROSOFT_TRAN = export const URL_MICROSOFT_TRAN =
"https://api-edge.cognitive.microsofttranslator.com/translate"; "https://api-edge.cognitive.microsofttranslator.com/translate";
export const URL_MICROSOFT_AUTH = "https://edge.microsoft.com/translate/auth"; export const URL_MICROSOFT_AUTH = "https://edge.microsoft.com/translate/auth";
export const URL_MICROSOFT_LANGDETECT =
"https://api-edge.cognitive.microsofttranslator.com/detect?api-version=3.0";
export const URL_GOOGLE_TRAN =
"https://translate.googleapis.com/translate_a/single";
export const URL_GOOGLE_TRAN2 =
"https://translate-pa.googleapis.com/v1/translateHtml";
export const DEFAULT_GOOGLE_API_KEY = "AIzaSyATBXajvzQLTDHEQbcpq0Ihe0vWDHmO520";
export const URL_BAIDU_LANGDETECT = "https://fanyi.baidu.com/langdetect"; export const URL_BAIDU_LANGDETECT = "https://fanyi.baidu.com/langdetect";
export const URL_BAIDU_SUGGEST = "https://fanyi.baidu.com/sug"; export const URL_BAIDU_SUGGEST = "https://fanyi.baidu.com/sug";
export const URL_BAIDU_TTS = "https://fanyi.baidu.com/gettts"; export const URL_BAIDU_TTS = "https://fanyi.baidu.com/gettts";
@@ -89,13 +106,18 @@ export const URL_BAIDU_TRANSAPI = "https://fanyi.baidu.com/transapi";
export const URL_BAIDU_TRANSAPI_V2 = "https://fanyi.baidu.com/v2transapi"; export const URL_BAIDU_TRANSAPI_V2 = "https://fanyi.baidu.com/v2transapi";
export const URL_DEEPLFREE_TRAN = "https://www2.deepl.com/jsonrpc"; export const URL_DEEPLFREE_TRAN = "https://www2.deepl.com/jsonrpc";
export const URL_TENCENT_TRANSMART = "https://transmart.qq.com/api/imt"; export const URL_TENCENT_TRANSMART = "https://transmart.qq.com/api/imt";
export const URL_VOLCENGINE_TRAN =
"https://translate.volcengine.com/crx/translate/v1";
export const URL_NIUTRANS_REG = export const URL_NIUTRANS_REG =
"https://niutrans.com/login?active=3&userSource=kiss-translator"; "https://niutrans.com/login?active=3&userSource=kiss-translator";
export const DEFAULT_USER_AGENT = export const DEFAULT_USER_AGENT =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"; "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36";
export const OPT_DICT_BAIDU = "Baidu";
export const OPT_TRANS_GOOGLE = "Google"; export const OPT_TRANS_GOOGLE = "Google";
export const OPT_TRANS_GOOGLE_2 = "Google2";
export const OPT_TRANS_MICROSOFT = "Microsoft"; export const OPT_TRANS_MICROSOFT = "Microsoft";
export const OPT_TRANS_DEEPL = "DeepL"; export const OPT_TRANS_DEEPL = "DeepL";
export const OPT_TRANS_DEEPLX = "DeepLX"; export const OPT_TRANS_DEEPLX = "DeepLX";
@@ -103,10 +125,13 @@ export const OPT_TRANS_DEEPLFREE = "DeepLFree";
export const OPT_TRANS_NIUTRANS = "NiuTrans"; export const OPT_TRANS_NIUTRANS = "NiuTrans";
export const OPT_TRANS_BAIDU = "Baidu"; export const OPT_TRANS_BAIDU = "Baidu";
export const OPT_TRANS_TENCENT = "Tencent"; export const OPT_TRANS_TENCENT = "Tencent";
export const OPT_TRANS_VOLCENGINE = "Volcengine";
export const OPT_TRANS_OPENAI = "OpenAI"; export const OPT_TRANS_OPENAI = "OpenAI";
export const OPT_TRANS_OPENAI_2 = "OpenAI2"; export const OPT_TRANS_OPENAI_2 = "OpenAI2";
export const OPT_TRANS_OPENAI_3 = "OpenAI3"; export const OPT_TRANS_OPENAI_3 = "OpenAI3";
export const OPT_TRANS_GEMINI = "Gemini"; export const OPT_TRANS_GEMINI = "Gemini";
export const OPT_TRANS_GEMINI_2 = "Gemini2";
export const OPT_TRANS_CLAUDE = "Claude";
export const OPT_TRANS_CLOUDFLAREAI = "CloudflareAI"; export const OPT_TRANS_CLOUDFLAREAI = "CloudflareAI";
export const OPT_TRANS_OLLAMA = "Ollama"; export const OPT_TRANS_OLLAMA = "Ollama";
export const OPT_TRANS_OLLAMA_2 = "Ollama2"; export const OPT_TRANS_OLLAMA_2 = "Ollama2";
@@ -118,9 +143,11 @@ export const OPT_TRANS_CUSTOMIZE_4 = "Custom4";
export const OPT_TRANS_CUSTOMIZE_5 = "Custom5"; export const OPT_TRANS_CUSTOMIZE_5 = "Custom5";
export const OPT_TRANS_ALL = [ export const OPT_TRANS_ALL = [
OPT_TRANS_GOOGLE, OPT_TRANS_GOOGLE,
OPT_TRANS_GOOGLE_2,
OPT_TRANS_MICROSOFT, OPT_TRANS_MICROSOFT,
OPT_TRANS_BAIDU, OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT, OPT_TRANS_TENCENT,
OPT_TRANS_VOLCENGINE,
OPT_TRANS_DEEPL, OPT_TRANS_DEEPL,
OPT_TRANS_DEEPLFREE, OPT_TRANS_DEEPLFREE,
OPT_TRANS_DEEPLX, OPT_TRANS_DEEPLX,
@@ -129,6 +156,8 @@ export const OPT_TRANS_ALL = [
OPT_TRANS_OPENAI_2, OPT_TRANS_OPENAI_2,
OPT_TRANS_OPENAI_3, OPT_TRANS_OPENAI_3,
OPT_TRANS_GEMINI, OPT_TRANS_GEMINI,
OPT_TRANS_GEMINI_2,
OPT_TRANS_CLAUDE,
OPT_TRANS_CLOUDFLAREAI, OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_OLLAMA, OPT_TRANS_OLLAMA,
OPT_TRANS_OLLAMA_2, OPT_TRANS_OLLAMA_2,
@@ -140,6 +169,13 @@ export const OPT_TRANS_ALL = [
OPT_TRANS_CUSTOMIZE_5, OPT_TRANS_CUSTOMIZE_5,
]; ];
export const OPT_LANGDETECTOR_ALL = [
OPT_TRANS_GOOGLE,
OPT_TRANS_MICROSOFT,
OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT,
];
export const OPT_LANGS_TO = [ export const OPT_LANGS_TO = [
["en", "English - English"], ["en", "English - English"],
["zh-CN", "Simplified Chinese - 简体中文"], ["zh-CN", "Simplified Chinese - 简体中文"],
@@ -182,6 +218,7 @@ export const OPT_LANGS_TO = [
export const OPT_LANGS_FROM = [["auto", "Auto-detect"], ...OPT_LANGS_TO]; export const OPT_LANGS_FROM = [["auto", "Auto-detect"], ...OPT_LANGS_TO];
export const OPT_LANGS_SPECIAL = { export const OPT_LANGS_SPECIAL = {
[OPT_TRANS_GOOGLE]: new Map(OPT_LANGS_FROM.map(([key]) => [key, key])), [OPT_TRANS_GOOGLE]: new Map(OPT_LANGS_FROM.map(([key]) => [key, key])),
[OPT_TRANS_GOOGLE_2]: new Map(OPT_LANGS_FROM.map(([key]) => [key, key])),
[OPT_TRANS_MICROSOFT]: new Map([ [OPT_TRANS_MICROSOFT]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]), ...OPT_LANGS_FROM.map(([key]) => [key, key]),
["auto", ""], ["auto", ""],
@@ -202,7 +239,7 @@ export const OPT_LANGS_SPECIAL = {
]), ]),
[OPT_TRANS_DEEPLX]: new Map([ [OPT_TRANS_DEEPLX]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key.toUpperCase()]), ...OPT_LANGS_FROM.map(([key]) => [key, key.toUpperCase()]),
["auto", ""], ["auto", "auto"],
["zh-CN", "ZH"], ["zh-CN", "ZH"],
["zh-TW", "ZH"], ["zh-TW", "ZH"],
]), ]),
@@ -212,6 +249,12 @@ export const OPT_LANGS_SPECIAL = {
["zh-CN", "zh"], ["zh-CN", "zh"],
["zh-TW", "cht"], ["zh-TW", "cht"],
]), ]),
[OPT_TRANS_VOLCENGINE]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]),
["auto", "auto"],
["zh-CN", "zh"],
["zh-TW", "zh-Hant"],
]),
[OPT_TRANS_BAIDU]: new Map([ [OPT_TRANS_BAIDU]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]), ...OPT_LANGS_FROM.map(([key]) => [key, key]),
["zh-CN", "zh"], ["zh-CN", "zh"],
@@ -273,6 +316,12 @@ export const OPT_LANGS_SPECIAL = {
[OPT_TRANS_GEMINI]: new Map( [OPT_TRANS_GEMINI]: new Map(
OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]])
), ),
[OPT_TRANS_GEMINI_2]: new Map(
OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]])
),
[OPT_TRANS_CLAUDE]: new Map(
OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]])
),
[OPT_TRANS_OLLAMA]: new Map( [OPT_TRANS_OLLAMA]: new Map(
OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]])
), ),
@@ -318,6 +367,12 @@ export const OPT_LANGS_SPECIAL = {
]), ]),
}; };
export const OPT_LANGS_LIST = OPT_LANGS_TO.map(([lang]) => lang); export const OPT_LANGS_LIST = OPT_LANGS_TO.map(([lang]) => lang);
export const OPT_LANGS_MICROSOFT = new Map(
Array.from(OPT_LANGS_SPECIAL[OPT_TRANS_MICROSOFT].entries()).map(([k, v]) => [
v,
k,
])
);
export const OPT_LANGS_BAIDU = new Map( export const OPT_LANGS_BAIDU = new Map(
Array.from(OPT_LANGS_SPECIAL[OPT_TRANS_BAIDU].entries()).map(([k, v]) => [ Array.from(OPT_LANGS_SPECIAL[OPT_TRANS_BAIDU].entries()).map(([k, v]) => [
v, v,
@@ -413,6 +468,7 @@ export const GLOBLA_RULE = {
transTiming: OPT_TIMING_PAGESCROLL, // 翻译时机/鼠标悬停翻译 transTiming: OPT_TIMING_PAGESCROLL, // 翻译时机/鼠标悬停翻译
transTag: DEFAULT_TRANS_TAG, // 译文元素标签 transTag: DEFAULT_TRANS_TAG, // 译文元素标签
transTitle: "false", // 是否同时翻译页面标题 transTitle: "false", // 是否同时翻译页面标题
transSelected: "true", // 是否启用划词翻译
detectRemote: "false", // 是否使用远程语言检测 detectRemote: "false", // 是否使用远程语言检测
skipLangs: [], // 不翻译的语言 skipLangs: [], // 不翻译的语言
fixerSelector: "", // 修复函数选择器 fixerSelector: "", // 修复函数选择器
@@ -451,7 +507,7 @@ export const OPT_TRANBOX_TRIGGER_ALL = [
]; ];
export const DEFAULT_TRANBOX_SHORTCUT = ["AltLeft", "KeyS"]; export const DEFAULT_TRANBOX_SHORTCUT = ["AltLeft", "KeyS"];
export const DEFAULT_TRANBOX_SETTING = { export const DEFAULT_TRANBOX_SETTING = {
transOpen: true, // transOpen: true, // 是否启用划词翻译作废移至rule
translator: OPT_TRANS_MICROSOFT, translator: OPT_TRANS_MICROSOFT,
fromLang: "auto", fromLang: "auto",
toLang: "zh-CN", toLang: "zh-CN",
@@ -467,6 +523,7 @@ export const DEFAULT_TRANBOX_SETTING = {
followSelection: false, // 翻译框是否跟随选中文本 followSelection: false, // 翻译框是否跟随选中文本
triggerMode: OPT_TRANBOX_TRIGGER_CLICK, // 触发翻译方式 triggerMode: OPT_TRANBOX_TRIGGER_CLICK, // 触发翻译方式
extStyles: "", // 附加样式 extStyles: "", // 附加样式
enDict: OPT_DICT_BAIDU, // 英文词典
}; };
// 订阅列表 // 订阅列表
@@ -485,6 +542,8 @@ export const DEFAULT_SUBRULES_LIST = [
}, },
]; ];
export const DEFAULT_HTTP_TIMEOUT = 5000; // 调用超时时间
// 翻译接口 // 翻译接口
const defaultCustomApi = { const defaultCustomApi = {
url: "", url: "",
@@ -494,57 +553,109 @@ const defaultCustomApi = {
resHook: "", // response 钩子函数 resHook: "", // response 钩子函数
fetchLimit: DEFAULT_FETCH_LIMIT, fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL, fetchInterval: DEFAULT_FETCH_INTERVAL,
apiName: "",
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}; };
const defaultOpenaiApi = { const defaultOpenaiApi = {
url: "https://api.openai.com/v1/chat/completions", url: "https://api.openai.com/v1/chat/completions",
key: "", key: "",
model: "gpt-4", model: "gpt-4",
prompt: `You will be provided with a sentence in ${INPUT_PLACE_FROM}, and your task is to translate it into ${INPUT_PLACE_TO}.`, systemPrompt: `You are a professional, authentic machine translation engine.`,
userPrompt: `Translate the following source text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}. Output translation directly without any additional text.\n\nSource Text: ${INPUT_PLACE_TEXT}\n\nTranslated Text:`,
temperature: 0,
maxTokens: 256,
fetchLimit: 1, fetchLimit: 1,
fetchInterval: 500, fetchInterval: 500,
apiName: "",
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT * 2,
}; };
const defaultOllamaApi = { const defaultOllamaApi = {
url: "http://localhost:11434/api/generate", url: "http://localhost:11434/api/generate",
key: "", key: "",
model: "llama3", model: "llama3.1",
prompt: `Translate the following text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}:\n\n${INPUT_PLACE_TEXT}`, systemPrompt: `You are a professional, authentic machine translation engine.`,
userPrompt: `Translate the following source text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}. Output translation directly without any additional text.\n\nSource Text: ${INPUT_PLACE_TEXT}\n\nTranslated Text:`,
think: false,
thinkIgnore: `qwen3,deepseek-r1`,
fetchLimit: 1, fetchLimit: 1,
fetchInterval: 500, fetchInterval: 500,
apiName: "",
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT * 2,
}; };
export const DEFAULT_TRANS_APIS = { export const DEFAULT_TRANS_APIS = {
[OPT_TRANS_GOOGLE]: { [OPT_TRANS_GOOGLE]: {
url: "https://translate.googleapis.com/translate_a/single", url: URL_GOOGLE_TRAN,
key: "", key: "",
fetchLimit: DEFAULT_FETCH_LIMIT, // 最大任务数量 fetchLimit: DEFAULT_FETCH_LIMIT, // 最大任务数量
fetchInterval: DEFAULT_FETCH_INTERVAL, // 任务间隔时间 fetchInterval: DEFAULT_FETCH_INTERVAL, // 任务间隔时间
apiName: OPT_TRANS_GOOGLE, // 接口自定义名称
isDisabled: false, // 是否禁用
httpTimeout: DEFAULT_HTTP_TIMEOUT, // 超时时间
},
[OPT_TRANS_GOOGLE_2]: {
url: URL_GOOGLE_TRAN2,
key: DEFAULT_GOOGLE_API_KEY,
fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL,
apiName: OPT_TRANS_GOOGLE_2,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}, },
[OPT_TRANS_MICROSOFT]: { [OPT_TRANS_MICROSOFT]: {
fetchLimit: DEFAULT_FETCH_LIMIT, fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL, fetchInterval: DEFAULT_FETCH_INTERVAL,
apiName: OPT_TRANS_MICROSOFT,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}, },
[OPT_TRANS_BAIDU]: { [OPT_TRANS_BAIDU]: {
fetchLimit: DEFAULT_FETCH_LIMIT, fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL, fetchInterval: DEFAULT_FETCH_INTERVAL,
apiName: OPT_TRANS_BAIDU,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}, },
[OPT_TRANS_TENCENT]: { [OPT_TRANS_TENCENT]: {
fetchLimit: DEFAULT_FETCH_LIMIT, fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL, fetchInterval: DEFAULT_FETCH_INTERVAL,
apiName: OPT_TRANS_TENCENT,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
},
[OPT_TRANS_VOLCENGINE]: {
fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL,
apiName: OPT_TRANS_VOLCENGINE,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}, },
[OPT_TRANS_DEEPL]: { [OPT_TRANS_DEEPL]: {
url: "https://api-free.deepl.com/v2/translate", url: "https://api-free.deepl.com/v2/translate",
key: "", key: "",
fetchLimit: 1, fetchLimit: 1,
fetchInterval: 500, fetchInterval: 500,
apiName: OPT_TRANS_DEEPL,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}, },
[OPT_TRANS_DEEPLFREE]: { [OPT_TRANS_DEEPLFREE]: {
fetchLimit: 1, fetchLimit: 1,
fetchInterval: 500, fetchInterval: 500,
apiName: OPT_TRANS_DEEPLFREE,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}, },
[OPT_TRANS_DEEPLX]: { [OPT_TRANS_DEEPLX]: {
url: "http://localhost:1188/translate", url: "http://localhost:1188/translate",
key: "", key: "",
fetchLimit: 1, fetchLimit: 1,
fetchInterval: 500, fetchInterval: 500,
apiName: OPT_TRANS_DEEPLX,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}, },
[OPT_TRANS_NIUTRANS]: { [OPT_TRANS_NIUTRANS]: {
url: "https://api.niutrans.com/NiuTransServer/translation", url: "https://api.niutrans.com/NiuTransServer/translation",
@@ -553,6 +664,9 @@ export const DEFAULT_TRANS_APIS = {
memoryNo: "", memoryNo: "",
fetchLimit: DEFAULT_FETCH_LIMIT, fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL, fetchInterval: DEFAULT_FETCH_INTERVAL,
apiName: OPT_TRANS_NIUTRANS,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
}, },
[OPT_TRANS_OPENAI]: defaultOpenaiApi, [OPT_TRANS_OPENAI]: defaultOpenaiApi,
[OPT_TRANS_OPENAI_2]: defaultOpenaiApi, [OPT_TRANS_OPENAI_2]: defaultOpenaiApi,
@@ -560,16 +674,53 @@ export const DEFAULT_TRANS_APIS = {
[OPT_TRANS_GEMINI]: { [OPT_TRANS_GEMINI]: {
url: `https://generativelanguage.googleapis.com/v1/models/${INPUT_PLACE_MODEL}:generateContent?key=${INPUT_PLACE_KEY}`, url: `https://generativelanguage.googleapis.com/v1/models/${INPUT_PLACE_MODEL}:generateContent?key=${INPUT_PLACE_KEY}`,
key: "", key: "",
model: "gemini-pro", model: "gemini-2.5-flash",
prompt: `Translate the following text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}:\n\n${INPUT_PLACE_TEXT}`, systemPrompt: `You are a professional, authentic machine translation engine.`,
userPrompt: `Translate the following source text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}. Output translation directly without any additional text.\n\nSource Text: ${INPUT_PLACE_TEXT}\n\nTranslated Text:`,
temperature: 0,
maxTokens: 2048,
fetchLimit: 1, fetchLimit: 1,
fetchInterval: 500, fetchInterval: 500,
apiName: OPT_TRANS_GEMINI,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT * 2,
},
[OPT_TRANS_GEMINI_2]: {
url: `https://generativelanguage.googleapis.com/v1beta/openai/chat/completions`,
key: "",
model: "gemini-2.0-flash",
systemPrompt: `You are a professional, authentic machine translation engine.`,
userPrompt: `Translate the following source text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}. Output translation directly without any additional text.\n\nSource Text: ${INPUT_PLACE_TEXT}\n\nTranslated Text:`,
temperature: 0,
maxTokens: 2048,
fetchLimit: 1,
fetchInterval: 500,
apiName: OPT_TRANS_GEMINI_2,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT * 2,
},
[OPT_TRANS_CLAUDE]: {
url: "https://api.anthropic.com/v1/messages",
key: "",
model: "claude-3-haiku-20240307",
systemPrompt: `You are a professional, authentic machine translation engine.`,
userPrompt: `Translate the following source text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}. Output translation directly without any additional text.\n\nSource Text: ${INPUT_PLACE_TEXT}\n\nTranslated Text:`,
temperature: 0,
maxTokens: 1024,
fetchLimit: 1,
fetchInterval: 500,
apiName: OPT_TRANS_CLAUDE,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT * 2,
}, },
[OPT_TRANS_CLOUDFLAREAI]: { [OPT_TRANS_CLOUDFLAREAI]: {
url: "https://api.cloudflare.com/client/v4/accounts/{{ACCOUNT_ID}}/ai/run/@cf/meta/m2m100-1.2b", url: "https://api.cloudflare.com/client/v4/accounts/{{ACCOUNT_ID}}/ai/run/@cf/meta/m2m100-1.2b",
key: "", key: "",
fetchLimit: 1, fetchLimit: 1,
fetchInterval: 500, fetchInterval: 500,
apiName: OPT_TRANS_CLOUDFLAREAI,
isDisabled: false,
httpTimeout: DEFAULT_HTTP_TIMEOUT * 2,
}, },
[OPT_TRANS_OLLAMA]: defaultOllamaApi, [OPT_TRANS_OLLAMA]: defaultOllamaApi,
[OPT_TRANS_OLLAMA_2]: defaultOllamaApi, [OPT_TRANS_OLLAMA_2]: defaultOllamaApi,
@@ -613,6 +764,7 @@ export const DEFAULT_SETTING = {
minLength: TRANS_MIN_LENGTH, minLength: TRANS_MIN_LENGTH,
maxLength: TRANS_MAX_LENGTH, maxLength: TRANS_MAX_LENGTH,
newlineLength: TRANS_NEWLINE_LENGTH, newlineLength: TRANS_NEWLINE_LENGTH,
httpTimeout: DEFAULT_HTTP_TIMEOUT,
clearCache: false, // 是否在浏览器下次启动时清除缓存 clearCache: false, // 是否在浏览器下次启动时清除缓存
injectRules: true, // 是否注入订阅规则 injectRules: true, // 是否注入订阅规则
// injectWebfix: true, // 是否注入修复补丁(作废) // injectWebfix: true, // 是否注入修复补丁(作废)
@@ -634,6 +786,7 @@ export const DEFAULT_SETTING = {
csplist: DEFAULT_CSPLIST.join(",\n"), // 禁用CSP名单 csplist: DEFAULT_CSPLIST.join(",\n"), // 禁用CSP名单
// disableLangs: [], // 不翻译的语言(移至rule作废) // disableLangs: [], // 不翻译的语言(移至rule作废)
transInterval: 500, // 翻译间隔时间 transInterval: 500, // 翻译间隔时间
langDetector: OPT_TRANS_MICROSOFT, // 远程语言识别服务
}; };
export const DEFAULT_RULES = [GLOBLA_RULE]; export const DEFAULT_RULES = [GLOBLA_RULE];

View File

@@ -26,6 +26,7 @@ export const DEFAULT_RULE = {
transTiming: GLOBAL_KEY, // 翻译时机/鼠标悬停翻译 transTiming: GLOBAL_KEY, // 翻译时机/鼠标悬停翻译
transTag: GLOBAL_KEY, // 译文元素标签 transTag: GLOBAL_KEY, // 译文元素标签
transTitle: GLOBAL_KEY, // 是否同时翻译页面标题 transTitle: GLOBAL_KEY, // 是否同时翻译页面标题
transSelected: GLOBAL_KEY, // 是否启用划词翻译
detectRemote: GLOBAL_KEY, // 是否使用远程语言检测 detectRemote: GLOBAL_KEY, // 是否使用远程语言检测
skipLangs: [], // 不翻译的语言 skipLangs: [], // 不翻译的语言
fixerSelector: "", // 修复函数选择器 fixerSelector: "", // 修复函数选择器

View File

@@ -30,7 +30,14 @@ export function useTranslate(q, rule, setting) {
return; return;
} }
const deLang = await tryDetectLang(q, detectRemote === "true"); let deLang = "";
if (fromLang === "auto") {
deLang = await tryDetectLang(
q,
detectRemote === "true",
setting.langDetector
);
}
if (deLang && (toLang.includes(deLang) || skipLangs.includes(deLang))) { if (deLang && (toLang.includes(deLang) || skipLangs.includes(deLang))) {
setSamelang(true); setSamelang(true);
} else { } else {

View File

@@ -1,20 +1,21 @@
import { isExt, isGm } from "./client"; import { isExt, isGm } from "./client";
import { sendBgMsg } from "./msg"; import { sendBgMsg } from "./msg";
import { taskPool } from "./pool"; import { taskPool } from "./pool";
import { getSettingWithDefault } from "./storage";
import { import {
MSG_FETCH, MSG_FETCH,
MSG_GET_HTTPCACHE, MSG_GET_HTTPCACHE,
CACHE_NAME, CACHE_NAME,
DEFAULT_FETCH_INTERVAL, DEFAULT_FETCH_INTERVAL,
DEFAULT_FETCH_LIMIT, DEFAULT_FETCH_LIMIT,
DEFAULT_HTTP_TIMEOUT,
} from "../config"; } from "../config";
import { isBg } from "./browser"; import { isBg } from "./browser";
import { genTransReq } from "../apis/trans"; import { genTransReq } from "../apis/trans";
import { kissLog } from "./log"; import { kissLog } from "./log";
import { blobToBase64 } from "./utils"; import { blobToBase64 } from "./utils";
const TIMEOUT = 5000;
/** /**
* 构造缓存 request * 构造缓存 request
* @param {*} input * @param {*} input
@@ -39,7 +40,10 @@ const newCacheReq = async (input, init) => {
* @param {*} init * @param {*} init
* @returns * @returns
*/ */
export const fetchGM = async (input, { method = "GET", headers, body } = {}) => export const fetchGM = async (
input,
{ method = "GET", headers, body, timeout } = {}
) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
GM.xmlHttpRequest({ GM.xmlHttpRequest({
method, method,
@@ -47,7 +51,7 @@ export const fetchGM = async (input, { method = "GET", headers, body } = {}) =>
headers, headers,
data: body, data: body,
// withCredentials: true, // withCredentials: true,
timeout: TIMEOUT, timeout,
onload: ({ response, responseHeaders, status, statusText }) => { onload: ({ response, responseHeaders, status, statusText }) => {
const headers = {}; const headers = {};
responseHeaders.split("\n").forEach((line) => { responseHeaders.split("\n").forEach((line) => {
@@ -81,35 +85,60 @@ export const fetchPatcher = async (input, init, transOpts, apiSetting) => {
throw new Error("url is empty"); throw new Error("url is empty");
} }
if (isGm) { let timeout = apiSetting?.httpTimeout || DEFAULT_HTTP_TIMEOUT;
let info; if (!apiSetting) {
if (window.KISS_GM) { try {
info = await window.KISS_GM.getInfo(); timeout = (await getSettingWithDefault()).httpTimeout;
} else { } catch (err) {
info = GM.info; //
}
// Tampermonkey --> .connects
// Violentmonkey --> .connect
const connects = info?.script?.connects || info?.script?.connect || [];
const url = new URL(input);
const isSafe = connects.find((item) => url.hostname.endsWith(item));
if (isSafe) {
const { body, headers, status, statusText } = window.KISS_GM
? await window.KISS_GM.fetch(input, init)
: await fetchGM(input, init);
return new Response(body, {
headers: new Headers(headers),
status,
statusText,
});
} }
} }
if (AbortSignal?.timeout) { if (isGm) {
Object.assign(init, { signal: AbortSignal.timeout(TIMEOUT) }); // let info;
// if (window.KISS_GM) {
// info = await window.KISS_GM.getInfo();
// } else {
// info = GM.info;
// }
// Tampermonkey --> .connects
// Violentmonkey --> .connect
// const connects = info?.script?.connects || info?.script?.connect || [];
// const url = new URL(input);
// const isSafe = connects.find((item) => url.hostname.endsWith(item));
// if (isSafe) {
// // todo: 自定义接口 init 可能包含了 signal
// Object.assign(init, { timeout });
// const { body, headers, status, statusText } = window.KISS_GM
// ? await window.KISS_GM.fetch(input, init)
// : await fetchGM(input, init);
// return new Response(body, {
// headers: new Headers(headers),
// status,
// statusText,
// });
// }
// todo: 自定义接口 init 可能包含了 signal
Object.assign(init, { timeout });
const { body, headers, status, statusText } = window.KISS_GM
? await window.KISS_GM.fetch(input, init)
: await fetchGM(input, init);
return new Response(body, {
headers: new Headers(headers),
status,
statusText,
});
}
if (AbortSignal?.timeout && !init.signal) {
Object.assign(init, { signal: AbortSignal.timeout(timeout) });
} }
return fetch(input, init); return fetch(input, init);

View File

@@ -1,8 +1,26 @@
import { CACHE_NAME } from "../config"; import {
CACHE_NAME,
OPT_TRANS_GOOGLE,
OPT_TRANS_MICROSOFT,
OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT,
} from "../config";
import { browser } from "./browser"; import { browser } from "./browser";
import { apiBaiduLangdetect } from "../apis"; import {
apiGoogleLangdetect,
apiMicrosoftLangdetect,
apiBaiduLangdetect,
apiTencentLangdetect,
} from "../apis";
import { kissLog } from "./log"; import { kissLog } from "./log";
const langdetectMap = {
[OPT_TRANS_GOOGLE]: apiGoogleLangdetect,
[OPT_TRANS_MICROSOFT]: apiMicrosoftLangdetect,
[OPT_TRANS_BAIDU]: apiBaiduLangdetect,
[OPT_TRANS_TENCENT]: apiTencentLangdetect,
};
/** /**
* 清除缓存数据 * 清除缓存数据
*/ */
@@ -19,12 +37,16 @@ export const tryClearCaches = async () => {
* @param {*} q * @param {*} q
* @returns * @returns
*/ */
export const tryDetectLang = async (q, useRemote = false) => { export const tryDetectLang = async (
q,
useRemote = false,
langDetector = OPT_TRANS_MICROSOFT
) => {
let lang = ""; let lang = "";
if (useRemote) { if (useRemote) {
try { try {
lang = await apiBaiduLangdetect(q); lang = await langdetectMap[langDetector](q);
} catch (err) { } catch (err) {
kissLog(err, "detect lang remote"); kissLog(err, "detect lang remote");
} }

View File

@@ -92,6 +92,7 @@ export const matchRule = async (
"transTiming", "transTiming",
"transTag", "transTag",
"transTitle", "transTitle",
"transSelected",
"detectRemote", "detectRemote",
"fixerFunc", "fixerFunc",
].forEach((key) => { ].forEach((key) => {
@@ -161,6 +162,7 @@ export const checkRules = (rules) => {
transTiming, transTiming,
transTag, transTag,
transTitle, transTitle,
transSelected,
detectRemote, detectRemote,
skipLangs, skipLangs,
fixerSelector, fixerSelector,
@@ -188,6 +190,7 @@ export const checkRules = (rules) => {
transTiming: matchValue([GLOBAL_KEY, ...OPT_TIMING_ALL], transTiming), transTiming: matchValue([GLOBAL_KEY, ...OPT_TIMING_ALL], transTiming),
transTag: matchValue([GLOBAL_KEY, "span", "font"], transTag), transTag: matchValue([GLOBAL_KEY, "span", "font"], transTag),
transTitle: matchValue([GLOBAL_KEY, "true", "false"], transTitle), transTitle: matchValue([GLOBAL_KEY, "true", "false"], transTitle),
transSelected: matchValue([GLOBAL_KEY, "true", "false"], transSelected),
detectRemote: matchValue([GLOBAL_KEY, "true", "false"], detectRemote), detectRemote: matchValue([GLOBAL_KEY, "true", "false"], detectRemote),
skipLangs: type(skipLangs) === "array" ? skipLangs : [], skipLangs: type(skipLangs) === "array" ? skipLangs : [],
fixerSelector: type(fixerSelector) === "string" ? fixerSelector : "", fixerSelector: type(fixerSelector) === "string" ? fixerSelector : "",

View File

@@ -53,7 +53,7 @@ export class Translator {
]; ];
_eventName = genEventName(); _eventName = genEventName();
_mouseoverNode = null; _mouseoverNode = null;
_keepSelector = [null, null]; _keepSelector = "";
_terms = []; _terms = [];
_docTitle = ""; _docTitle = "";
@@ -125,9 +125,7 @@ export class Translator {
this._setting = setting; this._setting = setting;
this._rule = rule; this._rule = rule;
this._keepSelector = (rule.keepSelector || "") this._keepSelector = rule.keepSelector || "";
.split(SHADOW_KEY)
.map((item) => item.trim());
this._terms = (rule.terms || "") this._terms = (rule.terms || "")
.split(/\n|;/) .split(/\n|;/)
.map((item) => item.split(",").map((item) => item.trim())) .map((item) => item.split(",").map((item) => item.trim()))
@@ -507,22 +505,18 @@ export class Translator {
} }
// 保留元素 // 保留元素
const [matchSelector, subSelector] = this._keepSelector; const keepSelector = this._keepSelector.trim();
if (matchSelector || subSelector) { if (keepSelector) {
let text = ""; let text = "";
el.childNodes.forEach((child) => { el.childNodes.forEach((child) => {
if ( if (child.nodeType === 1 && child.matches(keepSelector)) {
child.nodeType === 1 &&
((matchSelector && child.matches(matchSelector)) ||
(subSelector && child.querySelector(subSelector)))
) {
if (child.nodeName === "IMG") { if (child.nodeName === "IMG") {
child.style.cssText += `width: ${child.width}px;`; child.style.cssText += `width: ${child.width}px;`;
child.style.cssText += `height: ${child.height}px;`; child.style.cssText += `height: ${child.height}px;`;
} }
text += `[${keeps.length}]`; text += `[${keeps.length}]`;
keeps.push(child.outerHTML); keeps.push(child.outerHTML);
} else { } else if (child.nodeType === 1 || child.nodeType === 3) {
text += child.textContent; text += child.textContent;
} }
}); });

View File

@@ -15,6 +15,16 @@ export const limitNumber = (num, min = 0, max = 100) => {
return number; return number;
}; };
export const limitFloat = (num, min = 0, max = 100) => {
const number = parseFloat(num);
if (Number.isNaN(number) || number < min) {
return min;
} else if (number > max) {
return max;
}
return number;
};
/** /**
* 匹配是否为数组中的值 * 匹配是否为数组中的值
* @param {*} arr * @param {*} arr

View File

@@ -2,6 +2,9 @@ import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import LoadingButton from "@mui/lab/LoadingButton"; import LoadingButton from "@mui/lab/LoadingButton";
import MenuItem from "@mui/material/MenuItem";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import { import {
OPT_TRANS_ALL, OPT_TRANS_ALL,
OPT_TRANS_MICROSOFT, OPT_TRANS_MICROSOFT,
@@ -10,20 +13,28 @@ import {
OPT_TRANS_DEEPLFREE, OPT_TRANS_DEEPLFREE,
OPT_TRANS_BAIDU, OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT, OPT_TRANS_TENCENT,
OPT_TRANS_VOLCENGINE,
OPT_TRANS_OPENAI, OPT_TRANS_OPENAI,
OPT_TRANS_OPENAI_2, OPT_TRANS_OPENAI_2,
OPT_TRANS_OPENAI_3, OPT_TRANS_OPENAI_3,
OPT_TRANS_GEMINI, OPT_TRANS_GEMINI,
OPT_TRANS_GEMINI_2,
OPT_TRANS_CLAUDE,
OPT_TRANS_CLOUDFLAREAI, OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_OLLAMA, OPT_TRANS_OLLAMA,
OPT_TRANS_OLLAMA_2, OPT_TRANS_OLLAMA_2,
OPT_TRANS_OLLAMA_3, OPT_TRANS_OLLAMA_3,
OPT_TRANS_CUSTOMIZE, OPT_TRANS_CUSTOMIZE,
OPT_TRANS_CUSTOMIZE_2,
OPT_TRANS_CUSTOMIZE_3,
OPT_TRANS_CUSTOMIZE_4,
OPT_TRANS_CUSTOMIZE_5,
OPT_TRANS_NIUTRANS, OPT_TRANS_NIUTRANS,
URL_KISS_PROXY, URL_KISS_PROXY,
URL_NIUTRANS_REG, URL_NIUTRANS_REG,
DEFAULT_FETCH_LIMIT, DEFAULT_FETCH_LIMIT,
DEFAULT_FETCH_INTERVAL, DEFAULT_FETCH_INTERVAL,
DEFAULT_HTTP_TIMEOUT,
} from "../../config"; } from "../../config";
import { useState } from "react"; import { useState } from "react";
import { useI18n } from "../../hooks/I18n"; import { useI18n } from "../../hooks/I18n";
@@ -38,7 +49,7 @@ import { useApi } from "../../hooks/Api";
import { apiTranslate } from "../../apis"; import { apiTranslate } from "../../apis";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Link from "@mui/material/Link"; import Link from "@mui/material/Link";
import { limitNumber } from "../../libs/utils"; import { limitNumber, limitFloat } from "../../libs/utils";
function TestButton({ translator, api }) { function TestButton({ translator, api }) {
const i18n = useI18n(); const i18n = useI18n();
@@ -56,7 +67,7 @@ function TestButton({ translator, api }) {
useCache: false, useCache: false,
}); });
if (!text) { if (!text) {
throw new Error("empty reault"); throw new Error("empty result");
} }
alert.success(i18n("test_success")); alert.success(i18n("test_success"));
} catch (err) { } catch (err) {
@@ -114,13 +125,21 @@ function ApiFields({ translator }) {
url = "", url = "",
key = "", key = "",
model = "", model = "",
prompt = "", systemPrompt = "",
userPrompt = "",
think = false,
thinkIgnore = "",
fetchLimit = DEFAULT_FETCH_LIMIT, fetchLimit = DEFAULT_FETCH_LIMIT,
fetchInterval = DEFAULT_FETCH_INTERVAL, fetchInterval = DEFAULT_FETCH_INTERVAL,
httpTimeout = DEFAULT_HTTP_TIMEOUT,
dictNo = "", dictNo = "",
memoryNo = "", memoryNo = "",
reqHook = "", reqHook = "",
resHook = "", resHook = "",
temperature = 0,
maxTokens = 256,
apiName = "",
isDisabled = false,
} = api; } = api;
const handleChange = (e) => { const handleChange = (e) => {
@@ -132,6 +151,15 @@ function ApiFields({ translator }) {
case "fetchInterval": case "fetchInterval":
value = limitNumber(value, 0, 5000); value = limitNumber(value, 0, 5000);
break; break;
case "httpTimeout":
value = limitNumber(value, 5000, 30000);
break;
case "temperature":
value = limitFloat(value, 0, 2);
break;
case "maxTokens":
value = limitNumber(value, 0, 2 ** 15);
break;
default: default:
} }
updateApi({ updateApi({
@@ -144,6 +172,7 @@ function ApiFields({ translator }) {
OPT_TRANS_DEEPLFREE, OPT_TRANS_DEEPLFREE,
OPT_TRANS_BAIDU, OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT, OPT_TRANS_TENCENT,
OPT_TRANS_VOLCENGINE,
]; ];
const mulkeysTranslators = [ const mulkeysTranslators = [
@@ -152,11 +181,18 @@ function ApiFields({ translator }) {
OPT_TRANS_OPENAI_2, OPT_TRANS_OPENAI_2,
OPT_TRANS_OPENAI_3, OPT_TRANS_OPENAI_3,
OPT_TRANS_GEMINI, OPT_TRANS_GEMINI,
OPT_TRANS_GEMINI_2,
OPT_TRANS_CLAUDE,
OPT_TRANS_CLOUDFLAREAI, OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_OLLAMA, OPT_TRANS_OLLAMA,
OPT_TRANS_OLLAMA_2, OPT_TRANS_OLLAMA_2,
OPT_TRANS_OLLAMA_3, OPT_TRANS_OLLAMA_3,
OPT_TRANS_NIUTRANS, OPT_TRANS_NIUTRANS,
OPT_TRANS_CUSTOMIZE,
OPT_TRANS_CUSTOMIZE_2,
OPT_TRANS_CUSTOMIZE_3,
OPT_TRANS_CUSTOMIZE_4,
OPT_TRANS_CUSTOMIZE_5,
]; ];
const keyHelper = const keyHelper =
@@ -175,6 +211,14 @@ function ApiFields({ translator }) {
return ( return (
<Stack spacing={3}> <Stack spacing={3}>
<TextField
size="small"
label={i18n("api_name")}
name="apiName"
value={apiName}
onChange={handleChange}
/>
{!builtinTranslators.includes(translator) && ( {!builtinTranslators.includes(translator) && (
<> <>
<TextField <TextField
@@ -204,7 +248,8 @@ function ApiFields({ translator }) {
{(translator.startsWith(OPT_TRANS_OPENAI) || {(translator.startsWith(OPT_TRANS_OPENAI) ||
translator.startsWith(OPT_TRANS_OLLAMA) || translator.startsWith(OPT_TRANS_OLLAMA) ||
translator === OPT_TRANS_GEMINI) && ( translator === OPT_TRANS_CLAUDE ||
translator.startsWith(OPT_TRANS_GEMINI)) && (
<> <>
<TextField <TextField
size="small" size="small"
@@ -215,13 +260,69 @@ function ApiFields({ translator }) {
/> />
<TextField <TextField
size="small" size="small"
label={"PROMPT"} label={"SYSTEM PROMPT"}
name="prompt" name="systemPrompt"
value={prompt} value={systemPrompt}
onChange={handleChange} onChange={handleChange}
multiline multiline
maxRows={10} maxRows={10}
/> />
<TextField
size="small"
label={"USER PROMPT"}
name="userPrompt"
value={userPrompt}
onChange={handleChange}
multiline
maxRows={10}
/>
</>
)}
{translator.startsWith(OPT_TRANS_OLLAMA) && (
<>
<TextField
select
size="small"
name="think"
value={think}
label={i18n("if_think")}
onChange={handleChange}
>
<MenuItem value={false}>{i18n("nothink")}</MenuItem>
<MenuItem value={true}>{i18n("think")}</MenuItem>
</TextField>
<TextField
size="small"
label={i18n("think_ignore")}
name="thinkIgnore"
value={thinkIgnore}
onChange={handleChange}
/>
</>
)}
{(translator.startsWith(OPT_TRANS_OPENAI) ||
translator === OPT_TRANS_CLAUDE ||
translator === OPT_TRANS_GEMINI ||
translator === OPT_TRANS_GEMINI_2) && (
<>
<TextField
size="small"
label={"Temperature"}
type="number"
name="temperature"
value={temperature}
onChange={handleChange}
/>
<TextField
size="small"
label={"Max Tokens"}
type="number"
name="maxTokens"
value={maxTokens}
onChange={handleChange}
/>
</> </>
)} )}
@@ -285,6 +386,29 @@ function ApiFields({ translator }) {
onChange={handleChange} onChange={handleChange}
/> />
<TextField
size="small"
label={i18n("http_timeout")}
type="number"
name="httpTimeout"
defaultValue={httpTimeout}
onChange={handleChange}
/>
<FormControlLabel
control={
<Switch
size="small"
name="isDisabled"
checked={isDisabled}
onChange={() => {
updateApi({ isDisabled: !isDisabled });
}}
/>
}
label={i18n("is_disabled")}
/>
<Stack direction="row" spacing={2}> <Stack direction="row" spacing={2}>
<TestButton translator={translator} api={api} /> <TestButton translator={translator} api={api} />
<Button <Button

View File

@@ -96,9 +96,7 @@ export default function FavWords() {
.join(" "), .join(" "),
dictResult.content[0].mean dictResult.content[0].mean
.map(({ pre, cont }) => { .map(({ pre, cont }) => {
return ` - ${pre ? `[${pre}] ` : ""}${Object.keys(cont).join( return ` - ${pre ? `[${pre}] ` : ""}${Object.keys(cont).join("; ")}`;
"; "
)}`;
}) })
.join("\n"), .join("\n"),
].join("\n\n") ].join("\n\n")

View File

@@ -27,6 +27,7 @@ import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary"; import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails"; import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
import { useRules } from "../../hooks/Rules"; import { useRules } from "../../hooks/Rules";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
@@ -93,6 +94,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
transTiming = OPT_TIMING_PAGESCROLL, transTiming = OPT_TIMING_PAGESCROLL,
transTag = DEFAULT_TRANS_TAG, transTag = DEFAULT_TRANS_TAG,
transTitle = "false", transTitle = "false",
transSelected = "true",
detectRemote = "false", detectRemote = "false",
skipLangs = [], skipLangs = [],
fixerSelector = "", fixerSelector = "",
@@ -180,6 +182,30 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
</MenuItem> </MenuItem>
); );
const ShowMoreButton = showMore ? (
<Button
size="small"
variant="text"
onClick={() => {
setShowMore(false);
}}
startIcon={<ExpandLessIcon />}
>
{i18n("less")}
</Button>
) : (
<Button
size="small"
variant="text"
onClick={() => {
setShowMore(true);
}}
startIcon={<ExpandMoreIcon />}
>
{i18n("more")}
</Button>
);
return ( return (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Stack spacing={2}> <Stack spacing={2}>
@@ -342,95 +368,139 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
/> />
)} )}
<Box>
<Grid container spacing={2} columns={12}>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transOnly"
value={transOnly}
label={i18n("show_only_translations")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTiming"
value={transTiming}
label={i18n("trigger_mode")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
{OPT_TIMING_ALL.map((item) => (
<MenuItem key={item} value={item}>
{i18n(item)}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTag"
value={transTag}
label={i18n("translation_element_tag")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"span"}>{`<span>`}</MenuItem>
<MenuItem value={"font"}>{`<font>`}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTitle"
value={transTitle}
label={i18n("translate_page_title")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transSelected"
value={transSelected}
label={i18n("translate_selected")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="detectRemote"
value={detectRemote}
label={i18n("detect_lang_remote")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
</Grid>
</Box>
{showMore && ( {showMore && (
<> <>
<Box> <TextField
<Grid container spacing={2} columns={12}> size="small"
<Grid item xs={12} sm={6} md={3} lg={2}> label={i18n("fixer_selector")}
<TextField name="fixerSelector"
select value={fixerSelector}
size="small" disabled={disabled}
fullWidth onChange={handleChange}
name="transOnly" multiline
value={transOnly} maxRows={10}
label={i18n("show_only_translations")} />
disabled={disabled} <TextField
onChange={handleChange} select
> size="small"
{GlobalItem} name="fixerFunc"
<MenuItem value={"false"}>{i18n("disable")}</MenuItem> value={fixerFunc}
<MenuItem value={"true"}>{i18n("enable")}</MenuItem> label={i18n("fixer_function")}
</TextField> helperText={i18n("fixer_function_helper")}
</Grid> disabled={disabled}
<Grid item xs={12} sm={6} md={3} lg={2}> onChange={handleChange}
<TextField >
select {GlobalItem}
size="small" {FIXER_ALL.map((item) => (
fullWidth <MenuItem key={item} value={item}>
name="transTiming" {item}
value={transTiming} </MenuItem>
label={i18n("trigger_mode")} ))}
disabled={disabled} </TextField>
onChange={handleChange}
>
{GlobalItem}
{OPT_TIMING_ALL.map((item) => (
<MenuItem key={item} value={item}>
{i18n(item)}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTag"
value={transTag}
label={i18n("translation_element_tag")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"span"}>{`<span>`}</MenuItem>
<MenuItem value={"font"}>{`<font>`}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTitle"
value={transTitle}
label={i18n("translate_page_title")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="detectRemote"
value={detectRemote}
label={i18n("detect_lang_remote")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
</Grid>
</Box>
<TextField <TextField
select select
@@ -464,34 +534,6 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
maxRows={10} maxRows={10}
/> />
<TextField
size="small"
label={i18n("fixer_selector")}
name="fixerSelector"
value={fixerSelector}
disabled={disabled}
onChange={handleChange}
multiline
maxRows={10}
/>
<TextField
select
size="small"
name="fixerFunc"
value={fixerFunc}
label={i18n("fixer_function")}
helperText={i18n("fixer_function_helper")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
{FIXER_ALL.map((item) => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</TextField>
<TextField <TextField
size="small" size="small"
label={i18n("translate_start_hook")} label={i18n("translate_start_hook")}
@@ -603,18 +645,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
{i18n("delete")} {i18n("delete")}
</Button> </Button>
)} )}
{!showMore && ( {ShowMoreButton}
<Button
size="small"
variant="text"
onClick={() => {
setShowMore(true);
}}
startIcon={<ExpandMoreIcon />}
>
{i18n("more")}
</Button>
)}
</> </>
) : ( ) : (
<> <>
@@ -634,18 +665,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
> >
{i18n("cancel")} {i18n("cancel")}
</Button> </Button>
{!showMore && ( {ShowMoreButton}
<Button
size="small"
variant="text"
onClick={() => {
setShowMore(true);
}}
startIcon={<ExpandMoreIcon />}
>
{i18n("more")}
</Button>
)}
</> </>
)} )}
</Stack> </Stack>
@@ -668,17 +688,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
> >
{i18n("cancel")} {i18n("cancel")}
</Button> </Button>
{!showMore && ( {ShowMoreButton}
<Button
size="small"
variant="text"
onClick={() => {
setShowMore(true);
}}
>
{i18n("more")}
</Button>
)}
</Stack> </Stack>
))} ))}
</Stack> </Stack>

View File

@@ -17,6 +17,8 @@ import {
UI_LANGS, UI_LANGS,
TRANS_NEWLINE_LENGTH, TRANS_NEWLINE_LENGTH,
CACHE_NAME, CACHE_NAME,
OPT_TRANS_MICROSOFT,
OPT_LANGDETECTOR_ALL,
OPT_SHORTCUT_TRANSLATE, OPT_SHORTCUT_TRANSLATE,
OPT_SHORTCUT_STYLE, OPT_SHORTCUT_STYLE,
OPT_SHORTCUT_POPUP, OPT_SHORTCUT_POPUP,
@@ -25,6 +27,7 @@ import {
DEFAULT_CSPLIST, DEFAULT_CSPLIST,
MSG_CONTEXT_MENUS, MSG_CONTEXT_MENUS,
MSG_UPDATE_CSP, MSG_UPDATE_CSP,
DEFAULT_HTTP_TIMEOUT,
} from "../../config"; } from "../../config";
import { useShortcut } from "../../hooks/Shortcut"; import { useShortcut } from "../../hooks/Shortcut";
import ShortcutInput from "./ShortcutInput"; import ShortcutInput from "./ShortcutInput";
@@ -69,6 +72,9 @@ export default function Settings() {
case "newlineLength": case "newlineLength":
value = limitNumber(value, 1, 1000); value = limitNumber(value, 1, 1000);
break; break;
case "httpTimeout":
value = limitNumber(value, 5000, 30000);
break;
case "touchTranslate": case "touchTranslate":
value = limitNumber(value, 0, 4); value = limitNumber(value, 0, 4);
break; break;
@@ -108,11 +114,13 @@ export default function Settings() {
maxLength, maxLength,
clearCache, clearCache,
newlineLength = TRANS_NEWLINE_LENGTH, newlineLength = TRANS_NEWLINE_LENGTH,
httpTimeout = DEFAULT_HTTP_TIMEOUT,
contextMenuType = 1, contextMenuType = 1,
touchTranslate = 2, touchTranslate = 2,
blacklist = DEFAULT_BLACKLIST.join(",\n"), blacklist = DEFAULT_BLACKLIST.join(",\n"),
csplist = DEFAULT_CSPLIST.join(",\n"), csplist = DEFAULT_CSPLIST.join(",\n"),
transInterval = 500, transInterval = 500,
langDetector = OPT_TRANS_MICROSOFT,
} = setting; } = setting;
const { isHide = false } = fab || {}; const { isHide = false } = fab || {};
@@ -185,7 +193,14 @@ export default function Settings() {
defaultValue={transInterval} defaultValue={transInterval}
onChange={handleChange} onChange={handleChange}
/> />
<TextField
size="small"
label={i18n("http_timeout")}
type="number"
name="httpTimeout"
defaultValue={httpTimeout}
onChange={handleChange}
/>
<FormControl size="small"> <FormControl size="small">
<InputLabel>{i18n("touch_translate_shortcut")}</InputLabel> <InputLabel>{i18n("touch_translate_shortcut")}</InputLabel>
<Select <Select
@@ -231,6 +246,22 @@ export default function Settings() {
</Select> </Select>
</FormControl> </FormControl>
<FormControl size="small">
<InputLabel>{i18n("detect_lang_remote")}</InputLabel>
<Select
name="langDetector"
value={langDetector}
label={i18n("detect_lang_remote")}
onChange={handleChange}
>
{OPT_LANGDETECTOR_ALL.map((item) => (
<MenuItem value={item} key={item}>
{item}
</MenuItem>
))}
</Select>
</FormControl>
{isExt ? ( {isExt ? (
<> <>
<FormControl size="small"> <FormControl size="small">

View File

@@ -9,10 +9,9 @@ import {
OPT_LANGS_TO, OPT_LANGS_TO,
OPT_TRANBOX_TRIGGER_CLICK, OPT_TRANBOX_TRIGGER_CLICK,
OPT_TRANBOX_TRIGGER_ALL, OPT_TRANBOX_TRIGGER_ALL,
OPT_DICT_BAIDU,
} from "../../config"; } from "../../config";
import ShortcutInput from "./ShortcutInput"; import ShortcutInput from "./ShortcutInput";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import { useCallback } from "react"; import { useCallback } from "react";
import { limitNumber } from "../../libs/utils"; import { limitNumber } from "../../libs/utils";
import { useTranbox } from "../../hooks/Tranbox"; import { useTranbox } from "../../hooks/Tranbox";
@@ -47,7 +46,6 @@ export default function Tranbox() {
); );
const { const {
transOpen,
translator, translator,
fromLang, fromLang,
toLang, toLang,
@@ -63,25 +61,12 @@ export default function Tranbox() {
followSelection = false, followSelection = false,
triggerMode = OPT_TRANBOX_TRIGGER_CLICK, triggerMode = OPT_TRANBOX_TRIGGER_CLICK,
extStyles = "", extStyles = "",
enDict = OPT_DICT_BAIDU,
} = tranboxSetting; } = tranboxSetting;
return ( return (
<Box> <Box>
<Stack spacing={3}> <Stack spacing={3}>
<FormControlLabel
control={
<Switch
size="small"
name="transOpen"
checked={transOpen}
onChange={() => {
updateTranbox({ transOpen: !transOpen });
}}
/>
}
label={i18n("toggle_selection_translate")}
/>
<TextField <TextField
select select
size="small" size="small"
@@ -143,6 +128,18 @@ export default function Tranbox() {
))} ))}
</TextField> </TextField>
<TextField
select
size="small"
name="enDict"
value={enDict}
label={i18n("english_dict")}
onChange={handleChange}
>
<MenuItem value={"-"}>{i18n("disable")}</MenuItem>
<MenuItem value={OPT_DICT_BAIDU}>{OPT_DICT_BAIDU}</MenuItem>
</TextField>
<TextField <TextField
size="small" size="small"
label={i18n("tranbtn_offset_x")} label={i18n("tranbtn_offset_x")}

View File

@@ -1,4 +1,4 @@
import { useState, useEffect } from "react"; import { useState, useEffect, useMemo } from "react";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack"; import Stack from "@mui/material/Stack";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
@@ -23,6 +23,7 @@ import {
OPT_LANGS_FROM, OPT_LANGS_FROM,
OPT_LANGS_TO, OPT_LANGS_TO,
OPT_STYLE_ALL, OPT_STYLE_ALL,
DEFAULT_TRANS_APIS,
} from "../../config"; } from "../../config";
import { sendIframeMsg } from "../../libs/iframe"; import { sendIframeMsg } from "../../libs/iframe";
import { saveRule } from "../../libs/rules"; import { saveRule } from "../../libs/rules";
@@ -32,6 +33,7 @@ import { kissLog } from "../../libs/log";
export default function Popup({ setShowPopup, translator: tran }) { export default function Popup({ setShowPopup, translator: tran }) {
const i18n = useI18n(); const i18n = useI18n();
const [rule, setRule] = useState(tran?.rule); const [rule, setRule] = useState(tran?.rule);
const [transApis, setTransApis] = useState(tran?.setting?.transApis || []);
const [commands, setCommands] = useState({}); const [commands, setCommands] = useState({});
const handleOpenSetting = () => { const handleOpenSetting = () => {
@@ -106,7 +108,8 @@ export default function Popup({ setShowPopup, translator: tran }) {
try { try {
const res = await sendTabMsg(MSG_TRANS_GETRULE); const res = await sendTabMsg(MSG_TRANS_GETRULE);
if (!res.error) { if (!res.error) {
setRule(res.data); setRule(res.rule);
setTransApis(res.setting.transApis);
} }
} catch (err) { } catch (err) {
kissLog(err, "query rule"); kissLog(err, "query rule");
@@ -138,6 +141,20 @@ export default function Popup({ setShowPopup, translator: tran }) {
})(); })();
}, [tran]); }, [tran]);
const optApis = useMemo(
() =>
OPT_TRANS_ALL.map((key) => ({
...(transApis[key] || DEFAULT_TRANS_APIS[key]),
apiKey: key,
}))
.filter((item) => !item.isDisabled)
.map(({ apiKey, apiName }) => ({
key: apiKey,
name: apiName?.trim() || apiKey,
})),
[transApis]
);
if (!rule) { if (!rule) {
return ( return (
<Box minWidth={300}> <Box minWidth={300}>
@@ -197,9 +214,9 @@ export default function Popup({ setShowPopup, translator: tran }) {
label={i18n("translate_service")} label={i18n("translate_service")}
onChange={handleChange} onChange={handleChange}
> >
{OPT_TRANS_ALL.map((item) => ( {optApis.map(({ key, name }) => (
<MenuItem key={item} value={item}> <MenuItem key={key} value={key}>
{item} {name}
</MenuItem> </MenuItem>
))} ))}
</TextField> </TextField>

View File

@@ -93,9 +93,7 @@ export default function DictCont({ text }) {
key={key} key={key}
style={{ display: "inline-block" }} style={{ display: "inline-block" }}
> >
<Typography component="span">{`${ <Typography component="span">{`${PHONIC_MAP[key]?.[0] || key} ${val}`}</Typography>
PHONIC_MAP[key]?.[0] || key
} ${val}`}</Typography>
<AudioBtn text={dictResult.src} lan={PHONIC_MAP[key]?.[1]} /> <AudioBtn text={dictResult.src} lan={PHONIC_MAP[key]?.[1]} />
</Typography> </Typography>
))} ))}

View File

@@ -18,8 +18,13 @@ import LockIcon from "@mui/icons-material/Lock";
import LockOpenIcon from "@mui/icons-material/LockOpen"; import LockOpenIcon from "@mui/icons-material/LockOpen";
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import { useI18n } from "../../hooks/I18n"; import { useI18n } from "../../hooks/I18n";
import { OPT_TRANS_ALL, OPT_LANGS_FROM, OPT_LANGS_TO } from "../../config"; import {
import { useState, useRef } from "react"; OPT_TRANS_ALL,
OPT_LANGS_FROM,
OPT_LANGS_TO,
DEFAULT_TRANS_APIS,
} from "../../config";
import { useState, useRef, useMemo } from "react";
import TranCont from "./TranCont"; import TranCont from "./TranCont";
import DictCont from "./DictCont"; import DictCont from "./DictCont";
import SugCont from "./SugCont"; import SugCont from "./SugCont";
@@ -101,7 +106,15 @@ function Header({
); );
} }
function TranForm({ text, setText, tranboxSetting, transApis, simpleStyle }) { function TranForm({
text,
setText,
tranboxSetting,
transApis,
simpleStyle,
langDetector,
enDict,
}) {
const i18n = useI18n(); const i18n = useI18n();
const [editMode, setEditMode] = useState(false); const [editMode, setEditMode] = useState(false);
@@ -111,6 +124,20 @@ function TranForm({ text, setText, tranboxSetting, transApis, simpleStyle }) {
const [toLang, setToLang] = useState(tranboxSetting.toLang); const [toLang, setToLang] = useState(tranboxSetting.toLang);
const inputRef = useRef(null); const inputRef = useRef(null);
const optApis = useMemo(
() =>
OPT_TRANS_ALL.map((key) => ({
...(transApis[key] || DEFAULT_TRANS_APIS[key]),
apiKey: key,
}))
.filter((item) => !item.isDisabled)
.map(({ apiKey, apiName }) => ({
key: apiKey,
name: apiName?.trim() || apiKey,
})),
[transApis]
);
return ( return (
<Stack <Stack
className="KT-transbox-container" className="KT-transbox-container"
@@ -174,9 +201,9 @@ function TranForm({ text, setText, tranboxSetting, transApis, simpleStyle }) {
setTranslator(e.target.value); setTranslator(e.target.value);
}} }}
> >
{OPT_TRANS_ALL.map((item) => ( {optApis.map(({ key, name }) => (
<MenuItem key={item} value={item}> <MenuItem key={key} value={key}>
{item} {name}
</MenuItem> </MenuItem>
))} ))}
</TextField> </TextField>
@@ -233,7 +260,10 @@ function TranForm({ text, setText, tranboxSetting, transApis, simpleStyle }) {
</> </>
)} )}
{(!simpleStyle || !isValidWord(text) || !toLang.startsWith("zh")) && ( {(!simpleStyle ||
!isValidWord(text) ||
!toLang.startsWith("zh") ||
enDict === "-") && (
<TranCont <TranCont
text={text} text={text}
translator={translator} translator={translator}
@@ -242,11 +272,16 @@ function TranForm({ text, setText, tranboxSetting, transApis, simpleStyle }) {
toLang2={tranboxSetting.toLang2} toLang2={tranboxSetting.toLang2}
transApis={transApis} transApis={transApis}
simpleStyle={simpleStyle} simpleStyle={simpleStyle}
langDetector={langDetector}
/> />
)} )}
<DictCont text={text} /> {enDict !== "-" && (
<SugCont text={text} /> <>
<DictCont text={text} />
<SugCont text={text} />
</>
)}
</Stack> </Stack>
); );
} }
@@ -268,6 +303,8 @@ export default function TranBox({
followSelection, followSelection,
setFollowSelection, setFollowSelection,
extStyles, extStyles,
langDetector,
enDict,
}) { }) {
const [mouseHover, setMouseHover] = useState(false); const [mouseHover, setMouseHover] = useState(false);
return ( return (
@@ -300,6 +337,8 @@ export default function TranBox({
tranboxSetting={tranboxSetting} tranboxSetting={tranboxSetting}
transApis={transApis} transApis={transApis}
simpleStyle={simpleStyle} simpleStyle={simpleStyle}
langDetector={langDetector}
enDict={enDict}
/> />
</DraggableResizable> </DraggableResizable>
</ThemeProvider> </ThemeProvider>

View File

@@ -5,10 +5,11 @@ import Stack from "@mui/material/Stack";
import { useI18n } from "../../hooks/I18n"; import { useI18n } from "../../hooks/I18n";
import { DEFAULT_TRANS_APIS } from "../../config"; import { DEFAULT_TRANS_APIS } from "../../config";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { apiTranslate, apiBaiduLangdetect } from "../../apis"; import { apiTranslate } from "../../apis";
import CopyBtn from "./CopyBtn"; import CopyBtn from "./CopyBtn";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Alert from "@mui/material/Alert"; import Alert from "@mui/material/Alert";
import { tryDetectLang } from "../../libs";
export default function TranCont({ export default function TranCont({
text, text,
@@ -18,6 +19,7 @@ export default function TranCont({
toLang2 = "en", toLang2 = "en",
transApis, transApis,
simpleStyle, simpleStyle,
langDetector,
}) { }) {
const i18n = useI18n(); const i18n = useI18n();
const [trText, setTrText] = useState(""); const [trText, setTrText] = useState("");
@@ -32,8 +34,8 @@ export default function TranCont({
setError(""); setError("");
let to = toLang; let to = toLang;
if (toLang !== toLang2 && toLang2 !== "none") { if (fromLang === "auto" && toLang !== toLang2 && toLang2 !== "none") {
const detectLang = await apiBaiduLangdetect(text); const detectLang = await tryDetectLang(text, true, langDetector);
if (detectLang === toLang) { if (detectLang === toLang) {
to = toLang2; to = toLang2;
} }
@@ -55,7 +57,7 @@ export default function TranCont({
setLoading(false); setLoading(false);
} }
})(); })();
}, [text, translator, fromLang, toLang, toLang2, transApis]); }, [text, translator, fromLang, toLang, toLang2, transApis, langDetector]);
if (simpleStyle) { if (simpleStyle) {
return ( return (

View File

@@ -10,6 +10,7 @@ import {
OPT_TRANBOX_TRIGGER_CLICK, OPT_TRANBOX_TRIGGER_CLICK,
OPT_TRANBOX_TRIGGER_HOVER, OPT_TRANBOX_TRIGGER_HOVER,
OPT_TRANBOX_TRIGGER_SELECT, OPT_TRANBOX_TRIGGER_SELECT,
OPT_DICT_BAIDU,
} from "../../config"; } from "../../config";
import { isMobile } from "../../libs/mobile"; import { isMobile } from "../../libs/mobile";
import { kissLog } from "../../libs/log"; import { kissLog } from "../../libs/log";
@@ -20,6 +21,7 @@ export default function Slection({
tranboxSetting, tranboxSetting,
transApis, transApis,
uiLang, uiLang,
langDetector,
}) { }) {
const { const {
hideTranBtn = false, hideTranBtn = false,
@@ -33,6 +35,7 @@ export default function Slection({
btnOffsetY, btnOffsetY,
boxOffsetX = 0, boxOffsetX = 0,
boxOffsetY = 10, boxOffsetY = 10,
enDict = OPT_DICT_BAIDU,
} = tranboxSetting; } = tranboxSetting;
const boxWidth = const boxWidth =
@@ -234,6 +237,8 @@ export default function Slection({
followSelection={followSelection} followSelection={followSelection}
setFollowSelection={setFollowSelection} setFollowSelection={setFollowSelection}
extStyles={extStyles} extStyles={extStyles}
langDetector={langDetector}
enDict={enDict}
/> />
)} )}