From 44eba6557da5532ead7606458820b6c48cd5074d Mon Sep 17 00:00:00 2001 From: Owen Young Date: Sun, 13 Nov 2022 15:55:20 +0800 Subject: [PATCH] support custom page special rules, fix gmail, support discord, telegram --- CHANGELOG.md | 8 ++ src/_locales/en/messages.json | 6 + src/_locales/zh_CN/messages.json | 9 ++ src/_locales/zh_TW/messages.json | 10 ++ src/chrome_manifest.json | 4 +- src/contentScript/enhance.js | 173 ++-------------------------- src/contentScript/pageTranslator.js | 12 ++ src/lib/config.js | 7 ++ src/lib/i18n.js | 10 ++ src/manifest.json | 4 +- src/options/options.html | 13 +++ src/options/options.js | 48 ++++++++ 12 files changed, 138 insertions(+), 166 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f904aa47..27fdf8bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log + +## 0.0.24 + +- Support discord.com +- Support web.telegram.org/z/ +- Fix mail.google.com , now it'll be better for long articles. +- Support Custom Page Special Rules + ## 0.0.23 - Support mail.google.com diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index baefee8e..44c0e0ee 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -483,5 +483,11 @@ }, "msgIsShowDualLanguage":{ "message": "Is Show dual language?" + }, + "optionsSpecialRules":{ + "message": "Special Page Rules" + }, + "optionsSpecialRulesNotes":{ + "message":"This is an advanced option, please make sure you know what you are doing before editing. The priority of the rules added here is higher than that of the rules built into the extension
It's actually not very complicated, it's essentially some css selectors, and here's a tutorial to help you.
The ultimate goal is to make the default rules for this extension better, so please consider contributing your rules to the project" } } diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index e8da7a9f..d52e25c1 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -516,6 +516,15 @@ }, "msgOpenOptions":{ "message": "选项设置" + }, + "optionsSpecialRules":{ + "message": "特殊页面翻译规则" + }, + "optionsSpecialRulesNotes":{ + "message":"这是一个高级选项,请确保你在编辑之前知道自己在做什么! 这里添加的规则的优先级高于内置的规则
别被吓到,其实也不是很复杂啦,本质上就是一些css选择器,这里有一个教程来帮助你。
我们最终目标是让这个扩展的默认规则更人性化,所以请考虑将你的规则贡献给本项目。" + }, + "lblTelegram":{ + "message": "加Telegram群" } } diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index d7036fa6..e778961e 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -513,5 +513,15 @@ }, "msgOpenOptions":{ "message": "選項設置" + }, + "optionsSpecialRules":{ + "message": "特殊頁面翻譯規則" + }, + "optionsSpecialRulesNotes":{ + "message":"這是一個高級選項,請確保你在編輯之前知道自己在做什麼! 這裡添加的規則的優先級高於內置的規則
別被嚇到,其實也不是很複雜啦,本質上就是一些css選擇器,這裡有一個教程來幫助你。
我們最終目標是讓這個擴展的默認規則更人性化,所以請考慮將你的規則貢獻給本項目。" + }, + "lblTelegram":{ + "message": "加Telegram群" } + } diff --git a/src/chrome_manifest.json b/src/chrome_manifest.json index 8a3a6707..1aa153e0 100644 --- a/src/chrome_manifest.json +++ b/src/chrome_manifest.json @@ -3,7 +3,7 @@ "default_locale": "en", "name": "Immersive Translate", "description": "Let's experience immersive web translation, with bilingual simultaneous display and translation of only the important content.", - "version": "0.0.23", + "version": "0.0.24", "homepage_url": "https://github.com/immersive-translate/immersive-translate", "commands": { @@ -101,7 +101,7 @@ "run_at": "document_end", "all_frames": true, "match_about_blank": true, - "js": ["/lib/i18n.js", "/contentScript/showOriginal.js", "/contentScript/enhance.js", "/contentScript/pageTranslator.js","/contentScript/play.js", "/contentScript/translateSelected.js", "/contentScript/showTranslated.js"] + "js": ["/lib/i18n.js", "/contentScript/showOriginal.js", "/lib/specialRules.js", "/contentScript/enhance.js", "/contentScript/pageTranslator.js","/contentScript/play.js", "/contentScript/translateSelected.js", "/contentScript/showTranslated.js"] }, { "matches": [""], diff --git a/src/contentScript/enhance.js b/src/contentScript/enhance.js index 7f1db30e..c4362622 100644 --- a/src/contentScript/enhance.js +++ b/src/contentScript/enhance.js @@ -3,7 +3,7 @@ const enhanceMarkAttributeName = "data-translationmark"; const enhanceOriginalDisplayValueAttributeName = "data-translationoriginaldisplay"; const enhanceHtmlTagsInlineIgnore = ['BR', 'CODE', 'KBD', 'WBR'] // and input if type is submit or button, and pre depending on settings const enhanceHtmlTagsNoTranslate = ['TITLE', 'SCRIPT', 'STYLE', 'TEXTAREA', 'SVG', 'svg'] //TODO verificar porque 'svg' é com letras minúsculas -const blockElements = [ +let blockElements = [ 'H1', 'H2', 'H3', 'H4', 'H5', 'H6','TABLE', 'OL', 'P','LI' ]; if (twpConfig.get('translateTag_pre') !== 'yes') { @@ -14,12 +14,8 @@ const headingElements = ['h1' ]; const pdfSelectorsConfig = { regex: - "translatewebpages.org/result/.+$", - selectors:[ - 'div' - ], - style:"none", - }; + "translatewebpages.org/result/.+$" +}; const inlineElements = [ "a", @@ -57,160 +53,6 @@ const inlineElements = [ "var", ]; -const translateSelectors = [ - { - hostname:["twitter.com","tweetdeck.twitter.com","mobile.twitter.com"], - selectors:[ - '[data-testid="tweetText"]',".tweet-text" ,".js-quoted-tweet-text" - ] - }, - { - hostname:"news.ycombinator.com", - selectors:[ - ".titleline >a", - '.comment', - '.toptext' - - ] - }, - { - hostname:"www.reddit.com", - selectors:[ - // "[data-adclicklocation=title]", - "h1", - "[data-click-id=body] h3","[data-click-id=background] h3" - ], - containerSelectors:[ - "[data-testid=comment]", - "[data-adclicklocation=media]" - ] - }, - { - hostname:"old.reddit.com", - selectors:[ - "a.title", - ], - containerSelectors:[ - "[role=main] .md-container" - ] - }, - { - regex:"finance\.yahoo\.com\/$", - selectors:[ - "h3" - ] - - }, - { - regex:["www\.bloomberg\.com\/[A-Za-z0-9]+$","www\.bloomberg\.com\/$"], - selectors:[ - "article h3", - "article .single-story-module__headline-link", - "article [data-tracking-type=Story]", - "article .story-list-story__info__headline" - - ] - }, - pdfSelectorsConfig, - { - hostname:"www.cell.com", - selectors:[ - "div.section-paragraph > div.section-paragraph > div.section-paragraph", - "section > div.section-paragraph", - "h4","h3","h2" - ], - }, - { - // TODO - hostname:["www.msdmanuals.com","localhost"], - noTranslateSelectors:[ - ".d-none" - ] - }, - { - hostname:"www.reuters.com", - containerSelectors:'main', - }, - { - regex:"finance\.yahoo\.com/news", - containerSelectors:"[role=article]" - }, - { - hostname:"www.whatsonweibo.com", - containerSelectors:"#mvp-post-main" - }, - { - hostname:["www.wsj.com","www.economist.com"], - containerSelectors:"main" - }, - - { - hostname:["mail.jabber.org","antirez.com"], - selectors:["pre"], - containerSelectors: "pre", - style: 'none' - }, - { - hostname:"github.com", - containerSelectors:".markdown-body" - }, - { - hostname:"www.youtube.com", - selectors:["#content-text"] - }, - { - hostname:"www.facebook.com", - selectors:["div[data-ad-comet-preview=message] > div > div","div[role=article] > div > div > div > div > div > div > div > div "] - - }, - { - regex:'\.substack\.com\/', - selectors:[ - ".post-preview-title", - ".post-preview-description" - ], - containerSelectors:[ - ".post" - ] - }, - { - hostname:"www.nature.com", - containerSelectors:"article" - },{ - hostname:"seekingalpha.com", - containerSelectors:"div.wsb_section", - brToParagraph: true - },{ - hostname:"hn.algolia.com", - selectors:[".Story_title"] - },{ - hostname:"read.readwise.io", - selectors:['div[class^="_titleRow_"]','div[class^="_description_"]'], - containerSelectors:[ - "#document-text-content" - ] - },{ - hostname:"www.inoreader.com", - selectors:[".article_title",], - containerSelectors:[ - ".article_content" - ] - }, - { - hostname:"mail.google.com", - selectors:["h2[data-thread-perm-id]","span[data-thread-id]"], - containerSelectors:[ - "div[data-message-id] > div > div[class='']" - ] - },{ - hostname:"www.producthunt.com", - selectors:["a[data-test^='post-']",'h2',"div.layoutCompact div[class^='styles_htmlText__']"], - },{ - hostname:"arxiv.org", - selectors:["blockquote.abstract","h1"] - } - -] function addWrapperToNode(node, wrapper){ try{ @@ -231,9 +73,12 @@ function getPageSpecialConfig(ctx){ const currentUrlObj = new URL(currentUrl); const currentHostname = currentUrlObj.hostname; const currentUrlWithoutSearch = currentUrlObj.origin + currentUrlObj.pathname; + + // merge spcialRules + let specialConfig = null; - for(const enhance of translateSelectors){ + for(const enhance of specialRules){ if(enhance.hostname){ if(!Array.isArray(enhance.hostname)){ enhance.hostname = [enhance.hostname]; @@ -365,6 +210,10 @@ async function getNodesThatNeedToTranslate(root,ctx,options){ const currentHostname = currentUrlObj.hostname; let currentTargetLanguage = twpConfig.get("targetLanguage") + // special for mail.google.com, cause there are too many table, we should remove table + if(pageSpecialConfig && pageSpecialConfig.blockElements){ + blockElements = pageSpecialConfig.blockElements; + } // check sites if(allBlocksSelectors.length>0){ for(const selector of allBlocksSelectors){ diff --git a/src/contentScript/pageTranslator.js b/src/contentScript/pageTranslator.js index e85500e9..ebb65783 100644 --- a/src/contentScript/pageTranslator.js +++ b/src/contentScript/pageTranslator.js @@ -225,6 +225,18 @@ Promise.all([twpConfig.onReady(), getTabUrl()]) const htmlTagsInlineText = ['#text', 'A', 'ABBR', 'ACRONYM', 'B', 'BDO', 'BIG', 'CITE', 'DFN', 'EM', 'I', 'LABEL', 'Q', 'S', 'SMALL', 'SPAN', 'STRONG', 'SUB', 'SUP', 'U', 'TT', 'VAR'] const htmlTagsInlineIgnore = ['BR', 'CODE', 'KBD', 'WBR'] // and input if type is submit or button, and pre depending on settings const htmlTagsNoTranslate = ['TITLE', 'SCRIPT', 'STYLE', 'TEXTAREA', 'SVG', 'svg'] //TODO verificar porque 'svg' é com letras minúsculas + const specialRulesConfigs = twpConfig.get('specialRules'); + if(Array.isArray(specialRulesConfigs) && specialRulesConfigs.length > 0){ + for(const specialRuleString of specialRulesConfigs){ + // add to specialRules + try{ + const specialRule = JSON.parse(specialRuleString); + specialRules.unshift(specialRule); + }catch(e){ + console.warn(`Error parsing special rule: ${specialRuleString}`) + } + } + } if (twpConfig.get('translateTag_pre') !== 'yes') { htmlTagsInlineIgnore.push('PRE') diff --git a/src/lib/config.js b/src/lib/config.js index 84e385cc..97b7dfe7 100644 --- a/src/lib/config.js +++ b/src/lib/config.js @@ -18,6 +18,7 @@ const twpConfig = (function () { targetLanguages: [], // "en", "es", "de" alwaysTranslateSites: [], neverTranslateSites: [], + specialRules: [], sitesToTranslateWhenHovering: [], langsToTranslateWhenHovering: [], alwaysTranslateLangs: [], @@ -400,12 +401,18 @@ const twpConfig = (function () { removeFromArray("alwaysTranslateSites", hostname); removeFromArray("sitesToTranslateWhenHovering", hostname); }; + twpConfig.addRuleToSpecialRules = function (hostname) { + addInArray("specialRules", hostname); + }; twpConfig.addKeyWordTocustomDictionary = function (key, value) { addInMap("customDictionary", key, value); }; twpConfig.removeSiteFromNeverTranslate = function (hostname) { removeFromArray("neverTranslateSites", hostname); }; + twpConfig.removeRuleFromSpecialRules = function (hostname) { + removeFromArray("specialRules", hostname); + }; twpConfig.removeKeyWordFromcustomDictionary = function (keyWord) { removeFromMap("customDictionary", keyWord); }; diff --git a/src/lib/i18n.js b/src/lib/i18n.js index 9e4bd42e..56904498 100644 --- a/src/lib/i18n.js +++ b/src/lib/i18n.js @@ -54,6 +54,16 @@ void (function () { element.textContent = text; } + for (const element of root.querySelectorAll("[data-i18n-html]")) { + let text = getMessage( + element.getAttribute("data-i18n-html"), + undefined + ); + if (!text) { + continue; + } + element.innerHTML = text; + } translateAttributes(root, "title"); translateAttributes(root, "placeholder"); translateAttributes(root, "label"); diff --git a/src/manifest.json b/src/manifest.json index f70d6761..c0b37def 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -3,7 +3,7 @@ "default_locale": "en", "name": "Immersive Translate", "description": "Let's experience immersive web translation, with bilingual simultaneous display and translation of only the important content.", - "version": "0.0.23", + "version": "0.0.24", "homepage_url": "https://github.com/immersive-translate/immersive-translate", "browser_specific_settings": { @@ -126,7 +126,7 @@ "run_at": "document_end", "all_frames": true, "match_about_blank": true, - "js": ["/lib/i18n.js", "/contentScript/showOriginal.js", "/contentScript/enhance.js", "/contentScript/pageTranslator.js", "/contentScript/play.js","/contentScript/translateSelected.js", "/contentScript/showTranslated.js"] + "js": ["/lib/i18n.js", "/contentScript/showOriginal.js","/lib/specialRules.js", "/contentScript/enhance.js", "/contentScript/pageTranslator.js", "/contentScript/play.js","/contentScript/translateSelected.js", "/contentScript/showTranslated.js"] }, { "matches": [""], diff --git a/src/options/options.html b/src/options/options.html index ca0bb3bf..fcdea0cd 100644 --- a/src/options/options.html +++ b/src/options/options.html @@ -28,6 +28,8 @@

Immersive Translate

Release Notes Report a problem + Telegram Group @@ -176,6 +178,17 @@

Immersive Translate

    +
    +
    +
    +

    Special Rules (Advanced)

    +
    +
    +

    This is an advanced option, please make sure you know what you are doing before editing. The priority of the rules added here is higher than that of the rules built into the extension
    It's actually not very complicated, it's essentially some css selectors, and here's a tutorial to help you.
    The ultimate goal is to make the default rules for this extension better, so please consider contributing your rules to the project

    + + +
      diff --git a/src/options/options.js b/src/options/options.js index 52b8769f..8b13f77c 100644 --- a/src/options/options.js +++ b/src/options/options.js @@ -388,6 +388,54 @@ twpConfig.onReady(function () { twpConfig.addSiteToNeverTranslate(hostname) } + function createNodeToSpecialRulesList(hostname) { + const li = document.createElement("li") + li.setAttribute("class", "w3-display-container") + li.value = hostname + li.textContent = hostname + + const close = document.createElement("span") + close.setAttribute("class", "w3-button w3-transparent w3-display-right") + close.innerHTML = "×" + + close.onclick = e => { + e.preventDefault() + + twpConfig.removeRuleFromSpecialRules(hostname) + li.remove() + } + + li.appendChild(close) + + return li + } + const specialRules = twpConfig.get("specialRules") + specialRules.forEach(hostname => { + const li = createNodeToSpecialRulesList(hostname) + $("#specialRules").appendChild(li) + }) + + $("#addToSpecialRules").onclick = e => { + + const rule = document.querySelector("#specialRule").value + if (!rule) return; + + // check rule is valid + // it must an valid json object + + try{ + const ruleObj = JSON.parse(rule) + }catch(e){ + alert("Invalid rule, it should be an json object string") + return + } + const li = createNodeToSpecialRulesList(rule) + $("#specialRules").appendChild(li) + // clean textarea + document.querySelector("#specialRule").value = "" + + twpConfig.addRuleToSpecialRules(rule) + } function createcustomDictionary(keyWord,customValue) { const li = document.createElement("li") li.setAttribute("class", "w3-display-container")