ditto/packages/translators/DeepLTranslator.ts
2025-02-17 00:41:49 -06:00

92 lines
2.4 KiB
TypeScript

import { LanguageCode } from 'iso-639-1';
import { z } from 'zod';
import { DittoTranslator } from '@/interfaces/DittoTranslator.ts';
import { languageSchema } from '@/schema.ts';
interface DeepLTranslatorOpts {
/** DeepL base URL to use. Default: 'https://api.deepl.com' */
baseUrl?: string;
/** DeepL API key. */
apiKey: string;
/** Custom fetch implementation. */
fetch?: typeof fetch;
}
export class DeepLTranslator implements DittoTranslator {
private readonly baseUrl: string;
private readonly apiKey: string;
private readonly fetch: typeof fetch;
readonly provider = 'DeepL.com';
constructor(opts: DeepLTranslatorOpts) {
this.baseUrl = opts.baseUrl ?? 'https://api.deepl.com';
this.fetch = opts.fetch ?? globalThis.fetch;
this.apiKey = opts.apiKey;
}
async translate(
texts: string[],
source: LanguageCode | undefined,
dest: LanguageCode,
opts?: { signal?: AbortSignal },
) {
const { translations } = await this.translateMany(texts, source, dest, opts);
return {
results: translations.map((value) => value.text),
source_lang: translations[0]?.detected_source_language,
};
}
/** DeepL translate request. */
private async translateMany(
texts: string[],
source: LanguageCode | undefined,
targetLanguage: LanguageCode,
opts?: { signal?: AbortSignal },
) {
const body = {
text: texts,
target_lang: targetLanguage.toUpperCase(),
tag_handling: 'html',
split_sentences: '1',
source_lang: source?.toUpperCase(),
};
const url = new URL('/v2/translate', this.baseUrl);
const request = new Request(url, {
method: 'POST',
body: JSON.stringify(body),
headers: {
'Authorization': `DeepL-Auth-Key ${this.apiKey}`,
'Content-Type': 'application/json',
},
signal: opts?.signal,
});
const response = await this.fetch(request);
const json = await response.json();
if (!response.ok) {
throw new Error(json['message']);
}
return DeepLTranslator.schema().parse(json);
}
/** DeepL response schema.
* https://developers.deepl.com/docs/api-reference/translate/openapi-spec-for-text-translation */
private static schema() {
return z.object({
translations: z.array(
z.object({
detected_source_language: languageSchema,
text: z.string(),
}),
),
});
}
}