Compare commits

...

9 Commits

Author SHA1 Message Date
Gabe Yuan
535a43b698 release: v1.7.14 2023-11-30 16:44:56 +08:00
Gabe Yuan
59752ed4aa fix: set FormControl small size 2023-11-30 15:11:06 +08:00
Gabe Yuan
b3e7b8f3f1 fix: readme 2023-11-28 15:15:02 +08:00
Gabe Yuan
c4e9365512 fix: clipboard.writeText run with async 2023-11-28 14:59:31 +08:00
Gabe Yuan
7d3972d3a8 perf: merge Translate Popup/Selected shortcut 2023-11-28 13:36:40 +08:00
Gabe Yuan
52ca4306fd feat: blockquote style 2023-11-28 11:41:45 +08:00
Gabe Yuan
da368ee612 feat: disable languages 2023-11-28 11:11:59 +08:00
Gabe Yuan
22c50e7765 feat: translate blacklist 2023-11-24 17:07:29 +08:00
Gabe Yuan
7bc39dd1bc fix: default shortcut: open setting page 2023-11-23 17:47:50 +08:00
22 changed files with 287 additions and 307 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.7.13 REACT_APP_VERSION=1.7.14
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator

View File

@@ -31,10 +31,9 @@ A simple, open source [bilingual translation extension & Greasemonkey script](ht
- `Alt+Q` Toggle Translation - `Alt+Q` Toggle Translation
- `Alt+C` Toggle Styles - `Alt+C` Toggle Styles
- `Alt+K` Open Setting Popup - `Alt+K` Open Setting Popup
- `Alt+B` Open Translate Popup - `Alt+S` Open Translate Popup / Translate Selected Text
- `Alt+O` Open Options Page - `Alt+O` Open Options Page
- `Alt+I` Input Box Translation - `Alt+I` Input Box Translation
- `Alt+S` Translate Selected Text
## Install ## Install

View File

@@ -31,10 +31,9 @@
- `Alt+Q` 开启翻译 - `Alt+Q` 开启翻译
- `Alt+C` 切换样式 - `Alt+C` 切换样式
- `Alt+K` 打开设置弹窗 - `Alt+K` 打开设置弹窗
- `Alt+B` 打开翻译弹窗 - `Alt+S` 打开翻译弹窗/翻译选中文字
- `Alt+O` 打开设置页面 - `Alt+O` 打开设置页面
- `Alt+I` 输入框翻译 - `Alt+I` 输入框翻译
- `Alt+S` 翻译选中文字
## 安装 ## 安装

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.7.13", "version": "1.7.14",
"author": "Gabe<yugang2002@gmail.com>", "author": "Gabe<yugang2002@gmail.com>",
"private": true, "private": true,
"dependencies": { "dependencies": {

View File

@@ -15,9 +15,6 @@
"message": "Open Options (Alt+O)" "message": "Open Options (Alt+O)"
}, },
"open_tranbox": { "open_tranbox": {
"message": "Open Translate Popup (Alt+B)" "message": "Translate Popup/Selected (Alt+S)"
},
"translate_selected": {
"message": "Translate Selected Text (Alt+S)"
} }
} }

View File

@@ -15,9 +15,6 @@
"message": "打开设置 (Alt+O)" "message": "打开设置 (Alt+O)"
}, },
"open_tranbox": { "open_tranbox": {
"message": "打开翻译弹窗 (Alt+B)" "message": "翻译弹窗/选中文字 (Alt+S)"
},
"translate_selected": {
"message": "翻译选中文字 (Alt+S)"
} }
} }

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.7.13", "version": "1.7.14",
"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.7.13", "version": "1.7.14",
"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

@@ -7,13 +7,11 @@ import {
MSG_OPEN_OPTIONS, MSG_OPEN_OPTIONS,
MSG_SAVE_RULE, MSG_SAVE_RULE,
MSG_TRANS_TOGGLE_STYLE, MSG_TRANS_TOGGLE_STYLE,
MSG_TRANSLATE_SELECTED,
MSG_OPEN_TRANBOX, MSG_OPEN_TRANBOX,
CMD_TOGGLE_TRANSLATE, CMD_TOGGLE_TRANSLATE,
CMD_TOGGLE_STYLE, CMD_TOGGLE_STYLE,
CMD_OPEN_OPTIONS, CMD_OPEN_OPTIONS,
CMD_OPEN_TRANBOX, CMD_OPEN_TRANBOX,
CMD_TRANSLATE_SELECTED,
} 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";
@@ -32,16 +30,6 @@ browser.runtime.onInstalled.addListener(() => {
tryInitDefaultData(); tryInitDefaultData();
// 右键菜单 // 右键菜单
browser.contextMenus.create({
id: CMD_TRANSLATE_SELECTED,
title: browser.i18n.getMessage("translate_selected"),
contexts: ["selection"],
});
browser.contextMenus.create({
id: "selection_separator",
type: "separator",
contexts: ["selection"],
});
browser.contextMenus.create({ browser.contextMenus.create({
id: CMD_TOGGLE_TRANSLATE, id: CMD_TOGGLE_TRANSLATE,
title: browser.i18n.getMessage("toggle_translate"), title: browser.i18n.getMessage("toggle_translate"),
@@ -53,13 +41,13 @@ browser.runtime.onInstalled.addListener(() => {
contexts: ["all"], contexts: ["all"],
}); });
browser.contextMenus.create({ browser.contextMenus.create({
id: "options_separator", id: CMD_OPEN_TRANBOX,
type: "separator", title: browser.i18n.getMessage("open_tranbox"),
contexts: ["all"], contexts: ["all"],
}); });
browser.contextMenus.create({ browser.contextMenus.create({
id: CMD_OPEN_TRANBOX, id: "options_separator",
title: browser.i18n.getMessage("open_tranbox"), type: "separator",
contexts: ["all"], contexts: ["all"],
}); });
browser.contextMenus.create({ browser.contextMenus.create({
@@ -151,9 +139,6 @@ browser.contextMenus.onClicked.addListener(({ menuItemId }) => {
case CMD_TOGGLE_STYLE: case CMD_TOGGLE_STYLE:
sendTabMsg(MSG_TRANS_TOGGLE_STYLE); sendTabMsg(MSG_TRANS_TOGGLE_STYLE);
break; break;
case CMD_TRANSLATE_SELECTED:
sendTabMsg(MSG_TRANSLATE_SELECTED);
break;
case CMD_OPEN_TRANBOX: case CMD_OPEN_TRANBOX:
sendTabMsg(MSG_OPEN_TRANBOX); sendTabMsg(MSG_OPEN_TRANBOX);
break; break;

View File

@@ -8,27 +8,79 @@ import {
MSG_TRANS_TOGGLE_STYLE, MSG_TRANS_TOGGLE_STYLE,
MSG_TRANS_GETRULE, MSG_TRANS_GETRULE,
MSG_TRANS_PUTRULE, MSG_TRANS_PUTRULE,
MSG_OPEN_TRANBOX,
APP_LCNAME, APP_LCNAME,
DEFAULT_TRANBOX_SETTING, DEFAULT_TRANBOX_SETTING,
} from "./config"; } from "./config";
import { getRulesWithDefault, getFabWithDefault } from "./libs/storage"; import { getFabWithDefault, getSettingWithDefault } from "./libs/storage";
import { Translator } from "./libs/translator"; import { Translator } from "./libs/translator";
import { sendIframeMsg, sendParentMsg } from "./libs/iframe"; import { isIframe, sendIframeMsg, sendParentMsg } from "./libs/iframe";
import { matchRule } from "./libs/rules";
import Slection from "./views/Selection"; import Slection from "./views/Selection";
import { touchTapListener } from "./libs/touch"; import { touchTapListener } from "./libs/touch";
import { debounce } from "./libs/utils"; import { debounce, genEventName } from "./libs/utils";
import { handlePing, injectScript } from "./libs/gm";
import { browser } from "./libs/browser";
import { runWebfix } from "./libs/webfix";
import { matchRule } from "./libs/rules";
import { trySyncAllSubRules } from "./libs/subRules";
import { isInBlacklist } from "./libs/blacklist";
export async function runTranslator(setting) { /**
const href = document.location.href; * 油猴脚本设置页面
const rules = await getRulesWithDefault(); */
const rule = await matchRule(rules, href, setting); function runSettingPage() {
const translator = new Translator(rule, setting); if (GM?.info?.script?.grant?.includes("unsafeWindow")) {
unsafeWindow.GM = GM;
return { translator, rule }; unsafeWindow.APP_INFO = {
name: process.env.REACT_APP_NAME,
version: process.env.REACT_APP_VERSION,
};
} else {
const ping = genEventName();
window.addEventListener(ping, handlePing);
// window.eval(`(${injectScript})("${ping}")`); // eslint-disable-line
const script = document.createElement("script");
script.textContent = `(${injectScript})("${ping}")`;
document.head.append(script);
}
} }
export function runIframe(setting) { /**
* 插件监听后端事件
* @param {*} translator
*/
function runtimeListener(translator) {
browser?.runtime.onMessage.addListener(async ({ action, args }) => {
switch (action) {
case MSG_TRANS_TOGGLE:
translator.toggle();
sendIframeMsg(MSG_TRANS_TOGGLE);
break;
case MSG_TRANS_TOGGLE_STYLE:
translator.toggleStyle();
sendIframeMsg(MSG_TRANS_TOGGLE_STYLE);
break;
case MSG_TRANS_GETRULE:
break;
case MSG_TRANS_PUTRULE:
translator.updateRule(args);
sendIframeMsg(MSG_TRANS_PUTRULE, args);
break;
case MSG_OPEN_TRANBOX:
window.dispatchEvent(new CustomEvent(MSG_OPEN_TRANBOX));
break;
default:
return { error: `message action is unavailable: ${action}` };
}
return { data: translator.rule };
});
}
/**
* iframe 页面执行
* @param {*} setting
*/
function runIframe(setting) {
let translator; let translator;
window.addEventListener("message", (e) => { window.addEventListener("message", (e) => {
const { action, args } = e.data || {}; const { action, args } = e.data || {};
@@ -52,7 +104,12 @@ export function runIframe(setting) {
sendParentMsg(MSG_TRANS_GETRULE); sendParentMsg(MSG_TRANS_GETRULE);
} }
export async function showFab(translator) { /**
* 悬浮按钮
* @param {*} translator
* @returns
*/
async function showFab(translator) {
const fab = await getFabWithDefault(); const fab = await getFabWithDefault();
if (fab.isHide) { if (fab.isHide) {
return; return;
@@ -80,10 +137,12 @@ export async function showFab(translator) {
); );
} }
export function showTransbox({ /**
tranboxSetting = DEFAULT_TRANBOX_SETTING, * 划词翻译
transApis, * @param {*} param0
}) { * @returns
*/
function showTransbox({ tranboxSetting = DEFAULT_TRANBOX_SETTING, transApis }) {
if (!tranboxSetting?.transOpen) { if (!tranboxSetting?.transOpen) {
return; return;
} }
@@ -110,7 +169,11 @@ export function showTransbox({
); );
} }
export function windowListener(rule) { /**
* 监听来自iframe页面消息
* @param {*} rule
*/
function windowListener(rule) {
window.addEventListener("message", (e) => { window.addEventListener("message", (e) => {
const { action } = e.data || {}; const { action } = e.data || {};
switch (action) { switch (action) {
@@ -122,14 +185,23 @@ export function windowListener(rule) {
}); });
} }
export function showErr(message) { /**
* 显示错误信息到页面顶部
* @param {*} message
*/
function showErr(message) {
const $err = document.createElement("div"); const $err = document.createElement("div");
$err.innerText = `KISS-Translator: ${message}`; $err.innerText = `KISS-Translator: ${message}`;
$err.style.cssText = "background:red; color:#fff;"; $err.style.cssText = "background:red; color:#fff;";
document.body.prepend($err); document.body.prepend($err);
} }
export function touchOperation(translator) { /**
* 监听触屏操作
* @param {*} translator
* @returns
*/
function touchOperation(translator) {
const { touchTranslate = 2 } = translator.setting; const { touchTranslate = 2 } = translator.setting;
if (touchTranslate === 0) { if (touchTranslate === 0) {
return; return;
@@ -141,3 +213,63 @@ export function touchOperation(translator) {
}); });
touchTapListener(handleTap, touchTranslate); touchTapListener(handleTap, touchTranslate);
} }
/**
* 入口函数
*/
export async function run(isUserscript = false) {
try {
const href = document.location.href;
// 设置页面
if (
isUserscript &&
(href.includes(process.env.REACT_APP_OPTIONSPAGE_DEV) ||
href.includes(process.env.REACT_APP_OPTIONSPAGE) ||
href.includes(process.env.REACT_APP_OPTIONSPAGE2))
) {
runSettingPage();
return;
}
// 读取设置信息
const setting = await getSettingWithDefault();
// 黑名单
if (isInBlacklist(href, setting)) {
return;
}
// 适配iframe
if (isIframe) {
runIframe(setting);
return;
}
// 不规范网页修复
await runWebfix(setting);
// 翻译网页
const rule = await matchRule(href, setting);
const translator = new Translator(rule, setting);
// 监听消息
windowListener(rule);
!isUserscript && runtimeListener(translator);
// 划词翻译
showTransbox(setting);
// 浮球按钮
await showFab(translator);
// 触屏操作
touchOperation(translator);
// 同步订阅规则
isUserscript && (await trySyncAllSubRules(setting));
} catch (err) {
console.error("[KISS-Translator]", err);
showErr(err.message);
}
}

View File

@@ -335,6 +335,10 @@ export const I18N = {
zh: `高亮`, zh: `高亮`,
en: `Highlight`, en: `Highlight`,
}, },
blockquote: {
zh: `引用`,
en: `Blockquote`,
},
diy_style: { diy_style: {
zh: `自定义样式`, zh: `自定义样式`,
en: `Custom Style`, en: `Custom Style`,
@@ -352,8 +356,8 @@ export const I18N = {
en: `URL pattern`, en: `URL pattern`,
}, },
pattern_helper: { pattern_helper: {
zh: `1、支持星号(*)通配符。2、多个URL支持英文逗号“,”分隔。`, zh: `1、支持星号(*)通配符。2、多个URL英文逗号“,”分隔。`,
en: `1. The asterisk (*) wildcard is supported. 2. Multiple URLs can be separated by English commas ",".`, en: `1. The asterisk (*) wildcard is supported. 2. Multiple URLs separated by English commas ",".`,
}, },
selector_helper: { selector_helper: {
zh: `1、遵循CSS选择器语法。2、留空表示采用全局设置。3、多个CSS选择器之间用“;”隔开。4、“shadow root”选择器和内部选择器用“>>>”隔开。`, zh: `1、遵循CSS选择器语法。2、留空表示采用全局设置。3、多个CSS选择器之间用“;”隔开。4、“shadow root”选择器和内部选择器用“>>>”隔开。`,
@@ -632,12 +636,8 @@ export const I18N = {
en: `Use Selection Translate`, en: `Use Selection Translate`,
}, },
trigger_tranbox_shortcut: { trigger_tranbox_shortcut: {
zh: `显示翻译框快捷键`, zh: `显示翻译框/翻译选中文字快捷键`,
en: `Toggle Translate Box Shortcut`, en: `Open Translate Popup/Translate Selected Shortcut`,
},
trigger_transel_shortcut: {
zh: `翻译选中文字快捷键`,
en: `Translate Selected Shortcut`,
}, },
tranbtn_offset_x: { tranbtn_offset_x: {
zh: `翻译按钮偏移X0-100`, zh: `翻译按钮偏移X0-100`,
@@ -683,4 +683,16 @@ export const I18N = {
zh: `四指轻触`, zh: `四指轻触`,
en: `Four finger tap`, en: `Four finger tap`,
}, },
translate_blacklist: {
zh: `禁用翻译名单`,
en: `Translate Blacklist`,
},
disable_langs: {
zh: `不翻译的语言`,
en: `Disable Languages`,
},
disable_langs_helper: {
zh: `此功能依赖准确的语言检测,建议启用远程语言检测。`,
en: `This feature relies on accurate language detection. It is recommended to enable remote language detection.`,
}
}; };

View File

@@ -34,7 +34,6 @@ export const CMD_TOGGLE_TRANSLATE = "toggleTranslate";
export const CMD_TOGGLE_STYLE = "toggleStyle"; export const CMD_TOGGLE_STYLE = "toggleStyle";
export const CMD_OPEN_OPTIONS = "openOptions"; export const CMD_OPEN_OPTIONS = "openOptions";
export const CMD_OPEN_TRANBOX = "openTranbox"; export const CMD_OPEN_TRANBOX = "openTranbox";
export const CMD_TRANSLATE_SELECTED = "translateSelected";
export const CLIENT_WEB = "web"; export const CLIENT_WEB = "web";
export const CLIENT_CHROME = "chrome"; export const CLIENT_CHROME = "chrome";
@@ -60,7 +59,6 @@ export const MSG_OPEN_OPTIONS = "open_options";
export const MSG_SAVE_RULE = "save_rule"; export const MSG_SAVE_RULE = "save_rule";
export const MSG_TRANS_TOGGLE = "trans_toggle"; export const MSG_TRANS_TOGGLE = "trans_toggle";
export const MSG_TRANS_TOGGLE_STYLE = "trans_toggle_style"; export const MSG_TRANS_TOGGLE_STYLE = "trans_toggle_style";
export const MSG_TRANSLATE_SELECTED = "translate_selected";
export const MSG_OPEN_TRANBOX = "open_tranbox"; export const MSG_OPEN_TRANBOX = "open_tranbox";
export const MSG_TRANS_GETRULE = "trans_getrule"; export const MSG_TRANS_GETRULE = "trans_getrule";
export const MSG_TRANS_PUTRULE = "trans_putrule"; export const MSG_TRANS_PUTRULE = "trans_putrule";
@@ -269,6 +267,7 @@ export const OPT_STYLE_DASHLINE = "dash_line"; // 虚线
export const OPT_STYLE_WAVYLINE = "wavy_line"; // 波浪线 export const OPT_STYLE_WAVYLINE = "wavy_line"; // 波浪线
export const OPT_STYLE_FUZZY = "fuzzy"; // 模糊 export const OPT_STYLE_FUZZY = "fuzzy"; // 模糊
export const OPT_STYLE_HIGHLIGHT = "highlight"; // 高亮 export const OPT_STYLE_HIGHLIGHT = "highlight"; // 高亮
export const OPT_STYLE_BLOCKQUOTE = "blockquote"; // 引用
export const OPT_STYLE_DIY = "diy_style"; // 自定义样式 export const OPT_STYLE_DIY = "diy_style"; // 自定义样式
export const OPT_STYLE_ALL = [ export const OPT_STYLE_ALL = [
OPT_STYLE_NONE, OPT_STYLE_NONE,
@@ -278,6 +277,7 @@ export const OPT_STYLE_ALL = [
OPT_STYLE_WAVYLINE, OPT_STYLE_WAVYLINE,
OPT_STYLE_FUZZY, OPT_STYLE_FUZZY,
OPT_STYLE_HIGHLIGHT, OPT_STYLE_HIGHLIGHT,
OPT_STYLE_BLOCKQUOTE,
OPT_STYLE_DIY, OPT_STYLE_DIY,
]; ];
export const OPT_STYLE_USE_COLOR = [ export const OPT_STYLE_USE_COLOR = [
@@ -286,6 +286,7 @@ export const OPT_STYLE_USE_COLOR = [
OPT_STYLE_DASHLINE, OPT_STYLE_DASHLINE,
OPT_STYLE_WAVYLINE, OPT_STYLE_WAVYLINE,
OPT_STYLE_HIGHLIGHT, OPT_STYLE_HIGHLIGHT,
OPT_STYLE_BLOCKQUOTE,
]; ];
export const OPT_MOUSEKEY_DISABLE = "mk_disable"; export const OPT_MOUSEKEY_DISABLE = "mk_disable";
@@ -337,15 +338,13 @@ export const DEFAULT_INPUT_RULE = {
}; };
// 划词翻译 // 划词翻译
export const DEFAULT_TRANBOX_SHORTCUT = ["AltLeft", "KeyB"]; export const DEFAULT_TRANBOX_SHORTCUT = ["AltLeft", "KeyS"];
export const DEFAULT_TRANSEL_SHORTCUT = ["AltLeft", "KeyS"];
export const DEFAULT_TRANBOX_SETTING = { export const DEFAULT_TRANBOX_SETTING = {
transOpen: true, transOpen: true,
translator: OPT_TRANS_MICROSOFT, translator: OPT_TRANS_MICROSOFT,
fromLang: "auto", fromLang: "auto",
toLang: "zh-CN", toLang: "zh-CN",
tranboxShortcut: DEFAULT_TRANBOX_SHORTCUT, tranboxShortcut: DEFAULT_TRANBOX_SHORTCUT,
transelShortcut: DEFAULT_TRANSEL_SHORTCUT,
btnOffsetX: 10, btnOffsetX: 10,
btnOffsetY: 10, btnOffsetY: 10,
hideTranBtn: false, hideTranBtn: false,
@@ -406,12 +405,19 @@ export const DEFAULT_SHORTCUTS = {
[OPT_SHORTCUT_TRANSLATE]: ["AltLeft", "KeyQ"], [OPT_SHORTCUT_TRANSLATE]: ["AltLeft", "KeyQ"],
[OPT_SHORTCUT_STYLE]: ["AltLeft", "KeyC"], [OPT_SHORTCUT_STYLE]: ["AltLeft", "KeyC"],
[OPT_SHORTCUT_POPUP]: ["AltLeft", "KeyK"], [OPT_SHORTCUT_POPUP]: ["AltLeft", "KeyK"],
[OPT_SHORTCUT_SETTING]: ["AltLeft", "KeyN"], [OPT_SHORTCUT_SETTING]: ["AltLeft", "KeyO"],
}; };
export const TRANS_MIN_LENGTH = 5; // 最短翻译长度 export const TRANS_MIN_LENGTH = 5; // 最短翻译长度
export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度 export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度
export const TRANS_NEWLINE_LENGTH = 20; // 换行字符数 export const TRANS_NEWLINE_LENGTH = 20; // 换行字符数
export const DEFAULT_BLACKLIST = [
"https://fishjar.github.io/kiss-translator/options.html",
"https://translate.google.com",
"https://www.deepl.com/translator",
"oapi.dingtalk.com",
"login.dingtalk.com",
]; // 禁用翻译名单
export const DEFAULT_SETTING = { export const DEFAULT_SETTING = {
darkMode: false, // 深色模式 darkMode: false, // 深色模式
@@ -433,6 +439,8 @@ export const DEFAULT_SETTING = {
inputRule: DEFAULT_INPUT_RULE, // 输入框设置 inputRule: DEFAULT_INPUT_RULE, // 输入框设置
tranboxSetting: DEFAULT_TRANBOX_SETTING, // 划词翻译设置 tranboxSetting: DEFAULT_TRANBOX_SETTING, // 划词翻译设置
touchTranslate: 2, // 触屏翻译 touchTranslate: 2, // 触屏翻译
blacklist: DEFAULT_BLACKLIST.join(",\n"), // 禁用翻译名单
disableLangs: [], // 不翻译的语言
}; };
export const DEFAULT_RULES = [GLOBLA_RULE]; export const DEFAULT_RULES = [GLOBLA_RULE];

View File

@@ -1,89 +1,3 @@
import { browser } from "./libs/browser"; import { run } from "./common";
import {
MSG_TRANS_TOGGLE,
MSG_TRANS_TOGGLE_STYLE,
MSG_TRANSLATE_SELECTED,
MSG_OPEN_TRANBOX,
MSG_TRANS_GETRULE,
MSG_TRANS_PUTRULE,
} from "./config";
import { getSettingWithDefault } from "./libs/storage";
import { isIframe, sendIframeMsg } from "./libs/iframe";
import { runWebfix } from "./libs/webfix";
import {
runIframe,
runTranslator,
showFab,
showTransbox,
windowListener,
showErr,
touchOperation,
} from "./common";
function runtimeListener(translator) { run();
browser?.runtime.onMessage.addListener(async ({ action, args }) => {
switch (action) {
case MSG_TRANS_TOGGLE:
translator.toggle();
sendIframeMsg(MSG_TRANS_TOGGLE);
break;
case MSG_TRANS_TOGGLE_STYLE:
translator.toggleStyle();
sendIframeMsg(MSG_TRANS_TOGGLE_STYLE);
break;
case MSG_TRANS_GETRULE:
break;
case MSG_TRANS_PUTRULE:
translator.updateRule(args);
sendIframeMsg(MSG_TRANS_PUTRULE, args);
break;
case MSG_TRANSLATE_SELECTED:
window.dispatchEvent(new CustomEvent(MSG_TRANSLATE_SELECTED));
break;
case MSG_OPEN_TRANBOX:
window.dispatchEvent(new CustomEvent(MSG_OPEN_TRANBOX));
break;
default:
return { error: `message action is unavailable: ${action}` };
}
return { data: translator.rule };
});
}
/**
* 入口函数
*/
(async () => {
try {
// 读取设置信息
const setting = await getSettingWithDefault();
// 适配iframe
if (isIframe) {
runIframe(setting);
return;
}
// 不规范网页修复
await runWebfix(setting);
// 翻译网页
const { translator, rule } = await runTranslator(setting);
// 监听消息
windowListener(rule);
runtimeListener(translator);
// 划词翻译
showTransbox(setting);
// 浮球按钮
await showFab(translator);
// 触屏操作
touchOperation(translator);
} catch (err) {
console.error("[KISS-Translator]", err);
showErr(err.message);
}
})();

View File

@@ -24,7 +24,11 @@ export function useTranslate(q, rule, setting) {
setLoading(true); setLoading(true);
const deLang = await tryDetectLang(q, setting.detectRemote); const deLang = await tryDetectLang(q, setting.detectRemote);
if (deLang && toLang.includes(deLang)) { const disableLangs = setting.disableLangs || [];
if (
deLang &&
(toLang.includes(deLang) || disableLangs.includes(deLang))
) {
setSamelang(true); setSamelang(true);
} else { } else {
const [trText, isSame] = await apiTranslate({ const [trText, isSame] = await apiTranslate({

13
src/libs/blacklist.js Normal file
View File

@@ -0,0 +1,13 @@
import { isMatch } from "./utils";
import { DEFAULT_BLACKLIST } from "../config";
/**
* 检查是否在黑名单中
* @param {*} href
* @param {*} param1
* @returns
*/
export const isInBlacklist = (
href,
{ blacklist = DEFAULT_BLACKLIST.join(",\n") }
) => blacklist.split(",").some((url) => isMatch(href, url.trim()));

View File

@@ -21,7 +21,6 @@ import { trySyncRules } from "./sync";
* @returns * @returns
*/ */
export const matchRule = async ( export const matchRule = async (
rules,
href, href,
{ {
injectRules = true, injectRules = true,
@@ -29,7 +28,7 @@ export const matchRule = async (
owSubrule = DEFAULT_OW_RULE, owSubrule = DEFAULT_OW_RULE,
} }
) => { ) => {
rules = [...rules]; const rules = await getRulesWithDefault();
if (injectRules) { if (injectRules) {
try { try {
const selectedSub = subrulesList.find((item) => item.selected); const selectedSub = subrulesList.find((item) => item.selected);

View File

@@ -1,82 +1,3 @@
import { getSettingWithDefault } from "./libs/storage"; import { run } from "./common";
import { trySyncAllSubRules } from "./libs/subRules";
import { isIframe } from "./libs/iframe";
import { handlePing, injectScript } from "./libs/gm";
import { genEventName } from "./libs/utils";
import { runWebfix } from "./libs/webfix";
import {
runIframe,
runTranslator,
showFab,
showTransbox,
windowListener,
showErr,
touchOperation,
} from "./common";
function runSettingPage() { run(true);
if (GM?.info?.script?.grant?.includes("unsafeWindow")) {
unsafeWindow.GM = GM;
unsafeWindow.APP_INFO = {
name: process.env.REACT_APP_NAME,
version: process.env.REACT_APP_VERSION,
};
} else {
const ping = genEventName();
window.addEventListener(ping, handlePing);
// window.eval(`(${injectScript})("${ping}")`); // eslint-disable-line
const script = document.createElement("script");
script.textContent = `(${injectScript})("${ping}")`;
document.head.append(script);
}
}
/**
* 入口函数
*/
(async () => {
try {
// 设置页面
if (
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE_DEV) ||
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE) ||
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE2)
) {
runSettingPage();
return;
}
// 读取设置信息
const setting = await getSettingWithDefault();
// 适配iframe
if (isIframe) {
runIframe(setting);
return;
}
// 不规范网页修复
await runWebfix(setting);
// 翻译网页
const { translator, rule } = await runTranslator(setting);
// 监听消息
windowListener(rule);
// 划词翻译
showTransbox(setting);
// 浮球按钮
await showFab(translator);
// 触屏操作
touchOperation(translator);
// 同步订阅规则
await trySyncAllSubRules(setting);
} catch (err) {
console.error("[KISS-Translator]", err);
showErr(err.message);
}
})();

View File

@@ -7,6 +7,7 @@ import {
OPT_STYLE_WAVYLINE, OPT_STYLE_WAVYLINE,
OPT_STYLE_FUZZY, OPT_STYLE_FUZZY,
OPT_STYLE_HIGHLIGHT, OPT_STYLE_HIGHLIGHT,
OPT_STYLE_BLOCKQUOTE,
OPT_STYLE_DIY, OPT_STYLE_DIY,
DEFAULT_COLOR, DEFAULT_COLOR,
MSG_TRANS_CURRULE, MSG_TRANS_CURRULE,
@@ -34,6 +35,18 @@ const LineSpan = styled("span")`
} }
`; `;
const BlockquoteSpan = styled("span")`
opacity: 0.6;
-webkit-opacity: 0.6;
display: block;
padding: 0 0.75em;
border-left: 0.25em solid ${(props) => props.$lineColor};
&:hover {
opacity: 1;
-webkit-opacity: 1;
}
`;
const FuzzySpan = styled("span")` const FuzzySpan = styled("span")`
filter: blur(0.2em); filter: blur(0.2em);
-webkit-filter: blur(0.2em); -webkit-filter: blur(0.2em);
@@ -86,6 +99,12 @@ function StyledSpan({ textStyle, textDiyStyle, bgColor, children }) {
{children} {children}
</HighlightSpan> </HighlightSpan>
); );
case OPT_STYLE_BLOCKQUOTE: // 引用
return (
<BlockquoteSpan $lineColor={bgColor || DEFAULT_COLOR}>
{children}
</BlockquoteSpan>
);
case OPT_STYLE_DIY: // 自定义 case OPT_STYLE_DIY: // 自定义
return <DiySpan $diyStyle={textDiyStyle}>{children}</DiySpan>; return <DiySpan $diyStyle={textDiyStyle}>{children}</DiySpan>;
default: default:

View File

@@ -23,6 +23,8 @@ import {
OPT_SHORTCUT_STYLE, OPT_SHORTCUT_STYLE,
OPT_SHORTCUT_POPUP, OPT_SHORTCUT_POPUP,
OPT_SHORTCUT_SETTING, OPT_SHORTCUT_SETTING,
OPT_LANGS_TO,
DEFAULT_BLACKLIST,
} from "../../config"; } from "../../config";
import { useShortcut } from "../../hooks/Shortcut"; import { useShortcut } from "../../hooks/Shortcut";
import ShortcutInput from "./ShortcutInput"; import ShortcutInput from "./ShortcutInput";
@@ -42,6 +44,7 @@ export default function Settings() {
const { fab, updateFab } = useFab(); const { fab, updateFab } = useFab();
const handleChange = (e) => { const handleChange = (e) => {
console.log("e", e);
e.preventDefault(); e.preventDefault();
let { name, value } = e.target; let { name, value } = e.target;
switch (name) { switch (name) {
@@ -90,6 +93,8 @@ export default function Settings() {
mouseKey = OPT_MOUSEKEY_DISABLE, mouseKey = OPT_MOUSEKEY_DISABLE,
detectRemote = false, detectRemote = false,
touchTranslate = 2, touchTranslate = 2,
blacklist = DEFAULT_BLACKLIST.join(",\n"),
disableLangs = [],
} = setting; } = setting;
const { isHide = false } = fab || {}; const { isHide = false } = fab || {};
@@ -218,6 +223,24 @@ export default function Settings() {
<FormHelperText>{i18n("detect_lang_remote_help")}</FormHelperText> <FormHelperText>{i18n("detect_lang_remote_help")}</FormHelperText>
</FormControl> </FormControl>
<FormControl size="small">
<InputLabel>{i18n("disable_langs")}</InputLabel>
<Select
multiple
name="disableLangs"
value={disableLangs}
label={i18n("disable_langs")}
onChange={handleChange}
>
{OPT_LANGS_TO.map(([langKey, langName]) => (
<MenuItem key={langKey} value={langKey}>
{langName}
</MenuItem>
))}
</Select>
<FormHelperText>{i18n("disable_langs_helper")}</FormHelperText>
</FormControl>
{isExt ? ( {isExt ? (
<FormControl size="small"> <FormControl size="small">
<InputLabel>{i18n("if_clear_cache")}</InputLabel> <InputLabel>{i18n("if_clear_cache")}</InputLabel>
@@ -268,6 +291,16 @@ export default function Settings() {
</Box> </Box>
</> </>
)} )}
<TextField
size="small"
label={i18n("translate_blacklist")}
helperText={i18n("pattern_helper")}
name="blacklist"
defaultValue={blacklist}
onChange={handleChange}
multiline
/>
</Stack> </Stack>
</Box> </Box>
); );

View File

@@ -3,12 +3,7 @@ import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import { useI18n } from "../../hooks/I18n"; import { useI18n } from "../../hooks/I18n";
import { import { OPT_TRANS_ALL, OPT_LANGS_FROM, OPT_LANGS_TO } from "../../config";
OPT_TRANS_ALL,
OPT_LANGS_FROM,
OPT_LANGS_TO,
DEFAULT_TRANSEL_SHORTCUT,
} from "../../config";
import ShortcutInput from "./ShortcutInput"; import ShortcutInput from "./ShortcutInput";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
@@ -44,20 +39,12 @@ export default function Tranbox() {
[updateTranbox] [updateTranbox]
); );
const handleShortcutTransel = useCallback(
(val) => {
updateTranbox({ transelShortcut: val });
},
[updateTranbox]
);
const { const {
transOpen, transOpen,
translator, translator,
fromLang, fromLang,
toLang, toLang,
tranboxShortcut, tranboxShortcut,
transelShortcut = DEFAULT_TRANSEL_SHORTCUT,
btnOffsetX, btnOffsetX,
btnOffsetY, btnOffsetY,
hideTranBtn = false, hideTranBtn = false,
@@ -160,12 +147,6 @@ export default function Tranbox() {
onChange={handleShortcutInput} onChange={handleShortcutInput}
label={i18n("trigger_tranbox_shortcut")} label={i18n("trigger_tranbox_shortcut")}
/> />
<ShortcutInput
value={transelShortcut}
onChange={handleShortcutTransel}
label={i18n("trigger_transel_shortcut")}
/>
</Stack> </Stack>
</Box> </Box>
); );

View File

@@ -5,9 +5,9 @@ import { useState } from "react";
export default function CopyBtn({ text }) { export default function CopyBtn({ text }) {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const handleClick = (e) => { const handleClick = async (e) => {
e.stopPropagation(); e.stopPropagation();
navigator.clipboard.writeText(text); await navigator.clipboard.writeText(text);
setCopied(true); setCopied(true);
const timer = setTimeout(() => { const timer = setTimeout(() => {
clearTimeout(timer); clearTimeout(timer);

View File

@@ -4,11 +4,7 @@ import TranBox from "./TranBox";
import { shortcutRegister } from "../../libs/shortcut"; import { shortcutRegister } from "../../libs/shortcut";
import { sleep } from "../../libs/utils"; import { sleep } from "../../libs/utils";
import { isGm } from "../../libs/client"; import { isGm } from "../../libs/client";
import { import { MSG_OPEN_TRANBOX, DEFAULT_TRANBOX_SHORTCUT } from "../../config";
MSG_TRANSLATE_SELECTED,
MSG_OPEN_TRANBOX,
DEFAULT_TRANSEL_SHORTCUT,
} from "../../config";
export default function Slection({ tranboxSetting, transApis }) { export default function Slection({ tranboxSetting, transApis }) {
const [showBox, setShowBox] = useState(false); const [showBox, setShowBox] = useState(false);
@@ -30,11 +26,12 @@ export default function Slection({ tranboxSetting, transApis }) {
setShowBox(true); setShowBox(true);
}; };
const handleTranSelected = useCallback(() => { const handleTranbox = useCallback(() => {
setShowBtn(false); setShowBtn(false);
const selectedText = window.getSelection()?.toString()?.trim() || ""; const selectedText = window.getSelection()?.toString()?.trim() || "";
if (!selectedText) { if (!selectedText) {
setShowBox((pre) => !pre);
return; return;
} }
@@ -66,44 +63,21 @@ export default function Slection({ tranboxSetting, transApis }) {
useEffect(() => { useEffect(() => {
const clearShortcut = shortcutRegister( const clearShortcut = shortcutRegister(
tranboxSetting.tranboxShortcut, tranboxSetting.tranboxShortcut || DEFAULT_TRANBOX_SHORTCUT,
() => { handleTranbox
setShowBox((pre) => !pre);
}
); );
return () => { return () => {
clearShortcut(); clearShortcut();
}; };
}, [tranboxSetting.tranboxShortcut]); }, [tranboxSetting.tranboxShortcut, handleTranbox]);
useEffect(() => { useEffect(() => {
const clearShortcut = shortcutRegister( window.addEventListener(MSG_OPEN_TRANBOX, handleTranbox);
tranboxSetting.transelShortcut || DEFAULT_TRANSEL_SHORTCUT,
handleTranSelected
);
return () => { return () => {
clearShortcut(); window.removeEventListener(MSG_OPEN_TRANBOX, handleTranbox);
}; };
}, [tranboxSetting.transelShortcut, handleTranSelected]); }, [handleTranbox]);
useEffect(() => {
window.addEventListener(MSG_TRANSLATE_SELECTED, handleTranSelected);
return () => {
window.removeEventListener(MSG_TRANSLATE_SELECTED, handleTranSelected);
};
}, [handleTranSelected]);
useEffect(() => {
const handleOpenTranbox = () => {
setShowBox((pre) => !pre);
};
window.addEventListener(MSG_OPEN_TRANBOX, handleOpenTranbox);
return () => {
window.removeEventListener(MSG_OPEN_TRANBOX, handleOpenTranbox);
};
}, []);
useEffect(() => { useEffect(() => {
if (!isGm) { if (!isGm) {
@@ -117,16 +91,9 @@ export default function Slection({ tranboxSetting, transApis }) {
GM.registerMenuCommand( GM.registerMenuCommand(
"Translate Selected Text (Alt+S)", "Translate Selected Text (Alt+S)",
(event) => { (event) => {
handleTranSelected(); handleTranbox();
}, },
"S" "S"
),
GM.registerMenuCommand(
"Open Translate Popup (Alt+B)",
(event) => {
setShowBox((pre) => !pre);
},
"B"
) )
); );
@@ -138,7 +105,7 @@ export default function Slection({ tranboxSetting, transApis }) {
} catch (err) { } catch (err) {
console.log("[registerMenuCommand]", err); console.log("[registerMenuCommand]", err);
} }
}, [handleTranSelected]); }, [handleTranbox]);
return ( return (
<> <>