Compare commits

...

26 Commits

Author SHA1 Message Date
Gabe
39b3b00117 release: v1.9.2 2025-08-10 23:00:21 +08:00
Gabe
763019f0c5 doc: update custom api help 2025-08-10 22:56:36 +08:00
Gabe
d743271be8 fix: show custom api name 2025-08-10 22:32:38 +08:00
Gabe
992dad26aa doc: update readme 2025-08-10 22:01:50 +08:00
Gabe
9bd0e67474 doc: update readme 2025-08-10 21:57:26 +08:00
Gabe
5767a4afb2 doc: update readme 2025-08-10 21:54:24 +08:00
Gabe
9e4c510684 doc: update custom api help 2025-08-10 21:50:33 +08:00
Gabe
16607fb069 doc: add custom api help 2025-08-10 21:40:58 +08:00
Gabe
e047a06432 doc: update readme 2025-08-10 20:12:12 +08:00
Gabe
9e09fd898a doc: update readme 2025-08-10 17:37:15 +08:00
Gabe
799c32a871 Merge remote-tracking branch 'origin/master' 2025-08-10 17:33:14 +08:00
Gabe
483f33b5c9 Merge pull request #269 from WilliamK7/patch-1
doc: update readme
2025-08-10 17:32:23 +08:00
Gabe
d444fd4fba fix: set max-width to loading svg 2025-08-10 17:28:28 +08:00
Gabe
0aae93ba2e doc: update help text 2025-08-10 16:56:34 +08:00
Gabe
9608bea3bf doc: update warn text 2025-08-10 16:44:57 +08:00
WilliamK7
a038a1ecdc doc: update readme
Remove duplicates in Chinese README
2025-08-10 13:56:29 +08:00
Gabe
c82cdd7f8f doc: update readme 2025-08-10 12:48:40 +08:00
Gabe
0c6d5c3c61 fix: replace deault tag from span to font 2025-08-09 22:32:41 +08:00
Gabe
900426f359 Merge branch 'master' into dev 2025-08-09 22:03:52 +08:00
Gabe
1d760fc93a Merge pull request #258 from mumu-lhl/push-mlqssmoqoqko
API: Replace max_tokens with max_completion_tokens
2025-08-09 22:03:12 +08:00
Gabe
61571e0f61 fix: remove browser contextMenus (issue #262) 2025-08-09 21:36:43 +08:00
Gabe
c9eb423c89 doc: update readme 2025-08-09 21:06:18 +08:00
Gabe
45b294a121 fix: retranslate loadmore text (issue #257) 2025-08-09 20:55:04 +08:00
Gabe
3a3f1fabe1 doc: update readme 2025-08-09 11:41:24 +08:00
Mumulhl
03177a09b3 API: Replace max_tokens with max_completion_tokens 2025-08-09 11:35:47 +08:00
Gabe
cae391f62b fix: zip script 2025-07-23 21:56:54 +08:00
17 changed files with 660 additions and 66 deletions

2
.env
View File

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

View File

@@ -11,10 +11,18 @@ A simple, open source [bilingual translation extension & Greasemonkey script](ht
- [x] Keep it simple, smart
- [x] Open source
- [x] Adapt to common browsers
- [x] Chrome/Edge/Firefox/Kiwi/Orion
- [x] Chrome/Edge
- [x] Firefox
- [x] Kiwi (Android)
- [x] Orion (iOS)
- [ ] Safari
- [x] Safari (Mac)
- [x] Thunderbird
- [x] Supports multiple translation services
- [x] Google/Microsoft/DeepL/NiuTrans/OpenAI/Gemini/CloudflareAI/Baidu/Tencent
- [x] Google/Microsoft
- [x] Baidu/Tencent/Volcengine
- [x] OpenAI/Gemini/Claude/Ollama/DeepSeek/CloudflareAI
- [x] DeepL/DeepLX/NiuTrans
- [x] Custom translation interface
- [x] Covers common translation scenarios
- [x] Web bilingual translation
@@ -52,6 +60,8 @@ A simple, open source [bilingual translation extension & Greasemonkey script](ht
- [x] Edge [Installation address](https://microsoftedge.microsoft.com/addons/detail/%E7%AE%80%E7%BA%A6%E7%BF%BB%E8%AF%91/jemckldkclkinpjighnoilpbldbdmmlh?hl=zh-CN)
- [x] Firefox [Installation address](https://addons.mozilla.org/zh-CN/firefox/addon/kiss-translator/)
- [ ] Safari
- [x] Safari (Mac) Compiled by a third party, not verified, obtained by yourself: https://www.nodeloc.com/t/topic/54245
- [x] Thunderbird [Download address](https://github.com/fishjar/kiss-translator/releases)
- [x] GreaseMonkey Script
- [x] Chrome/Edge/Firefox ([Tampermonkey](https://www.tampermonkey.net/)/[Violentmonkey](https://violentmonkey.github.io/)) [Installation link](https://fishjar.github.io/kiss-translator/kiss-translator.user.js)
- [Greasy Fork](https://greasyfork.org/zh-CN/scripts/472840-kiss-translator)
@@ -69,16 +79,112 @@ A simple, open source [bilingual translation extension & Greasemonkey script](ht
- Translation interface agent: [https://github.com/fishjar/kiss-proxy](https://github.com/fishjar/kiss-proxy)
- If you encounter network problems when accessing a certain translation interface, this proxy service may help you.
- Deploy and manage by yourself.
- Minimalistic Dictionary Plugin: [https://github.com/fishjar/kiss-dictionary](https://github.com/fishjar/kiss-dictionary)
- A word-marking translation plug-in used with this project.
- Supports query of English words, sentences and Chinese characters.
- Supports history records and word collections.
## Frequently Asked Questions
### How to Turn Off Automatic Translation
You can achieve this through `Rules Setting` with the following methods:
- Personal Rules: RULES-> Global Rule -> Translate Switch -> Disaabled
- Subscription Rules: SUBSCRIBE -> Select the third option `kiss-rules-off.json`
- Override Subscription Rules: OVERWRITE -> Translate Switch -> Disaabled
- Add a Personal Rule for a Specific Website: Translate Switch -> Disaabled
### How to Set Keyboard Shortcuts
Set this in the extension management page, for example:
- chrome [chrome://extensions/shortcuts](chrome://extensions/shortcuts)
- firefox [about:addons](about:addons)
### How to Turn Off Selection Translation
Set this in the `Rules Setting`: RULES -> Global Rule -> If translate selected -> Disable
### How to Set it to Show Only the Translation
Set this in the `Rules Setting`: RULES -> Global Rule -> Show Only Translations -> Enable
### How to Set Mouse Hover Translation
Set this in the `Rules Setting`: RULES -> Global Rule -> TTrigger Mode
### Why are some web pages not fully translated?
This extension's webpage translation is based on CSS selectors. Generic rules cannot adapt to all websites, and sometimes you need to manually add site-specific rules. If you don't know how to write rules, you can seek help here:
https://github.com/fishjar/kiss-rules/issues
### What is the priority order of rule settings?
Personal Rules > Override Subscription Rules > Subscription Rules > Global Rules
Among these, Global Rules have the lowest priority but are very important as they serve as the default rules.
### Why are YouTube subtitles translated in broken sentences?
This extension has no special development for video content. Support for YouTube is also treated as regular webpage translation. Auto-generated subtitles are streamed and output progressively, resulting in poorer support.
To disable this extension's subtitle translation, add a rule. Reference: https://github.com/fishjar/kiss-translator/issues/62
### Local Ollama interface cannot be used
If encountering a 403 error, refer to: https://github.com/fishjar/kiss-translator/issues/174
### Custom API doesn't work in Tampermonkey scripts
Tampermonkey scripts require adding domains to the whitelist; otherwise, requests cannot be sent.
### How to Set Up Hook Functions for Custom Interfaces
The custom interface feature is highly flexible and can theoretically integrate with any translation interface.
Example of a Request Hook function:
```js
/**
* Request Hook
* @param {string} text Text to be translated
* @param {string} from Source language
* @param {string} to Target language
* @param {string} url Translation interface URL
* @param {string} key Translation interface API key
* @returns {Array[string, object]} [Interface URL, request object]
*/
(text, from, to, url, key) => [url, {
headers: {
"Content-type": "application/json",
"Authorization": `Bearer ${key}`
},
method: "POST",
body: { text, to },
}]
```
Example of a Response Hook function:
```js
* Response Hook
* @param {string} res JSON data returned by the interface
* @param {string} text Text to be translated
* @param {string} from Source language
* @param {string} to Target language
* @returns {Array[string, boolean]} [Translated text, whether target language is same as source]
* Note: If the second return value is true (target language same as source),
* the translation will not be displayed on the page,
* If the parameters are incomplete, it is recommended to return false directly
*/
(res, text, from, to) => [res.text, to === res.src]
```
For more custom interface examples, refer to: [custom-api.md](https://github.com/fishjar/kiss-translator/blob/master/custom-api.md)
## Development Guidelines
```sh
git clone https://github.com/fishjar/kiss-translator.git
cd kiss-translator
git checkout dev # Submit a PR suggestion to push to the dev branch
pnpm install
pnpm build
```

117
README.md
View File

@@ -11,10 +11,18 @@
- [x] 保持简约
- [x] 开放源代码
- [x] 适配常见浏览器
- [x] Chrome/Edge/Firefox/Kiwi/Orion
- [x] Chrome/Edge
- [x] Firefox
- [x] Kiwi (Android)
- [x] Orion (iOS)
- [ ] Safari
- [x] Safari (Mac)
- [x] Thunderbird
- [x] 支持多种翻译服务
- [x] Google/Microsoft/DeepL/NiuTrans/OpenAI/Gemini/CloudflareAI/Baidu/Tencent
- [x] Google/Microsoft
- [x] Baidu/Tencent/Volcengine
- [x] OpenAI/Gemini/Claude/Ollama/DeepSeek/CloudflareAI
- [x] DeepL/DeepLX/NiuTrans
- [x] 自定义翻译接口
- [x] 覆盖常见翻译场景
- [x] 网页双语对照翻译
@@ -52,6 +60,8 @@
- [x] Edge [安装地址](https://microsoftedge.microsoft.com/addons/detail/%E7%AE%80%E7%BA%A6%E7%BF%BB%E8%AF%91/jemckldkclkinpjighnoilpbldbdmmlh?hl=zh-CN)
- [x] Firefox [安装地址](https://addons.mozilla.org/zh-CN/firefox/addon/kiss-translator/)
- [ ] Safari
- [x] Safari (Mac) 第三方编译,未作验证,自行获取: https://www.nodeloc.com/t/topic/54245
- [x] Thunderbird [下载地址](https://github.com/fishjar/kiss-translator/releases)
- [x] 油猴脚本
- [x] Chrome/Edge/Firefox ([Tampermonkey](https://www.tampermonkey.net/)/[Violentmonkey](https://violentmonkey.github.io/)) [安装链接](https://fishjar.github.io/kiss-translator/kiss-translator.user.js)
- [Greasy Fork](https://greasyfork.org/zh-CN/scripts/472840-kiss-translator)
@@ -69,16 +79,111 @@
- 翻译接口代理: [https://github.com/fishjar/kiss-proxy](https://github.com/fishjar/kiss-proxy)
- 如果访问某个翻译接口遇到网络问题,这个代理服务也许可以帮到你。
- 自己部署,自己管理。
- 简约词典插件: [https://github.com/fishjar/kiss-dictionary](https://github.com/fishjar/kiss-dictionary)
- 搭配本项目一起使用的划词翻译插件。
- 支持英文单词、句子、汉字的查询。
- 支持历史记录、单词收藏。
## 常见问题
### 如何关闭自动翻译
通过规则设置,以下方法均可实现:
- 个人规则:全局规则 -> 开启翻译 -> 默认关闭
- 订阅规则:选择第三个 `kiss-rules-off.json`
- 覆写订阅规则:开启翻译 -> 默认关闭
- 添加一条针对某个网站的个人规则:开启翻译 -> 默认关闭
### 如何设置快捷键
在插件管理那里设置,例如:
- chrome [chrome://extensions/shortcuts](chrome://extensions/shortcuts)
- firefox [about:addons](about:addons)
### 如何关闭划词翻译
通过规则设置:个人规则 -> 全局规则 -> 是否启用划词翻译 -> 禁用
### 如何设置仅显示译文
通过规则设置:个人规则 -> 全局规则 -> 仅显示译文 -> 启用
### 如何设置鼠标悬停翻译
通过规则设置:个人规则 -> 全局规则 -> 触发方式
### 为什么有些网页翻译不全
本插件的网页翻译是基于CSS选择器的通用规则不能适配所有网页有时需要自行添加相应网站的单独规则。如果不会写规则可以到这里求助 https://github.com/fishjar/kiss-rules/issues
### 规则设置的优先级是如何的
个人规则 > 覆写订阅规则 > 订阅规则 > 全局规则
其中全局规则优先级最低,但非常重要,相当于默认规则。
### 为什么油管字幕一句话会断开翻译
本插件目前没有针对视频做特殊开发,对油管的支持也是当做网页翻译看待,自动生成字幕是流式生成并输出的,所以支持较差。
如果需要关闭本插件的字幕翻译增加一条规则即可参考https://github.com/fishjar/kiss-translator/issues/62
### 本地的Ollama接口不能使用
如果出现403的情况参考https://github.com/fishjar/kiss-translator/issues/174
### 填写的接口在油猴脚本不能使用
油猴脚本需要增加域名白名单,否则不能发出请求。
### 如何设置自定义接口的hook函数
自定义接口功能非常灵活,理论可以接入任何翻译接口。
Request Hook 函数示例如下:
```js
/**
* Request Hook
* @param {string} text 需要翻译的原文
* @param {string} from 原文语言
* @param {string} to 译文语言
* @param {string} url 翻译接口地址
* @param {string} key 翻译接口密钥
* @returns {Array[string, object]} [接口地址, 请求参数对象]
*/
(text, from, to, url, key) => [url, {
headers: {
"Content-type": "application/json",
"Authorization": `Bearer ${key}`
},
method: "POST",
body: { text, to },
}]
```
Response Hook 函数示例如下:
```js
/**
* Request Hook
* @param {string} res 接口返回的json数据
* @param {string} text 需要翻译的原文
* @param {string} from 原文语言
* @param {string} to 译文语言
* @returns {Array[string, boolean]} [译文, 译文语言与原文语言是否相同]
* 注如果返回值第二个值为true译文语言与原文语言相同则译文不会在页面显示
* 参数不全的情况建议直接返回false
*/
(res, text, from, to) => [res.text, to === res.src]
```
更多的自定义接口示例,请参考: [custom-api.md](https://github.com/fishjar/kiss-translator/blob/master/custom-api.md)
## 开发指引
```sh
git clone https://github.com/fishjar/kiss-translator.git
cd kiss-translator
git checkout dev # 提交PR建议推送到dev分支
pnpm install
pnpm build
```

346
custom-api.md Normal file
View File

@@ -0,0 +1,346 @@
# 自定义接口示例
以下示例为网友提供,仅供学习参考。
## 本地运行 Seed-X-PPO-7B 量化模型
> 由网友 emptyghost6 提供来源https://linux.do/t/topic/828257
URL
```sh
http://localhost:8000/v1/completions
```
Request Hook
```js
(text, from, to, url, key) => {
// 模型支持的语言代码到完整名称的映射
const langFullNameMap = {
ar: 'Arabic', fr: 'French', ms: 'Malay', ru: 'Russian',
cs: 'Czech', hr: 'Croatian', nb: 'Norwegian Bokmal', sv: 'Swedish',
da: 'Danish', hu: 'Hungarian', nl: 'Dutch', th: 'Thai',
de: 'German', id: 'Indonesian', no: 'Norwegian', tr: 'Turkish',
en: 'English', it: 'Italian', pl: 'Polish', uk: 'Ukrainian',
es: 'Spanish', ja: 'Japanese', pt: 'Portuguese', vi: 'Vietnamese',
fi: 'Finnish', ko: 'Korean', ro: 'Romanian', zh: 'Chinese'
};
// 将 Hook 系统的语言代码转换为模型 API 支持的代码
const getModelLangCode = (lang) => {
if (lang === 'zh-CN' || lang === 'zh-TW') return 'zh';
return lang;
};
const sourceLangCode = getModelLangCode(from);
const targetLangCode = getModelLangCode(to);
const sourceLangName = langFullNameMap[sourceLangCode] || from;
const targetLangName = langFullNameMap[targetLangCode] || to;
const prompt = `Translate it to ${targetLangName}:\n${text} <${targetLangCode}>`;
// 构建请求体对象
const bodyObject = {
model: "./ByteDance-Seed/Seed-X-PPO-7B-AWQ-Int4",
prompt: prompt,
max_tokens: 2048,
temperature: 0.0,
};
// 返回最终的请求配置
return [url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
// 关键改动:将 JavaScript 对象转换为 JSON 字符串
body: JSON.stringify(bodyObject),
}];
}
```
Response Hook
```js
(res, text, from, to) => {
// 检查返回是否有效
if (res && res.choices && res.choices.length > 0 && res.choices[0].text) {
// 提取译文并去除可能存在的前后空格
const translatedText = res.choices[0].text.trim();
// 比较原文与译文,相同为 true否则为 false。
const areTextsIdentical = text.trim() === translatedText;
// 返回数组:[翻译后的文本, 是否与原文相同]
return [translatedText, areTextsIdentical];
}
// 如果响应格式不正确或没有结果,则抛出错误
throw new Error("Invalid API response format or no translation found.");
}
```
## 接入 openrouter
> 由网友 Rick Sanchez 提供
URL
```sh
https://openrouter.ai/api/v1/chat/completions
```
Request Hook
```js
(text, from, to, url, key) => [url, {
method: "POST",
headers: {
"Authorization": `Bearer ${key}`,
"Content-type": "application/json",
},
body: JSON.stringify({
"model": "deepseek/deepseek-chat-v3-0324:free", //可自定义你的模型
"messages": [
{
"role": "user",
"content": //可自定义你的提示词
`You are a professional ${to} native translator. Your task is to produce a fluent, natural, and culturally appropriate translation of the following text from ${from} to ${to}, fully conveying the meaning, tone, and nuance of the original.
## Translation Rules
1. Output only the final polished translation — no explanations, intermediate drafts, or notes.
2. Translate in a way that reads naturally to a native ${to} audience, adapting idioms, cultural references, and tone when necessary.
3. Preserve proper nouns, technical terms, brand names, and URLs exactly as in the original text unless a widely accepted ${to} equivalent exists.
4. Keep any formatting (Markdown, HTML tags, bullet points, numbering) intact and positioned naturally within the translation.
5. Adapt humor, metaphors, and figurative language to culturally relevant forms in ${to} while keeping the original intent.
6. Maintain the same level of formality or informality as the original.
Source Text: ${text}
Translated Text:`
}
]
})
}]
```
Response Hook
```js
(res, text, from, to) => [
res.choices?.[0]?.message?.content ?? "",
false
]
```
## 接入 gemini-2.5-flash, 关闭思考模式, 去审查
> 由网友 Rick Sanchez 提供
URL
```sh
https://generativelanguage.googleapis.com/v1beta/models
```
Request Hook
```js
(text, from, to, url, key) => [`${url}/gemini-2.5-flash:generateContent?key=${key}`, {
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({
"generationConfig": {
"temperature": 0.8,
"thinkingConfig": {
"thinkingBudget": 0, //gemini-2.5-flash设为0关闭思考模式
},
},
"safetySettings": [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_NONE",
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_NONE",
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_NONE",
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_NONE",
}
],
"contents": [{
"parts": [{
"text": `自定义提示词`
}]
}],
}),
}]
```
Response Hook
```js
(res, text, from, to) => [
res.candidates?.[0]?.content?.parts?.[0]?.text ?? "",
false
]
```
## 接入 Qwen-MT
> 由网友 atom 提供
URL
```sh
https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
```
Request Hook
```js
(text, from, to, url, key) => {
const mapLanguageCode = (lang) => ({
'zh-CN': 'zh',
'zh-TW': 'zh_tw',
})[lang] || lang;
const targetLang = mapLanguageCode(to);
return [
url,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${key}`
},
body: JSON.stringify({
"model": "qwen-mt-turbo",
"messages": [
{
"role": "user",
"content": text
}
],
"translation_options": {
"source_lang": "auto",
"target_lang": targetLang
}
})
}
];
}
```
Response Hook
```js
(res, text, from, to) => [res.choices?.[0]?.message?.content ?? "", false]
```
## 接入 deepl 接口
> 来源: https://github.com/fishjar/kiss-translator/issues/101#issuecomment-2123786236
Request Hook
```js
(text, from, to, url, key) => [
url,
{
headers: {
"Content-type": "application/json",
},
method: "POST",
body: JSON.stringify({
text,
target_lang: "ZH",
source_lang: "auto",
}),
},
]
```
Response Hook
```js
(res, text, from, to) => [res.data, "ZH" === res.source_lang]
```
## 接入智谱AI大模型
> 来源: https://github.com/fishjar/kiss-translator/issues/205#issuecomment-2642422679
Request Hook
```js
(text, from, to, url, key) => [url, {
"method": "POST",
"headers": {
"Content-type": "application/json",
"Authorization": key
},
"body": JSON.stringify({
"model": "glm-4-flash",
"messages": [
{
"role":"system",
"content": "You are a professional, authentic machine translation engine. You only return the translated text, without any explanations."
},
{
"role": "user",
"content": `Translate the following text into ${to}. If translation is unnecessary (e.g. proper nouns, codes, etc.), return the original text. NO explanations. NO notes:\n\n ${text} `
}
]
})
}]
```
## 接入谷歌新接口
> 由网友 Bush2021 提供来源https://github.com/fishjar/kiss-translator/issues/225#issuecomment-2810950717
URL
```sh
https://translate-pa.googleapis.com/v1/translateHtml
```
KEY
```sh
AIzaSyATBXajvzQLTDHEQbcpq0Ihe0vWDHmO520
```
Request Hook
```js
(text, from, to, url, key) => [url, {
method: "POST",
headers: {
"Content-Type": "application/json+protobuf",
"X-Goog-API-Key": key
},
body: JSON.stringify([[[text], from || "auto", to], "wt_lib"])
}]
```
Response Hook
```js
(res, text, from, to) => [res?.[0]?.join(" ") || "Translation unavailable", to === res?.[1]?.[0]]
```

View File

@@ -1,7 +1,7 @@
{
"name": "kiss-translator",
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
"version": "1.9.1",
"version": "1.9.2",
"author": "Gabe<yugang2002@gmail.com>",
"private": true,
"dependencies": {
@@ -33,7 +33,7 @@
"build:userscript": "rm -rf build/userscript && mkdir build/userscript && cp build/web/*.user.js build/userscript/",
"build:rules": "babel-node src/rules.js",
"build": "pnpm 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 && 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 .)",
"zip": "cd build && rm -f *.zip && zip -r chrome.zip chrome && zip -r edge.zip edge && zip -r userscript.zip userscript && (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",

View File

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

View File

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

View File

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

View File

@@ -267,7 +267,7 @@ const genOpenAI = ({
},
],
temperature,
max_tokens: maxTokens,
max_completion_tokens: maxTokens,
};
const init = {

View File

@@ -45,9 +45,9 @@ const REMOVE_HEADERS = [
async function addContextMenus(contextMenuType = 1) {
// 添加前先删除,避免重复ID的错误
try {
await browser.menus.removeAll();
await browser.contextMenus.removeAll();
} catch (err) {
//
kissLog(err, "remove contextMenus");
}
switch (contextMenuType) {

View File

@@ -42,21 +42,6 @@ const customApiLangs = `["en", "English - English"],
["vi", "Vietnamese - Tiếng Việt"],
`;
const hookExample = `// URL
https://translate.googleapis.com/translate_a/single?client=gtx&dj=1&dt=t&ie=UTF-8&q={{text}}&sl=en&tl=zh-CN
// Request Hook
(text, from, to, url, key) => [url, {
headers: {
"Content-type": "application/json",
},
method: "GET",
body: null,
}]
// Response Hook
(res, text, from, to) => [res.sentences.map((item) => item.trans).join(" "), to === res.src]`;
const customApiHelpZH = `// 请求数据默认格式
{
"url": "{{url}}",
@@ -82,7 +67,21 @@ const customApiHelpZH = `// 请求数据默认格式
// Hook 范例
${hookExample}
// URL
https://translate.googleapis.com/translate_a/single?client=gtx&dj=1&dt=t&ie=UTF-8&q={{text}}&sl=en&tl=zh-CN
// Request Hook
(text, from, to, url, key) => [url, {
headers: {
"Content-type": "application/json",
},
method: "GET",
body: null,
}]
// Response Hook
// 其中返回数组第一个值表示译文字符串,第二个值为布尔值,表示原文语言与目标语言是否相同
(res, text, from, to) => [res.sentences.map((item) => item.trans).join(" "), to === res.src]
// 支持的语言代码如下
@@ -114,7 +113,22 @@ const customApiHelpEN = `// Default request
/// Hook Example
${hookExample}
// URL
https://translate.googleapis.com/translate_a/single?client=gtx&dj=1&dt=t&ie=UTF-8&q={{text}}&sl=en&tl=zh-CN
// Request Hook
(text, from, to, url, key) => [url, {
headers: {
"Content-type": "application/json",
},
method: "GET",
body: null,
}]
// Response Hook
// In the returned array, the first value is the translated string, while the second value is a boolean
// that indicates whether the source language is the same as the target language.
(res, text, from, to) => [res.sentences.map((item) => item.trans).join(" "), to === res.src]
// The supported language codes are as follows
@@ -359,12 +373,20 @@ export const I18N = {
en: `3. Regarding filling in the rules: Leave the input box blank or select "*" in the drop-down box to use global rule.`,
},
sync_warn: {
zh: `涉及隐私数据的同步请谨慎选择第三方同步服务,建议自行搭建 kiss-worker 或 WebDAV 服务。`,
en: `When synchronizing data that involves privacy, please be cautious about choosing third-party sync services. It is recommended to set up your own sync service using kiss-worker or WebDAV.`,
},
sync_warn_2: {
zh: `如果服务器存在其他客户端同步的数据,第一次同步将直接覆盖本地配置,后面则根据修改时间,新的覆盖旧的。`,
en: `If the server has data synchronized by other clients, the first synchronization will directly overwrite the local configuration, and later, according to the modification time, the new one will overwrite the old one.`,
},
about_sync_api: {
zh: `查看关于数据同步接口部署`,
en: `View About Data Synchronization Interface Deployment`,
zh: `自建kiss-wroker数据同步服务`,
en: `Self-hosting a Kiss-worker data sync service`,
},
about_api: {
zh: `暂未列出的接口,理论上都可以通过自定义接口的形式支持。`,
en: `Interfaces that have not yet been launched can theoretically be supported through custom interfaces.`,
},
about_api_proxy: {
zh: `查看自建一个翻译接口代理`,

View File

@@ -443,7 +443,7 @@ export const INPUT_PLACE_MODEL = "{{model}}"; // 占位符
export const DEFAULT_COLOR = "#209CEE"; // 默认高亮背景色/线条颜色
export const DEFAULT_TRANS_TAG = "span";
export const DEFAULT_TRANS_TAG = "font";
export const DEFAULT_SELECT_STYLE =
"-webkit-line-clamp: unset; max-height: none; height: auto;";

View File

@@ -1,5 +1,5 @@
export const loadingSvg = `
<svg viewBox="0 0 100 100" style="display:inline-block; width:100%; height: 100%;">
<svg viewBox="0 0 100 100" style="display:inline-block; width:100%; height: 100%; max-width: 24; max-height: 24;">
<circle fill="#209CEE" stroke="none" cx="6" cy="50" r="6">
<animateTransform
attributeName="transform"

View File

@@ -18,7 +18,7 @@ import {
} from "../config";
import Content from "../views/Content";
import { updateFetchPool, clearFetchPool } from "./fetch";
import { debounce, genEventName } from "./utils";
import { debounce, genEventName, getHtmlText } from "./utils";
import { runFixer } from "./webfix";
import { apiTranslate } from "../apis";
import { sendBgMsg } from "./msg";
@@ -475,26 +475,19 @@ export class Translator {
return;
}
const preText = this._tranNodes.get(el);
const curText = el.innerText.trim();
// const traText = traEl.innerText.trim();
// todo
// 1. traText when loading
// 2. replace startsWith
if (curText.startsWith(preText)) {
const preText = getHtmlText(this._tranNodes.get(el));
const curText = getHtmlText(el.innerHTML, APP_LCNAME);
if (preText === curText) {
return;
}
traEl.remove();
}
// 缓存已翻译元素
this._tranNodes.set(el, el.innerHTML);
let q = el.innerText.trim();
if (this._rule.transOnly === "true") {
this._tranNodes.set(el, el.innerHTML);
} else {
this._tranNodes.set(el, q);
}
const keeps = [];
// 翻译开始钩子函数

View File

@@ -250,3 +250,20 @@ export const blobToBase64 = (blob) => {
reader.readAsDataURL(blob);
});
};
/**
* 获取html内的文本
* @param {*} htmlStr
* @param {*} skipTag
* @returns
*/
export const getHtmlText = (htmlStr, skipTag = "") => {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlStr, "text/html");
if (skipTag) {
doc.querySelectorAll(skipTag).forEach((el) => el.remove());
}
return doc.body.innerText.trim();
};

View File

@@ -30,7 +30,6 @@ import {
OPT_TRANS_CUSTOMIZE_4,
OPT_TRANS_CUSTOMIZE_5,
OPT_TRANS_NIUTRANS,
URL_KISS_PROXY,
URL_NIUTRANS_REG,
DEFAULT_FETCH_LIMIT,
DEFAULT_FETCH_INTERVAL,
@@ -118,9 +117,8 @@ function TestButton({ translator, api }) {
);
}
function ApiFields({ translator }) {
function ApiFields({ translator, api, updateApi, resetApi }) {
const i18n = useI18n();
const { api, updateApi, resetApi } = useApi(translator);
const {
url = "",
key = "",
@@ -431,6 +429,7 @@ function ApiFields({ translator }) {
function ApiAccordion({ translator }) {
const [expanded, setExpanded] = useState(false);
const { api, updateApi, resetApi } = useApi(translator);
const handleChange = (e) => {
setExpanded((pre) => !pre);
@@ -439,10 +438,19 @@ function ApiAccordion({ translator }) {
return (
<Accordion expanded={expanded} onChange={handleChange}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{translator}</Typography>
<Typography>
{api.apiName ? `${translator} (${api.apiName})` : translator}
</Typography>
</AccordionSummary>
<AccordionDetails>
{expanded && <ApiFields translator={translator} />}
{expanded && (
<ApiFields
translator={translator}
api={api}
updateApi={updateApi}
resetApi={resetApi}
/>
)}
</AccordionDetails>
</Accordion>
);
@@ -453,11 +461,7 @@ export default function Apis() {
return (
<Box>
<Stack spacing={3}>
<Alert severity="info">
<Link href={URL_KISS_PROXY} target="_blank">
{i18n("about_api_proxy")}
</Link>
</Alert>
<Alert severity="info">{i18n("about_api")}</Alert>
<Box>
{OPT_TRANS_ALL.map((translator) => (

View File

@@ -65,6 +65,7 @@ export default function SyncSetting() {
<Box>
<Stack spacing={3}>
<Alert severity="warning">{i18n("sync_warn")}</Alert>
<Alert severity="warning">{i18n("sync_warn_2")}</Alert>
<TextField
select