高级自定义选项
你可以在扩展配置页面 -> 开发者设置 -> User Config 里编辑更多 UI 里无法编辑的自定义配置,适用于高级用户,参数讲解详见最后的说明。当前内置的 config
可以在这里,点击 Click to expand the final config
找到。
User Rules
通过 Rules
可以对特定的网站进行自定义配置,决定哪些内容是否需要被翻译,或调整网页样式等。
[
{
"matches": "www.google.com",
"selectors": [".title"]
},
{
"matches": "twitter.com",
"selectors": [".text"],
"excludeSelectors": ["nav", "footer"]
}
]
使用 matches
来匹配对应的网站。允许通配符,如 *.google.com
,www.google.com/test/*
,file://*
使用 selectors
会覆盖智能翻译范围,仅翻译该选择器匹配到的元素。
使用 excludeSelectors
可以排除元素,不翻译该位置。
使用 selectors.add
会在默认的基础上添加一些 selectors
使用 selectors.remove
会在默认的基础上减少一些 selectors
[
{
"matches": "www.google.com",
"selectors.add": ["baidu.com"],
"excludeSelectors": ["buzzing.cc"]
}
]
如果希望翻译某个区域时,将元素视为一个整体,不将其分行,可以用 atomicBlockSelectors
选择器。比如 Instagram 的个人简介。要注意的是,使用 atomicBlockSelectors
前需要先用 selectors
进行选择。
{
"matches": "https://www.instagram.com/*",
"selectors": [
"div._aa_c h1",
"li._acaz div[role=\"menuitem\"]"
]
"atomicBlockSelectors": [
"div._aa_c h1",
"li._acaz div[role=\"menuitem\"]"
]
}
如果译文导致页面错位,文字重叠等边缘情况,可以使用 globalStyles
调整网页样式来修复。比如 youtube 的标题,用来移除原网页的最大高度。
{
"matches": "www.google.com",
"globalStyles": { ".title": "max-height:unset;" }
}
Injected CSS
通过 Injected CSS 可以向全局注入自定义网页样式。可以搭配 Rules
的 translationClasses
一起使用。
.immersive-translate-target-wrapper img { width: 16px; height: 16px }
也可以像常规的网页样式管理器那样,对网站进行更加个性化的样式设计。(甚至利用 display:none
去广告)
.title {
color: red;
}
User Config
通过 Config 可以自定义此插件的相关配置,如翻译服务、特定语言语言翻译选项等。
{
"translationService": "tencent",
"translationServices": {
"tencent": {
"secretId": "xxx",
"secretKey": "xxx",
"matches": ["twitter.com"]
}
},
"translationUrlPattern": {
"excludeMatches": ["www.google.com"]
},
"translationLanguagePattern": {
"matches": ["en"]
},
"translationTheme": "none",
"translationThemePatterns": {
"underline": {
"matches": ["discord.com"]
}
},
"sourceLanguageUrlPattern": {
"en": {
"matches": ["*.google.com"]
}
},
"generalRule": {
"_comment": "",
"normalizeBody": "",
"injectedCss": [],
"additionalInjectedCss": [],
"wrapperPrefix": "smart",
"wrapperSuffix": "smart",
"isPdf": false,
"isTransformPreTagNewLine": false,
"urlChangeDelay": 20,
"isShowUserscriptPagePopup": true,
"observeUrlChange": true,
"paragraphMinTextCount": 8,
"paragraphMinWordCount": 2,
"blockMinTextCount": 32,
"blockMinWordCount": 5,
"containerMinTextCount": 18,
"lineBreakMaxTextCount": 0,
"globalAttributes": {},
"globalStyles": {},
"selectors": [],
"preWhitespaceDetectedTags": ["DIV", "SPAN"],
"stayOriginalSelectors": [],
"additionalSelectors": [],
"atomicBlockTags": [],
"excludeSelectors": [],
"additionalExcludeSelectors": [],
"translationClasses": [],
"atomicBlockSelectors": [],
"excludeTags": [],
"metaTags": ["META", "SCRIPT", "STYLE", "NOSCRIPT"],
"additionalExcludeTags": [],
"stayOriginalTags": ["CODE", "TT", "IMG", "SUP"],
"additionalStayOriginalTags": [],
"inlineTags": [],
"additionalInlineTags": [],
"extraInlineSelectors": [],
"additionalInlineSelectors": [],
"extraBlockSelectors": [],
"allBlockTags": [],
"pdfNewParagraphLineHeight": 2.4,
"pdfNewParagraphIndent": 1.2,
"pdfNewParagraphIndentRightIndentPx": 130,
"fingerCountToToggleTranslagePageWhenTouching": 4
},
"rules": [
{
"matches": "www.google.com",
"selectors": [".class"]
}
]
}
其中,rules
里的规则字段,可以使用 generalRule
里的全部字段。rules
拥有最高优先级,当匹配到特定网站的某一条 rule
时,会合并 generalRule
和该 rule
的规则。
介绍一些 Config 常见的字段。
允许渲染普通 HTML 标签
去 开发设置 -> Edit Full User Config
编辑 "enableRenderHtmlTag": true
不在 popup 面板里展示未配置的翻译服务
"showUnconfiguredTranslationServiceInPopup": false
翻译服务配置
使用 translationService
选择默认的翻译引擎,当前支持:
| "bing"
| "transmart"
| "google"
| "deepl"
| "openai"
| "gemini"
| "baidu"
| "volc"
| "youdao"
| "caiyun"
| "tencent"
| "openl"
使用 translationServices
配置各家翻译服务的 apikey
,不同服务商需要的参数不一样,它们的 API 密钥均可在各自官网的开发者中心申请。
如腾讯翻译君,需要配置 secretId
, secretKey
。你可以前往腾讯云申请 API 密钥,每月免费字符 500 万。具体申请过程参考这里
"translationServices": {
"tencent": {
"secretId": "xxx",
"secretKey": "xxx",
"matches":["twitter.com"],
"limit": 3,
"apiUrl":"",
"maxTextGroupLengthPerRequest": 25,
"maxTextLengthPerRequest": 1800
}
}
matches
字段, 为特定网站使用该翻译服务。
limit
字段,指定该翻译服务的每秒最多请求数(有些服务会限制每秒最大请求数)。
maxTextGroupLengthPerRequest
字段,每次请求最大的段落数
maxTextLengthPerRequest
字段,每次请求最大的字符数
apiUrl
可以自定义翻译接口的地址。
总是翻译特定网站
translationUrlPattern
配置总是翻译的网站,以及永不翻译的网站。
matches
配置总是翻译的网站,excludeMatches
配置永不翻译的网站。
配置值可以是域名或带有 *
的网址,比如:www.google.com/mail/*
"translationUrlPattern": {
"matches": ["stackoverflow.com"]
"excludeMatches": ["www.google.com/mail/*"]
}
总是翻译特定语言
translationLanguagePattern, 配置总是翻译的语言,以及永不翻译的语言。
matches
配置总是翻译的语言,比如en
,excludeMatches
配置永不翻译的语言。
译文显示格式
translationTheme
为译文的显示格式,当前支持以下样式:
| "none"
| "dashed"
| "dotted"
| "underline"
| "mask"
| "paper"
| "highlight"
| "blockquote"
| "weakening"
| "italic"
| "bold"
| "thinDashed";
对应的中文名:
{
"none": "无",
"dashed": "虚线下划线",
"dotted": "点状下划线",
"underline": "直线下划线",
"mask": "模糊效果",
"paper": "白纸阴影效果",
"highlight": "高亮",
"blockquote": "引用样式",
"weakening": "弱化",
"italic": "斜体",
"bold": "加粗",
"thinDashed": "细虚线下划线"
}
translationThemePatterns
可以为不同网站配置不同的译文样式。
"translationThemePatterns": {
"underline": {
"matches": ["discord.com"]
}
}
类 gpt 页面流消息翻译
{
"matches": ["chat.openai.com"], //类 gpt 网址
"excludeSelectors": [".markdown *"],
"aiRule": {
"streamingSelector": ".result-streaming.markdown",
"messageWrapperSelector": ".markdown",
"streamingChange": true
}
}
Rules
rules
为数组对象,可以配置针对特别网站的规则,比如让推特只翻译某一部分区域:
{
"rules": [
{
"id": "twitter",
"matches": ["twitter.com", "mobile.twitter.com", "tweetdeck.twitter.com"],
"selectors": [
"[data-testid='tweetText']",
".tweet-text",
".js-quoted-tweet-text",
"[data-testid='card.layoutSmall.detail'] > div:nth-child(2)",
"[data-testid='developerBuiltCardContainer'] > div:nth-child(2)",
"[data-testid='card.layoutLarge.detail'] > div:nth-child(2)"
],
"extraInlineSelectors": ["[data-testid=\"tweetText\"] div"]
}
]
}
当前内置的 rules
可以在这里 找到。
以下挑选部分重要字段进行说明:
export interface Rule {
// 匹配网站
id?: string; //系统每个适配的规则都有自己的id,如果用户想要复用这条规则在此基础之上变动的话,需要在自己的规则上加上这个相应的id就可以复用了
matches?: string | string[]; // 该条Rule将仅匹配此处的网站。
excludeMatches?: string | string[]; // 排除特定的网站。
selectorMatches?: string | string[]; // 用选择器来匹配,而无需指定所有url
excludeSelectorMatches?: string | string[]; // 排除规则,同上。
// 指定翻译范围
selectors?: string | string[]; // 仅翻译匹配到的元素
excludeSelectors?: string | string[]; // 排除元素,不翻译匹配的元素
excludeTags?: string | string[]; // 排除Tags,不翻译匹配的Tag
// 追加翻译范围,而不是覆盖
additionalSelectors?: string | string[]; // 追加翻译范围。在智能翻译的区域,追加翻译位置。
additionalExcludeSelectors?: string | string[]; // 追加排除元素,让智能翻译不翻译特定位置。
additionalExcludeTags?: string | string[]; // 追加排除Tags
// 保持原样
stayOriginalSelectors?: string | string[]; // 匹配的元素将保持原样。常用于论坛网站的标签。
stayOriginalTags?: string | string[]; // 匹配到的Tag将保持原样,比如 `code`
// 区域翻译
atomicBlockSelectors?: string | string[]; // 区域选择器, 匹配的元素将被视为一个整体, 不会分段翻译
atomicBlockTags?: string | string[]; // 区域Tag选择器, 同上
// Block or Inline
extraBlockSelectors?: string | string[]; // 额外的选择器,匹配的元素将作为 block 元素,独占一行。
extraInlineSelectors?: string | string[]; // 额外的选择器,匹配的元素将作为 inline 元素。
inlineTags?: string | string[]; // 匹配的 Tag 将作为 inline 元素
preWhitespaceDetectedTags?: string | string[]; // 匹配的 Tag 将自动换行
// 译文样式
translationClasses?: string | string | string[]; // 为译文添加额外的 Class
// 全局样式
globalStyles?: Record<string, string>; // 修改页面样式,若译文导致页面错乱,这个很有用。`
globalAttributes?: Record<string, Record<string, string>>; // 修改页面元素的属性
// 嵌入样式
injectedCss?: string | string[]; // 嵌入CSS样式
additionalInjectedCss?: string | string[]; // 追加CSS样式,而不是直接覆盖。
// 上下文
wrapperPrefix?: string; // 译文区域的前缀,默认为 smart,根据字数决定是否换行。
wrapperSuffix?: string; // 译文区域的后缀
// 译文换行字数
blockMinTextCount?: number; // 将译文作为 block 的最小字符数,否则译文为 inline 元素。
blockMinWordCount?: number; // 同上。如果希望它们始终换行, 可以都填0.
// 内容可翻译的最小字数
containerMinTextCount?: number; // 智能识别时,元素最少包含的字符数,才会被翻译,默认为18
paragraphMinTextCount?: number; // 原文段落的最小字符数, 大于数字的内容将被翻译
paragraphMinWordCount?: number; // 原文段落的最小单词数
// 长段落强制换行字数
lineBreakMaxTextCount?: number; // 开启翻译长段落时,强制进行分行的段落最大字符数。
// 启动翻译的时机
urlChangeDelay?: number; // 进入页面后,延迟多少毫秒开始翻译。为了等网页的初始化,目前默认为250ms
observeUrlChange?: boolean; // 检测url地址发生变化时,再次启动翻译,默认为true。
// 移动端
isShowUserscriptPagePopup?: boolean; // 在移动设备上展示页面内的浮窗, 默认为true.
fingerCountToToggleTranslagePageWhenTouching?: number; // 四指触摸则翻译,可以设置为 0,2,3,4,5
// AI streaming 翻译
aiRule: {
streamingSelector: string; //gpt 网页中标记正在翻译元素的选择器
messageWrapperSelector: string; // 消息正文选择器
streamingChange: boolean; //类 gpt 网页反复的消息是增量更新还是全量更新。gpt 是增量
};
}
高级自定义选项实战
实用小技巧
这部分会介绍一些即插即用的保姆级配置。
将这些配置一键复制,打开开发者设置,展开 Edit Full User Config
,复制到最后一项即可,注意不要忘记给前一项加上逗号,以及最后一项不能加逗号
不能用的翻译服务太多了,如何在插件面板里只展示能用的翻译服务
"showUnconfiguredTranslationServiceInPopup": false
如何让不同的站点默认选择不同的翻译服务?例如有的网站我想要好一点但要花钱的翻译效果,有的网站我只需要免费能看的翻译就行了
注意看,眼前这个配置叫翻译服务,他配置了谷歌翻译,让有关推特的相关站点的翻译都使用他去翻译,因为 google
翻译是免费的,推特是冲浪的,只要能看懂就行了。
仔细看,他还配置了 deepl
的翻译服务,他让 deepl
专门去翻译 scihub
这种容错率低的需要高精确的学术网站
"translationServices": {
"google": {
"matches":["https://twitter.com"]
},
"bing": {
"matches":["https://www.sci-hub.se"]
}
}
⚠️ 请注意,若您希望翻译属于同一域名的所有网站,简单使用 .twitter.com 或 https://twitter.com/ 是无效的。正确的做法应参照上文所示。这是因为 .twitter.com 仅能匹配子域名如 xxx.twitter.com,而不包括顶级域名本身。
网站适配案例
这部分会介绍一些插件自己对常见的网站的 rules
,通过实际例子来理解高级自定义选项。同时为了简洁,这里只会介绍最常用的字段,比如 selectors
, excludeSelectors
等等,如果你对这部分内容感兴趣的话,欢迎联系我们,我们会继续更新相关的内容。
在介绍之前,一个非常关键的东西就是沉浸式翻译插件的工作原理,同时也是一个插件的工作原理。在此之前,需要有一定的 HTML
、CSS
、 JavaScript
基础,相关基础可以在 MDN
网站上学习。Okay,话不多说,让我们走进沉浸式翻译的内部一探究竟。插件的工作机制简单来说,就是向网页中注入第三方脚本,这个脚本可以对网页结构,样式,甚至行为进行相当自由地魔改。
我们的沉浸式翻译插件也不例外,让我们来简单分析一下沉浸式翻译它干了个什么事
- 获取需要翻译的元素集合
- 翻译元素集合中的文本
- 将翻译的结果插入到元素集合中
Okay,但是再仔细想想,自然而然就会带出接下来两个问题
- 我们还需要确定哪些元素需要被翻译,如果全盘翻译,往往会破坏用户的沉浸式体验,像一些简单明了的按钮,或者导航栏。
- 将翻译的结果插入到元素集合中也会带来一个新的挑战,如何保证插入的结果与原生网页保持一致,不去影响原生网页的样式。
我们的 Rules
的核心就是解决上述两个问题。因为作为插件,沉浸式翻译面对的是市面上所有的网页,加起来可能超过几十万,甚至几百万的网页,这些网页的页面结构,使用的技术也是相差殆尽。因为网页的不同,导致了一个通用的逻辑是几乎不可能的,很难找到一套通用的逻辑,能够去适配所有的网站内容。这样看来,解决方法似乎只有挨着挨着对每个网站进行单独的适配。接着为了更方便地适配,我们又利用了配置即代码的思想,将适配的工作转换成了配置字段的工作。这样的另一个好处就是,用户也可以参与到适配工作起来。
同时,在进行配置的时候,最好不要直接使用下面几个字段,这样会导致覆盖掉原先的配置项,而是采用 selector.add
excludeSelector.add
这几个字段以继承的方式,在原先的配置项的基础上进行修改
下面,我们将会介绍沉浸式翻译对站点的适配工作
下面是推特的 Rules,为了简洁,我们将关注其中的几个关键字段,剩余字段可以结合上文中的 Rules
理解
[
{
"id": "twitter",
"matches": [
"twitter.com",
"mobile.twitter.com",
"tweetdeck.twitter.com",
"pro.twitter.com",
"https://platform.twitter.com/embed*"
],
"selectors": [
// 指定翻译的元素,只会翻译选择器匹配到的元素
"[data-testid=\"tweetText\"]",
".tweet-text",
".js-quoted-tweet-text",
"[data-testid='card.layoutSmall.detail'] > div:nth-child(2)",
"[data-testid='developerBuiltCardContainer'] > div:nth-child(2)",
"[data-testid='card.layoutLarge.detail'] > div:nth-child(2)",
"[data-testid='cellInnerDiv'] div[data-testid='UserCell'] > div> div:nth-child(2)",
"[data-testid='UserDescription']",
"[data-testid='HoverCard'] div[dir=auto]",
"[data-testid='HoverCard'] span[dir=auto]",
"[data-testid='HoverCard'] [role='dialog'] div[dir=ltr]",
"[data-testid='birdwatch-pivot'] div[dir=ltr]"
],
"excludeSelectors": [
// 不会翻译的被CSS选择器选中的元素
"[aria-describedby][role=button]",
"header",
"[data-testid='radioGroupplayback_rate'] div",
"[data-testid='userFollowIndicator']",
"[class='css-901oao r-14j79pv r-37j5jr r-n6v787 r-16dba41 r-1cwl3u0 r-bcqeeo r-qvutc0']",
"[class='css-175oi2r r-1wbh5a2 r-dnmrzs']"
],
"globalStyles": {
// 全局样式,强制覆盖掉原样式
"[data-testid='card.layoutLarge.detail'] > div:nth-child(2)": "-webkit-line-clamp: unset;",
"[data-testid='card.layoutSmall.detail'] > div:nth-child(2)": "-webkit-line-clamp: unset;",
"[data-testid='tweetText']": "-webkit-line-clamp: unset;"
}
}
]
selector
: 指定翻译的元素集合为什么需要这个字段
- 因为不是所有元素都有文字且需要翻译的,提供这样一个字段既可以保证性能又可以保证用户的沉浸式体验
举个例子
- 在推特中,如果我们不指定 selector,那么他将会将页面中的所有识别为英文的文字都进行翻译一遍,如下图,用户的昵称往往是不需要翻译的。
字段含义
"selectors": [ // 会被翻译的CSS选择器集合
"[data-testid=\"tweetText\"]",
]
这里数组的每一项都是一个 CSS 选择器,用来选择页面中的需要翻译的元素,这里我们以第一个选择器为例,如下图所示,第一个选择器命中的是所有推文的元素
excludeSelectors
: 不会被翻译的元素集合为什么需要这个字段
- 因为一个仅翻译的选择器是不够的,可能会出现,匹配中的元素却不需要翻译的,即两者可能存在重合的部分,因此需要再设置一个字段来排除掉不需要翻译的元素
- 由于页面结构是非常复杂的,提供这样两个配置项,让配置更加灵活
- 相关的优先级是:对于同等选择器,selectors > excludeSelectors,剩下的依靠 CSS 优先级来比较
字段含义
"excludeSelectors": [ // 不会翻译的被CSS选择器选中的元素
"[aria-describedby][role=button]",
],
还是看第一个,这里我们排除掉了关注按钮的这个翻译
globalStyles
: 添加全局样式,强制覆盖掉原先的样式为什么需要这个字段
- 在某些情况下,因为原先网页的相关 CSS 样式,会导致整个的翻译展示效果不是很好,出现被截断,不换行等等效果
- 通过这个字段,提供一种暴力的解决方案,直接修改原生网页的 CSS 属性来解决
字段含义
"globalStyles": {
// 全局样式,强制覆盖掉原样式
"[data-testid='card.layoutLarge.detail'] > div:nth-child(2)": "-webkit-line-clamp: unset;",
"[data-testid='card.layoutSmall.detail'] > div:nth-child(2)": "-webkit-line-clamp: unset;",
"[data-testid='tweetText']": "-webkit-line-clamp: unset;"
}
-webkit-line-clamp
这个属性用来控制显示的行数,多余的行会被截断,这里设置成unset
,可以保证译文不会被这个属性所截断
自定义网站适配
关于适配规则,当然你也可以自定义规则,进入到插件选项页面,点击开发者设置,展开 Edit User Rules
,在这里进行各个网站的自定义适配。下面结合实际规则进行讲解
[
{
"selectors.remove": [
"[data-testid=\"tweetText\"]"
],
"selectors.add": [
""
],
"excludeSelectors.add":[
""
],
"excludeSelectors.remove:[
""
],
"id": "twitter"
}
]
这个规则会让推特页面的推文不进行翻译。下面详细介绍字段的含义
id
是沉浸式翻译目前已经定义好的相关网站的集合,每个 id
都对应相关的站点。id
的好处有两个
- 使用
id
能继承沉浸式翻译之前的适配规则,用户可以在这基础上进行增删 - 使用
id
就不用写繁琐的匹配字段了
下面介绍一些沉浸式翻译内置服务的常见的 id
"isEbook"
epub 阅读器页面的配置"isEbookBuilder"
生成 epub 双语书页面的配置"pdf"
pdf 双语对照翻译页面的配置
完整的 id
集合可以在开发者设置中,Click to expand the final config
中找到
selectors
负责指定需要翻译的CSS选择器,建议使用子项 .add
.remove
在原先的基础上进行增删
excludeSelectors
负责排除不需要翻译的CSS选择器,建议使用子项 .add
.remove
在原先的基础上进行增删
更多讲解
Block 和 inline 的区别,如果想了解更多可以看这里
- block 元素会独占一行,多个相邻的 block 元素会各自新起一行.
- inline 元素不会独占一行,多个相邻的 inline 元素会排列在同一行里,直到一行排列不下才会新换一行。