customize api
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
||||
OPT_TRANS_DEEPL,
|
||||
OPT_TRANS_OPENAI,
|
||||
OPT_TRANS_CUSTOMIZE,
|
||||
URL_MICROSOFT_TRANS,
|
||||
OPT_LANGS_SPECIAL,
|
||||
PROMPT_PLACE_FROM,
|
||||
PROMPT_PLACE_TO,
|
||||
@@ -49,8 +48,13 @@ export const apiFetchRules = (url, isBg = false) =>
|
||||
* @param {*} from
|
||||
* @returns
|
||||
*/
|
||||
const apiGoogleTranslate = async (translator, text, to, from, setting) => {
|
||||
const { url, key } = setting;
|
||||
const apiGoogleTranslate = async (
|
||||
translator,
|
||||
text,
|
||||
to,
|
||||
from,
|
||||
{ url, key, useCache = true }
|
||||
) => {
|
||||
const params = {
|
||||
client: "gtx",
|
||||
dt: "t",
|
||||
@@ -61,15 +65,19 @@ const apiGoogleTranslate = async (translator, text, to, from, setting) => {
|
||||
q: text,
|
||||
};
|
||||
const input = `${url}?${queryString.stringify(params)}`;
|
||||
return fetchPolyfill(input, {
|
||||
const res = await fetchPolyfill(input, {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
useCache: true,
|
||||
useCache,
|
||||
usePool: true,
|
||||
translator,
|
||||
token: key,
|
||||
});
|
||||
const trText = res.sentences.map((item) => item.trans).join(" ");
|
||||
const isSame = to === res.src;
|
||||
|
||||
return [trText, isSame];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -79,23 +87,33 @@ const apiGoogleTranslate = async (translator, text, to, from, setting) => {
|
||||
* @param {*} from
|
||||
* @returns
|
||||
*/
|
||||
const apiMicrosoftTranslate = (translator, text, to, from) => {
|
||||
const apiMicrosoftTranslate = async (
|
||||
translator,
|
||||
text,
|
||||
to,
|
||||
from,
|
||||
{ url, useCache = true }
|
||||
) => {
|
||||
const params = {
|
||||
from,
|
||||
to,
|
||||
"api-version": "3.0",
|
||||
};
|
||||
const input = `${URL_MICROSOFT_TRANS}?${queryString.stringify(params)}`;
|
||||
return fetchPolyfill(input, {
|
||||
const input = `${url}?${queryString.stringify(params)}`;
|
||||
const res = await fetchPolyfill(input, {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify([{ Text: text }]),
|
||||
useCache: true,
|
||||
useCache,
|
||||
usePool: true,
|
||||
translator,
|
||||
});
|
||||
const trText = res[0].translations[0].text;
|
||||
const isSame = to === res[0].detectedLanguage?.language;
|
||||
|
||||
return [trText, isSame];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -105,8 +123,13 @@ const apiMicrosoftTranslate = (translator, text, to, from) => {
|
||||
* @param {*} from
|
||||
* @returns
|
||||
*/
|
||||
const apiDeepLTranslate = (translator, text, to, from, setting) => {
|
||||
const { url, key } = setting;
|
||||
const apiDeepLTranslate = async (
|
||||
translator,
|
||||
text,
|
||||
to,
|
||||
from,
|
||||
{ url, key, useCache = true }
|
||||
) => {
|
||||
const data = {
|
||||
text: [text],
|
||||
target_lang: to,
|
||||
@@ -115,17 +138,21 @@ const apiDeepLTranslate = (translator, text, to, from, setting) => {
|
||||
if (from) {
|
||||
data.source_lang = from;
|
||||
}
|
||||
return fetchPolyfill(url, {
|
||||
const res = await fetchPolyfill(url, {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
useCache: true,
|
||||
useCache,
|
||||
usePool: true,
|
||||
translator,
|
||||
token: key,
|
||||
});
|
||||
const trText = res.translations.map((item) => item.text).join(" ");
|
||||
const isSame = to === res.translations[0].detected_source_language;
|
||||
|
||||
return [trText, isSame];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -135,12 +162,17 @@ const apiDeepLTranslate = (translator, text, to, from, setting) => {
|
||||
* @param {*} from
|
||||
* @returns
|
||||
*/
|
||||
const apiOpenaiTranslate = async (translator, text, to, from, setting) => {
|
||||
let { url, key, model, prompt } = setting;
|
||||
const apiOpenaiTranslate = async (
|
||||
translator,
|
||||
text,
|
||||
to,
|
||||
from,
|
||||
{ url, key, model, prompt, useCache = true }
|
||||
) => {
|
||||
prompt = prompt
|
||||
.replaceAll(PROMPT_PLACE_FROM, from)
|
||||
.replaceAll(PROMPT_PLACE_TO, to);
|
||||
return fetchPolyfill(url, {
|
||||
const res = await fetchPolyfill(url, {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
@@ -160,11 +192,17 @@ const apiOpenaiTranslate = async (translator, text, to, from, setting) => {
|
||||
temperature: 0,
|
||||
max_tokens: 256,
|
||||
}),
|
||||
useCache: true,
|
||||
useCache,
|
||||
usePool: true,
|
||||
translator,
|
||||
token: key,
|
||||
});
|
||||
const trText = res?.choices?.[0].message.content;
|
||||
const sLang = await tryDetectLang(text);
|
||||
const tLang = await tryDetectLang(trText);
|
||||
const isSame = text === trText || (sLang && tLang && sLang === tLang);
|
||||
|
||||
return [trText, isSame];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -174,12 +212,16 @@ const apiOpenaiTranslate = async (translator, text, to, from, setting) => {
|
||||
* @param {*} from
|
||||
* @returns
|
||||
*/
|
||||
const apiCustomizeTranslate = async (translator, text, to, from, setting) => {
|
||||
let { url, key, headers } = setting;
|
||||
return fetchPolyfill(url, {
|
||||
const apiCustomTranslate = async (
|
||||
translator,
|
||||
text,
|
||||
to,
|
||||
from,
|
||||
{ url, key, useCache = true }
|
||||
) => {
|
||||
const res = await fetchPolyfill(url, {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
...JSON.parse(headers),
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
@@ -187,11 +229,15 @@ const apiCustomizeTranslate = async (translator, text, to, from, setting) => {
|
||||
from,
|
||||
to,
|
||||
}),
|
||||
useCache: true,
|
||||
useCache,
|
||||
usePool: true,
|
||||
translator,
|
||||
token: key,
|
||||
});
|
||||
const trText = res.text;
|
||||
const isSame = to === res.from;
|
||||
|
||||
return [trText, isSame];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -199,40 +245,29 @@ const apiCustomizeTranslate = async (translator, text, to, from, setting) => {
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export const apiTranslate = async ({
|
||||
export const apiTranslate = ({
|
||||
translator,
|
||||
q,
|
||||
text,
|
||||
fromLang,
|
||||
toLang,
|
||||
setting,
|
||||
apiSetting,
|
||||
}) => {
|
||||
let trText = "";
|
||||
let isSame = false;
|
||||
const from = OPT_LANGS_SPECIAL[translator]?.get(fromLang) ?? fromLang;
|
||||
const to = OPT_LANGS_SPECIAL[translator]?.get(toLang) ?? toLang;
|
||||
const callApi = (api) => api(translator, text, to, from, apiSetting);
|
||||
|
||||
let from = OPT_LANGS_SPECIAL?.[translator]?.get(fromLang) ?? fromLang;
|
||||
let to = OPT_LANGS_SPECIAL?.[translator]?.get(toLang) ?? toLang;
|
||||
|
||||
if (translator === OPT_TRANS_GOOGLE) {
|
||||
const res = await apiGoogleTranslate(translator, q, to, from, setting);
|
||||
trText = res.sentences.map((item) => item.trans).join(" ");
|
||||
isSame = to === res.src;
|
||||
} else if (translator === OPT_TRANS_MICROSOFT) {
|
||||
const res = await apiMicrosoftTranslate(translator, q, to, from);
|
||||
trText = res[0].translations[0].text;
|
||||
isSame = to === res[0].detectedLanguage?.language;
|
||||
} else if (translator === OPT_TRANS_DEEPL) {
|
||||
const res = await apiDeepLTranslate(translator, q, to, from, setting);
|
||||
trText = res.translations.map((item) => item.text).join(" ");
|
||||
isSame = to === (from || res.translations[0].detected_source_language);
|
||||
} else if (translator === OPT_TRANS_OPENAI) {
|
||||
const res = await apiOpenaiTranslate(translator, q, to, from, setting);
|
||||
trText = res?.choices?.[0].message.content;
|
||||
const sLang = await tryDetectLang(q);
|
||||
const tLang = await tryDetectLang(trText);
|
||||
isSame = q === trText || (sLang && tLang && sLang === tLang);
|
||||
} else if (translator === OPT_TRANS_CUSTOMIZE) {
|
||||
// todo
|
||||
switch (translator) {
|
||||
case OPT_TRANS_GOOGLE:
|
||||
return callApi(apiGoogleTranslate);
|
||||
case OPT_TRANS_MICROSOFT:
|
||||
return callApi(apiMicrosoftTranslate);
|
||||
case OPT_TRANS_DEEPL:
|
||||
return callApi(apiDeepLTranslate);
|
||||
case OPT_TRANS_OPENAI:
|
||||
return callApi(apiOpenaiTranslate);
|
||||
case OPT_TRANS_CUSTOMIZE:
|
||||
return callApi(apiCustomTranslate);
|
||||
default:
|
||||
return ["", false];
|
||||
}
|
||||
|
||||
return [trText, isSame];
|
||||
};
|
||||
|
||||
@@ -3,6 +3,99 @@ export const UI_LANGS = [
|
||||
["zh", "中文"],
|
||||
];
|
||||
|
||||
const customApiLangs = `["en", "English - English"],
|
||||
["zh-CN", "Simplified Chinese - 简体中文"],
|
||||
["zh-TW", "Traditional Chinese - 繁體中文"],
|
||||
["ar", "Arabic - العربية"],
|
||||
["bg", "Bulgarian - Български"],
|
||||
["ca", "Catalan - Català"],
|
||||
["hr", "Croatian - Hrvatski"],
|
||||
["cs", "Czech - Čeština"],
|
||||
["da", "Danish - Dansk"],
|
||||
["nl", "Dutch - Nederlands"],
|
||||
["fi", "Finnish - Suomi"],
|
||||
["fr", "French - Français"],
|
||||
["de", "German - Deutsch"],
|
||||
["el", "Greek - Ελληνικά"],
|
||||
["hi", "Hindi - हिन्दी"],
|
||||
["hu", "Hungarian - Magyar"],
|
||||
["id", "Indonesian - Indonesia"],
|
||||
["it", "Italian - Italiano"],
|
||||
["ja", "Japanese - 日本語"],
|
||||
["ko", "Korean - 한국어"],
|
||||
["ms", "Malay - Melayu"],
|
||||
["mt", "Maltese - Malti"],
|
||||
["nb", "Norwegian - Norsk Bokmål"],
|
||||
["pl", "Polish - Polski"],
|
||||
["pt", "Portuguese - Português"],
|
||||
["ro", "Romanian - Română"],
|
||||
["ru", "Russian - Русский"],
|
||||
["sk", "Slovak - Slovenčina"],
|
||||
["sl", "Slovenian - Slovenščina"],
|
||||
["es", "Spanish - Español"],
|
||||
["sv", "Swedish - Svenska"],
|
||||
["ta", "Tamil - தமிழ்"],
|
||||
["te", "Telugu - తెలుగు"],
|
||||
["th", "Thai - ไทย"],
|
||||
["tr", "Turkish - Türkçe"],
|
||||
["uk", "Ukrainian - Українська"],
|
||||
["vi", "Vietnamese - Tiếng Việt"],
|
||||
`;
|
||||
|
||||
const customApiHelpZH = `/// 自定义翻译源接口说明
|
||||
// 请求(Request)数据将按下面规范发送
|
||||
{
|
||||
url: {{YOUR_URL}},
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
"Authorization"] = "Bearer {{YOUR_KEY}}"
|
||||
},
|
||||
body: {
|
||||
text, // 需要翻译的文字
|
||||
from, // 源语言,可能为空,表示需要接口自动识别语言
|
||||
to, // 目标语言
|
||||
}
|
||||
}
|
||||
|
||||
// 返回(Response)数据需符合下面的JSON规范
|
||||
{
|
||||
text, // 翻译后的文字
|
||||
from, // 识别的源语言
|
||||
to, // 目标语言(可选)
|
||||
}
|
||||
|
||||
// 支持的语言代码如下
|
||||
${customApiLangs}
|
||||
`;
|
||||
|
||||
const customApiHelpEN = `/// Custom translation source interface description
|
||||
// Request data will be sent according to the following specifications
|
||||
{
|
||||
url: {{YOUR_URL}},
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
"Authorization"] = "Bearer {{YOUR_KEY}}"
|
||||
},
|
||||
body: {
|
||||
text, // text to be translated
|
||||
from, // Source language, may be empty
|
||||
to, // Target language
|
||||
}
|
||||
}
|
||||
|
||||
// The returned data must conform to the following JSON specification
|
||||
{
|
||||
text, // translated text
|
||||
from, // Recognized source language
|
||||
to, // Target language (optional)
|
||||
}
|
||||
|
||||
// The supported language codes are as follows
|
||||
${customApiLangs}
|
||||
`;
|
||||
|
||||
export const I18N = {
|
||||
app_name: {
|
||||
zh: `简约翻译`,
|
||||
@@ -12,6 +105,10 @@ export const I18N = {
|
||||
zh: `翻译`,
|
||||
en: `Translate`,
|
||||
},
|
||||
custom_api_help: {
|
||||
zh: customApiHelpZH,
|
||||
en: customApiHelpEN,
|
||||
},
|
||||
translate_alt: {
|
||||
zh: `翻译 (Alt+Q)`,
|
||||
en: `Translate (Alt+Q)`,
|
||||
|
||||
@@ -64,14 +64,12 @@ export const URL_KISS_RULES_NEW_ISSUE =
|
||||
export const URL_RAW_PREFIX =
|
||||
"https://raw.githubusercontent.com/fishjar/kiss-translator/master";
|
||||
export const URL_MICROSOFT_AUTH = "https://edge.microsoft.com/translate/auth";
|
||||
export const URL_MICROSOFT_TRANS =
|
||||
"https://api-edge.cognitive.microsofttranslator.com/translate";
|
||||
|
||||
export const OPT_TRANS_GOOGLE = "Google";
|
||||
export const OPT_TRANS_MICROSOFT = "Microsoft";
|
||||
export const OPT_TRANS_DEEPL = "DeepL";
|
||||
export const OPT_TRANS_OPENAI = "OpenAI";
|
||||
export const OPT_TRANS_CUSTOMIZE = "Customize";
|
||||
export const OPT_TRANS_CUSTOMIZE = "Custom";
|
||||
export const OPT_TRANS_ALL = [
|
||||
OPT_TRANS_GOOGLE,
|
||||
OPT_TRANS_MICROSOFT,
|
||||
@@ -135,6 +133,7 @@ export const OPT_LANGS_SPECIAL = {
|
||||
[OPT_TRANS_OPENAI]: new Map(
|
||||
OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]])
|
||||
),
|
||||
[OPT_TRANS_CUSTOMIZE]: new Map([["auto", ""]]),
|
||||
};
|
||||
|
||||
export const OPT_STYLE_NONE = "style_none"; // 无
|
||||
@@ -204,6 +203,7 @@ export const DEFAULT_SUBRULES_LIST = [
|
||||
export const DEFAULT_TRANS_APIS = {
|
||||
[OPT_TRANS_GOOGLE]: {
|
||||
url: "https://translate.googleapis.com/translate_a/single",
|
||||
key: "",
|
||||
},
|
||||
[OPT_TRANS_MICROSOFT]: {
|
||||
url: "https://api-edge.cognitive.microsofttranslator.com/translate",
|
||||
@@ -222,7 +222,6 @@ export const DEFAULT_TRANS_APIS = {
|
||||
[OPT_TRANS_CUSTOMIZE]: {
|
||||
url: "",
|
||||
key: "",
|
||||
headers: "",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -243,13 +242,6 @@ export const DEFAULT_SETTING = {
|
||||
subrulesList: DEFAULT_SUBRULES_LIST, // 订阅列表
|
||||
owSubrule: DEFAULT_OW_RULE, // 覆写订阅规则
|
||||
transApis: DEFAULT_TRANS_APIS, // 翻译接口
|
||||
googleUrl: "https://translate.googleapis.com/translate_a/single", // 谷歌翻译接口
|
||||
deeplUrl: "https://api-free.deepl.com/v2/translate",
|
||||
deeplKey: "",
|
||||
openaiUrl: "https://api.openai.com/v1/chat/completions",
|
||||
openaiKey: "",
|
||||
openaiModel: "gpt-4",
|
||||
openaiPrompt: `You will be provided with a sentence in ${PROMPT_PLACE_FROM}, and your task is to translate it into ${PROMPT_PLACE_TO}.`,
|
||||
};
|
||||
|
||||
export const DEFAULT_RULES = [GLOBLA_RULE];
|
||||
|
||||
@@ -5,8 +5,6 @@ import { useSetting } from "./Setting";
|
||||
export function useApi(translator) {
|
||||
const { setting, updateSetting } = useSetting();
|
||||
const apis = setting?.transApis || DEFAULT_TRANS_APIS;
|
||||
const api = apis[translator] || {};
|
||||
console.log("apis", translator, apis);
|
||||
|
||||
const updateApi = useCallback(
|
||||
async (obj) => {
|
||||
@@ -22,5 +20,5 @@ export function useApi(translator) {
|
||||
await updateSetting({ transApis });
|
||||
}, [translator, apis, updateSetting]);
|
||||
|
||||
return { api, updateApi, resetApi };
|
||||
return { api: apis[translator] || {}, updateApi, resetApi };
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createContext, useCallback, useContext, useMemo } from "react";
|
||||
import { debounce } from "../libs/utils";
|
||||
|
||||
const SettingContext = createContext({
|
||||
setting: null,
|
||||
setting: {},
|
||||
updateSetting: async () => {},
|
||||
reloadSetting: async () => {},
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect } from "react";
|
||||
import { useState } from "react";
|
||||
import { tryDetectLang } from "../libs";
|
||||
import { apiTranslate } from "../apis";
|
||||
import { DEFAULT_TRANS_APIS } from "../config";
|
||||
|
||||
/**
|
||||
* 翻译hook
|
||||
@@ -28,10 +29,10 @@ export function useTranslate(q, rule, setting) {
|
||||
} else {
|
||||
const [trText, isSame] = await apiTranslate({
|
||||
translator,
|
||||
q,
|
||||
text: q,
|
||||
fromLang,
|
||||
toLang,
|
||||
setting: setting[translator],
|
||||
apiSetting: (setting.transApis || DEFAULT_TRANS_APIS)[translator],
|
||||
});
|
||||
setText(trText);
|
||||
setSamelang(isSame);
|
||||
|
||||
@@ -177,6 +177,10 @@ export const fetchData = async (
|
||||
* @returns
|
||||
*/
|
||||
export const fetchPolyfill = async (input, { isBg = false, ...opts } = {}) => {
|
||||
if (!input.trim()) {
|
||||
throw new Error("URL is empty");
|
||||
}
|
||||
|
||||
// 插件
|
||||
if (isExt && !isBg) {
|
||||
const res = await sendBgMsg(MSG_FETCH, { input, opts });
|
||||
|
||||
@@ -211,6 +211,10 @@ export class Translator {
|
||||
};
|
||||
|
||||
_register = () => {
|
||||
if (this._rule.fromLang === this._rule.toLang) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 搜索节点
|
||||
this._queryNodes();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
OPT_TRANS_MICROSOFT,
|
||||
OPT_TRANS_OPENAI,
|
||||
OPT_TRANS_CUSTOMIZE,
|
||||
URL_KISS_PROXY,
|
||||
} from "../../config";
|
||||
import { useState } from "react";
|
||||
import { useI18n } from "../../hooks/I18n";
|
||||
@@ -15,9 +16,12 @@ import Accordion from "@mui/material/Accordion";
|
||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import Alert from "@mui/material/Alert";
|
||||
import { useAlert } from "../../hooks/Alert";
|
||||
import { useApi } from "../../hooks/Api";
|
||||
import { apiTranslate } from "../../apis";
|
||||
import Box from "@mui/material/Box";
|
||||
import Link from "@mui/material/Link";
|
||||
|
||||
function TestButton({ translator, api }) {
|
||||
const i18n = useI18n();
|
||||
@@ -28,10 +32,10 @@ function TestButton({ translator, api }) {
|
||||
setLoading(true);
|
||||
const [text] = await apiTranslate({
|
||||
translator,
|
||||
q: "hello world",
|
||||
fromLang: "auto",
|
||||
text: "hello world",
|
||||
fromLang: "en",
|
||||
toLang: "zh-CN",
|
||||
setting: api,
|
||||
apiSetting: { ...api, useCache: false },
|
||||
});
|
||||
if (!text) {
|
||||
throw new Error("empty reault");
|
||||
@@ -45,7 +49,7 @@ function TestButton({ translator, api }) {
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <CircularProgress sx={{ marginLeft: "2em" }} size={16} />;
|
||||
return <CircularProgress size={16} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -58,7 +62,7 @@ function TestButton({ translator, api }) {
|
||||
function ApiFields({ translator }) {
|
||||
const i18n = useI18n();
|
||||
const { api, updateApi, resetApi } = useApi(translator);
|
||||
const { url = "", key = "", model = "", prompt = "", headers = "" } = api;
|
||||
const { url = "", key = "", model = "", prompt = "" } = api;
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
@@ -106,16 +110,6 @@ function ApiFields({ translator }) {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{translator === OPT_TRANS_CUSTOMIZE && (
|
||||
<TextField
|
||||
size="small"
|
||||
label={"HEADERS"}
|
||||
name="headers"
|
||||
value={headers}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
)}
|
||||
|
||||
<Stack direction="row" spacing={2}>
|
||||
<TestButton translator={translator} api={api} />
|
||||
@@ -131,6 +125,10 @@ function ApiFields({ translator }) {
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{translator === OPT_TRANS_CUSTOMIZE && (
|
||||
<pre>{i18n("custom_api_help")}</pre>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -155,7 +153,22 @@ function ApiAccordion({ translator }) {
|
||||
}
|
||||
|
||||
export default function Apis() {
|
||||
return OPT_TRANS_ALL.map((translator) => (
|
||||
const i18n = useI18n();
|
||||
return (
|
||||
<Box>
|
||||
<Stack spacing={3}>
|
||||
<Alert severity="info">
|
||||
<Link href={URL_KISS_PROXY} target="_blank">
|
||||
{i18n("about_api_proxy")}
|
||||
</Link>
|
||||
</Alert>
|
||||
|
||||
<Box>
|
||||
{OPT_TRANS_ALL.map((translator) => (
|
||||
<ApiAccordion key={translator} translator={translator} />
|
||||
));
|
||||
))}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,55 +10,8 @@ import FormHelperText from "@mui/material/FormHelperText";
|
||||
import { useSetting } from "../../hooks/Setting";
|
||||
import { limitNumber } from "../../libs/utils";
|
||||
import { useI18n } from "../../hooks/I18n";
|
||||
import { apiTranslate } from "../../apis";
|
||||
import { useAlert } from "../../hooks/Alert";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import {
|
||||
UI_LANGS,
|
||||
URL_KISS_PROXY,
|
||||
TRANS_NEWLINE_LENGTH,
|
||||
CACHE_NAME,
|
||||
OPT_TRANS_GOOGLE,
|
||||
OPT_TRANS_DEEPL,
|
||||
OPT_TRANS_OPENAI,
|
||||
} from "../../config";
|
||||
import { useState } from "react";
|
||||
|
||||
function TestLink({ translator, setting }) {
|
||||
const i18n = useI18n();
|
||||
const alert = useAlert();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handleApiTest = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const [text] = await apiTranslate({
|
||||
translator,
|
||||
q: "hello world",
|
||||
fromLang: "en",
|
||||
toLang: "zh-CN",
|
||||
setting,
|
||||
});
|
||||
if (!text) {
|
||||
throw new Error("empty reault");
|
||||
}
|
||||
alert.success(i18n("test_success"));
|
||||
} catch (err) {
|
||||
alert.error(`${i18n("test_failed")}: ${err.message}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <CircularProgress sx={{ marginLeft: "2em" }} size={12} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link sx={{ marginLeft: "1em" }} component="button" onClick={handleApiTest}>
|
||||
{i18n("click_test")}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
import { UI_LANGS, TRANS_NEWLINE_LENGTH, CACHE_NAME } from "../../config";
|
||||
|
||||
export default function Settings() {
|
||||
const i18n = useI18n();
|
||||
@@ -102,17 +55,10 @@ export default function Settings() {
|
||||
|
||||
const {
|
||||
uiLang,
|
||||
googleUrl,
|
||||
fetchLimit,
|
||||
fetchInterval,
|
||||
minLength,
|
||||
maxLength,
|
||||
openaiUrl,
|
||||
deeplUrl = "",
|
||||
deeplKey = "",
|
||||
openaiKey,
|
||||
openaiModel,
|
||||
openaiPrompt,
|
||||
clearCache,
|
||||
newlineLength = TRANS_NEWLINE_LENGTH,
|
||||
} = setting;
|
||||
@@ -198,95 +144,6 @@ export default function Settings() {
|
||||
</Link>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label={
|
||||
<>
|
||||
{i18n("google_api")}
|
||||
{googleUrl && (
|
||||
<TestLink translator={OPT_TRANS_GOOGLE} setting={setting} />
|
||||
)}
|
||||
</>
|
||||
}
|
||||
name="googleUrl"
|
||||
value={googleUrl}
|
||||
onChange={handleChange}
|
||||
helperText={
|
||||
<Link href={URL_KISS_PROXY} target="_blank">
|
||||
{i18n("about_api_proxy")}
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label={
|
||||
<>
|
||||
{i18n("deepl_api")}
|
||||
{deeplUrl && (
|
||||
<TestLink translator={OPT_TRANS_DEEPL} setting={setting} />
|
||||
)}
|
||||
</>
|
||||
}
|
||||
name="deeplUrl"
|
||||
value={deeplUrl}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("deepl_key")}
|
||||
name="deeplKey"
|
||||
value={deeplKey}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label={
|
||||
<>
|
||||
{i18n("openai_api")}
|
||||
{openaiUrl && openaiPrompt && (
|
||||
<TestLink translator={OPT_TRANS_OPENAI} setting={setting} />
|
||||
)}
|
||||
</>
|
||||
}
|
||||
name="openaiUrl"
|
||||
value={openaiUrl}
|
||||
onChange={handleChange}
|
||||
helperText={
|
||||
<Link href={URL_KISS_PROXY} target="_blank">
|
||||
{i18n("about_api_proxy")}
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
type="password"
|
||||
label={i18n("openai_key")}
|
||||
name="openaiKey"
|
||||
value={openaiKey}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("openai_model")}
|
||||
name="openaiModel"
|
||||
value={openaiModel}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("openai_prompt")}
|
||||
name="openaiPrompt"
|
||||
value={openaiPrompt}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user