customize api
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
|||||||
OPT_TRANS_DEEPL,
|
OPT_TRANS_DEEPL,
|
||||||
OPT_TRANS_OPENAI,
|
OPT_TRANS_OPENAI,
|
||||||
OPT_TRANS_CUSTOMIZE,
|
OPT_TRANS_CUSTOMIZE,
|
||||||
URL_MICROSOFT_TRANS,
|
|
||||||
OPT_LANGS_SPECIAL,
|
OPT_LANGS_SPECIAL,
|
||||||
PROMPT_PLACE_FROM,
|
PROMPT_PLACE_FROM,
|
||||||
PROMPT_PLACE_TO,
|
PROMPT_PLACE_TO,
|
||||||
@@ -49,8 +48,13 @@ export const apiFetchRules = (url, isBg = false) =>
|
|||||||
* @param {*} from
|
* @param {*} from
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const apiGoogleTranslate = async (translator, text, to, from, setting) => {
|
const apiGoogleTranslate = async (
|
||||||
const { url, key } = setting;
|
translator,
|
||||||
|
text,
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
{ url, key, useCache = true }
|
||||||
|
) => {
|
||||||
const params = {
|
const params = {
|
||||||
client: "gtx",
|
client: "gtx",
|
||||||
dt: "t",
|
dt: "t",
|
||||||
@@ -61,15 +65,19 @@ const apiGoogleTranslate = async (translator, text, to, from, setting) => {
|
|||||||
q: text,
|
q: text,
|
||||||
};
|
};
|
||||||
const input = `${url}?${queryString.stringify(params)}`;
|
const input = `${url}?${queryString.stringify(params)}`;
|
||||||
return fetchPolyfill(input, {
|
const res = await fetchPolyfill(input, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-type": "application/json",
|
"Content-type": "application/json",
|
||||||
},
|
},
|
||||||
useCache: true,
|
useCache,
|
||||||
usePool: true,
|
usePool: true,
|
||||||
translator,
|
translator,
|
||||||
token: key,
|
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
|
* @param {*} from
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const apiMicrosoftTranslate = (translator, text, to, from) => {
|
const apiMicrosoftTranslate = async (
|
||||||
|
translator,
|
||||||
|
text,
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
{ url, useCache = true }
|
||||||
|
) => {
|
||||||
const params = {
|
const params = {
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
"api-version": "3.0",
|
"api-version": "3.0",
|
||||||
};
|
};
|
||||||
const input = `${URL_MICROSOFT_TRANS}?${queryString.stringify(params)}`;
|
const input = `${url}?${queryString.stringify(params)}`;
|
||||||
return fetchPolyfill(input, {
|
const res = await fetchPolyfill(input, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-type": "application/json",
|
"Content-type": "application/json",
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify([{ Text: text }]),
|
body: JSON.stringify([{ Text: text }]),
|
||||||
useCache: true,
|
useCache,
|
||||||
usePool: true,
|
usePool: true,
|
||||||
translator,
|
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
|
* @param {*} from
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const apiDeepLTranslate = (translator, text, to, from, setting) => {
|
const apiDeepLTranslate = async (
|
||||||
const { url, key } = setting;
|
translator,
|
||||||
|
text,
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
{ url, key, useCache = true }
|
||||||
|
) => {
|
||||||
const data = {
|
const data = {
|
||||||
text: [text],
|
text: [text],
|
||||||
target_lang: to,
|
target_lang: to,
|
||||||
@@ -115,17 +138,21 @@ const apiDeepLTranslate = (translator, text, to, from, setting) => {
|
|||||||
if (from) {
|
if (from) {
|
||||||
data.source_lang = from;
|
data.source_lang = from;
|
||||||
}
|
}
|
||||||
return fetchPolyfill(url, {
|
const res = await fetchPolyfill(url, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-type": "application/json",
|
"Content-type": "application/json",
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
useCache: true,
|
useCache,
|
||||||
usePool: true,
|
usePool: true,
|
||||||
translator,
|
translator,
|
||||||
token: key,
|
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
|
* @param {*} from
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const apiOpenaiTranslate = async (translator, text, to, from, setting) => {
|
const apiOpenaiTranslate = async (
|
||||||
let { url, key, model, prompt } = setting;
|
translator,
|
||||||
|
text,
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
{ url, key, model, prompt, useCache = true }
|
||||||
|
) => {
|
||||||
prompt = prompt
|
prompt = prompt
|
||||||
.replaceAll(PROMPT_PLACE_FROM, from)
|
.replaceAll(PROMPT_PLACE_FROM, from)
|
||||||
.replaceAll(PROMPT_PLACE_TO, to);
|
.replaceAll(PROMPT_PLACE_TO, to);
|
||||||
return fetchPolyfill(url, {
|
const res = await fetchPolyfill(url, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-type": "application/json",
|
"Content-type": "application/json",
|
||||||
},
|
},
|
||||||
@@ -160,11 +192,17 @@ const apiOpenaiTranslate = async (translator, text, to, from, setting) => {
|
|||||||
temperature: 0,
|
temperature: 0,
|
||||||
max_tokens: 256,
|
max_tokens: 256,
|
||||||
}),
|
}),
|
||||||
useCache: true,
|
useCache,
|
||||||
usePool: true,
|
usePool: true,
|
||||||
translator,
|
translator,
|
||||||
token: key,
|
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
|
* @param {*} from
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const apiCustomizeTranslate = async (translator, text, to, from, setting) => {
|
const apiCustomTranslate = async (
|
||||||
let { url, key, headers } = setting;
|
translator,
|
||||||
return fetchPolyfill(url, {
|
text,
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
{ url, key, useCache = true }
|
||||||
|
) => {
|
||||||
|
const res = await fetchPolyfill(url, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-type": "application/json",
|
"Content-type": "application/json",
|
||||||
...JSON.parse(headers),
|
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -187,11 +229,15 @@ const apiCustomizeTranslate = async (translator, text, to, from, setting) => {
|
|||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
}),
|
}),
|
||||||
useCache: true,
|
useCache,
|
||||||
usePool: true,
|
usePool: true,
|
||||||
translator,
|
translator,
|
||||||
token: key,
|
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
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const apiTranslate = async ({
|
export const apiTranslate = ({
|
||||||
translator,
|
translator,
|
||||||
q,
|
text,
|
||||||
fromLang,
|
fromLang,
|
||||||
toLang,
|
toLang,
|
||||||
setting,
|
apiSetting,
|
||||||
}) => {
|
}) => {
|
||||||
let trText = "";
|
const from = OPT_LANGS_SPECIAL[translator]?.get(fromLang) ?? fromLang;
|
||||||
let isSame = false;
|
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;
|
switch (translator) {
|
||||||
let to = OPT_LANGS_SPECIAL?.[translator]?.get(toLang) ?? toLang;
|
case OPT_TRANS_GOOGLE:
|
||||||
|
return callApi(apiGoogleTranslate);
|
||||||
if (translator === OPT_TRANS_GOOGLE) {
|
case OPT_TRANS_MICROSOFT:
|
||||||
const res = await apiGoogleTranslate(translator, q, to, from, setting);
|
return callApi(apiMicrosoftTranslate);
|
||||||
trText = res.sentences.map((item) => item.trans).join(" ");
|
case OPT_TRANS_DEEPL:
|
||||||
isSame = to === res.src;
|
return callApi(apiDeepLTranslate);
|
||||||
} else if (translator === OPT_TRANS_MICROSOFT) {
|
case OPT_TRANS_OPENAI:
|
||||||
const res = await apiMicrosoftTranslate(translator, q, to, from);
|
return callApi(apiOpenaiTranslate);
|
||||||
trText = res[0].translations[0].text;
|
case OPT_TRANS_CUSTOMIZE:
|
||||||
isSame = to === res[0].detectedLanguage?.language;
|
return callApi(apiCustomTranslate);
|
||||||
} else if (translator === OPT_TRANS_DEEPL) {
|
default:
|
||||||
const res = await apiDeepLTranslate(translator, q, to, from, setting);
|
return ["", false];
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [trText, isSame];
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,99 @@ export const UI_LANGS = [
|
|||||||
["zh", "中文"],
|
["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 = {
|
export const I18N = {
|
||||||
app_name: {
|
app_name: {
|
||||||
zh: `简约翻译`,
|
zh: `简约翻译`,
|
||||||
@@ -12,6 +105,10 @@ export const I18N = {
|
|||||||
zh: `翻译`,
|
zh: `翻译`,
|
||||||
en: `Translate`,
|
en: `Translate`,
|
||||||
},
|
},
|
||||||
|
custom_api_help: {
|
||||||
|
zh: customApiHelpZH,
|
||||||
|
en: customApiHelpEN,
|
||||||
|
},
|
||||||
translate_alt: {
|
translate_alt: {
|
||||||
zh: `翻译 (Alt+Q)`,
|
zh: `翻译 (Alt+Q)`,
|
||||||
en: `Translate (Alt+Q)`,
|
en: `Translate (Alt+Q)`,
|
||||||
|
|||||||
@@ -64,14 +64,12 @@ export const URL_KISS_RULES_NEW_ISSUE =
|
|||||||
export const URL_RAW_PREFIX =
|
export const URL_RAW_PREFIX =
|
||||||
"https://raw.githubusercontent.com/fishjar/kiss-translator/master";
|
"https://raw.githubusercontent.com/fishjar/kiss-translator/master";
|
||||||
export const URL_MICROSOFT_AUTH = "https://edge.microsoft.com/translate/auth";
|
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_GOOGLE = "Google";
|
||||||
export const OPT_TRANS_MICROSOFT = "Microsoft";
|
export const OPT_TRANS_MICROSOFT = "Microsoft";
|
||||||
export const OPT_TRANS_DEEPL = "DeepL";
|
export const OPT_TRANS_DEEPL = "DeepL";
|
||||||
export const OPT_TRANS_OPENAI = "OpenAI";
|
export const OPT_TRANS_OPENAI = "OpenAI";
|
||||||
export const OPT_TRANS_CUSTOMIZE = "Customize";
|
export const OPT_TRANS_CUSTOMIZE = "Custom";
|
||||||
export const OPT_TRANS_ALL = [
|
export const OPT_TRANS_ALL = [
|
||||||
OPT_TRANS_GOOGLE,
|
OPT_TRANS_GOOGLE,
|
||||||
OPT_TRANS_MICROSOFT,
|
OPT_TRANS_MICROSOFT,
|
||||||
@@ -135,6 +133,7 @@ export const OPT_LANGS_SPECIAL = {
|
|||||||
[OPT_TRANS_OPENAI]: new Map(
|
[OPT_TRANS_OPENAI]: new Map(
|
||||||
OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]])
|
OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]])
|
||||||
),
|
),
|
||||||
|
[OPT_TRANS_CUSTOMIZE]: new Map([["auto", ""]]),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OPT_STYLE_NONE = "style_none"; // 无
|
export const OPT_STYLE_NONE = "style_none"; // 无
|
||||||
@@ -204,6 +203,7 @@ export const DEFAULT_SUBRULES_LIST = [
|
|||||||
export const DEFAULT_TRANS_APIS = {
|
export const DEFAULT_TRANS_APIS = {
|
||||||
[OPT_TRANS_GOOGLE]: {
|
[OPT_TRANS_GOOGLE]: {
|
||||||
url: "https://translate.googleapis.com/translate_a/single",
|
url: "https://translate.googleapis.com/translate_a/single",
|
||||||
|
key: "",
|
||||||
},
|
},
|
||||||
[OPT_TRANS_MICROSOFT]: {
|
[OPT_TRANS_MICROSOFT]: {
|
||||||
url: "https://api-edge.cognitive.microsofttranslator.com/translate",
|
url: "https://api-edge.cognitive.microsofttranslator.com/translate",
|
||||||
@@ -222,7 +222,6 @@ export const DEFAULT_TRANS_APIS = {
|
|||||||
[OPT_TRANS_CUSTOMIZE]: {
|
[OPT_TRANS_CUSTOMIZE]: {
|
||||||
url: "",
|
url: "",
|
||||||
key: "",
|
key: "",
|
||||||
headers: "",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -243,13 +242,6 @@ export const DEFAULT_SETTING = {
|
|||||||
subrulesList: DEFAULT_SUBRULES_LIST, // 订阅列表
|
subrulesList: DEFAULT_SUBRULES_LIST, // 订阅列表
|
||||||
owSubrule: DEFAULT_OW_RULE, // 覆写订阅规则
|
owSubrule: DEFAULT_OW_RULE, // 覆写订阅规则
|
||||||
transApis: DEFAULT_TRANS_APIS, // 翻译接口
|
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];
|
export const DEFAULT_RULES = [GLOBLA_RULE];
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import { useSetting } from "./Setting";
|
|||||||
export function useApi(translator) {
|
export function useApi(translator) {
|
||||||
const { setting, updateSetting } = useSetting();
|
const { setting, updateSetting } = useSetting();
|
||||||
const apis = setting?.transApis || DEFAULT_TRANS_APIS;
|
const apis = setting?.transApis || DEFAULT_TRANS_APIS;
|
||||||
const api = apis[translator] || {};
|
|
||||||
console.log("apis", translator, apis);
|
|
||||||
|
|
||||||
const updateApi = useCallback(
|
const updateApi = useCallback(
|
||||||
async (obj) => {
|
async (obj) => {
|
||||||
@@ -22,5 +20,5 @@ export function useApi(translator) {
|
|||||||
await updateSetting({ transApis });
|
await updateSetting({ transApis });
|
||||||
}, [translator, apis, updateSetting]);
|
}, [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";
|
import { debounce } from "../libs/utils";
|
||||||
|
|
||||||
const SettingContext = createContext({
|
const SettingContext = createContext({
|
||||||
setting: null,
|
setting: {},
|
||||||
updateSetting: async () => {},
|
updateSetting: async () => {},
|
||||||
reloadSetting: async () => {},
|
reloadSetting: async () => {},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useEffect } from "react";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { tryDetectLang } from "../libs";
|
import { tryDetectLang } from "../libs";
|
||||||
import { apiTranslate } from "../apis";
|
import { apiTranslate } from "../apis";
|
||||||
|
import { DEFAULT_TRANS_APIS } from "../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 翻译hook
|
* 翻译hook
|
||||||
@@ -28,10 +29,10 @@ export function useTranslate(q, rule, setting) {
|
|||||||
} else {
|
} else {
|
||||||
const [trText, isSame] = await apiTranslate({
|
const [trText, isSame] = await apiTranslate({
|
||||||
translator,
|
translator,
|
||||||
q,
|
text: q,
|
||||||
fromLang,
|
fromLang,
|
||||||
toLang,
|
toLang,
|
||||||
setting: setting[translator],
|
apiSetting: (setting.transApis || DEFAULT_TRANS_APIS)[translator],
|
||||||
});
|
});
|
||||||
setText(trText);
|
setText(trText);
|
||||||
setSamelang(isSame);
|
setSamelang(isSame);
|
||||||
|
|||||||
@@ -177,6 +177,10 @@ export const fetchData = async (
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const fetchPolyfill = async (input, { isBg = false, ...opts } = {}) => {
|
export const fetchPolyfill = async (input, { isBg = false, ...opts } = {}) => {
|
||||||
|
if (!input.trim()) {
|
||||||
|
throw new Error("URL is empty");
|
||||||
|
}
|
||||||
|
|
||||||
// 插件
|
// 插件
|
||||||
if (isExt && !isBg) {
|
if (isExt && !isBg) {
|
||||||
const res = await sendBgMsg(MSG_FETCH, { input, opts });
|
const res = await sendBgMsg(MSG_FETCH, { input, opts });
|
||||||
|
|||||||
@@ -211,6 +211,10 @@ export class Translator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_register = () => {
|
_register = () => {
|
||||||
|
if (this._rule.fromLang === this._rule.toLang) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 搜索节点
|
// 搜索节点
|
||||||
this._queryNodes();
|
this._queryNodes();
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
OPT_TRANS_MICROSOFT,
|
OPT_TRANS_MICROSOFT,
|
||||||
OPT_TRANS_OPENAI,
|
OPT_TRANS_OPENAI,
|
||||||
OPT_TRANS_CUSTOMIZE,
|
OPT_TRANS_CUSTOMIZE,
|
||||||
|
URL_KISS_PROXY,
|
||||||
} from "../../config";
|
} from "../../config";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useI18n } from "../../hooks/I18n";
|
import { useI18n } from "../../hooks/I18n";
|
||||||
@@ -15,9 +16,12 @@ import Accordion from "@mui/material/Accordion";
|
|||||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
|
import Alert from "@mui/material/Alert";
|
||||||
import { useAlert } from "../../hooks/Alert";
|
import { useAlert } from "../../hooks/Alert";
|
||||||
import { useApi } from "../../hooks/Api";
|
import { useApi } from "../../hooks/Api";
|
||||||
import { apiTranslate } from "../../apis";
|
import { apiTranslate } from "../../apis";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Link from "@mui/material/Link";
|
||||||
|
|
||||||
function TestButton({ translator, api }) {
|
function TestButton({ translator, api }) {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
@@ -28,10 +32,10 @@ function TestButton({ translator, api }) {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
const [text] = await apiTranslate({
|
const [text] = await apiTranslate({
|
||||||
translator,
|
translator,
|
||||||
q: "hello world",
|
text: "hello world",
|
||||||
fromLang: "auto",
|
fromLang: "en",
|
||||||
toLang: "zh-CN",
|
toLang: "zh-CN",
|
||||||
setting: api,
|
apiSetting: { ...api, useCache: false },
|
||||||
});
|
});
|
||||||
if (!text) {
|
if (!text) {
|
||||||
throw new Error("empty reault");
|
throw new Error("empty reault");
|
||||||
@@ -45,7 +49,7 @@ function TestButton({ translator, api }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <CircularProgress sx={{ marginLeft: "2em" }} size={16} />;
|
return <CircularProgress size={16} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -58,7 +62,7 @@ function TestButton({ translator, api }) {
|
|||||||
function ApiFields({ translator }) {
|
function ApiFields({ translator }) {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const { api, updateApi, resetApi } = useApi(translator);
|
const { api, updateApi, resetApi } = useApi(translator);
|
||||||
const { url = "", key = "", model = "", prompt = "", headers = "" } = api;
|
const { url = "", key = "", model = "", prompt = "" } = api;
|
||||||
|
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
const { name, value } = e.target;
|
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}>
|
<Stack direction="row" spacing={2}>
|
||||||
<TestButton translator={translator} api={api} />
|
<TestButton translator={translator} api={api} />
|
||||||
@@ -131,6 +125,10 @@ function ApiFields({ translator }) {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
{translator === OPT_TRANS_CUSTOMIZE && (
|
||||||
|
<pre>{i18n("custom_api_help")}</pre>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -155,7 +153,22 @@ function ApiAccordion({ translator }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Apis() {
|
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} />
|
<ApiAccordion key={translator} translator={translator} />
|
||||||
));
|
))}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,55 +10,8 @@ import FormHelperText from "@mui/material/FormHelperText";
|
|||||||
import { useSetting } from "../../hooks/Setting";
|
import { useSetting } from "../../hooks/Setting";
|
||||||
import { limitNumber } from "../../libs/utils";
|
import { limitNumber } from "../../libs/utils";
|
||||||
import { useI18n } from "../../hooks/I18n";
|
import { useI18n } from "../../hooks/I18n";
|
||||||
import { apiTranslate } from "../../apis";
|
|
||||||
import { useAlert } from "../../hooks/Alert";
|
import { useAlert } from "../../hooks/Alert";
|
||||||
import CircularProgress from "@mui/material/CircularProgress";
|
import { UI_LANGS, TRANS_NEWLINE_LENGTH, CACHE_NAME } from "../../config";
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
@@ -102,17 +55,10 @@ export default function Settings() {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
uiLang,
|
uiLang,
|
||||||
googleUrl,
|
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
fetchInterval,
|
fetchInterval,
|
||||||
minLength,
|
minLength,
|
||||||
maxLength,
|
maxLength,
|
||||||
openaiUrl,
|
|
||||||
deeplUrl = "",
|
|
||||||
deeplKey = "",
|
|
||||||
openaiKey,
|
|
||||||
openaiModel,
|
|
||||||
openaiPrompt,
|
|
||||||
clearCache,
|
clearCache,
|
||||||
newlineLength = TRANS_NEWLINE_LENGTH,
|
newlineLength = TRANS_NEWLINE_LENGTH,
|
||||||
} = setting;
|
} = setting;
|
||||||
@@ -198,95 +144,6 @@ export default function Settings() {
|
|||||||
</Link>
|
</Link>
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</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>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user