mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
92 lines
2.4 KiB
TypeScript
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 as LanguageCode,
|
|
};
|
|
}
|
|
|
|
/** 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(),
|
|
}),
|
|
),
|
|
});
|
|
}
|
|
}
|