diff --git a/src/equicordplugins/translatePlus/index.tsx b/src/equicordplugins/translatePlus/index.tsx
index c7a73165f..3eb882d51 100644
--- a/src/equicordplugins/translatePlus/index.tsx
+++ b/src/equicordplugins/translatePlus/index.tsx
@@ -18,19 +18,16 @@
import "./style.css";
-import { addChatBarButton, removeChatBarButton } from "@api/ChatButtons";
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { addAccessory, removeAccessory } from "@api/MessageAccessories";
-import { addPreSendListener, removePreSendListener } from "@api/MessageEvents";
import { addButton, removeButton } from "@api/MessagePopover";
import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin from "@utils/types";
import { ChannelStore, Menu } from "@webpack/common";
import { settings } from "./settings";
-import { translate } from "./utils/misc";
-import { TranslateChatBarIcon, TranslateIcon } from "./utils/TranslateIcon";
-import { handleTranslate, TranslationAccessory } from "./utils/TranslationAccessory";
+import { Accessory, handleTranslate } from "./utils/accessory";
+import { Icon } from "./utils/icon";
const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) => {
if (!message.content) return;
@@ -40,61 +37,37 @@ const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) =>
group.splice(group.findIndex(c => c?.props?.id === "copy-text") + 1, 0, (
{
- const trans = await translate("received", message.content);
- handleTranslate(message.id, trans);
- }}
+ icon={Icon}
+ action={() => handleTranslate(message)}
/>
));
};
export default definePlugin({
name: "Translate+",
- description: "Translate messages with Google Translate and Toki Pona AI",
+ description: "Vencord's translate plugin but with support for artistic languages!",
+ dependencies: ["MessageAccessoriesAPI"],
authors: [Devs.Ven, EquicordDevs.Prince527],
- dependencies: ["MessageAccessoriesAPI", "MessagePopoverAPI", "MessageEventsAPI", "ChatInputButtonAPI"],
settings,
contextMenus: {
"message": messageCtxPatch
},
- // not used, just here in case some other plugin wants it or w/e
- translate,
start() {
- addAccessory("vc-translation", props => );
+ addAccessory("ec-translation", props => );
- addChatBarButton("vc-translate", TranslateChatBarIcon);
-
- addButton("vc-translate", message => {
- if (!message.content) return null;
-
- return {
- label: "Translate",
- icon: TranslateIcon,
- message,
- channel: ChannelStore.getChannel(message.channel_id),
- onClick: async () => {
- const trans = await translate("received", message.content);
- handleTranslate(message.id, trans);
- }
- };
- });
-
- this.preSend = addPreSendListener(async (_, message) => {
- if (!settings.store.autoTranslate) return;
- if (!message.content) return;
-
- message.content = (await translate("sent", message.content)).text;
- });
+ addButton("ec-translate", message => ({
+ label: "Translate",
+ icon: Icon,
+ message: message,
+ channel: ChannelStore.getChannel(message.channel_id),
+ onClick: () => handleTranslate(message),
+ }));
},
-
stop() {
- removePreSendListener(this.preSend);
- removeChatBarButton("vc-translate");
- removeButton("vc-translate");
- removeAccessory("vc-translation");
- },
+ removeButton("ec-translate");
+ removeAccessory("ec-translation");
+ }
});
diff --git a/src/equicordplugins/translatePlus/misc/languages.ts b/src/equicordplugins/translatePlus/misc/languages.ts
index 75454f593..20b0448dc 100644
--- a/src/equicordplugins/translatePlus/misc/languages.ts
+++ b/src/equicordplugins/translatePlus/misc/languages.ts
@@ -31,9 +31,9 @@ copy(Object.fromEntries(
))
*/
-export type Language = keyof typeof Languages;
+export type languages = keyof typeof languages;
-export const Languages = {
+export const languages = {
"auto": "Detect language",
"af": "Afrikaans",
"sq": "Albanian",
@@ -68,6 +68,7 @@ export const Languages = {
"ee": "Ewe",
"tl": "Filipino",
"tp": "Toki Pona",
+ "sh": "Shavian",
"fi": "Finnish",
"fr": "French",
"fy": "Frisian",
diff --git a/src/equicordplugins/translatePlus/misc/types.ts b/src/equicordplugins/translatePlus/misc/types.ts
new file mode 100644
index 000000000..dde38179e
--- /dev/null
+++ b/src/equicordplugins/translatePlus/misc/types.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { classNameFactory } from "@api/Styles";
+
+export const cl = classNameFactory("eq-trans-");
+
+export interface Translation {
+ text: string;
+ src: string;
+}
+
+export type IconProps = {
+ width?: number;
+ height?: number;
+};
diff --git a/src/equicordplugins/translatePlus/settings.ts b/src/equicordplugins/translatePlus/settings.ts
index 17e386f3c..3fd2f030b 100644
--- a/src/equicordplugins/translatePlus/settings.ts
+++ b/src/equicordplugins/translatePlus/settings.ts
@@ -1,69 +1,35 @@
/*
- * Vencord, a modification for Discord's desktop app
- * Copyright (c) 2023 Vendicated and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-*/
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
import { definePluginSettings } from "@api/Settings";
import { OptionType } from "@utils/types";
export const settings = definePluginSettings({
- receivedInput: {
+ target: {
type: OptionType.STRING,
- description: "Input language for received messages",
- default: "auto",
- hidden: true
- },
- receivedOutput: {
- type: OptionType.STRING,
- description: "Output language for received messages",
+ description: "Target language",
default: "en",
- hidden: true
+ restartNeeded: true
},
- sentInput: {
- type: OptionType.STRING,
- description: "Input language for sent messages",
- default: "auto",
- hidden: true
- },
- sentOutput: {
- type: OptionType.STRING,
- description: "Output language for sent messages",
- default: "en",
- hidden: true
- },
- autoTranslate: {
+ toki: {
type: OptionType.BOOLEAN,
- description: "Automatically translate your messages before sending. You can also shift/right click the translate button to toggle this",
- default: false
+ description: "Enable Toki Pona",
+ default: true,
+ restartNeeded: true
},
- showChatBarButton: {
+ sitelen: {
type: OptionType.BOOLEAN,
- description: "Show translate button in chat bar",
- default: true
+ description: "Enable Sitelen Pona",
+ default: true,
+ restartNeeded: true
},
- tokiPonaAPI: {
- type: OptionType.STRING,
- description: "An API to translate messages to Toki Pona",
- default: "https://aiapi.serversmp.xyz/toki"
- },
- tokiPonaAuth: {
- type: OptionType.STRING,
- description: "An API key to use with the Toki Pona API",
- default: ""
+ shavian: {
+ type: OptionType.BOOLEAN,
+ description: "Enable Shavian",
+ default: true,
+ restartNeeded: true
}
-}).withPrivateSettings<{
- showAutoTranslateAlert: boolean;
-}>();
+});
diff --git a/src/equicordplugins/translatePlus/style.css b/src/equicordplugins/translatePlus/style.css
index e9085b4ee..e81dad641 100644
--- a/src/equicordplugins/translatePlus/style.css
+++ b/src/equicordplugins/translatePlus/style.css
@@ -1,41 +1,20 @@
-.vc-trans-modal-content {
- padding: 1em;
-}
-
-.vc-trans-modal-header {
- justify-content: space-between;
- align-content: center;
-}
-
-.vc-trans-modal-header h1 {
- margin: 0;
-}
-
-.vc-trans-accessory {
+.eq-trans-accessory {
color: var(--text-muted);
margin-top: 0.5em;
font-style: italic;
font-weight: 400;
}
-.vc-trans-accessory svg {
+.eq-trans-accessory svg {
margin-right: 0.25em;
}
-.vc-trans-dismiss {
+.eq-trans-dismiss {
all: unset;
cursor: pointer;
color: var(--text-link);
}
-.vc-trans-dismiss:is(:hover, :focus) {
+.eq-trans-dismiss:is(:hover, :focus) {
text-decoration: underline;
-}
-
-.vc-trans-auto-translate {
- color: var(--green-360);
-}
-
-.vc-trans-chat-button {
- scale: 1.085;
-}
+}
\ No newline at end of file
diff --git a/src/equicordplugins/translatePlus/utils/TranslateIcon.tsx b/src/equicordplugins/translatePlus/utils/TranslateIcon.tsx
deleted file mode 100644
index e8171ccbe..000000000
--- a/src/equicordplugins/translatePlus/utils/TranslateIcon.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Vencord, a modification for Discord's desktop app
- * Copyright (c) 2023 Vendicated and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-*/
-
-import { ChatBarButton } from "@api/ChatButtons";
-import { Margins } from "@utils/margins";
-import { classes } from "@utils/misc";
-import { openModal } from "@utils/modal";
-import { Alerts, Forms } from "@webpack/common";
-
-import { settings } from "../settings";
-import { cl } from "./misc";
-import { TranslateModal } from "./TranslateModal";
-
-export function TranslateIcon({ height = 24, width = 24, className }: { height?: number; width?: number; className?: string; }) {
- return (
-
- );
-}
-
-export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
- const { autoTranslate, showChatBarButton } = settings.use(["autoTranslate", "showChatBarButton"]);
-
- if (!isMainChat || !showChatBarButton) return null;
-
- const toggle = () => {
- const newState = !autoTranslate;
- settings.store.autoTranslate = newState;
- if (newState && settings.store.showAutoTranslateAlert !== false)
- Alerts.show({
- title: "Vencord Auto-Translate Enabled",
- body: <>
-
- You just enabled auto translate (by right clicking the Translate icon). Any message you send will automatically be translated before being sent.
-
-
- If this was an accident, disable it again, or it will change your message content before sending.
-
- >,
- cancelText: "Disable Auto-Translate",
- confirmText: "Got it",
- secondaryConfirmText: "Don't show again",
- onConfirmSecondary: () => settings.store.showAutoTranslateAlert = false,
- onCancel: () => settings.store.autoTranslate = false
- });
- };
-
- return (
- {
- if (e.shiftKey) return toggle();
-
- openModal(props => (
-
- ));
- }}
- onContextMenu={() => toggle()}
- buttonProps={{
- "aria-haspopup": "dialog"
- }}
- >
-
-
- );
-};
diff --git a/src/equicordplugins/translatePlus/utils/TranslateModal.tsx b/src/equicordplugins/translatePlus/utils/TranslateModal.tsx
deleted file mode 100644
index 04cd1d7be..000000000
--- a/src/equicordplugins/translatePlus/utils/TranslateModal.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Vencord, a modification for Discord's desktop app
- * Copyright (c) 2023 Vendicated and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-*/
-
-import { Margins } from "@utils/margins";
-import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
-import { Forms, SearchableSelect, Switch, useMemo } from "@webpack/common";
-
-import { Languages } from "../misc/languages";
-import { settings } from "../settings";
-import { cl } from "./misc";
-
-const LanguageSettingKeys = ["receivedInput", "receivedOutput", "sentInput", "sentOutput"] as const;
-
-function LanguageSelect({ settingsKey, includeAuto }: { settingsKey: typeof LanguageSettingKeys[number]; includeAuto: boolean; }) {
- const currentValue = settings.use([settingsKey])[settingsKey];
-
- const options = useMemo(
- () => {
- const options = Object.entries(Languages).map(([value, label]) => ({ value, label }));
- if (!includeAuto)
- options.shift();
-
- return options;
- }, []
- );
-
- return (
-
-
- {settings.def[settingsKey].description}
-
-
- o.value === currentValue)}
- placeholder={"Select a language"}
- maxVisibleItems={5}
- closeOnSelect={true}
- onChange={v => settings.store[settingsKey] = v}
- />
-
- );
-}
-
-function AutoTranslateToggle() {
- const value = settings.use(["autoTranslate"]).autoTranslate;
-
- return (
- settings.store.autoTranslate = v}
- note={settings.def.autoTranslate.description}
- hideBorder
- >
- Auto Translate
-
- );
-}
-
-
-export function TranslateModal({ rootProps }: { rootProps: ModalProps; }) {
- return (
-
-
-
- Translate
-
-
-
-
-
- {LanguageSettingKeys.map(s => (
-
- ))}
-
-
-
-
-
-
- );
-}
diff --git a/src/equicordplugins/translatePlus/utils/TranslationAccessory.tsx b/src/equicordplugins/translatePlus/utils/TranslationAccessory.tsx
deleted file mode 100644
index 94778bc71..000000000
--- a/src/equicordplugins/translatePlus/utils/TranslationAccessory.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Vencord, a modification for Discord's desktop app
- * Copyright (c) 2023 Vendicated and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-*/
-
-import { Parser, useEffect, useState } from "@webpack/common";
-import { Message } from "discord-types/general";
-
-import { Languages } from "../misc/languages";
-import { cl, TranslationValue } from "./misc";
-import { TranslateIcon } from "./TranslateIcon";
-
-const TranslationSetters = new Map void>();
-
-export function handleTranslate(messageId: string, data: TranslationValue) {
- TranslationSetters.get(messageId)!(data);
-}
-
-function Dismiss({ onDismiss }: { onDismiss: () => void; }) {
- return (
-
- );
-}
-
-export function TranslationAccessory({ message }: { message: Message; }) {
- const [translation, setTranslation] = useState();
-
- useEffect(() => {
- // Ignore MessageLinkEmbeds messages
- if ((message as any).vencordEmbeddedBy) return;
-
- TranslationSetters.set(message.id, setTranslation);
-
- return () => void TranslationSetters.delete(message.id);
- }, []);
-
- if (!translation) return null;
-
- return (
-
-
- {Parser.parse(translation.text)}
- {" "}
- (translated from {Languages[translation.src] ?? translation.src} - setTranslation(undefined)} />)
-
- );
-}
diff --git a/src/equicordplugins/translatePlus/utils/accessory.tsx b/src/equicordplugins/translatePlus/utils/accessory.tsx
new file mode 100644
index 000000000..7d473b196
--- /dev/null
+++ b/src/equicordplugins/translatePlus/utils/accessory.tsx
@@ -0,0 +1,42 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { Parser, useEffect, useState } from "@webpack/common";
+import { Message } from "discord-types/general";
+
+import { languages } from "../misc/languages";
+import { cl, Translation } from "../misc/types";
+import { Icon } from "./icon";
+import { translate } from "./translator";
+
+const setters = new Map();
+
+export function Accessory({ message }: { message: Message; }) {
+ const [translation, setTranslation] = useState(undefined);
+
+ useEffect(() => {
+ if ((message as any).vencordEmbeddedBy) return;
+
+ setters.set(message.id, setTranslation);
+
+ return () => void setters.delete(message.id);
+ }, []);
+
+ if (!translation) return null;
+
+ return (
+
+
+ {Parser.parse(translation.text)}
+ {" "}
+ (translated from {languages[translation.src] ?? translation.src} - )
+
+ );
+}
+
+export async function handleTranslate(message: Message) {
+ setters.get(message.id)!(await translate(message.content));
+}
diff --git a/src/equicordplugins/translatePlus/utils/icon.tsx b/src/equicordplugins/translatePlus/utils/icon.tsx
new file mode 100644
index 000000000..6c644e0bb
--- /dev/null
+++ b/src/equicordplugins/translatePlus/utils/icon.tsx
@@ -0,0 +1,17 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { cl, IconProps } from "../misc/types";
+
+export function Icon({ width = 24, height = 24 }: IconProps) {
+ return (
+
+ );
+}
diff --git a/src/equicordplugins/translatePlus/utils/misc.ts b/src/equicordplugins/translatePlus/utils/misc.ts
deleted file mode 100644
index bdb984b6e..000000000
--- a/src/equicordplugins/translatePlus/utils/misc.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Vencord, a modification for Discord's desktop app
- * Copyright (c) 2023 Vendicated and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-*/
-
-import { classNameFactory } from "@api/Styles";
-
-import { settings } from "../settings";
-
-export const cl = classNameFactory("vc-trans-");
-
-interface TranslationData {
- src: string;
- sentences: {
- // 🏳️⚧️
- trans: string;
- }[];
-}
-
-export interface TranslationValue {
- src: string;
- text: string;
-}
-
-const dictonary = /\b(?:leko|weka|pan|lete|linja|lipu|suli|nimi|akesi|misikeke|selo|ike|sijelo|sona|lili|pimeja|ante|jo|loje|telo|walo|kijetesantakalu|kasi|waso|wile|utala|lukin|sina|lape|ma|pilin|jasima|la|olin|pipi|meso|lawa|pi|pakala|oko|tan|ken|jaki|unpa|esun|seme|sitelen|len|kule|soko|open|ala|tenpo|lon|sinpin|pini|kokosila|mama|musi|monsi|mewika|taso|ona|mun|kiwen|tomo|mute|mi|nena|palisa|meli|laso|wawa|ale|kipisi|kulupu|ilo|lupa|nanpa|en|mu|jelo|kili|tonsi|moku|ni|kama|pu|poki|monsuta|sin|lasina|poka|soweli|sewi|elena|epiku|moli|pona|lanpan|alasa|anu|kute|uta|luka|suno|sama|awen|namako|suwi|noka|seli|mije|sike|jan|pali|tawa|inli|nasa|mani|wan|insa|nijon|nasin|kalama|ijo|toki|anpa|kala|kepeken|ko|kon|pana|tu|supa|kin|usawi|yupekosi)\b/gm;
-
-function isTokiPona(string) {
- const words = string.split(/\s+/);
-
- const matches = string.match(dictonary) || [];
-
- const percentage = (matches.length / words.length) * 100;
-
- return percentage >= 50;
-}
-
-export async function translate(kind: "received" | "sent", text: string): Promise {
- let output;
-
- if (isTokiPona(text)) {
- const [api, auth] = [settings.store.tokiPonaAPI, settings.store.tokiPonaAuth];
-
- const translate = await (await fetch(api, {
- method: "POST",
- headers: {
- "Accept": "application/json",
- "Content-Type": "application/json",
- "Authorization": `Basic ${auth}`
- },
- body: JSON.stringify({
- text: text,
- src: "tl",
- target: "en"
- })
- })).json();
-
- output = {
- src: "tp",
- text: translate.translation[0]
- };
- } else {
- const [sourceLang, targetLang] = [settings.store[kind + "Input"], settings.store[kind + "Output"]];
-
- const translate = await (await fetch(`https://translate.googleapis.com/translate_a/single?${new URLSearchParams({ client: "gtx", sl: sourceLang, tl: targetLang, dt: "t", dj: "1", source: "input", q: text })}`)).json();
-
- output = {
- src: translate.src,
- text: translate.sentences.map(s => s.trans).filter(Boolean).join("")
- };
- }
-
- return output;
-}
diff --git a/src/equicordplugins/translatePlus/utils/translator.ts b/src/equicordplugins/translatePlus/utils/translator.ts
new file mode 100644
index 000000000..4598909e1
--- /dev/null
+++ b/src/equicordplugins/translatePlus/utils/translator.ts
@@ -0,0 +1,135 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { settings } from "../settings";
+
+function isTokiPona(text: string) {
+ const dictionary = /\b(?:leko|weka|pan|lete|linja|lipu|suli|nimi|akesi|misikeke|selo|ike|sijelo|sona|lili|pimeja|ante|jo|loje|telo|walo|kijetesantakalu|kasi|waso|wile|utala|lukin|sina|lape|ma|pilin|jasima|la|olin|pipi|meso|lawa|pi|pakala|oko|tan|ken|jaki|unpa|esun|seme|sitelen|len|kule|soko|open|ala|tenpo|lon|sinpin|pini|kokosila|mama|musi|monsi|mewika|taso|ona|mun|kiwen|tomo|mute|mi|nena|palisa|meli|laso|wawa|ale|kipisi|kulupu|ilo|lupa|nanpa|en|mu|jelo|kili|tonsi|moku|ni|kama|pu|poki|monsuta|sin|lasina|poka|soweli|sewi|elena|epiku|moli|pona|lanpan|alasa|anu|kute|uta|luka|suno|sama|awen|namako|suwi|noka|seli|mije|sike|jan|pali|tawa|inli|nasa|mani|wan|insa|nijon|nasin|kalama|ijo|toki|anpa|kala|kepeken|ko|kon|pana|tu|supa|kin|usawi|yupekosi)\b/gm;
+
+ return (text.match(dictionary) || []).length >= text.split(/\s+/).length * 0.5;
+}
+
+function isSitelen(text: string) {
+ const dictionary = /(?:||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||)/gm;
+
+ return dictionary.test(text);
+}
+
+function isShavian(text: string) {
+ const shavianRegex = /[\u{10450}-\u{1047F}]+/u;
+
+ return shavianRegex.test(text);
+}
+
+async function translateShavian(message: string) {
+ const dictionary = await (await fetch("https://forkprince.github.io/TranslatePlus/shavian.json")).json();
+
+ const punctuationMap = {
+ '"': "\"",
+ "«": "\"",
+ "»": "\"",
+ ",": ",",
+ "!": "!",
+ "?": "?",
+ ".": ".",
+ "(": "(",
+ ")": ")",
+ "/": "/",
+ ";": ";",
+ ":": ":"
+ };
+
+ let translated = "";
+ const words = message.split(/\s+/);
+
+ for (let word of words) {
+ let punctuationBefore = "", punctuationAfter = "";
+
+ if (word[0] in punctuationMap) {
+ punctuationBefore = punctuationMap[word[0]];
+ word = word.slice(1);
+ }
+
+ if (word[word.length - 1] in punctuationMap) {
+ punctuationAfter = punctuationMap[word[word.length - 1]];
+ word = word.slice(0, -1);
+ }
+
+ translated += punctuationBefore;
+
+ if (word in dictionary) translated += dictionary[word];
+ else translated += word;
+
+ translated += punctuationAfter + " ";
+ }
+
+ return translated.trim();
+}
+
+async function translateSitelen(message: string) {
+ message = Array.from(message).join(" ");
+
+ const dictionary = await (await fetch("https://forkprince.github.io/TranslatePlus/sitelen-pona.json")).json();
+
+ const sorted = Object.keys(dictionary).sort((a, b) => b.length - a.length);
+
+ const pattern = new RegExp(`(${sorted.join("|")})`, "g");
+
+ const translate = message.replace(pattern, match => dictionary[match]);
+
+ return translate;
+}
+
+async function google(target: string, text: string) {
+ const translate = await (await fetch(`https://translate.googleapis.com/translate_a/single?${new URLSearchParams({ client: "gtx", sl: "auto", tl: target, dt: "t", dj: "1", source: "input", q: text })}`)).json();
+
+ return {
+ src: translate.src,
+ text: translate.sentences.map(s => s.trans).filter(Boolean).join("")
+ };
+}
+
+export async function translate(text: string): Promise {
+ const { target, toki, sitelen, shavian } = settings.store;
+
+ const output = { src: "", text: "" };
+
+ if ((isTokiPona(text) || isSitelen(text)) && (toki || sitelen)) {
+ if (isSitelen(text) && sitelen) text = await translateSitelen(text);
+
+ console.log(text);
+
+ const translate = await (await fetch("https://aiapi.serversmp.xyz/toki", {
+ method: "POST",
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ text: text,
+ src: "tl",
+ target: "en"
+ })
+ })).json();
+
+ console.log(translate);
+
+ output.src = "tp";
+ output.text = target === "en" ? translate.translation[0] : (await google(target, translate.translation[0])).text;
+ } else if (isShavian(text) && shavian) {
+ const translate = await translateShavian(text);
+
+ output.src = "sh";
+ output.text = target === "en" ? translate : (await google(target, translate)).text;
+ } else {
+ const translate = await google(target, text);
+
+ output.src = translate.src;
+ output.text = translate.text;
+ }
+
+ return output;
+}