Support DeepL API
This commit is contained in:
parent
a262963906
commit
cfcb01c93d
|
@ -50,6 +50,42 @@
|
|||
"generalLabel": {
|
||||
"message": "General"
|
||||
},
|
||||
"translationApiLabel": {
|
||||
"message": "Translation engine"
|
||||
},
|
||||
"googleApiLabel": {
|
||||
"message": "Google translate API"
|
||||
},
|
||||
"googleApiCaptionLabel": {
|
||||
"message": "Use Google Translate API. No registration is required."
|
||||
},
|
||||
"deeplApiLabel": {
|
||||
"message": "DeepL API"
|
||||
},
|
||||
"deeplApiCaptionLabel": {
|
||||
"message": "Use DeepL API. You must register with DeepL API Free or DeepL API Pro to obtain an authorization key."
|
||||
},
|
||||
"howToUseDeeplLabel": {
|
||||
"message": "How to register DeepL API"
|
||||
},
|
||||
"deeplPlanLabel": {
|
||||
"message": "DeepL API plan"
|
||||
},
|
||||
"deeplPlanCaptionLabel": {
|
||||
"message": "Select the DeepL API plan for which you registered."
|
||||
},
|
||||
"deeplFreeLabel": {
|
||||
"message": "DeepL API Free"
|
||||
},
|
||||
"deeplProLabel": {
|
||||
"message": "DeepL API Pro"
|
||||
},
|
||||
"deeplAuthKeyLabel": {
|
||||
"message": "Authorization key"
|
||||
},
|
||||
"deeplAuthKeyCaptionLabel": {
|
||||
"message": "Enter the authentication key for the DeepL API."
|
||||
},
|
||||
"targetLangCaptionLabel": {
|
||||
"message": "Select the default target language."
|
||||
},
|
||||
|
@ -383,6 +419,9 @@
|
|||
"unavailableError": {
|
||||
"message": "Error: Service usage limit reached. Please wait a while and try again."
|
||||
},
|
||||
"deeplAuthError": {
|
||||
"message": "Error: Authentication of DeepL API failed. Please set the authentication key and plan correctly on the settings page."
|
||||
},
|
||||
"unknownError": {
|
||||
"message": "Error: Unknown error"
|
||||
},
|
||||
|
@ -724,5 +763,20 @@
|
|||
},
|
||||
"lang_zu": {
|
||||
"message": "Zulu"
|
||||
},
|
||||
"lang_en-US": {
|
||||
"message": "English (American)"
|
||||
},
|
||||
"lang_en-GB": {
|
||||
"message": "English (British)"
|
||||
},
|
||||
"lang_pt-PT": {
|
||||
"message": "Portuguese"
|
||||
},
|
||||
"lang_pt-BR": {
|
||||
"message": "Portuguese (Brazilian)"
|
||||
},
|
||||
"lang_zh": {
|
||||
"message": "Chinese"
|
||||
}
|
||||
}
|
|
@ -2,9 +2,11 @@ import browser from "webextension-polyfill";
|
|||
const alphabeticallySort = (a, b) => a.name.localeCompare(b.name);
|
||||
|
||||
const langListGoogle = ["af", "sq", "am", "ar", "hy", "az", "eu", "be", "bn", "bs", "bg", "ca", "ceb", "zh-CN", "zh-TW", "co", "hr", "cs", "da", "nl", "en", "eo", "et", "fi", "fr", "fy", "gl", "ka", "de", "el", "gu", "ht", "ha", "haw", "he", "hi", "hmn", "hu", "is", "ig", "id", "ga", "it", "ja", "jv", "kn", "kk", "km", "rw", "ko", "ku", "ky", "lo", "lv", "lt", "lb", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mn", "my", "ne", "no", "ny", "or", "ps", "fa", "pl", "pt", "pa", "ro", "ru", "sm", "gd", "sr", "st", "sn", "sd", "si", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", "tt", "te", "th", "tr", "tk", "uk", "ur", "ug", "uz", "vi", "cy", "xh", "yi", "yo", "zu"];
|
||||
const langListDeepl = ["bg", "cs", "da", "de", "el", "en-GB", "en-US", "es", "et", "fi", "fr", "hu", "it", "ja", "lt", "lv", "nl", "pl", "pt-PT", "pt-BR", "ro", "ru", "sk", "sl", "sv", "zh"];
|
||||
|
||||
export default () => {
|
||||
const langOptions = langListGoogle.map(lang => ({
|
||||
export default (translationApi) => {
|
||||
const langList = translationApi === "google" ? langListGoogle : langListDeepl;
|
||||
const langOptions = langList.map(lang => ({
|
||||
value: lang,
|
||||
name: browser.i18n.getMessage("lang_" + lang)
|
||||
}));
|
||||
|
|
|
@ -1,30 +1,34 @@
|
|||
import log from "loglevel";
|
||||
import axios from "axios";
|
||||
import { getSettings } from "src/settings/settings";
|
||||
|
||||
let translationHistory = [];
|
||||
|
||||
const logDir = "common/translate";
|
||||
|
||||
const getHistory = (sourceWord, sourceLang, targetLang) => {
|
||||
const getHistory = (sourceWord, sourceLang, targetLang, translationApi) => {
|
||||
const history = translationHistory.find(
|
||||
history =>
|
||||
history.sourceWord == sourceWord &&
|
||||
history.sourceLang == sourceLang &&
|
||||
history.targetLang == targetLang &&
|
||||
history.result.statusText == "OK"
|
||||
history.translationApi == translationApi &&
|
||||
!history.result.isError
|
||||
);
|
||||
return history;
|
||||
};
|
||||
|
||||
const setHistory = (sourceWord, sourceLang, targetLang, formattedResult) => {
|
||||
const setHistory = (sourceWord, sourceLang, targetLang, translationApi, result) => {
|
||||
translationHistory.push({
|
||||
sourceWord: sourceWord,
|
||||
sourceLang: sourceLang,
|
||||
targetLang: targetLang,
|
||||
result: formattedResult
|
||||
translationApi: translationApi,
|
||||
result: result
|
||||
});
|
||||
};
|
||||
|
||||
const sendRequest = async (word, sourceLang, targetLang) => {
|
||||
const sendRequestToGoogle = async (word, sourceLang, targetLang) => {
|
||||
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sourceLang}&tl=${targetLang}&dt=t&dt=bd&dj=1&q=${encodeURIComponent(
|
||||
word
|
||||
)}`;
|
||||
|
@ -64,24 +68,47 @@ const sendRequest = async (word, sourceLang, targetLang) => {
|
|||
};
|
||||
|
||||
const sendRequestToDeepL = async (word, sourceLang, targetLang) => {
|
||||
log.log(logDir, "sendRequestToDeepL()");
|
||||
|
||||
let params = new URLSearchParams();
|
||||
|
||||
const key = "f5a2c02c-7871-af5c-0d6a-244a9e6d4a1f:fx";
|
||||
params.append("auth_key", key);
|
||||
const authKey = getSettings("deeplAuthKey");
|
||||
params.append("auth_key", authKey);
|
||||
params.append("text", word);
|
||||
params.append("target_lang", "ja");
|
||||
params.append("target_lang", targetLang);
|
||||
const url = getSettings("deeplPlan") === "deeplFree" ?
|
||||
"https://api-free.deepl.com/v2/translate" :
|
||||
"https://api.deepl.com/v2/translate";
|
||||
const result = await axios.post(url, params).catch(e => e.response);
|
||||
|
||||
const url = "https://api-free.deepl.com/v2/translate";
|
||||
const resultData = {
|
||||
resultText: "",
|
||||
candidateText: "",
|
||||
sourceLanguage: "",
|
||||
percentage: 0,
|
||||
isError: false,
|
||||
errorMessage: ""
|
||||
};
|
||||
|
||||
const res = await axios.post(url, params).catch(e => e.response);
|
||||
console.log("!!!!!!!!!!!!!!!", res);
|
||||
if (!result || result?.status !== 200) {
|
||||
resultData.isError = true;
|
||||
|
||||
if (!result || result.status === 0) resultData.errorMessage = browser.i18n.getMessage("networkError");
|
||||
else if (result.status === 403) resultData.errorMessage = browser.i18n.getMessage("deeplAuthError");
|
||||
else resultData.errorMessage = `${browser.i18n.getMessage("unknownError")} [${result?.status} ${result?.statusText}] ${result?.data.message}`;
|
||||
|
||||
log.error(logDir, "sendRequestToDeepL()", result);
|
||||
return resultData;
|
||||
}
|
||||
|
||||
resultData.resultText = result.data.translations[0].text;
|
||||
resultData.sourceLanguage = result.data.translations[0].detected_source_language.toLowerCase();
|
||||
resultData.percentage = 1;
|
||||
|
||||
log.log(logDir, "sendRequestToDeepL()", resultData);
|
||||
return resultData;
|
||||
};
|
||||
|
||||
|
||||
export default async (sourceWord, sourceLang = "auto", targetLang) => {
|
||||
log.log(logDir, "tranlate()", sourceWord, targetLang);
|
||||
export default async (sourceWord, sourceLang = "auto", targetLang, translationApi) => {
|
||||
log.log(logDir, "tranlate()", sourceWord, targetLang, translationApi);
|
||||
sourceWord = sourceWord.trim();
|
||||
if (sourceWord === "")
|
||||
return {
|
||||
|
@ -95,7 +122,9 @@ export default async (sourceWord, sourceLang = "auto", targetLang) => {
|
|||
const history = getHistory(sourceWord, sourceLang, targetLang);
|
||||
if (history) return history.result;
|
||||
|
||||
const result = await sendRequest(sourceWord, sourceLang, targetLang);
|
||||
setHistory(sourceWord, sourceLang, targetLang, result);
|
||||
const result = getSettings("translationApi") === "google" ?
|
||||
await sendRequestToGoogle(sourceWord, sourceLang, targetLang) :
|
||||
await sendRequestToDeepL(sourceWord, sourceLang, targetLang);
|
||||
setHistory(sourceWord, sourceLang, targetLang, translationApi, result);
|
||||
return result;
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ const matchesTargetLang = async selectedText => {
|
|||
const isNotText = result.percentage === 0;
|
||||
if (isNotText) return true;
|
||||
|
||||
const matchsLangs = targetLang === result.sourceLanguage;
|
||||
const matchsLangs = targetLang.split("-")[0] === result.sourceLanguage.split("-")[0]; // split("-")[0] : deepLでenとen-USを区別しないために必要
|
||||
return matchsLangs;
|
||||
};
|
||||
|
||||
|
@ -90,7 +90,7 @@ export default class TranslateContainer extends Component {
|
|||
const secondLang = getSettings("secondTargetLang");
|
||||
const shouldSwitchSecondLang =
|
||||
getSettings("ifChangeSecondLangOnPage") &&
|
||||
result.sourceLanguage === targetLang && result.percentage > 0 && targetLang !== secondLang;
|
||||
result.sourceLanguage.split("-")[0] === targetLang.split("-")[0] && result.percentage > 0 && targetLang !== secondLang;
|
||||
if (shouldSwitchSecondLang) result = await translateText(this.selectedText, secondLang);
|
||||
|
||||
this.setState({
|
||||
|
|
|
@ -18,6 +18,8 @@ export default props => {
|
|||
}
|
||||
|
||||
setSettings(id, value);
|
||||
|
||||
if (props.handleChange) props.handleChange();
|
||||
};
|
||||
|
||||
const handleCheckedChange = e => {
|
||||
|
@ -113,8 +115,8 @@ export default props => {
|
|||
formId = id;
|
||||
optionForm = (
|
||||
<div className="selectWrap">
|
||||
<select id={formId} onChange={handleValueChange} defaultValue={currentValue}>
|
||||
{props.options.map((option, index) => (
|
||||
<select id={formId} onChange={handleValueChange} value={currentValue}>
|
||||
{(typeof props.options === 'function' ? props.options() : props.options).map((option, index) => (
|
||||
<option value={option.value} key={index}>
|
||||
{props.useRawOptionName ? option.name : browser.i18n.getMessage(option.name)}
|
||||
</option>
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import React, { Component } from "react";
|
||||
import browser from "webextension-polyfill";
|
||||
import generateLangOptions from "src/common/generateLangOptions";
|
||||
import openUrl from "src/common/openUrl";
|
||||
import "../styles/Footer.scss";
|
||||
|
||||
export default class Footer extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.langList = generateLangOptions();
|
||||
}
|
||||
|
||||
handleLinkClick = async () => {
|
||||
|
@ -23,7 +21,7 @@ export default class Footer extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { tabUrl, targetLang, langHistory } = this.props;
|
||||
const { tabUrl, targetLang, langHistory, langList } = this.props;
|
||||
|
||||
return (
|
||||
<div id="footer">
|
||||
|
@ -39,7 +37,7 @@ export default class Footer extends Component {
|
|||
>
|
||||
|
||||
<optgroup label={browser.i18n.getMessage("recentLangLabel")}>
|
||||
{this.langList.filter(option => langHistory.includes(option.value))
|
||||
{langList.filter(option => langHistory.includes(option.value))
|
||||
.map(option => (
|
||||
<option value={option.value} key={option.value}>
|
||||
{option.name}
|
||||
|
@ -47,7 +45,7 @@ export default class Footer extends Component {
|
|||
))}
|
||||
</optgroup>
|
||||
<optgroup label={browser.i18n.getMessage("allLangLabel")}>
|
||||
{this.langList.map(option => (
|
||||
{langList.map(option => (
|
||||
<option value={option.value} key={option.value}>
|
||||
{option.name}
|
||||
</option>
|
||||
|
|
|
@ -4,6 +4,7 @@ import log from "loglevel";
|
|||
import { initSettings, getSettings, setSettings } from "src/settings/settings";
|
||||
import { updateLogLevel, overWriteLogLevel } from "src/common/log";
|
||||
import translate from "src/common/translate";
|
||||
import generateLangOptions from "src/common/generateLangOptions";
|
||||
import Header from "./Header";
|
||||
import InputArea from "./InputArea";
|
||||
import ResultArea from "./ResultArea";
|
||||
|
@ -42,6 +43,7 @@ export default class PopupPage extends Component {
|
|||
sourceLang: "",
|
||||
isError: false,
|
||||
errorMessage: "",
|
||||
langList: [],
|
||||
tabUrl: "",
|
||||
isConnected: true,
|
||||
isEnabledOnPage: true,
|
||||
|
@ -66,7 +68,8 @@ export default class PopupPage extends Component {
|
|||
}
|
||||
this.setState({
|
||||
targetLang: targetLang,
|
||||
langHistory: langHistory
|
||||
langHistory: langHistory,
|
||||
langList: generateLangOptions(getSettings("translationApi"))
|
||||
});
|
||||
|
||||
const tabInfo = await getTabInfo();
|
||||
|
@ -128,9 +131,10 @@ export default class PopupPage extends Component {
|
|||
if (defaultTargetLang === secondLang) return;
|
||||
|
||||
const equalsSourceAndTarget =
|
||||
result.sourceLanguage === this.state.targetLang && result.percentage > 0;
|
||||
result.sourceLanguage.split("-")[0] === this.state.targetLang.split("-")[0] && result.percentage > 0;
|
||||
const equalsSourceAndDefault =
|
||||
result.sourceLanguage === defaultTargetLang && result.percentage > 0;
|
||||
result.sourceLanguage.split("-")[0] === defaultTargetLang.split("-")[0] && result.percentage > 0;
|
||||
// split("-")[0] : deepLでenとen-USを区別しないために必要
|
||||
|
||||
if (!this.isSwitchedSecondLang) {
|
||||
if (equalsSourceAndTarget && equalsSourceAndDefault) {
|
||||
|
@ -184,6 +188,7 @@ export default class PopupPage extends Component {
|
|||
targetLang={this.state.targetLang}
|
||||
langHistory={this.state.langHistory}
|
||||
handleLangChange={this.handleLangChange}
|
||||
langList={this.state.langList}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React from "react";
|
||||
import browser from "webextension-polyfill";
|
||||
import generateLangOptions from "src/common/generateLangOptions";
|
||||
import { getSettings, setSettings } from "./settings";
|
||||
|
||||
const getDefaultLangs = () => {
|
||||
const uiLang = browser.i18n.getUILanguage();
|
||||
|
@ -12,7 +14,31 @@ const getDefaultLangs = () => {
|
|||
return { targetLang, secondTargetLang };
|
||||
};
|
||||
|
||||
const langListOptions = generateLangOptions();
|
||||
const updateLangsWhenChangeTranslationApi = () => {
|
||||
const translationApi = getSettings("translationApi");
|
||||
const targetLang = getSettings("targetLang");
|
||||
const secondTargetLang = getSettings("secondTargetLang");;
|
||||
const currentLangs = generateLangOptions(translationApi).map(option => option.value);
|
||||
|
||||
const mappingLang = lang => {
|
||||
switch (lang) {
|
||||
case "en": return "en-US";
|
||||
case "en-US":
|
||||
case "en-GB": return "en";
|
||||
case "zh": return "zh-CN";
|
||||
case "zh-CN":
|
||||
case "zh-TW": return "zh";
|
||||
case "pt": return "pt-PT";
|
||||
case "pt-PT":
|
||||
case "pt-BR": return "pt";
|
||||
default: return currentLangs[0];
|
||||
}
|
||||
};
|
||||
|
||||
if (!currentLangs.includes(targetLang)) setSettings("targetLang", mappingLang(targetLang));
|
||||
if (!currentLangs.includes(secondTargetLang)) setSettings("secondTargetLang", mappingLang(secondTargetLang));
|
||||
};
|
||||
|
||||
const defaultLangs = getDefaultLangs();
|
||||
const getTheme = () =>
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches ? "dark" : "light";
|
||||
|
@ -21,13 +47,77 @@ export default [
|
|||
{
|
||||
category: "generalLabel",
|
||||
elements: [
|
||||
{
|
||||
id: "translationApi",
|
||||
title: "translationApiLabel",
|
||||
captions: [],
|
||||
type: "none",
|
||||
default: "google",
|
||||
new: true,
|
||||
childElements: [
|
||||
{
|
||||
id: "translationApi",
|
||||
title: "googleApiLabel",
|
||||
captions: ["googleApiCaptionLabel"],
|
||||
type: "radio",
|
||||
value: "google",
|
||||
handleChange: () => updateLangsWhenChangeTranslationApi()
|
||||
},
|
||||
{
|
||||
id: "translationApi",
|
||||
title: "deeplApiLabel",
|
||||
captions: ["deeplApiCaptionLabel"],
|
||||
extraCaption:
|
||||
React.createElement("p",
|
||||
{ className: "caption" },
|
||||
React.createElement("a",
|
||||
{
|
||||
href: "https://github.com/sienori/simple-translate/wiki/How-to-register-DeepL-API",
|
||||
target: "_blank"
|
||||
},
|
||||
browser.i18n.getMessage("howToUseDeeplLabel"))
|
||||
),
|
||||
type: "radio",
|
||||
value: "deepl",
|
||||
handleChange: () => updateLangsWhenChangeTranslationApi()
|
||||
},
|
||||
{
|
||||
id: "deeplPlan",
|
||||
title: "deeplPlanLabel",
|
||||
captions: ["deeplPlanCaptionLabel"],
|
||||
type: "select",
|
||||
default: "deeplFree",
|
||||
shouldShow: () => (getSettings("translationApi") === "deepl"),
|
||||
hr: true,
|
||||
options: [
|
||||
{
|
||||
name: "deeplFreeLabel",
|
||||
value: "deeplFree"
|
||||
},
|
||||
{
|
||||
name: "deeplProLabel",
|
||||
value: "deeplPro"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "deeplAuthKey",
|
||||
title: "deeplAuthKeyLabel",
|
||||
captions: ["deeplAuthKeyCaptionLabel"],
|
||||
type: "text",
|
||||
default: "",
|
||||
placeholder: "00000000-0000-0000-0000-00000000000000:fx",
|
||||
shouldShow: () => (getSettings("translationApi") === "deepl"),
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "targetLang",
|
||||
title: "targetLangLabel",
|
||||
captions: ["targetLangCaptionLabel"],
|
||||
type: "select",
|
||||
default: defaultLangs.targetLang,
|
||||
options: langListOptions,
|
||||
options: () => generateLangOptions(getSettings("translationApi")),
|
||||
useRawOptionName: true
|
||||
},
|
||||
{
|
||||
|
@ -36,7 +126,7 @@ export default [
|
|||
captions: ["secondTargetLangCaptionLabel"],
|
||||
type: "select",
|
||||
default: defaultLangs.secondTargetLang,
|
||||
options: langListOptions,
|
||||
options: () => generateLangOptions(getSettings("translationApi")),
|
||||
useRawOptionName: true
|
||||
},
|
||||
{
|
||||
|
@ -44,7 +134,8 @@ export default [
|
|||
title: "ifShowCandidateLabel",
|
||||
captions: ["ifShowCandidateCaptionLabel"],
|
||||
type: "checkbox",
|
||||
default: true
|
||||
default: true,
|
||||
shouldShow: () => (getSettings("translationApi") === "google")
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue