Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc31a8004a | ||
|
|
fa14851596 | ||
|
|
d56c46e944 | ||
|
|
9f8bcf1fe1 | ||
|
|
e50387a796 | ||
|
|
3d2eac8772 | ||
|
|
343f529cac |
2
.env
2
.env
@@ -2,7 +2,7 @@ GENERATE_SOURCEMAP=false
|
||||
|
||||
REACT_APP_NAME=KISS Translator
|
||||
REACT_APP_NAME_CN=简约翻译
|
||||
REACT_APP_VERSION=2.0.8
|
||||
REACT_APP_VERSION=2.0.9
|
||||
|
||||
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
version: latest
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: "25.1.0"
|
||||
cache: "pnpm"
|
||||
- run: pnpm install
|
||||
- run: pnpm build+zip
|
||||
|
||||
@@ -5,6 +5,37 @@
|
||||
如果接口的请求数据和返回数据符合以下规范,
|
||||
则无需填写 `Request Hook` 或 `Response Hook`。
|
||||
|
||||
|
||||
### 非聚合翻译 (v2.0.9)
|
||||
|
||||
Request body
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "hello", // 需要翻译的文本列表
|
||||
"from":"auto", // 原文语言
|
||||
"to": "zh-CN" // 目标语言
|
||||
}
|
||||
```
|
||||
|
||||
Response
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "你好", // 译文
|
||||
"src": "en" // 原文语言
|
||||
}
|
||||
|
||||
// 或者
|
||||
{
|
||||
"text": "你好", // 译文
|
||||
"from": "en" // 原文语言
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 聚合翻译
|
||||
|
||||
Request body
|
||||
|
||||
```json
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kiss-translator",
|
||||
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
||||
"version": "2.0.8",
|
||||
"version": "2.0.9",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
||||
20
public/_locales/de/messages.json
Normal file
20
public/_locales/de/messages.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app_name": {
|
||||
"message": "KISS Übersetzer"
|
||||
},
|
||||
"app_description": {
|
||||
"message": "Eine einfache zweisprachige Übersetzungs-Erweiterung und Greasemonkey-Skript"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "Übersetzung umschalten"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "Stile umschalten"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "Einstellungen öffnen"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "Popup-Fenster öffnen"
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,15 @@
|
||||
"message": "A simple bilingual translation extension & Greasemonkey script"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "Toggle Translate"
|
||||
"message": "Toggle Translation"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "Toggle Style"
|
||||
"message": "Toggle Styles"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "Open Options"
|
||||
"message": "Open Setting"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "Translate Popup/Selected"
|
||||
"message": "Open Popup Box"
|
||||
}
|
||||
}
|
||||
|
||||
20
public/_locales/es/messages.json
Normal file
20
public/_locales/es/messages.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app_name": {
|
||||
"message": "KISS Traductor"
|
||||
},
|
||||
"app_description": {
|
||||
"message": "Una sencilla extensión y script de Greasemonkey para traducción bilingüe"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "Alternar traducción"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "Cambiar estilo"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "Abrir configuración"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "Abrir ventana emergente"
|
||||
}
|
||||
}
|
||||
20
public/_locales/fr/messages.json
Normal file
20
public/_locales/fr/messages.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app_name": {
|
||||
"message": "KISS Traducteur"
|
||||
},
|
||||
"app_description": {
|
||||
"message": "Une extension et un script Greasemonkey de traduction bilingue simple"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "Activer/désactiver la traduction"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "Changer de style"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "Ouvrir les paramètres"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "Ouvrir la fenêtre contextuelle"
|
||||
}
|
||||
}
|
||||
20
public/_locales/ja/messages.json
Normal file
20
public/_locales/ja/messages.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app_name": {
|
||||
"message": "シンプル翻訳"
|
||||
},
|
||||
"app_description": {
|
||||
"message": "シンプルなバイリンガル対訳翻訳拡張機能&Tampermonkeyスクリプト"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "翻訳の切り替え"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "スタイル切り替え"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "設定を開く"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "ポップアップを開く"
|
||||
}
|
||||
}
|
||||
20
public/_locales/ko/messages.json
Normal file
20
public/_locales/ko/messages.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app_name": {
|
||||
"message": "심플 번역"
|
||||
},
|
||||
"app_description": {
|
||||
"message": "심플한 이중 언어 대조 번역 확장 프로그램 & Tampermonkey 스크립트"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "번역 켜기"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "스타일 전환"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "설정 열기"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "팝업 열기"
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,6 @@
|
||||
"message": "打开设置"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "翻译弹窗/选中文字"
|
||||
"message": "打开弹窗"
|
||||
}
|
||||
}
|
||||
|
||||
20
public/_locales/zh_TW/messages.json
Normal file
20
public/_locales/zh_TW/messages.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app_name": {
|
||||
"message": "簡約翻譯"
|
||||
},
|
||||
"app_description": {
|
||||
"message": "一個簡約的雙語對照翻譯擴充功能與 Tampermonkey 腳本"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "開啟翻譯"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "切換樣式"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "開啟設定"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "開啟彈出視窗"
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "2.0.8",
|
||||
"version": "2.0.9",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "2.0.8",
|
||||
"version": "2.0.9",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "2.0.8",
|
||||
"version": "2.0.9",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
|
||||
@@ -589,8 +589,10 @@ const genCloudflareAI = ({ texts, from, to, url, key }) => {
|
||||
return { url, body, headers };
|
||||
};
|
||||
|
||||
const genCustom = ({ texts, fromLang, toLang, url, key }) => {
|
||||
const body = { texts, from: fromLang, to: toLang };
|
||||
const genCustom = ({ texts, fromLang, toLang, url, key, useBatchFetch }) => {
|
||||
const body = useBatchFetch
|
||||
? { texts, from: fromLang, to: toLang }
|
||||
: { text: texts[0], from: fromLang, to: toLang };
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
@@ -810,6 +812,8 @@ export const parseTransRes = async (
|
||||
history.add(userMsg, hookResult.modelMsg);
|
||||
}
|
||||
return hookResult.translations;
|
||||
} else if (Array.isArray(hookResult)) {
|
||||
return hookResult;
|
||||
}
|
||||
} catch (err) {
|
||||
kissLog("run res hook", err);
|
||||
@@ -912,7 +916,10 @@ export const parseTransRes = async (
|
||||
}
|
||||
return parseAIRes(modelMsg?.content, useBatchFetch);
|
||||
case OPT_TRANS_CUSTOMIZE:
|
||||
return (res?.translations ?? res)?.map((item) => [item.text, item.src]);
|
||||
if (useBatchFetch) {
|
||||
return (res?.translations ?? res)?.map((item) => [item.text, item.src]);
|
||||
}
|
||||
return [[res.text, res.src || res.from]];
|
||||
default:
|
||||
}
|
||||
|
||||
|
||||
@@ -559,7 +559,6 @@ const defaultApiOpts = {
|
||||
},
|
||||
[OPT_TRANS_CUSTOMIZE]: {
|
||||
...defaultApi,
|
||||
url: "https://translate.googleapis.com/translate_a/single?client=gtx&dj=1&dt=t&ie=UTF-8&q={{text}}&sl=en&tl=zh-CN",
|
||||
reqHook: defaultRequestHook,
|
||||
resHook: defaultResponseHook,
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -226,9 +226,15 @@ export const saveRule = async (curRule) => {
|
||||
}
|
||||
|
||||
const newRule = {};
|
||||
Object.entries(GLOBLA_RULE).forEach(([key, val]) => {
|
||||
const globalRule = {
|
||||
...GLOBLA_RULE,
|
||||
...(rules.find((r) => r.pattern === GLOBAL_KEY) || {}),
|
||||
};
|
||||
Object.keys(GLOBLA_RULE).forEach((key) => {
|
||||
newRule[key] =
|
||||
!curRule[key] || curRule[key] === val ? DEFAULT_RULE[key] : curRule[key];
|
||||
!curRule[key] || curRule[key] === globalRule[key]
|
||||
? DEFAULT_RULE[key]
|
||||
: curRule[key];
|
||||
});
|
||||
|
||||
rules.unshift(newRule);
|
||||
|
||||
@@ -26,7 +26,7 @@ async function set(key, val) {
|
||||
} else if (isGm) {
|
||||
await (window.KISS_GM || GM).setValue(key, val);
|
||||
} else {
|
||||
window.localStorage.setItem(key, val);
|
||||
window?.localStorage.setItem(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ async function get(key) {
|
||||
const val = await (window.KISS_GM || GM).getValue(key);
|
||||
return val;
|
||||
}
|
||||
return window.localStorage.getItem(key);
|
||||
return window?.localStorage.getItem(key);
|
||||
}
|
||||
|
||||
async function del(key) {
|
||||
@@ -47,7 +47,7 @@ async function del(key) {
|
||||
} else if (isGm) {
|
||||
await (window.KISS_GM || GM).deleteValue(key);
|
||||
} else {
|
||||
window.localStorage.removeItem(key);
|
||||
window?.localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ export function Menus({
|
||||
return i18n("processing_subtitles");
|
||||
}, [progressed, i18n]);
|
||||
|
||||
const { isAISegment, skipAd, isBilingual } = formData;
|
||||
const { isAISegment, skipAd, isBilingual, showOrigin } = formData;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -157,6 +157,12 @@ export function Menus({
|
||||
value={isBilingual}
|
||||
label={i18n("is_bilingual_view")}
|
||||
/>
|
||||
<Switch
|
||||
onChange={handleChange}
|
||||
name="showOrigin"
|
||||
value={showOrigin}
|
||||
label={i18n("show_origin_subtitle")}
|
||||
/>
|
||||
<Switch
|
||||
onChange={handleChange}
|
||||
name="skipAd"
|
||||
|
||||
@@ -42,7 +42,7 @@ class YouTubeCaptionProvider {
|
||||
#menuEventName = "kiss-event";
|
||||
|
||||
constructor(setting = {}) {
|
||||
this.#setting = { ...setting, isAISegment: false };
|
||||
this.#setting = { ...setting, isAISegment: false, showOrigin: false };
|
||||
this.#i18n = newI18n(setting.uiLang || "zh");
|
||||
this.#menuEventName = genEventName();
|
||||
}
|
||||
@@ -151,6 +151,10 @@ class YouTubeCaptionProvider {
|
||||
|
||||
if (node.matches(adLayoutSelector)) {
|
||||
logger.debug("Youtube Provider: Ad ends!");
|
||||
|
||||
if (!this.#setting.showOrigin) {
|
||||
this.#hideYtCaption();
|
||||
}
|
||||
if (videoEl && skipAd) {
|
||||
videoEl.playbackRate = 1;
|
||||
}
|
||||
@@ -200,6 +204,16 @@ class YouTubeCaptionProvider {
|
||||
this.#managerInstance?.updateSetting({ [name]: value });
|
||||
} else if (name === "isAISegment") {
|
||||
this.#reProcessEvents();
|
||||
} else if (name === "showOrigin") {
|
||||
this.#toggleShowOrigin();
|
||||
}
|
||||
}
|
||||
|
||||
#toggleShowOrigin() {
|
||||
if (this.#setting.showOrigin) {
|
||||
this.#destroyManager();
|
||||
} else {
|
||||
this.#startManager();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +255,8 @@ class YouTubeCaptionProvider {
|
||||
toggleButton.appendChild(createLogoSVG());
|
||||
kissControls.appendChild(toggleButton);
|
||||
|
||||
const { segApiSetting, isAISegment, skipAd, isBilingual } = this.#setting;
|
||||
const { segApiSetting, isAISegment, skipAd, isBilingual, showOrigin } =
|
||||
this.#setting;
|
||||
const menu = new ShadowDomManager({
|
||||
id: "kiss-subtitle-menus",
|
||||
className: "notranslate",
|
||||
@@ -254,9 +269,10 @@ class YouTubeCaptionProvider {
|
||||
hasSegApi: !!segApiSetting,
|
||||
eventName: this.#menuEventName,
|
||||
initData: {
|
||||
isAISegment,
|
||||
skipAd,
|
||||
isBilingual,
|
||||
isAISegment, // AI智能断句
|
||||
skipAd, // 快进广告
|
||||
isBilingual, // 双语显示
|
||||
showOrigin, // 显示原字幕
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -268,6 +284,10 @@ class YouTubeCaptionProvider {
|
||||
createLogoSVG({ isSelected: true })
|
||||
);
|
||||
menu.show();
|
||||
this.#sendMenusMsg({
|
||||
action: MSG_MENUS_PROGRESSED,
|
||||
data: this.#progressed,
|
||||
});
|
||||
} else {
|
||||
this.#isMenuShow = false;
|
||||
this.#toggleButton?.replaceChildren(createLogoSVG());
|
||||
@@ -512,6 +532,9 @@ class YouTubeCaptionProvider {
|
||||
}
|
||||
|
||||
#reProcessEvents() {
|
||||
this.#progressed = 0;
|
||||
this.#subtitles = [];
|
||||
|
||||
const videoId = this.#videoId;
|
||||
const flatEvents = this.#flatEvents;
|
||||
const fromLang = this.#fromLang;
|
||||
@@ -557,19 +580,19 @@ class YouTubeCaptionProvider {
|
||||
return subtitlesFallback();
|
||||
}
|
||||
|
||||
const chunkCount = eventChunks.length;
|
||||
if (chunkCount > 1) {
|
||||
if (eventChunks.length > 1) {
|
||||
const remainingChunks = eventChunks.slice(1);
|
||||
this.#processRemainingChunksAsync({
|
||||
chunks: remainingChunks,
|
||||
chunkCount,
|
||||
videoId,
|
||||
fromLang,
|
||||
toLang,
|
||||
segApiSetting,
|
||||
});
|
||||
|
||||
return [firstBatchSubtitles, 100 / eventChunks.length];
|
||||
const processed = Math.floor(100 / eventChunks.length);
|
||||
|
||||
return [firstBatchSubtitles, processed];
|
||||
} else {
|
||||
return [firstBatchSubtitles, 100];
|
||||
}
|
||||
@@ -583,6 +606,10 @@ class YouTubeCaptionProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.#setting.showOrigin) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.#subtitles.length) {
|
||||
this.#showNotification(this.#i18n("waitting_for_subtitle"));
|
||||
return;
|
||||
@@ -605,8 +632,7 @@ class YouTubeCaptionProvider {
|
||||
|
||||
this.#showNotification(this.#i18n("subtitle_load_succeed"));
|
||||
|
||||
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
||||
ytCaption && (ytCaption.style.display = "none");
|
||||
this.#hideYtCaption();
|
||||
}
|
||||
|
||||
#destroyManager() {
|
||||
@@ -619,6 +645,15 @@ class YouTubeCaptionProvider {
|
||||
this.#managerInstance.destroy();
|
||||
this.#managerInstance = null;
|
||||
|
||||
this.#showYtCaption();
|
||||
}
|
||||
|
||||
#hideYtCaption() {
|
||||
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
||||
ytCaption && (ytCaption.style.display = "none");
|
||||
}
|
||||
|
||||
#showYtCaption() {
|
||||
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
||||
ytCaption && (ytCaption.style.display = "block");
|
||||
}
|
||||
@@ -920,7 +955,6 @@ class YouTubeCaptionProvider {
|
||||
|
||||
async #processRemainingChunksAsync({
|
||||
chunks,
|
||||
chunkCount,
|
||||
videoId,
|
||||
fromLang,
|
||||
toLang,
|
||||
@@ -968,7 +1002,7 @@ class YouTubeCaptionProvider {
|
||||
}
|
||||
|
||||
if (subtitlesForThisChunk.length > 0) {
|
||||
const progressed = (chunkNum * 100) / chunkCount;
|
||||
const progressed = Math.floor((chunkNum * 100) / (chunks.length + 1));
|
||||
this.#subtitles.push(...subtitlesForThisChunk);
|
||||
this.#progressed = progressed;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user