Translate: Add DeepL support (#2721)
Co-authored-by: v <vendicated@riseup.net>
This commit is contained in:
parent
2382294e8b
commit
f8b01c1a31
|
@ -17,10 +17,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ChatBarButton } from "@api/ChatButtons";
|
import { ChatBarButton } from "@api/ChatButtons";
|
||||||
import { Margins } from "@utils/margins";
|
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { openModal } from "@utils/modal";
|
import { openModal } from "@utils/modal";
|
||||||
import { Alerts, Forms } from "@webpack/common";
|
import { Alerts, Forms, Tooltip, useEffect, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { TranslateModal } from "./TranslateModal";
|
import { TranslateModal } from "./TranslateModal";
|
||||||
|
@ -39,9 +38,17 @@ export function TranslateIcon({ height = 24, width = 24, className }: { height?:
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export let setShouldShowTranslateEnabledTooltip: undefined | ((show: boolean) => void);
|
||||||
|
|
||||||
export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
const { autoTranslate, showChatBarButton } = settings.use(["autoTranslate", "showChatBarButton"]);
|
const { autoTranslate, showChatBarButton } = settings.use(["autoTranslate", "showChatBarButton"]);
|
||||||
|
|
||||||
|
const [shouldShowTranslateEnabledTooltip, setter] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
setShouldShowTranslateEnabledTooltip = setter;
|
||||||
|
return () => setShouldShowTranslateEnabledTooltip = undefined;
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!isMainChat || !showChatBarButton) return null;
|
if (!isMainChat || !showChatBarButton) return null;
|
||||||
|
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
|
@ -52,21 +59,20 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
title: "Vencord Auto-Translate Enabled",
|
title: "Vencord Auto-Translate Enabled",
|
||||||
body: <>
|
body: <>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
You just enabled auto translate (by right clicking the Translate icon). Any message you send will automatically be translated before being sent.
|
You just enabled Auto Translate! Any message <b>will automatically be translated</b> before being sent.
|
||||||
</Forms.FormText>
|
|
||||||
<Forms.FormText className={Margins.top16}>
|
|
||||||
If this was an accident, disable it again, or it will change your message content before sending.
|
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</>,
|
</>,
|
||||||
cancelText: "Disable Auto-Translate",
|
confirmText: "Disable Auto-Translate",
|
||||||
confirmText: "Got it",
|
cancelText: "Got it",
|
||||||
secondaryConfirmText: "Don't show again",
|
secondaryConfirmText: "Don't show again",
|
||||||
onConfirmSecondary: () => settings.store.showAutoTranslateAlert = false,
|
onConfirmSecondary: () => settings.store.showAutoTranslateAlert = false,
|
||||||
onCancel: () => settings.store.autoTranslate = false
|
onConfirm: () => settings.store.autoTranslate = false,
|
||||||
|
// troll
|
||||||
|
confirmColor: "vc-notification-log-danger-btn",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const button = (
|
||||||
<ChatBarButton
|
<ChatBarButton
|
||||||
tooltip="Open Translate Modal"
|
tooltip="Open Translate Modal"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
|
@ -76,7 +82,7 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
<TranslateModal rootProps={props} />
|
<TranslateModal rootProps={props} />
|
||||||
));
|
));
|
||||||
}}
|
}}
|
||||||
onContextMenu={() => toggle()}
|
onContextMenu={toggle}
|
||||||
buttonProps={{
|
buttonProps={{
|
||||||
"aria-haspopup": "dialog"
|
"aria-haspopup": "dialog"
|
||||||
}}
|
}}
|
||||||
|
@ -84,4 +90,13 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
<TranslateIcon className={cl({ "auto-translate": autoTranslate, "chat-button": true })} />
|
<TranslateIcon className={cl({ "auto-translate": autoTranslate, "chat-button": true })} />
|
||||||
</ChatBarButton>
|
</ChatBarButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (shouldShowTranslateEnabledTooltip && settings.store.showAutoTranslateTooltip)
|
||||||
|
return (
|
||||||
|
<Tooltip text="Auto Translate Enabled" forceOpen>
|
||||||
|
{() => button}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
return button;
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,9 +20,8 @@ import { Margins } from "@utils/margins";
|
||||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||||
import { Forms, SearchableSelect, Switch, useMemo } from "@webpack/common";
|
import { Forms, SearchableSelect, Switch, useMemo } from "@webpack/common";
|
||||||
|
|
||||||
import { Languages } from "./languages";
|
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { cl } from "./utils";
|
import { cl, getLanguages } from "./utils";
|
||||||
|
|
||||||
const LanguageSettingKeys = ["receivedInput", "receivedOutput", "sentInput", "sentOutput"] as const;
|
const LanguageSettingKeys = ["receivedInput", "receivedOutput", "sentInput", "sentOutput"] as const;
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ function LanguageSelect({ settingsKey, includeAuto }: { settingsKey: typeof Lang
|
||||||
|
|
||||||
const options = useMemo(
|
const options = useMemo(
|
||||||
() => {
|
() => {
|
||||||
const options = Object.entries(Languages).map(([value, label]) => ({ value, label }));
|
const options = Object.entries(getLanguages()).map(([value, label]) => ({ value, label }));
|
||||||
if (!includeAuto)
|
if (!includeAuto)
|
||||||
options.shift();
|
options.shift();
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
import { Parser, useEffect, useState } from "@webpack/common";
|
import { Parser, useEffect, useState } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
import { Languages } from "./languages";
|
|
||||||
import { TranslateIcon } from "./TranslateIcon";
|
import { TranslateIcon } from "./TranslateIcon";
|
||||||
import { cl, TranslationValue } from "./utils";
|
import { cl, TranslationValue } from "./utils";
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ export function TranslationAccessory({ message }: { message: Message; }) {
|
||||||
<TranslateIcon width={16} height={16} />
|
<TranslateIcon width={16} height={16} />
|
||||||
{Parser.parse(translation.text)}
|
{Parser.parse(translation.text)}
|
||||||
{" "}
|
{" "}
|
||||||
(translated from {Languages[translation.src] ?? translation.src} - <Dismiss onDismiss={() => setTranslation(undefined)} />)
|
(translated from {translation.sourceLanguage} - <Dismiss onDismiss={() => setTranslation(undefined)} />)
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import definePlugin from "@utils/types";
|
||||||
import { ChannelStore, Menu } from "@webpack/common";
|
import { ChannelStore, Menu } from "@webpack/common";
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { TranslateChatBarIcon, TranslateIcon } from "./TranslateIcon";
|
import { setShouldShowTranslateEnabledTooltip, TranslateChatBarIcon, TranslateIcon } from "./TranslateIcon";
|
||||||
import { handleTranslate, TranslationAccessory } from "./TranslationAccessory";
|
import { handleTranslate, TranslationAccessory } from "./TranslationAccessory";
|
||||||
import { translate } from "./utils";
|
import { translate } from "./utils";
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) =>
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "Translate",
|
name: "Translate",
|
||||||
description: "Translate messages with Google Translate",
|
description: "Translate messages with Google Translate or DeepL",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven, Devs.AshtonMemer],
|
||||||
dependencies: ["MessageAccessoriesAPI", "MessagePopoverAPI", "MessageEventsAPI", "ChatInputButtonAPI"],
|
dependencies: ["MessageAccessoriesAPI", "MessagePopoverAPI", "MessageEventsAPI", "ChatInputButtonAPI"],
|
||||||
settings,
|
settings,
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
|
@ -83,11 +83,18 @@ export default definePlugin({
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let tooltipTimeout: any;
|
||||||
this.preSend = addPreSendListener(async (_, message) => {
|
this.preSend = addPreSendListener(async (_, message) => {
|
||||||
if (!settings.store.autoTranslate) return;
|
if (!settings.store.autoTranslate) return;
|
||||||
if (!message.content) return;
|
if (!message.content) return;
|
||||||
|
|
||||||
message.content = (await translate("sent", message.content)).text;
|
setShouldShowTranslateEnabledTooltip?.(true);
|
||||||
|
clearTimeout(tooltipTimeout);
|
||||||
|
tooltipTimeout = setTimeout(() => setShouldShowTranslateEnabledTooltip?.(false), 2000);
|
||||||
|
|
||||||
|
const trans = await translate("sent", message.content);
|
||||||
|
message.content = trans.text;
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,10 @@ copy(Object.fromEntries(
|
||||||
))
|
))
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type Language = keyof typeof Languages;
|
export type GoogleLanguage = keyof typeof GoogleLanguages;
|
||||||
|
export type DeeplLanguage = keyof typeof DeeplLanguages;
|
||||||
|
|
||||||
export const Languages = {
|
export const GoogleLanguages = {
|
||||||
"auto": "Detect language",
|
"auto": "Detect language",
|
||||||
"af": "Afrikaans",
|
"af": "Afrikaans",
|
||||||
"sq": "Albanian",
|
"sq": "Albanian",
|
||||||
|
@ -169,3 +170,57 @@ export const Languages = {
|
||||||
"yo": "Yoruba",
|
"yo": "Yoruba",
|
||||||
"zu": "Zulu"
|
"zu": "Zulu"
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const DeeplLanguages = {
|
||||||
|
"": "Detect language",
|
||||||
|
"ar": "Arabic",
|
||||||
|
"bg": "Bulgarian",
|
||||||
|
"zh-hans": "Chinese (Simplified)",
|
||||||
|
"zh-hant": "Chinese (Traditional)",
|
||||||
|
"cs": "Czech",
|
||||||
|
"da": "Danish",
|
||||||
|
"nl": "Dutch",
|
||||||
|
"en-us": "English (American)",
|
||||||
|
"en-gb": "English (British)",
|
||||||
|
"et": "Estonian",
|
||||||
|
"fi": "Finnish",
|
||||||
|
"fr": "French",
|
||||||
|
"de": "German",
|
||||||
|
"el": "Greek",
|
||||||
|
"hu": "Hungarian",
|
||||||
|
"id": "Indonesian",
|
||||||
|
"it": "Italian",
|
||||||
|
"ja": "Japanese",
|
||||||
|
"ko": "Korean",
|
||||||
|
"lv": "Latvian",
|
||||||
|
"lt": "Lithuanian",
|
||||||
|
"nb": "Norwegian",
|
||||||
|
"pl": "Polish",
|
||||||
|
"pt-br": "Portuguese (Brazilian)",
|
||||||
|
"pt-pt": "Portuguese (European)",
|
||||||
|
"ro": "Romanian",
|
||||||
|
"ru": "Russian",
|
||||||
|
"sk": "Slovak",
|
||||||
|
"sl": "Slovenian",
|
||||||
|
"es": "Spanish",
|
||||||
|
"sv": "Swedish",
|
||||||
|
"tr": "Turkish",
|
||||||
|
"uk": "Ukrainian"
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export function deeplLanguageToGoogleLanguage(language: string) {
|
||||||
|
switch (language) {
|
||||||
|
case "": return "auto";
|
||||||
|
case "nb": return "no";
|
||||||
|
case "zh-hans": return "zh-CN";
|
||||||
|
case "zh-hant": return "zh-TW";
|
||||||
|
case "en-us":
|
||||||
|
case "en-gb":
|
||||||
|
return "en";
|
||||||
|
case "pt-br":
|
||||||
|
case "pt-pt":
|
||||||
|
return "pt";
|
||||||
|
default:
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
29
src/plugins/translate/native.ts
Normal file
29
src/plugins/translate/native.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IpcMainInvokeEvent } from "electron";
|
||||||
|
|
||||||
|
export async function makeDeeplTranslateRequest(_: IpcMainInvokeEvent, pro: boolean, apiKey: string, payload: string) {
|
||||||
|
const url = pro
|
||||||
|
? "https://api.deepl.com/v2/translate"
|
||||||
|
: "https://api-free.deepl.com/v2/translate";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `DeepL-Auth-Key ${apiKey}`
|
||||||
|
},
|
||||||
|
body: payload
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.text();
|
||||||
|
return { status: res.status, data };
|
||||||
|
} catch (e) {
|
||||||
|
return { status: -1, data: String(e) };
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,38 +22,76 @@ import { OptionType } from "@utils/types";
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
receivedInput: {
|
receivedInput: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Input language for received messages",
|
description: "Language that received messages should be translated from",
|
||||||
default: "auto",
|
default: "auto",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
receivedOutput: {
|
receivedOutput: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Output language for received messages",
|
description: "Language that received messages should be translated to",
|
||||||
default: "en",
|
default: "en",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
sentInput: {
|
sentInput: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Input language for sent messages",
|
description: "Language that your own messages should be translated from",
|
||||||
default: "auto",
|
default: "auto",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
sentOutput: {
|
sentOutput: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Output language for sent messages",
|
description: "Language that your own messages should be translated to",
|
||||||
default: "en",
|
default: "en",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showChatBarButton: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show translate button in chat bar",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
service: {
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
description: IS_WEB ? "Translation service (Not supported on Web!)" : "Translation service",
|
||||||
|
disabled: () => IS_WEB,
|
||||||
|
options: [
|
||||||
|
{ label: "Google Translate", value: "google", default: true },
|
||||||
|
{ label: "DeepL Free", value: "deepl" },
|
||||||
|
{ label: "DeepL Pro", value: "deepl-pro" }
|
||||||
|
] as const,
|
||||||
|
onChange: resetLanguageDefaults
|
||||||
|
},
|
||||||
|
deeplApiKey: {
|
||||||
|
type: OptionType.STRING,
|
||||||
|
description: "DeepL API key",
|
||||||
|
default: "",
|
||||||
|
placeholder: "Get your API key from https://deepl.com/your-account",
|
||||||
|
disabled: () => IS_WEB
|
||||||
|
},
|
||||||
autoTranslate: {
|
autoTranslate: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Automatically translate your messages before sending. You can also shift/right click the translate button to toggle this",
|
description: "Automatically translate your messages before sending. You can also shift/right click the translate button to toggle this",
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
showChatBarButton: {
|
showAutoTranslateTooltip: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Show translate button in chat bar",
|
description: "Show a tooltip on the ChatBar button whenever a message is automatically translated",
|
||||||
default: true
|
default: true
|
||||||
}
|
},
|
||||||
}).withPrivateSettings<{
|
}).withPrivateSettings<{
|
||||||
showAutoTranslateAlert: boolean;
|
showAutoTranslateAlert: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
export function resetLanguageDefaults() {
|
||||||
|
if (IS_WEB || settings.store.service === "google") {
|
||||||
|
settings.store.receivedInput = "auto";
|
||||||
|
settings.store.receivedOutput = "en";
|
||||||
|
settings.store.sentInput = "auto";
|
||||||
|
settings.store.sentOutput = "en";
|
||||||
|
} else {
|
||||||
|
settings.store.receivedInput = "";
|
||||||
|
settings.store.receivedOutput = "en-us";
|
||||||
|
settings.store.sentInput = "";
|
||||||
|
settings.store.sentOutput = "en-us";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,12 +17,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { onlyOnce } from "@utils/onlyOnce";
|
||||||
|
import { PluginNative } from "@utils/types";
|
||||||
|
import { showToast, Toasts } from "@webpack/common";
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { DeeplLanguages, deeplLanguageToGoogleLanguage, GoogleLanguages } from "./languages";
|
||||||
|
import { resetLanguageDefaults, settings } from "./settings";
|
||||||
|
|
||||||
export const cl = classNameFactory("vc-trans-");
|
export const cl = classNameFactory("vc-trans-");
|
||||||
|
|
||||||
interface TranslationData {
|
const Native = VencordNative.pluginHelpers.Translate as PluginNative<typeof import("./native")>;
|
||||||
|
|
||||||
|
interface GoogleData {
|
||||||
src: string;
|
src: string;
|
||||||
sentences: {
|
sentences: {
|
||||||
// 🏳️⚧️
|
// 🏳️⚧️
|
||||||
|
@ -30,15 +36,47 @@ interface TranslationData {
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeeplData {
|
||||||
|
translations: {
|
||||||
|
detected_source_language: string;
|
||||||
|
text: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface TranslationValue {
|
export interface TranslationValue {
|
||||||
src: string;
|
sourceLanguage: string;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function translate(kind: "received" | "sent", text: string): Promise<TranslationValue> {
|
export const getLanguages = () => IS_WEB || settings.store.service === "google"
|
||||||
const sourceLang = settings.store[kind + "Input"];
|
? GoogleLanguages
|
||||||
const targetLang = settings.store[kind + "Output"];
|
: DeeplLanguages;
|
||||||
|
|
||||||
|
export async function translate(kind: "received" | "sent", text: string): Promise<TranslationValue> {
|
||||||
|
const translate = IS_WEB || settings.store.service === "google"
|
||||||
|
? googleTranslate
|
||||||
|
: deeplTranslate;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await translate(
|
||||||
|
text,
|
||||||
|
settings.store[`${kind}Input`],
|
||||||
|
settings.store[`${kind}Output`]
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
const userMessage = typeof e === "string"
|
||||||
|
? e
|
||||||
|
: "Something went wrong. If this issue persists, please check the console or ask for help in the support server.";
|
||||||
|
|
||||||
|
showToast(userMessage, Toasts.Type.FAILURE);
|
||||||
|
|
||||||
|
throw e instanceof Error
|
||||||
|
? e
|
||||||
|
: new Error(userMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function googleTranslate(text: string, sourceLang: string, targetLang: string): Promise<TranslationValue> {
|
||||||
const url = "https://translate.googleapis.com/translate_a/single?" + new URLSearchParams({
|
const url = "https://translate.googleapis.com/translate_a/single?" + new URLSearchParams({
|
||||||
// see https://stackoverflow.com/a/29537590 for more params
|
// see https://stackoverflow.com/a/29537590 for more params
|
||||||
// holy shidd nvidia
|
// holy shidd nvidia
|
||||||
|
@ -63,13 +101,69 @@ export async function translate(kind: "received" | "sent", text: string): Promis
|
||||||
+ `\n${res.status} ${res.statusText}`
|
+ `\n${res.status} ${res.statusText}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const { src, sentences }: TranslationData = await res.json();
|
const { src, sentences }: GoogleData = await res.json();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
src,
|
sourceLanguage: GoogleLanguages[src] ?? src,
|
||||||
text: sentences.
|
text: sentences.
|
||||||
map(s => s?.trans).
|
map(s => s?.trans).
|
||||||
filter(Boolean).
|
filter(Boolean).
|
||||||
join("")
|
join("")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fallbackToGoogle(text: string, sourceLang: string, targetLang: string): Promise<TranslationValue> {
|
||||||
|
return googleTranslate(
|
||||||
|
text,
|
||||||
|
deeplLanguageToGoogleLanguage(sourceLang),
|
||||||
|
deeplLanguageToGoogleLanguage(targetLang)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const showDeeplApiQuotaToast = onlyOnce(
|
||||||
|
() => showToast("Deepl API quota exceeded. Falling back to Google Translate", Toasts.Type.FAILURE)
|
||||||
|
);
|
||||||
|
|
||||||
|
async function deeplTranslate(text: string, sourceLang: string, targetLang: string): Promise<TranslationValue> {
|
||||||
|
if (!settings.store.deeplApiKey) {
|
||||||
|
showToast("DeepL API key is not set. Resetting to Google", Toasts.Type.FAILURE);
|
||||||
|
|
||||||
|
settings.store.service = "google";
|
||||||
|
resetLanguageDefaults();
|
||||||
|
|
||||||
|
return fallbackToGoogle(text, sourceLang, targetLang);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORS jumpscare
|
||||||
|
const { status, data } = await Native.makeDeeplTranslateRequest(
|
||||||
|
settings.store.service === "deepl-pro",
|
||||||
|
settings.store.deeplApiKey,
|
||||||
|
JSON.stringify({
|
||||||
|
text: [text],
|
||||||
|
target_lang: targetLang,
|
||||||
|
source_lang: sourceLang.split("-")[0]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 200:
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
throw "Failed to connect to DeepL API: " + data;
|
||||||
|
case 403:
|
||||||
|
throw "Invalid DeepL API key or version";
|
||||||
|
case 456:
|
||||||
|
showDeeplApiQuotaToast();
|
||||||
|
return fallbackToGoogle(text, sourceLang, targetLang);
|
||||||
|
default:
|
||||||
|
throw new Error(`Failed to translate "${text}" (${sourceLang} -> ${targetLang})\n${status} ${data}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { translations }: DeeplData = JSON.parse(data);
|
||||||
|
const src = translations[0].detected_source_language;
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceLanguage: DeeplLanguages[src] ?? src,
|
||||||
|
text: translations[0].text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -538,6 +538,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "Joona",
|
name: "Joona",
|
||||||
id: 297410829589020673n
|
id: 297410829589020673n
|
||||||
},
|
},
|
||||||
|
AshtonMemer: {
|
||||||
|
name: "AshtonMemer",
|
||||||
|
id: 373657230530052099n
|
||||||
|
},
|
||||||
surgedevs: {
|
surgedevs: {
|
||||||
name: "Chloe",
|
name: "Chloe",
|
||||||
id: 1084592643784331324n
|
id: 1084592643784331324n
|
||||||
|
|
Loading…
Reference in a new issue