feat: transTag && transOnly
This commit is contained in:
@@ -188,7 +188,7 @@ export const I18N = {
|
|||||||
en: `Translate Timing`,
|
en: `Translate Timing`,
|
||||||
},
|
},
|
||||||
mk_disable: {
|
mk_disable: {
|
||||||
zh: `滚动加载(建议)`,
|
zh: `滚动加载(推荐)`,
|
||||||
en: `Rolling Loading (Suggested)`,
|
en: `Rolling Loading (Suggested)`,
|
||||||
},
|
},
|
||||||
mk_pageopen: {
|
mk_pageopen: {
|
||||||
@@ -743,6 +743,18 @@ export const I18N = {
|
|||||||
zh: `支持用换行或英文逗号“,”分隔多个KEY轮询调用。`,
|
zh: `支持用换行或英文逗号“,”分隔多个KEY轮询调用。`,
|
||||||
en: `Supports multiple KEY polling calls separated by newlines or English commas ",".`,
|
en: `Supports multiple KEY polling calls separated by newlines or English commas ",".`,
|
||||||
},
|
},
|
||||||
|
translation_element_tag: {
|
||||||
|
zh: `译文元素标签`,
|
||||||
|
en: `Translation Element Tag`,
|
||||||
|
},
|
||||||
|
show_only_translations: {
|
||||||
|
zh: `仅显示译文`,
|
||||||
|
en: `Show Only Translations`,
|
||||||
|
},
|
||||||
|
show_only_translations_help: {
|
||||||
|
zh: `非完美实现,某些页面可能有样式等问题。`,
|
||||||
|
en: `It is not a perfect implementation and some pages may have style issues.`,
|
||||||
|
},
|
||||||
translate_page_title: {
|
translate_page_title: {
|
||||||
zh: `是否同时翻译页面标题`,
|
zh: `是否同时翻译页面标题`,
|
||||||
en: `Translate Page Title`,
|
en: `Translate Page Title`,
|
||||||
|
|||||||
@@ -437,7 +437,7 @@ export const DEFAULT_BLACKLIST = [
|
|||||||
"https://translate.google.com",
|
"https://translate.google.com",
|
||||||
"https://www.deepl.com/translator",
|
"https://www.deepl.com/translator",
|
||||||
"oapi.dingtalk.com",
|
"oapi.dingtalk.com",
|
||||||
"login.dingtalk.com"
|
"login.dingtalk.com",
|
||||||
]; // 禁用翻译名单
|
]; // 禁用翻译名单
|
||||||
|
|
||||||
export const DEFAULT_SETTING = {
|
export const DEFAULT_SETTING = {
|
||||||
@@ -454,6 +454,8 @@ export const DEFAULT_SETTING = {
|
|||||||
detectRemote: false, // 是否使用远程语言检测
|
detectRemote: false, // 是否使用远程语言检测
|
||||||
contextMenus: true, // 是否添加右键菜单(作废)
|
contextMenus: true, // 是否添加右键菜单(作废)
|
||||||
contextMenuType: 1, // 右键菜单类型(0不显示,1简单菜单,2多级菜单)
|
contextMenuType: 1, // 右键菜单类型(0不显示,1简单菜单,2多级菜单)
|
||||||
|
transTag: "span", // 译文元素标签
|
||||||
|
transOnly: false, // 是否仅显示译文
|
||||||
transTitle: false, // 是否同时翻译页面标题
|
transTitle: false, // 是否同时翻译页面标题
|
||||||
subrulesList: DEFAULT_SUBRULES_LIST, // 订阅列表
|
subrulesList: DEFAULT_SUBRULES_LIST, // 订阅列表
|
||||||
owSubrule: DEFAULT_OW_RULE, // 覆写订阅规则
|
owSubrule: DEFAULT_OW_RULE, // 覆写订阅规则
|
||||||
|
|||||||
@@ -372,31 +372,64 @@ export class Translator {
|
|||||||
// 解除节点显示监听
|
// 解除节点显示监听
|
||||||
// this._interseObserver.disconnect();
|
// this._interseObserver.disconnect();
|
||||||
|
|
||||||
if (
|
// 移除键盘监听
|
||||||
!this._setting.mouseKey ||
|
window.removeEventListener("keydown", this._handleKeydown);
|
||||||
this._setting.mouseKey === OPT_MOUSEKEY_DISABLE
|
|
||||||
) {
|
this._tranNodes.forEach((innerHTML, node) => {
|
||||||
// 解除节点显示监听
|
if (
|
||||||
this._tranNodes.forEach((_, node) => {
|
!this._setting.mouseKey ||
|
||||||
|
this._setting.mouseKey === OPT_MOUSEKEY_DISABLE
|
||||||
|
) {
|
||||||
|
// 解除节点显示监听
|
||||||
this._interseObserver.unobserve(node);
|
this._interseObserver.unobserve(node);
|
||||||
// 移除已插入元素
|
} else if (this._setting.mouseKey !== OPT_MOUSEKEY_PAGEOPEN) {
|
||||||
node.querySelector(APP_LCNAME)?.remove();
|
// 移除鼠标悬停监听
|
||||||
});
|
|
||||||
} else if (this._setting.mouseKey === OPT_MOUSEKEY_PAGEOPEN) {
|
|
||||||
this._tranNodes.forEach((_, node) => {
|
|
||||||
node.querySelector(APP_LCNAME)?.remove();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 移除鼠标悬停监听
|
|
||||||
window.removeEventListener("keydown", this._handleKeydown);
|
|
||||||
this._tranNodes.forEach((_, node) => {
|
|
||||||
// node.style.pointerEvents = "none";
|
// node.style.pointerEvents = "none";
|
||||||
node.removeEventListener("mouseenter", this._handleMouseover);
|
node.removeEventListener("mouseenter", this._handleMouseover);
|
||||||
node.removeEventListener("mouseleave", this._handleMouseout);
|
node.removeEventListener("mouseleave", this._handleMouseout);
|
||||||
// 移除已插入元素
|
}
|
||||||
node.querySelector(APP_LCNAME)?.remove();
|
|
||||||
});
|
// 移除已插入元素
|
||||||
}
|
node.querySelector(APP_LCNAME)?.remove();
|
||||||
|
if (innerHTML && this._setting.transOnly) {
|
||||||
|
node.innerHTML = innerHTML;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// !this._setting.mouseKey ||
|
||||||
|
// this._setting.mouseKey === OPT_MOUSEKEY_DISABLE
|
||||||
|
// ) {
|
||||||
|
// // 解除节点显示监听
|
||||||
|
// this._tranNodes.forEach((innerHTML, node) => {
|
||||||
|
// this._interseObserver.unobserve(node);
|
||||||
|
// // 移除已插入元素
|
||||||
|
// node.querySelector(APP_LCNAME)?.remove();
|
||||||
|
// if (innerHTML) {
|
||||||
|
// node.innerHTML = innerHTML;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// } else if (this._setting.mouseKey === OPT_MOUSEKEY_PAGEOPEN) {
|
||||||
|
// this._tranNodes.forEach((innerHTML, node) => {
|
||||||
|
// node.querySelector(APP_LCNAME)?.remove();
|
||||||
|
// if (innerHTML) {
|
||||||
|
// node.innerHTML = innerHTML;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// // 移除鼠标悬停监听
|
||||||
|
// window.removeEventListener("keydown", this._handleKeydown);
|
||||||
|
// this._tranNodes.forEach((innerHTML, node) => {
|
||||||
|
// // node.style.pointerEvents = "none";
|
||||||
|
// node.removeEventListener("mouseenter", this._handleMouseover);
|
||||||
|
// node.removeEventListener("mouseleave", this._handleMouseout);
|
||||||
|
// // 移除已插入元素
|
||||||
|
// node.querySelector(APP_LCNAME)?.remove();
|
||||||
|
// if (innerHTML) {
|
||||||
|
// node.innerHTML = innerHTML;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
// 清空节点集合
|
// 清空节点集合
|
||||||
this._rootNodes.clear();
|
this._rootNodes.clear();
|
||||||
@@ -422,6 +455,10 @@ export class Translator {
|
|||||||
|
|
||||||
// 已翻译
|
// 已翻译
|
||||||
if (traEl) {
|
if (traEl) {
|
||||||
|
if (this._setting.transOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const preText = this._tranNodes.get(el);
|
const preText = this._tranNodes.get(el);
|
||||||
const curText = el.innerText.trim();
|
const curText = el.innerText.trim();
|
||||||
// const traText = traEl.innerText.trim();
|
// const traText = traEl.innerText.trim();
|
||||||
@@ -437,7 +474,11 @@ export class Translator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let q = el.innerText.trim();
|
let q = el.innerText.trim();
|
||||||
this._tranNodes.set(el, q);
|
if (this._setting.transOnly) {
|
||||||
|
this._tranNodes.set(el, el.innerHTML);
|
||||||
|
} else {
|
||||||
|
this._tranNodes.set(el, q);
|
||||||
|
}
|
||||||
const keeps = [];
|
const keeps = [];
|
||||||
|
|
||||||
// 保留元素
|
// 保留元素
|
||||||
@@ -490,6 +531,9 @@ export class Translator {
|
|||||||
|
|
||||||
traEl = document.createElement(APP_LCNAME);
|
traEl = document.createElement(APP_LCNAME);
|
||||||
traEl.style.visibility = "visible";
|
traEl.style.visibility = "visible";
|
||||||
|
if (this._setting.transOnly) {
|
||||||
|
el.innerHTML = "";
|
||||||
|
}
|
||||||
el.appendChild(traEl);
|
el.appendChild(traEl);
|
||||||
el.style.cssText +=
|
el.style.cssText +=
|
||||||
"-webkit-line-clamp: unset; max-height: none; height: auto;";
|
"-webkit-line-clamp: unset; max-height: none; height: auto;";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useMemo } from "react";
|
||||||
import LoadingIcon from "./LoadingIcon";
|
import LoadingIcon from "./LoadingIcon";
|
||||||
import {
|
import {
|
||||||
OPT_STYLE_LINE,
|
OPT_STYLE_LINE,
|
||||||
@@ -16,7 +16,9 @@ import {
|
|||||||
import { useTranslate } from "../../hooks/Translate";
|
import { useTranslate } from "../../hooks/Translate";
|
||||||
import { styled } from "@mui/material/styles";
|
import { styled } from "@mui/material/styles";
|
||||||
|
|
||||||
const LineSpan = styled("span")`
|
const Span = styled("span")``;
|
||||||
|
|
||||||
|
const LineSpan = styled(Span)`
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
-webkit-opacity: 0.6;
|
-webkit-opacity: 0.6;
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
@@ -35,7 +37,7 @@ const LineSpan = styled("span")`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const BlockquoteSpan = styled("span")`
|
const BlockquoteSpan = styled(Span)`
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
-webkit-opacity: 0.6;
|
-webkit-opacity: 0.6;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -47,7 +49,7 @@ const BlockquoteSpan = styled("span")`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
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);
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -56,59 +58,63 @@ const FuzzySpan = styled("span")`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const HighlightSpan = styled("span")`
|
const HighlightSpan = styled(Span)`
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: ${(props) => props.$bgColor};
|
background-color: ${(props) => props.$bgColor};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const DiySpan = styled("span")`
|
const DiySpan = styled(Span)`
|
||||||
${(props) => props.$diyStyle}
|
${(props) => props.$diyStyle}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function StyledSpan({ textStyle, textDiyStyle, bgColor, children }) {
|
function StyledSpan({ textStyle, textDiyStyle, bgColor, children, ...props }) {
|
||||||
switch (textStyle) {
|
switch (textStyle) {
|
||||||
case OPT_STYLE_LINE: // 下划线
|
case OPT_STYLE_LINE: // 下划线
|
||||||
return (
|
return (
|
||||||
<LineSpan $lineStyle="solid" $lineColor={bgColor}>
|
<LineSpan $lineStyle="solid" $lineColor={bgColor} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</LineSpan>
|
</LineSpan>
|
||||||
);
|
);
|
||||||
case OPT_STYLE_DOTLINE: // 点状线
|
case OPT_STYLE_DOTLINE: // 点状线
|
||||||
return (
|
return (
|
||||||
<LineSpan $lineStyle="dotted" $lineColor={bgColor}>
|
<LineSpan $lineStyle="dotted" $lineColor={bgColor} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</LineSpan>
|
</LineSpan>
|
||||||
);
|
);
|
||||||
case OPT_STYLE_DASHLINE: // 虚线
|
case OPT_STYLE_DASHLINE: // 虚线
|
||||||
return (
|
return (
|
||||||
<LineSpan $lineStyle="dashed" $lineColor={bgColor}>
|
<LineSpan $lineStyle="dashed" $lineColor={bgColor} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</LineSpan>
|
</LineSpan>
|
||||||
);
|
);
|
||||||
case OPT_STYLE_WAVYLINE: // 波浪线
|
case OPT_STYLE_WAVYLINE: // 波浪线
|
||||||
return (
|
return (
|
||||||
<LineSpan $lineStyle="wavy" $lineColor={bgColor}>
|
<LineSpan $lineStyle="wavy" $lineColor={bgColor} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</LineSpan>
|
</LineSpan>
|
||||||
);
|
);
|
||||||
case OPT_STYLE_FUZZY: // 模糊
|
case OPT_STYLE_FUZZY: // 模糊
|
||||||
return <FuzzySpan>{children}</FuzzySpan>;
|
return <FuzzySpan {...props}>{children}</FuzzySpan>;
|
||||||
case OPT_STYLE_HIGHLIGHT: // 高亮
|
case OPT_STYLE_HIGHLIGHT: // 高亮
|
||||||
return (
|
return (
|
||||||
<HighlightSpan $bgColor={bgColor || DEFAULT_COLOR}>
|
<HighlightSpan $bgColor={bgColor || DEFAULT_COLOR} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</HighlightSpan>
|
</HighlightSpan>
|
||||||
);
|
);
|
||||||
case OPT_STYLE_BLOCKQUOTE: // 引用
|
case OPT_STYLE_BLOCKQUOTE: // 引用
|
||||||
return (
|
return (
|
||||||
<BlockquoteSpan $lineColor={bgColor || DEFAULT_COLOR}>
|
<BlockquoteSpan $lineColor={bgColor || DEFAULT_COLOR} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</BlockquoteSpan>
|
</BlockquoteSpan>
|
||||||
);
|
);
|
||||||
case OPT_STYLE_DIY: // 自定义
|
case OPT_STYLE_DIY: // 自定义
|
||||||
return <DiySpan $diyStyle={textDiyStyle}>{children}</DiySpan>;
|
return (
|
||||||
|
<DiySpan $diyStyle={textDiyStyle} {...props}>
|
||||||
|
{children}
|
||||||
|
</DiySpan>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return <span>{children}</span>;
|
return <Span {...props}>{children}</Span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +123,11 @@ export default function Content({ q, keeps, translator }) {
|
|||||||
const { text, sameLang, loading } = useTranslate(q, rule, translator.setting);
|
const { text, sameLang, loading } = useTranslate(q, rule, translator.setting);
|
||||||
const { textStyle, bgColor = "", textDiyStyle = "" } = rule;
|
const { textStyle, bgColor = "", textDiyStyle = "" } = rule;
|
||||||
|
|
||||||
const { newlineLength = TRANS_NEWLINE_LENGTH } = translator.setting;
|
const {
|
||||||
|
newlineLength = TRANS_NEWLINE_LENGTH,
|
||||||
|
transTag = "span",
|
||||||
|
transOnly = false,
|
||||||
|
} = translator.setting;
|
||||||
|
|
||||||
const handleKissEvent = (e) => {
|
const handleKissEvent = (e) => {
|
||||||
const { action, args } = e.detail;
|
const { action, args } = e.detail;
|
||||||
@@ -136,10 +146,27 @@ export default function Content({ q, keeps, translator }) {
|
|||||||
};
|
};
|
||||||
}, [translator.eventName]);
|
}, [translator.eventName]);
|
||||||
|
|
||||||
|
const gap = useMemo(() => {
|
||||||
|
if (transOnly) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return q.length >= newlineLength ? <br /> : " ";
|
||||||
|
}, [q, transOnly, newlineLength]);
|
||||||
|
|
||||||
|
const styles = useMemo(
|
||||||
|
() => ({
|
||||||
|
textStyle,
|
||||||
|
textDiyStyle,
|
||||||
|
bgColor,
|
||||||
|
as: transTag,
|
||||||
|
}),
|
||||||
|
[textStyle, textDiyStyle, bgColor, transTag]
|
||||||
|
);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{q.length >= newlineLength ? <br /> : " "}
|
{gap}
|
||||||
<LoadingIcon />
|
<LoadingIcon />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -149,24 +176,24 @@ export default function Content({ q, keeps, translator }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keeps.length > 0) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{gap}
|
||||||
|
<StyledSpan
|
||||||
|
{...styles}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: text.replace(/\[(\d+)\]/g, (_, p) => keeps[parseInt(p)]),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{q.length >= newlineLength ? <br /> : " "}
|
{gap}
|
||||||
<StyledSpan
|
<StyledSpan {...styles}>{text}</StyledSpan>
|
||||||
textStyle={textStyle}
|
|
||||||
textDiyStyle={textDiyStyle}
|
|
||||||
bgColor={bgColor}
|
|
||||||
>
|
|
||||||
{keeps.length > 0 ? (
|
|
||||||
<span
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: text.replace(/\[(\d+)\]/g, (_, p) => keeps[parseInt(p)]),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
text
|
|
||||||
)}
|
|
||||||
</StyledSpan>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ export default function Settings() {
|
|||||||
mouseKey = OPT_MOUSEKEY_DISABLE,
|
mouseKey = OPT_MOUSEKEY_DISABLE,
|
||||||
detectRemote = false,
|
detectRemote = false,
|
||||||
contextMenuType = 1,
|
contextMenuType = 1,
|
||||||
|
transTag = "span",
|
||||||
|
transOnly = false,
|
||||||
transTitle = false,
|
transTitle = false,
|
||||||
touchTranslate = 2,
|
touchTranslate = 2,
|
||||||
blacklist = DEFAULT_BLACKLIST.join(",\n"),
|
blacklist = DEFAULT_BLACKLIST.join(",\n"),
|
||||||
@@ -184,6 +186,33 @@ export default function Settings() {
|
|||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
<FormControl size="small">
|
||||||
|
<InputLabel>{i18n("translation_element_tag")}</InputLabel>
|
||||||
|
<Select
|
||||||
|
name="transTag"
|
||||||
|
value={transTag}
|
||||||
|
label={i18n("translation_element_tag")}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<MenuItem value={"span"}>{`<span>`}</MenuItem>
|
||||||
|
<MenuItem value={"font"}>{`<font>`}</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormControl size="small">
|
||||||
|
<InputLabel>{i18n("show_only_translations")}</InputLabel>
|
||||||
|
<Select
|
||||||
|
name="transOnly"
|
||||||
|
value={transOnly}
|
||||||
|
label={i18n("show_only_translations")}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<MenuItem value={false}>{i18n("disable")}</MenuItem>
|
||||||
|
<MenuItem value={true}>{i18n("enable")}</MenuItem>
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>{i18n("show_only_translations_help")}</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<FormControl size="small">
|
<FormControl size="small">
|
||||||
<InputLabel>{i18n("translate_page_title")}</InputLabel>
|
<InputLabel>{i18n("translate_page_title")}</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
Reference in New Issue
Block a user