firefish/packages/backend/src/misc/translate.ts
sup39 b71da18b03
refactor (backend): port fetch-meta to backend-rs
Co-authored-by: naskya <m@naskya.net>
2024-04-14 20:16:22 +09:00

94 lines
2.5 KiB
TypeScript

import fetch from "node-fetch";
import { Converter } from "opencc-js";
import { getAgentByUrl } from "@/misc/fetch.js";
import { fetchMeta } from "backend-rs";
import type { PostLanguage } from "@/misc/langmap";
import * as deepl from "deepl-node";
// DeepL translate and LibreTranslate don't provide
// zh-Hant-TW translations, so we convert zh-Hans-CN
// translations into zh-Hant-TW using opencc-js.
function convertChinese(convert: boolean, src: string) {
if (!convert) return src;
const converter = Converter({ from: "cn", to: "twp" });
return converter(src);
}
function stem(lang: PostLanguage): string {
let toReturn = lang as string;
if (toReturn.includes("-")) toReturn = toReturn.split("-")[0];
if (toReturn.includes("_")) toReturn = toReturn.split("_")[0];
return toReturn;
}
export async function translate(
text: string,
from: PostLanguage | null,
to: PostLanguage,
) {
const instance = await fetchMeta(true);
if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) {
throw Error("No translator is set up on this server.");
}
const source = from == null ? null : stem(from);
const target = stem(to);
if (instance.libreTranslateApiUrl != null) {
const jsonBody = {
q: text,
source: source ?? "auto",
target,
format: "text",
api_key: instance.libreTranslateApiKey ?? "",
};
const url = new URL(instance.libreTranslateApiUrl);
if (url.pathname.endsWith("/")) {
url.pathname = url.pathname.slice(0, -1);
}
if (!url.pathname.endsWith("/translate")) {
url.pathname += "/translate";
}
const res = await fetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(jsonBody),
agent: getAgentByUrl,
});
const json = (await res.json()) as {
detectedLanguage?: {
confidence: number;
language: string;
};
translatedText: string;
};
return {
sourceLang: source ?? json.detectedLanguage?.language,
text: convertChinese(
["zh-hant", "zh-TW"].includes(to),
json.translatedText,
),
};
}
const deeplTranslator = new deepl.Translator(instance.deeplAuthKey ?? "");
const result = await deeplTranslator.translateText(
text,
source as deepl.SourceLanguageCode | null,
// DeepL API requires us to specify "en-US" or "en-GB" for English
// translations ("en" does not work), so we need to address it
(target === "en" ? to : target) as deepl.TargetLanguageCode,
);
return {
sourceLang: source ?? result.detectedSourceLang,
text: convertChinese(["zh-hant", "zh-TW"].includes(to), result.text),
};
}