// ==UserScript== // @namespace KiohPun // @name Multiple Language Translation // @name:zh-CN 多语言划词翻译 // @description 任意网页上选中词汇后,自动根据选中词汇的语种请求翻译结果 // @version 1.0 // @match *://*/* // @grant GM.xmlHttpRequest // @grant GM_xmlhttpRequest // @downloadURL none // ==/UserScript== new (class { constructor() { this.panel = null; this.dot = null; this.text = null; this.onClickDot = this.onClickDot.bind(this); // 触发划词 document.addEventListener('mouseup', ({ pageX, pageY }) => { const text = window .getSelection() .toString() .trim(); if (text) { this.text = text; this.showDot(pageX + 10, pageY + 10); } }); // 隐藏翻译按钮和结果面板 document.addEventListener('selectionchange', () => { this.dot && this.hideDot(); this.panel && this.hidePanel(); }); } createDot() { const dot = document.createElement('button'); dot.style.cssText = ` position: absolute; z-index: 9999; width: 16px; height: 16px; min-height: 0; padding: 0; border: 0; border-radius: 50%; background-color: rgba(79, 128, 225, 0.9); box-shadow: none; `; dot.addEventListener('mouseup', event => event.stopPropagation()); dot.addEventListener('click', this.onClickDot); document.body.appendChild(dot); return dot; } showDot(left, top) { if (!this.dot) { this.dot = this.createDot(left, top); } this.dot.style.left = `${left}px`; this.dot.style.top = `${top}px`; this.dot.style.display = 'block'; }; hideDot() { this.dot.style.display = 'none'; }; onClickDot({ pageX, pageY }) { this.hideDot(); const x = (window.innerWidth - pageX > 220) ? pageX : window.innerWidth - 220; this.showPanel(x, pageY); const { text } = this; const requests = []; if (this.isLatin(text)) { requests.push( this.ajax('eng', 'ec', text), this.ajax('fr', 'fc', text), ); } else if (this.isKorean(text)) { requests.push(this.ajax('ko', 'kc', text)); } else { requests.push(this.ajax('jap', 'newjc', text)); } Promise.all(requests) .then(responses => responses.map(res => this.handleResponse(res))) .then(htmls => htmls.join('')) .then(html => this.writePanel(html || '查无结果')) .catch((err) => { console.log(err); this.writePanel('查无结果'); }); } createPanel() { const panel = document.createElement('div'); panel.style.cssText = ` position: absolute; z-index: 9999; display: flex; flex-direction: column; justify-content: center; align-items: center; width: 180px; min-height: 50px; padding: 0 8px; border: 1px solid #eee; background-color: #fff; color: #555; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); font-family: "Segoe UI", "Microsoft Yahei", meiryo, sans-serif; font-size: 12px; line-height: normal; `; panel.innerHTML = '查询中...'; document.body.appendChild(panel); return panel; } showPanel(left, top) { if (!this.panel) { this.panel = this.createPanel(); } this.panel.style.left = `${left}px`; this.panel.style.top = `${top}px`; this.panel.style.display = 'flex'; } hidePanel() { this.panel.style.display = 'none'; this.panel.innerHTML = '查询中...'; }; writePanel(html) { this.panel.innerHTML = html; } handleResponse(res) { if (!res.simple) { return null; } try { const meta = this.handleMeta(res); const contents = this.handleContent(res); return this.template(meta, contents); } catch (err) { console.log(err); return null; } } handleMeta({ input, le, simple }) { return { lang: le, phrase: simple.word[0]['return-phrase'] || input, }; } handleContent(res) { switch (res.le) { case 'en': return this.handleEnglish(res.ec.word, res.simple.word[0]); case 'fr': return this.handleFrench(res.fc.word); case 'ko': return this.handleKorean(res.kc.word); case 'ja': return this.handleJapanese(res.newjc.word); default: throw new Error('不能处理该种语言'); } } handleEnglish(ec, simple) { return ec.map((item) => { const { ukphone = simple.ukphone, usphone = simple.usphone, trs } = item; const phones = [ { name: '英', symbol: ukphone }, { name: '美', symbol: usphone }, ].filter(({ symbol }) => symbol); if (phones.length <= 0 && simple.phone) { phones.push({ symbol: simple.phone }); } return { phones, senses: [ { list: trs.map(({ tr }) => tr.map(trItem => trItem.l.i[0])[0]) } ], }; }); } handleFrench(fc) { return fc.map(({ phone, trs }) => ({ phones: [{ symbol: phone }], senses: [ { list: trs.map(({ pos, tr }) => `${pos || ''} ${tr.map(trItem => trItem.l.i[0])}`) } ], })); } handleKorean(word) { return [{ senses: word .map(({ trs }) => ({ type: trs[0].pos, list: trs[0].tr.map(item => item.l.i) })) .filter(({ list }) => typeof list[0][0] === 'string') }] } handleJapanese({ head, sense, mPhonicD = [] }) { return [{ head, sense }, ...mPhonicD].map(({ head: { pjm, tone }, sense }) => ({ phones: [{ symbol: pjm, tone }], senses: sense.map(({ cx, phrList }) => ({ type: cx, list: phrList.map(({ jmsy }) => jmsy), })), })); } template({ lang, phrase }, contents) { return `
`; } contentTemplate({ phones, senses }) { return `