import { z } from "zod"; import type { Language, TranslationService, AsyncRes } from "../types"; import { AiTranslator } from "./aitranslation"; const JSON_HEADER = { "Content-Type": "application/json" }; export class GoogleTranslate implements TranslationService { endpoint = "https://translate.googleapis.com/language/translate/v2"; constructor(private apiKey: string) { if (!apiKey) throw new Error("Google Translate API key is required"); } async call(path: string, body?: any) { try { const authH = { "X-goog-api-key": this.apiKey, }; const opts = body ? { method: "POST", headers: { ...authH, ...JSON_HEADER }, body: JSON.stringify(body), } : { headers: authH }; const response = await fetch(this.endpoint + path, opts); if (!response.ok) { const errorMessage = response.statusText; throw new Error( `Google Translate API error (${response.status}): ${errorMessage}`, ); } const data = await response.json(); return data; } catch (e) { throw new Error(`${e}`); } } async translate( text: string, sourceLang: string, targetLang: string, ): AsyncRes { try { const body = { q: text, source: sourceLang === "auto" ? undefined : sourceLang, target: targetLang, format: "text", }; const data = await this.call("", body); console.log("google translate res", data); if (!data.data?.translations?.[0]?.translatedText) { return { error: "Invalid response format from Google Translate API" }; } return { ok: data.data.translations[0].translatedText }; } catch (error) { return { error: "Failed to connect to Google Translate API `${error}`" }; } } async getSupportedLanguages() { try { const res = await this.call("/languages"); const languageNames = new Intl.DisplayNames(["en"], { type: "language" }); // lnguages are ISO 639 or BCP-47 const set = new Set(); const ret: Language[] = []; for (let ll of res.data.languages) { const l: { language: string } = ll; const code = l.language; const name = languageNames.of(code); if (!name) continue; if (!set.has(name)) ret.push({ code, name }); set.add(name); } return { ok: ret }; } catch (e) { return { error: `${e}` }; } } } export class MicrosoftTranslator implements TranslationService { endpoint = "https://api.cognitive.microsofttranslator.com"; constructor(private apiKey: string) { if (!apiKey) throw new Error("Microsoft Translator API key is required"); } async translate( text: string, sourceLang: string, targetLang: string, ): AsyncRes { const url = "https://api.cognitive.microsofttranslator.com"; // documents // https://sortug.cognitiveservices.azure.com/ // try { const res = await this.call( `/translate?api-version=3.0&from=${sourceLang === "auto" ? "" : sourceLang}&to=${targetLang}`, [{ text }], ); if (!res[0]?.translations?.[0]?.text) { throw new Error( "Invalid response format from Microsoft Translator API", ); } return { ok: res[0].translations[0].text }; } catch (error) { return { error: "Failed to connect to Microsoft Translator API" }; } } async getSupportedLanguages() { try { const res = await this.call(`/languages?api-version=3.0`); return { ok: Object.entries(res.translation).map(([code, l]: any) => ({ code, name: l.name, nativeName: l.nativeName, })), }; } catch (e) { return { error: `${e}` }; } } async dictionaryLookup(text: string, from: string, to: string) { const res = await this.call( `/Dictionary/Lookup?api-version=3.0&from=${from}&to=${to}`, ); console.log({ res }); return res; } async pinyin() { try { const res = await this.call(`/languages?api-version=3.0`); // return Object.entries(res.transliteration).map(([code, l]: any) => { // return { code, ...l }; // }); return { ok: res.transliteration }; } catch (e) { return { error: `${e}` }; } } async transliterate( text: string[], language: string, from: string, to: string, ) { const body = text.map((t) => ({ Text: t })); const url = `/transliterate?api-version=3.0&language=${language}&fromScript=${from}&toScript=${to}`; console.log({ url, body }); try { const res = await this.call(url, body); return { ok: res[0].text }; } catch (e) { return { error: `${e}` }; } } async call(path: string, body?: any) { const authH = { "Ocp-Apim-Subscription-Key": this.apiKey, "Ocp-Apim-Subscription-Region": "southeastasia", // "X-ClientTraceId": uuidv4().toString(), // Authorization: `Bearer ${this.apiKey}`, }; console.log({ authH }); const opts = body ? { method: "POST", headers: { ...authH, ...JSON_HEADER }, body: JSON.stringify(body), } : { headers: authH }; const res = await fetch(this.endpoint + path, opts); console.log({ res }); if (!res.ok) { const errorMessage = res.statusText; throw new Error( `Microsoft Translator API error (${res.status}): ${errorMessage}`, ); } const j = await res.json(); return j; } } export class DeepLTranslator implements TranslationService { // https://developers.deepl.com/docs/api-reference/client-libraries // endpoint = "https://api.deepl.com/v2"; endpoint = "https://api-free.deepl.com/v2"; constructor(private apiKey: string) { if (!apiKey) throw new Error("DeepL API key is required"); } async call(path: string, body?: any) { try { const authH = { Authorization: `DeepL-Auth-Key ${this.apiKey}`, }; const opts = body ? { method: "POST", headers: { ...authH, ...JSON_HEADER }, body: JSON.stringify(body), } : { headers: authH }; const response = await fetch(this.endpoint + path, opts); const data = await response.json(); if (!response.ok) { const errorMessage = data.message || response.statusText; throw new Error( `DeepL API error (${response.status}): ${errorMessage}`, ); } return data; } catch (error) { if (error instanceof Error) { throw error; } throw new Error("Failed to connect to DeepL API"); } } async translate( text: string, sourceLang: string, targetLang: string, context?: string, formality?: "default" | "more" | "less" | "prefer_more" | "prefer_less", ): AsyncRes { try { const data = await this.call("/translate", { text: [text], target_lang: targetLang, source_lang: sourceLang, context, formality, model_type: "prefer_quality_optimized", }); if (!data.translations?.[0]?.text) { throw new Error("Invalid response format from DeepL API"); } return { ok: data.translations[0].text }; } catch (error) { return { error: "Failed to connect to DeepL API" }; } } async getSupportedLanguages() { try { const data = await this.call("/languages"); return { ok: data.map((l: { language: string; name: string }) => ({ code: l.language.toLowerCase(), name: l.name, })), }; } catch (e) { return { error: `${e}` }; } } } // Factory function to create translation service based on provider export function createTranslationService(provider: string): TranslationService { const envSchema = z.object({ GOOGLE_TRANSLATE_API_KEY: z.string(), AZURE_TRANSLATE_API_KEY: z.string(), DEEPL_API_KEY: z.string(), }); const env = envSchema.parse(process.env); switch (provider) { case "google": return new GoogleTranslate(env.GOOGLE_TRANSLATE_API_KEY); case "microsoft": return new MicrosoftTranslator(env.AZURE_TRANSLATE_API_KEY); case "deepl": return new DeepLTranslator(env.DEEPL_API_KEY); case "deepseek": return new AiTranslator({ name: provider }); case "grok": return new AiTranslator({ name: provider }); case "claude": return new AiTranslator({ name: provider }); case "gemini": return new AiTranslator({ name: provider }); case "chatgpt": return new AiTranslator({ name: provider }); default: throw new Error(`Unsupported translation provider: ${provider}`); } }