Separate translation module
This commit is contained in:
parent
efa227fb1f
commit
2c5b8019e0
|
@ -50,7 +50,7 @@
|
|||
{
|
||||
"matches": ["http://*/*", "https://*/*", "<all_urls>"],
|
||||
"css": ["simple-translate.css"],
|
||||
"js": ["Settings.js", "simple-translate.js"]
|
||||
"js": ["Settings.js", "Translate.js", "simple-translate.js"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -97,6 +97,10 @@ hr {
|
|||
|
||||
#target .candidate {
|
||||
color: var(--sub-text);
|
||||
margin-top:1em;
|
||||
}
|
||||
#target .candidate:empty {
|
||||
margin-top:0;
|
||||
}
|
||||
|
||||
#footer {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
</div>
|
||||
|
||||
<script src="../Settings.js"></script>
|
||||
<script src="../translate.js"></script>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let S = new settingsObj();
|
||||
const S = new settingsObj();
|
||||
const T = new Translate();
|
||||
|
||||
//設定を読み出し
|
||||
S.init().then(function (value) {
|
||||
defaultTargetLang = value.targetLang;
|
||||
targetLang = value.targetLang;
|
||||
secondTargetLang = value.secondTargetLang;
|
||||
ifChangeSecondLang = value.ifChangeSecondLang;
|
||||
langList.value = targetLang; //リスト初期値をセット
|
||||
langList.value = value.targetLang; //リスト初期値をセット
|
||||
langList.addEventListener("change", changeLang);
|
||||
|
||||
document.body.style.fontSize = value.fontSize;
|
||||
|
@ -19,11 +20,9 @@ let target = document.getElementById("target");
|
|||
let langList = document.getElementById("langList");
|
||||
let textarea = document.getElementById("textarea");
|
||||
|
||||
//langList= browser.i18n.getMessage("langList");
|
||||
const initialText = browser.i18n.getMessage("initialTextArea");
|
||||
textarea.placeholder = initialText;
|
||||
|
||||
let targetLang;
|
||||
let secondTargetLang;
|
||||
let defaultTargetLang;
|
||||
let ifChangeSecondLang;
|
||||
|
@ -57,12 +56,13 @@ function alphabeticallySort(a, b) {
|
|||
}
|
||||
|
||||
//翻訳先言語変更時に更新
|
||||
function changeLang() {
|
||||
targetLang = langList.value;
|
||||
if (sourceWord !== "") {
|
||||
translate();
|
||||
}
|
||||
async function changeLang() {
|
||||
if (typeof (url) != "undefined") showLink();
|
||||
|
||||
if (sourceWord !== "") {
|
||||
const resultData = await T.translate(sourceWord, undefined, langList.value);
|
||||
showResult(resultData.resultText, resultData.candidateText);
|
||||
}
|
||||
}
|
||||
|
||||
//アクティブなタブを取得して渡す
|
||||
|
@ -91,15 +91,15 @@ function getSelectionWord(tabs) {
|
|||
|
||||
//ページ翻訳へのリンクを表示
|
||||
function showLink() {
|
||||
document.getElementById("link").innerHTML = "<a href=https://translate.google.com/translate?hl=" + targetLang + "&sl=auto&u=" + encodeURIComponent(url) + ">" + browser.i18n.getMessage('showLink') + "</a>";
|
||||
document.getElementById("link").innerHTML = "<a href=https://translate.google.com/translate?hl=" + langList.value + "&sl=auto&u=" + encodeURIComponent(url) + ">" + browser.i18n.getMessage('showLink') + "</a>";
|
||||
}
|
||||
|
||||
//翻訳元テキストを表示
|
||||
function refleshSource() {
|
||||
if (sourceWord !== "") {
|
||||
textarea.innerHTML = sourceWord;
|
||||
translate();
|
||||
resize();
|
||||
inputText();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,89 +130,49 @@ function textAreaClick() {
|
|||
}
|
||||
|
||||
//文字入力時の処理
|
||||
function inputText() {
|
||||
async function inputText() {
|
||||
sourceWord = textarea.value;
|
||||
translate();
|
||||
|
||||
const resultData = await T.translate(sourceWord, 'auto', langList.value);
|
||||
changeSecondLang(defaultTargetLang, resultData.sourceLanguage, resultData.percentage);
|
||||
showResult(resultData.resultText, resultData.candidateText);
|
||||
}
|
||||
|
||||
//改行で分割してgetRequestに渡す
|
||||
function translate() {
|
||||
let promises = [];
|
||||
sourceLine = sourceWord.split("\n");
|
||||
for (i = 0; i < sourceLine.length; i++) {
|
||||
promises.push(getRequest(sourceLine[i]));
|
||||
}
|
||||
Promise.all(promises)
|
||||
.then(function (results) {
|
||||
showResult(results); //翻訳結果が帰ってきたらshowResult
|
||||
});
|
||||
}
|
||||
|
||||
//翻訳リクエストを送信,取得して返す
|
||||
function getRequest(word) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
//let url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=" + targetLang + "&dt=t&q=" + encodeURIComponent(word);
|
||||
let url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=" + targetLang + "&dt=t&dt=bd&q=" + encodeURIComponent(word);
|
||||
|
||||
xhr.open("GET", url);
|
||||
xhr.send();
|
||||
xhr.onload = function () {
|
||||
resolve(xhr);
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
//翻訳結果を表示
|
||||
function showResult(results) {
|
||||
function showResult(resultText, candidateText) {
|
||||
const resultArea = target.getElementsByClassName("result")[0];
|
||||
const candidateArea = target.getElementsByClassName("candidate")[0];
|
||||
resultArea.innerText = "";
|
||||
candidateArea.innerText = "";
|
||||
|
||||
let resultText = "";
|
||||
let candidateText = "";
|
||||
let wordsCount = 0;
|
||||
let lineCount = 0;
|
||||
|
||||
//第二言語に変更
|
||||
if (ifChangeSecondLang) {
|
||||
let lang = results[0].response[2];
|
||||
let percentage = results[0].response[6];
|
||||
if (targetLang == defaultTargetLang && lang == defaultTargetLang && percentage > 0 && !changeLangFlag) changeSecondLang();
|
||||
else if ((lang != defaultTargetLang || percentage == 0) && changeLangFlag) unchangeSecondLang();
|
||||
}
|
||||
for (let j = 0; j < results.length; j++) {
|
||||
lineCount++;
|
||||
for (let i = 0; i < results[j].response[0].length; i++) {
|
||||
resultText += results[j].response[0][i][0];
|
||||
}
|
||||
resultText += "\n";
|
||||
|
||||
if (results[j].response[1]) {
|
||||
wordsCount++;
|
||||
for (let i = 0; i < results[j].response[1].length; i++) {
|
||||
const partsOfSpeech = results[j].response[1][i][0];
|
||||
const candidates = results[j].response[1][i][1];
|
||||
candidateText += `\n${partsOfSpeech}${partsOfSpeech!="" ? ": " : ""}${candidates.join(", ")}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
resultArea.innerText = resultText;
|
||||
if (S.get().ifShowCandidate && wordsCount == 1 && lineCount == 1) candidateArea.innerText = candidateText;
|
||||
if (S.get().ifShowCandidate) candidateArea.innerText = candidateText;
|
||||
}
|
||||
|
||||
let changeLangFlag = false;
|
||||
|
||||
function changeSecondLang() {
|
||||
function changeSecondLang(defaultTargetLang, sourceLang, percentage) {
|
||||
//検出された翻訳元言語がターゲット言語と一致
|
||||
const equalsSourceAndTarget = sourceLang == langList.value && percentage > 0;
|
||||
|
||||
//検出された翻訳元言語がデフォルト言語と一致
|
||||
const equalsSourceAndDefault = sourceLang == defaultTargetLang && percentage > 0;
|
||||
|
||||
if (!changeLangFlag) {
|
||||
//通常時
|
||||
if (equalsSourceAndTarget && equalsSourceAndDefault) {
|
||||
//ソースとターゲットとデフォルトが一致する場合
|
||||
//ターゲットを第2言語に変更
|
||||
changeLangFlag = true;
|
||||
langList.value = secondTargetLang;
|
||||
changeLang();
|
||||
}
|
||||
|
||||
function unchangeSecondLang() {
|
||||
} else {
|
||||
//第2言語に切替した後
|
||||
if (!equalsSourceAndDefault) {
|
||||
//ソースとデフォルトが異なる場合
|
||||
//ターゲットをデフォルトに戻す
|
||||
changeLangFlag = false;
|
||||
langList.value = defaultTargetLang;
|
||||
changeLang();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,11 @@
|
|||
|
||||
#simple-translate-panel .candidate {
|
||||
color: var(--simple-translate-sub-text);
|
||||
margin-top:1em;
|
||||
}
|
||||
|
||||
#simple-translate-panel .candidate:empty {
|
||||
margin-top:0;
|
||||
}
|
||||
|
||||
@keyframes simple-translate-showButton {
|
||||
|
|
|
@ -8,57 +8,54 @@ var panel = document.getElementById("simple-translate-panel");
|
|||
var selectionWord;
|
||||
var clickPosition;
|
||||
|
||||
let S = new settingsObj();
|
||||
const S = new settingsObj();
|
||||
const T = new Translate();
|
||||
S.init();
|
||||
window.addEventListener("mouseup", Select, false);
|
||||
//テキスト選択時の処理 ダブルクリックした時2回処理が走るのを何とかしたい
|
||||
function Select(e) {
|
||||
async function Select(e) {
|
||||
hidePanel(e);
|
||||
if (e.target.tagName == "INPUT" && e.target.type == "password") return;
|
||||
|
||||
setTimeout(function () { //誤動作防止の為ディレイを設ける
|
||||
if (e.target.tagName == "INPUT" || e.target.tagName == "TEXTAREA") {
|
||||
selectionWord = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
|
||||
} else {
|
||||
selectionWord = String(window.getSelection());
|
||||
}
|
||||
setTimeout(async () => { //誤動作防止の為ディレイを設ける
|
||||
//選択文の取得 テキストフィールド内を選択した場合にも対応
|
||||
const isTextField = (e.target.tagName == "INPUT") || (e.target.tagName == "TEXTAREA");
|
||||
if (isTextField) selectionWord = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
|
||||
else selectionWord = String(window.getSelection());
|
||||
|
||||
//選択文が存在し,パネル外を左クリックした場合は翻訳する
|
||||
const existsSelectionWord = (selectionWord.length !== 0);
|
||||
const isLeftClick = (e.button == 0);
|
||||
const isPanelOutside = (e.target.id !== 'simple-translate-panel') && (e.target.parentElement.id !== 'simple-translate-panel');
|
||||
const shouldTranslate = existsSelectionWord && isLeftClick && isPanelOutside;
|
||||
if (!shouldTranslate) return;
|
||||
|
||||
//選択した言語が翻訳先言語と異なれば翻訳する
|
||||
const needTranslate = await checkLang(selectionWord, 'auto', S.get().targetLang);
|
||||
if (!needTranslate) return;
|
||||
|
||||
if ((selectionWord.length !== 0) && (e.button == 0) && (e.target.id !== "simple-translate-panel") && (e.target.parentElement.id !== "simple-translate-panel")) { //選択範囲が存在かつ左クリックかつパネル以外のとき
|
||||
clickPosition = e;
|
||||
|
||||
checkLang().then(function (results) {
|
||||
if (results) {
|
||||
switch (S.get().whenSelectText) {
|
||||
case 'showButton':
|
||||
popupButton(e);
|
||||
break;
|
||||
case 'showPanel':
|
||||
translate();
|
||||
translate(selectionWord, 'auto', S.get().targetLang);
|
||||
showPanel(e);
|
||||
break;
|
||||
case 'dontShowButton':
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
//選択テキストの言語をチェックして返す
|
||||
function checkLang() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (S.get().ifCheckLang) { //設定がオンなら
|
||||
getRequest(selectionWord.substr(0, 100)) //先頭100文字を抽出して言語を取得
|
||||
.then(function (results) {
|
||||
let lang = results.response[2];
|
||||
let percentage = results.response[6];
|
||||
resolve(lang != S.get().targetLang && percentage > 0); //真偽値を返す
|
||||
});
|
||||
} else { //設定がオフならtrueを返す
|
||||
resolve(true);
|
||||
}
|
||||
})
|
||||
async function checkLang(sourceWord, sourceLang, targetLang) {
|
||||
if (!S.get().ifCheckLang) return true; //設定がオフならtrue
|
||||
|
||||
const resultData = await T.translate(sourceWord, sourceLang, targetLang);
|
||||
const needTranslate = (S.get().targetLang != resultData.sourceLanguage) && (resultData.percentage > 0);
|
||||
return needTranslate; //ターゲットとソースの言語が不一致ならtrue
|
||||
}
|
||||
|
||||
//ボタンを表示
|
||||
|
@ -89,66 +86,23 @@ function popupButton(e) {
|
|||
button.style.display = 'block';
|
||||
}
|
||||
button.addEventListener("click", function (e) {
|
||||
translate();
|
||||
translate(selectionWord, 'auto', S.get().targetLang);
|
||||
showPanel(e);
|
||||
}, false);
|
||||
|
||||
//改行で分割してgetRequestに渡す
|
||||
function translate() {
|
||||
promises = [];
|
||||
sourceLine = selectionWord.split("\n");
|
||||
for (i = 0; i < sourceLine.length; i++) {
|
||||
promises.push(getRequest(sourceLine[i]));
|
||||
}
|
||||
Promise.all(promises)
|
||||
.then(function (results) {
|
||||
showResult(results); //翻訳結果が帰ってきたらshowResult
|
||||
});
|
||||
}
|
||||
|
||||
//翻訳リクエストを送信,取得して返す
|
||||
function getRequest(word) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
let url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=" + S.get().targetLang + "&dt=t&dt=bd&q=" + encodeURIComponent(word);
|
||||
xhr.open("GET", url);
|
||||
xhr.send();
|
||||
xhr.onload = function () {
|
||||
resolve(xhr);
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
//翻訳結果を表示
|
||||
function showResult(results) {
|
||||
panel.innerText = "";
|
||||
let resultText = "";
|
||||
let candidateText = "";
|
||||
let wordsCount = 0;
|
||||
let lineCount = 0;
|
||||
|
||||
for (let j = 0; j < results.length; j++) {
|
||||
lineCount++;
|
||||
for (let i = 0; i < results[j].response[0].length; i++) {
|
||||
resultText += results[j].response[0][i][0];
|
||||
}
|
||||
resultText += "\n";
|
||||
|
||||
if (results[j].response[1]) {
|
||||
wordsCount++;
|
||||
for (let i = 0; i < results[j].response[1].length; i++) {
|
||||
const partsOfSpeech = results[j].response[1][i][0];
|
||||
const candidates = results[j].response[1][i][1];
|
||||
candidateText += `\n${partsOfSpeech}${partsOfSpeech!="" ? ": " : ""}${candidates.join(", ")}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
panel.innerHTML = "<p class=result></p><p class=candidate>"
|
||||
panel.getElementsByClassName("result")[0].innerText = resultText;
|
||||
if (S.get().ifShowCandidate && wordsCount == 1 && lineCount == 1) panel.getElementsByClassName("candidate")[0].innerText = candidateText;
|
||||
async function translate(sourceWord, sourceLang, targetLang) {
|
||||
const resultData = await T.translate(sourceWord, sourceLang, targetLang);
|
||||
showResult(resultData.resultText, resultData.candidateText);
|
||||
panelPosition(clickPosition);
|
||||
}
|
||||
|
||||
function showResult(resultText, candidateText) {
|
||||
panel.innerHTML = '<p class=result></p><p class=candidate>';
|
||||
const resultArea = panel.getElementsByClassName("result")[0];
|
||||
const candidateArea = panel.getElementsByClassName("candidate")[0];
|
||||
|
||||
resultArea.innerText = resultText;
|
||||
if (S.get().ifShowCandidate) candidateArea.innerText = candidateText;
|
||||
}
|
||||
|
||||
//パネル表示
|
||||
|
@ -219,6 +173,6 @@ function sendToPopup() {
|
|||
//コンテキストメニュークリックでパネルを表示
|
||||
function showPanelFromMenu() {
|
||||
button.style.display = "none";
|
||||
translate();
|
||||
translate(selectionWord, 'auto', S.get().targetLang);
|
||||
showPanel(clickPosition);
|
||||
}
|
||||
|
|
80
simple-translate/translate.js
Normal file
80
simple-translate/translate.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
class Translate {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
set sourceWord(word) {
|
||||
this.sourceWord = word;
|
||||
}
|
||||
|
||||
translate(sourceWord, sourceLang = 'auto', targetLang) {
|
||||
//改行で分割
|
||||
const sourceLines = sourceWord.split("\n");
|
||||
|
||||
let promises = [];
|
||||
for (let sourceLine of sourceLines) {
|
||||
promises.push(this.sendRequest(sourceLine, sourceLang, targetLang));
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
Promise.all(promises)
|
||||
.then((results) => {
|
||||
resolve(this.formatResult(results));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
sendRequest(word, sourceLang, targetLang) {
|
||||
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sourceLang}&tl=${targetLang}&dt=t&dt=bd&q=${encodeURIComponent(word)}`;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
xhr.open("GET", url);
|
||||
xhr.send();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
xhr.onload = () => {
|
||||
resolve(xhr);
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
formatResult(results) {
|
||||
const resultData = {
|
||||
resultText: '',
|
||||
candidateText: '',
|
||||
sourceLanguage: '',
|
||||
percentage: 0
|
||||
}
|
||||
|
||||
//翻訳元言語を取得
|
||||
resultData.sourceLanguage = results[0].response[2];
|
||||
resultData.percentage = results[0].response[6];
|
||||
|
||||
let candidateText = '';
|
||||
let wordCount = 0;
|
||||
let lineCount = 0;
|
||||
|
||||
for (const result of results) {
|
||||
lineCount++;
|
||||
|
||||
//翻訳文を取得
|
||||
for (const response of result.response[0]) {
|
||||
resultData.resultText += response[0];
|
||||
}
|
||||
|
||||
//訳候補を取得
|
||||
if (result.response[1]) {
|
||||
wordCount++;
|
||||
for (let i = 0; i < result.response[1].length; i++) {
|
||||
const partsOfSpeech = result.response[1][i][0];
|
||||
const candidates = result.response[1][i][1];
|
||||
candidateText += `${partsOfSpeech}${partsOfSpeech != '' ? ': ' : ''}${candidates.join(', ')}\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
//訳候補が一つの単語のみに対して存在するとき返す
|
||||
if (wordCount == 1 && lineCount == 1) resultData.candidateText = candidateText;
|
||||
|
||||
return resultData;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue