Greasy Fork is available in English.
划词翻译
// ==UserScript==// @name translator// @namespace https://lufei.so// @supportURL https://github.com/intellilab/translator.user.js// @description 划词翻译// @version 1.6.8// @run-at document-start// @grant GM_addStyle// @grant GM_getValue// @grant GM_setValue// @grant GM_xmlhttpRequest// @require https://cdn.jsdelivr.net/combine/npm/@violentmonkey/dom@1,npm/@violentmonkey/[email protected]// @include *// ==/UserScript==(function () {'use strict';function dumpQuery(query) {return Object.entries(query).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&');}function request({method = 'GET',url,params,responseType,data,headers}) {return new Promise((resolve, reject) => {if (params) {const sep = url.includes('?') ? '&' : '?';url += sep + dumpQuery(params);}GM_xmlhttpRequest({method,url,responseType,data,headers,onload(res) {if (res.status >= 300) return reject();resolve(res.response);},onerror: reject});});}const provider = {name: 'youdao',handle: async text => {const payload = {type: 'data',doctype: 'json',version: '1.1',relatedUrl: 'http://fanyi.youdao.com/',keyfrom: 'fanyiweb',key: null,translate: 'on',q: text,ts: Date.now()};const r###lt = await request({url: 'https://fanyi.youdao.com/openapi.do',params: payload,responseType: 'json'});if (r###lt.errorCode) throw r###lt;const {basic,query,translation} = r###lt;if (basic) {const noPhonetic = '♥';const {explains,'us-phonetic': us,'uk-phonetic': uk} = basic;return {query,phonetic: [{html: `UK: [${uk || noPhonetic}]`,url: `https://dict.youdao.com/dictvoice?audio=${encodeURIComponent(query)}&type=1`}, {html: `US: [${us || noPhonetic}]`,url: `https://dict.youdao.com/dictvoice?audio=${encodeURIComponent(query)}&type=2`}],explains,detailUrl: `http://dict.youdao.com/search?q=${encodeURIComponent(query)}`};}if (translation != null && translation[0]) {return {translations: translation};}}};const LANG_EN = 'en';const LANG_ZH_HANS = 'zh-Hans';async function translate(text, to) {const [data] = await request({method: 'POST',url: 'https://cn.bing.com/ttranslatev3',responseType: 'json',data: dumpQuery({fromLang: 'auto-detect',to,text}),headers: {'Content-Type': 'application/x-www-form-urlencoded'}});const {detectedLanguage,translations} = data;return {language: {from: detectedLanguage.language,to},translations: translations.map(item => item.text)};}const provider$1 = {name: 'bing',handle: async source => {let data = await translate(source, LANG_ZH_HANS);if (data.language.from === LANG_ZH_HANS) data = await translate(source, LANG_EN);return data;}};const LANG_EN$1 = 'en';const LANG_ZH_CN = 'zh-CN';async function translate$1(text, to) {var _data$;const data = await request({url: 'https://translate.google.cn/translate_a/single',params: {q: text,client: 'gtx',sl: 'auto',tl: to,dt: 'at'},responseType: 'json'});const language = {from: data[8][0][0],to};const translations = (_data$ = data[5]) == null ? void 0 : _data$.map(item => {var _item$, _item$$;return (_item$ = item[2]) == null ? void 0 : (_item$$ = _item$[0]) == null ? void 0 : _item$$[0];}).filter(Boolean);return {language,translations};}const provider$2 = {name: 'google',handle: async source => {let data = await translate$1(source, LANG_ZH_CN);if (data.language.from === LANG_ZH_CN) data = await translate$1(source, LANG_EN$1);return data;}};var styles = {"link":"style-module_link__YVV7m","section":"style-module_section__1Eiq1","block-top":"style-module_block__1NZsy","block-bottom":"style-module_block__1NZsy","label":"style-module_label__JD9KX","content":"style-module_content__1MvKK","phonetic":"style-module_phonetic__2SIsx","item":"style-module_item__2QPXK"};var stylesheet=":host .style-module_link__YVV7m{position:relative;color:#7cbef0;cursor:pointer}:host .style-module_link__YVV7m:hover{text-decoration:underline}:host .style-module_section__1Eiq1{display:flex;align-items:flex-start;font-size:12px;line-height:1.2}:host .style-module_section__1Eiq1:not(:first-child){border-top:1px solid #eee}:host .style-module_block__1NZsy{display:block}:host .style-module_label__JD9KX{display:block;margin:8px 8px 8px 0;padding:2px 0;color:#fff;background:#bbb;border-radius:4px;font-size:12px;line-height:1.4;text-transform:uppercase;writing-mode:vertical-rl}:host .style-module_content__1MvKK{flex:1;min-width:0;padding:8px 0}:host .style-module_content__1MvKK>*{display:block}:host .style-module_content__1MvKK>:not(:first-child){margin-top:8px}:host .style-module_phonetic__2SIsx{display:inline-block;margin-left:8px}:host .style-module_item__2QPXK{display:block}:host .style-module_item__2QPXK~.style-module_item__2QPXK{margin-top:8px}";const React = VM;let audio;function play(url) {if (!audio) audio = /*#__PURE__*/React.createElement("audio", {autoPlay: true});audio.src = url;}function getPlayer(url) {return () => {play(url);};}function handleOpenUrl(e) {const {href} = e.target.dataset;const a = /*#__PURE__*/React.createElement("a", {href: href,target: "_blank",rel: "noopener noreferrer"});a.click();}function render(r###lts, {event,panel}) {panel.clear();for (const [name, r###lt] of Object.entries(r###lts)) {const {query,phonetic,detailUrl,explains,translations} = r###lt;panel.append( /*#__PURE__*/React.createElement(panel.id, {className: styles.section}, /*#__PURE__*/React.createElement(panel.id, {className: styles.label}, name), /*#__PURE__*/React.createElement(panel.id, {className: styles.content}, !!(query || phonetic != null && phonetic.length) && /*#__PURE__*/React.createElement(panel.id, {className: styles.block}, query && /*#__PURE__*/React.createElement(panel.id, null, query), phonetic == null ? void 0 : phonetic.map(({html,url}) => /*#__PURE__*/React.createElement(panel.id, {className: `${styles.phonetic} ${styles.link}`,dangerouslySetInnerHTML: {__html: html},onClick: getPlayer(url)}))), explains && /*#__PURE__*/React.createElement(panel.id, {className: styles.block}, explains.map(item => /*#__PURE__*/React.createElement(panel.id, {className: styles.item,dangerouslySetInnerHTML: {__html: item}}))), detailUrl && /*#__PURE__*/React.createElement(panel.id, {className: styles.block}, /*#__PURE__*/React.createElement(panel.id, {className: styles.link,"data-href": detailUrl,onClick: handleOpenUrl}, "\u66F4\u591A...")), translations && /*#__PURE__*/React.createElement(panel.id, {className: styles.block}, translations.map(item => /*#__PURE__*/React.createElement(panel.id, {className: styles.item,dangerouslySetInnerHTML: {__html: item}}))))));}const {wrapper} = panel;const {innerWidth,innerHeight} = window;const {clientX,clientY} = event;if (clientY > innerHeight * 0.5) {wrapper.style.top = 'auto';wrapper.style.bottom = `${innerHeight - clientY + 10}px`;} else {wrapper.style.top = `${clientY + 10}px`;wrapper.style.bottom = 'auto';}if (clientX > innerWidth * 0.5) {wrapper.style.left = 'auto';wrapper.style.right = `${innerWidth - clientX}px`;} else {wrapper.style.left = `${clientX}px`;wrapper.style.right = 'auto';}panel.show();}const providers = [provider, provider$1, provider$2];let session;function translate$2(context) {const sel = window.getSelection();const text = sel.toString().trim();if (/^\s*$/.test(text)) return;const {activeElement} = document;if (['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) < 0 && !activeElement.contains(sel.getRangeAt(0).startContainer)) return;context.source = text;const r###lts = {};session = r###lts;providers.forEach(async provider => {const r###lt = await provider.handle(text);if (!r###lt || session !== r###lts) return;r###lts[provider.name] = r###lt;render(r###lts, context);});}function debounce(func, delay) {let timer;function exec(...args) {timer = null;func(...args);}return (...args) => {if (timer) clearTimeout(timer);timer = setTimeout(exec, delay, ...args);};}function initialize() {const panel = VM.getPanel({css: stylesheet,shadow: false});const panelStyle = panel.body.style;panelStyle.maxHeight = '50vh';panelStyle.padding = '0 8px';panelStyle.overflow = 'auto';panelStyle.overscrollBehavior = 'contain';const debouncedTranslate = debounce(event => translate$2({event,panel}));let isSelecting;document.addEventListener('mousedown', e => {isSelecting = false;if (panel.body.contains(e.target)) return;panel.hide();session = null;}, true);document.addEventListener('mousemove', () => {isSelecting = true;}, true);document.addEventListener('mouseup', e => {if (panel.body.contains(e.target) || !isSelecting) return;debouncedTranslate(e);}, true);document.addEventListener('dblclick', e => {if (panel.body.contains(e.target)) return;debouncedTranslate(e);}, true);}initialize();}());