@happyvertical/translator 0.74.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,211 @@
1
+ import { isValidText, InvalidTextError, TranslationError, getLanguageName, AuthenticationError, UnsupportedLanguageError } from "../index.js";
2
+ class LibreTranslateProvider {
3
+ baseUrl;
4
+ apiKey;
5
+ timeout;
6
+ constructor(options) {
7
+ this.baseUrl = options.apiUrl || "https://libretranslate.com";
8
+ this.apiKey = options.apiKey;
9
+ this.timeout = options.timeout || 3e4;
10
+ }
11
+ /**
12
+ * Translates text from source language to target language
13
+ */
14
+ async translate(text, targetLanguage, sourceLanguage) {
15
+ if (!isValidText(text)) {
16
+ throw new InvalidTextError("Text cannot be empty", "libretranslate");
17
+ }
18
+ try {
19
+ const body = {
20
+ q: text,
21
+ target: targetLanguage,
22
+ format: "text"
23
+ };
24
+ if (sourceLanguage) {
25
+ body.source = sourceLanguage;
26
+ } else {
27
+ body.source = "auto";
28
+ }
29
+ if (this.apiKey) {
30
+ body.api_key = this.apiKey;
31
+ }
32
+ const response = await this.fetchLibreTranslate("/translate", body);
33
+ const data = response;
34
+ return {
35
+ translatedText: data.translatedText,
36
+ sourceText: text,
37
+ sourceLanguage: data.detectedLanguage?.language || sourceLanguage || "unknown",
38
+ targetLanguage,
39
+ confidence: data.detectedLanguage?.confidence,
40
+ detectedSourceLanguage: !sourceLanguage,
41
+ raw: data
42
+ };
43
+ } catch (error) {
44
+ throw this.mapError(error);
45
+ }
46
+ }
47
+ /**
48
+ * Detects the language of the provided text
49
+ */
50
+ async detectLanguage(text) {
51
+ if (!isValidText(text)) {
52
+ throw new InvalidTextError("Text cannot be empty", "libretranslate");
53
+ }
54
+ try {
55
+ const body = {
56
+ q: text
57
+ };
58
+ if (this.apiKey) {
59
+ body.api_key = this.apiKey;
60
+ }
61
+ const response = await this.fetchLibreTranslate("/detect", body);
62
+ if (Array.isArray(response)) {
63
+ const detections = response;
64
+ if (detections.length === 0) {
65
+ throw new TranslationError(
66
+ "No language detected",
67
+ "NO_DETECTION",
68
+ "libretranslate"
69
+ );
70
+ }
71
+ return {
72
+ language: detections[0].language,
73
+ confidence: detections[0].confidence,
74
+ alternatives: detections.slice(1).map((d) => ({
75
+ language: d.language,
76
+ confidence: d.confidence
77
+ })),
78
+ raw: response
79
+ };
80
+ }
81
+ throw new TranslationError(
82
+ "Invalid detection response",
83
+ "INVALID_RESPONSE",
84
+ "libretranslate"
85
+ );
86
+ } catch (error) {
87
+ if (error instanceof TranslationError) {
88
+ throw error;
89
+ }
90
+ throw this.mapError(error);
91
+ }
92
+ }
93
+ /**
94
+ * Gets the list of languages supported by this provider
95
+ */
96
+ async getSupportedLanguages() {
97
+ try {
98
+ const response = await this.fetchLibreTranslate(
99
+ "/languages",
100
+ void 0,
101
+ "GET"
102
+ );
103
+ if (Array.isArray(response)) {
104
+ const languages = response;
105
+ return languages.map((lang) => ({
106
+ code: lang.code,
107
+ name: lang.name || getLanguageName(lang.code)
108
+ }));
109
+ }
110
+ throw new TranslationError(
111
+ "Invalid languages response",
112
+ "INVALID_RESPONSE",
113
+ "libretranslate"
114
+ );
115
+ } catch (error) {
116
+ if (error instanceof TranslationError) {
117
+ throw error;
118
+ }
119
+ throw this.mapError(error);
120
+ }
121
+ }
122
+ /**
123
+ * Translates multiple texts in a single batch operation
124
+ * Note: LibreTranslate doesn't have native batch support,
125
+ * so we translate texts sequentially
126
+ */
127
+ async translateBatch(texts, targetLanguage, sourceLanguage) {
128
+ if (texts.length === 0) {
129
+ return [];
130
+ }
131
+ const results = [];
132
+ for (const text of texts) {
133
+ const result = await this.translate(text, targetLanguage, sourceLanguage);
134
+ results.push(result);
135
+ }
136
+ return results;
137
+ }
138
+ /**
139
+ * Makes an HTTP request to LibreTranslate API
140
+ */
141
+ async fetchLibreTranslate(endpoint, body, method = "POST") {
142
+ const url = `${this.baseUrl}${endpoint}`;
143
+ const controller = new AbortController();
144
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
145
+ try {
146
+ const options = {
147
+ method,
148
+ signal: controller.signal
149
+ };
150
+ if (method === "POST" && body) {
151
+ options.headers = {
152
+ "Content-Type": "application/json"
153
+ };
154
+ options.body = JSON.stringify(body);
155
+ }
156
+ const response = await fetch(url, options);
157
+ clearTimeout(timeoutId);
158
+ if (!response.ok) {
159
+ if (response.status === 401 || response.status === 403) {
160
+ throw new AuthenticationError("libretranslate");
161
+ }
162
+ if (response.status === 400) {
163
+ const errorData = await response.json().catch(() => ({}));
164
+ if (errorData.error?.includes("language")) {
165
+ throw new UnsupportedLanguageError(
166
+ body.source || body.target || "unknown",
167
+ "libretranslate"
168
+ );
169
+ }
170
+ }
171
+ throw new TranslationError(
172
+ `HTTP ${response.status}: ${response.statusText}`,
173
+ "HTTP_ERROR",
174
+ "libretranslate"
175
+ );
176
+ }
177
+ return await response.json();
178
+ } catch (error) {
179
+ clearTimeout(timeoutId);
180
+ if (error instanceof TranslationError) {
181
+ throw error;
182
+ }
183
+ if (error.name === "AbortError") {
184
+ throw new TranslationError(
185
+ "Request timeout",
186
+ "TIMEOUT",
187
+ "libretranslate"
188
+ );
189
+ }
190
+ throw error;
191
+ }
192
+ }
193
+ /**
194
+ * Maps LibreTranslate errors to standardized error types
195
+ */
196
+ mapError(error) {
197
+ if (error instanceof TranslationError) {
198
+ return error;
199
+ }
200
+ const err = error;
201
+ return new TranslationError(
202
+ err?.message || "Translation failed",
203
+ err?.code || "UNKNOWN_ERROR",
204
+ "libretranslate"
205
+ );
206
+ }
207
+ }
208
+ export {
209
+ LibreTranslateProvider
210
+ };
211
+ //# sourceMappingURL=libretranslate-BeC38-0r.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"libretranslate-BeC38-0r.js","sources":["../../src/providers/libretranslate.ts"],"sourcesContent":["/**\n * LibreTranslate provider implementation\n */\n\nimport type {\n LanguageDetectionResult,\n LibreTranslateOptions,\n SupportedLanguage,\n TranslationProvider,\n TranslationResult,\n} from '../shared/types';\nimport {\n AuthenticationError,\n InvalidTextError,\n TranslationError,\n UnsupportedLanguageError,\n} from '../shared/types';\nimport { getLanguageName, isValidText } from '../shared/utils';\n\n/**\n * LibreTranslate API response structures\n */\ninterface LibreTranslateTranslateResponse {\n translatedText: string;\n detectedLanguage?: {\n confidence: number;\n language: string;\n };\n}\n\ninterface LibreTranslateDetectResponse {\n confidence: number;\n language: string;\n}\n\ninterface LibreTranslateLanguage {\n code: string;\n name: string;\n}\n\n/**\n * LibreTranslate provider implementation\n */\nexport class LibreTranslateProvider implements TranslationProvider {\n private baseUrl: string;\n private apiKey?: string;\n private timeout: number;\n\n constructor(options: LibreTranslateOptions) {\n this.baseUrl = options.apiUrl || 'https://libretranslate.com';\n this.apiKey = options.apiKey;\n this.timeout = options.timeout || 30000;\n }\n\n /**\n * Translates text from source language to target language\n */\n async translate(\n text: string,\n targetLanguage: string,\n sourceLanguage?: string,\n ): Promise<TranslationResult> {\n if (!isValidText(text)) {\n throw new InvalidTextError('Text cannot be empty', 'libretranslate');\n }\n\n try {\n const body: any = {\n q: text,\n target: targetLanguage,\n format: 'text',\n };\n\n if (sourceLanguage) {\n body.source = sourceLanguage;\n } else {\n body.source = 'auto';\n }\n\n if (this.apiKey) {\n body.api_key = this.apiKey;\n }\n\n const response = await this.fetchLibreTranslate('/translate', body);\n\n const data = response as LibreTranslateTranslateResponse;\n\n return {\n translatedText: data.translatedText,\n sourceText: text,\n sourceLanguage:\n data.detectedLanguage?.language || sourceLanguage || 'unknown',\n targetLanguage,\n confidence: data.detectedLanguage?.confidence,\n detectedSourceLanguage: !sourceLanguage,\n raw: data,\n };\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * Detects the language of the provided text\n */\n async detectLanguage(text: string): Promise<LanguageDetectionResult> {\n if (!isValidText(text)) {\n throw new InvalidTextError('Text cannot be empty', 'libretranslate');\n }\n\n try {\n const body: any = {\n q: text,\n };\n\n if (this.apiKey) {\n body.api_key = this.apiKey;\n }\n\n const response = await this.fetchLibreTranslate('/detect', body);\n\n if (Array.isArray(response)) {\n const detections = response as LibreTranslateDetectResponse[];\n\n if (detections.length === 0) {\n throw new TranslationError(\n 'No language detected',\n 'NO_DETECTION',\n 'libretranslate',\n );\n }\n\n return {\n language: detections[0].language,\n confidence: detections[0].confidence,\n alternatives: detections.slice(1).map((d) => ({\n language: d.language,\n confidence: d.confidence,\n })),\n raw: response,\n };\n }\n\n throw new TranslationError(\n 'Invalid detection response',\n 'INVALID_RESPONSE',\n 'libretranslate',\n );\n } catch (error) {\n if (error instanceof TranslationError) {\n throw error;\n }\n throw this.mapError(error);\n }\n }\n\n /**\n * Gets the list of languages supported by this provider\n */\n async getSupportedLanguages(): Promise<SupportedLanguage[]> {\n try {\n const response = await this.fetchLibreTranslate(\n '/languages',\n undefined,\n 'GET',\n );\n\n if (Array.isArray(response)) {\n const languages = response as LibreTranslateLanguage[];\n return languages.map((lang) => ({\n code: lang.code,\n name: lang.name || getLanguageName(lang.code),\n }));\n }\n\n throw new TranslationError(\n 'Invalid languages response',\n 'INVALID_RESPONSE',\n 'libretranslate',\n );\n } catch (error) {\n if (error instanceof TranslationError) {\n throw error;\n }\n throw this.mapError(error);\n }\n }\n\n /**\n * Translates multiple texts in a single batch operation\n * Note: LibreTranslate doesn't have native batch support,\n * so we translate texts sequentially\n */\n async translateBatch(\n texts: string[],\n targetLanguage: string,\n sourceLanguage?: string,\n ): Promise<TranslationResult[]> {\n if (texts.length === 0) {\n return [];\n }\n\n // Translate each text sequentially\n const results: TranslationResult[] = [];\n for (const text of texts) {\n const result = await this.translate(text, targetLanguage, sourceLanguage);\n results.push(result);\n }\n\n return results;\n }\n\n /**\n * Makes an HTTP request to LibreTranslate API\n */\n private async fetchLibreTranslate(\n endpoint: string,\n body?: any,\n method: 'GET' | 'POST' = 'POST',\n ): Promise<any> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const options: RequestInit = {\n method,\n signal: controller.signal,\n };\n\n if (method === 'POST' && body) {\n options.headers = {\n 'Content-Type': 'application/json',\n };\n options.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, options);\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new AuthenticationError('libretranslate');\n }\n\n if (response.status === 400) {\n const errorData = (await response.json().catch(() => ({}))) as any;\n if (errorData.error?.includes('language')) {\n throw new UnsupportedLanguageError(\n body.source || body.target || 'unknown',\n 'libretranslate',\n );\n }\n }\n\n throw new TranslationError(\n `HTTP ${response.status}: ${response.statusText}`,\n 'HTTP_ERROR',\n 'libretranslate',\n );\n }\n\n return await response.json();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof TranslationError) {\n throw error;\n }\n\n if ((error as Error).name === 'AbortError') {\n throw new TranslationError(\n 'Request timeout',\n 'TIMEOUT',\n 'libretranslate',\n );\n }\n\n throw error;\n }\n }\n\n /**\n * Maps LibreTranslate errors to standardized error types\n */\n private mapError(error: unknown): TranslationError {\n if (error instanceof TranslationError) {\n return error;\n }\n\n const err = error as any;\n return new TranslationError(\n err?.message || 'Translation failed',\n err?.code || 'UNKNOWN_ERROR',\n 'libretranslate',\n );\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=claude-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-context.d.ts","sourceRoot":"","sources":["../../src/cli/claude-context.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, copyFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const Dirname = dirname(fileURLToPath(import.meta.url));
6
+ const pkgRoot = join(Dirname, "../..");
7
+ const targetDir = join(process.cwd(), ".claude");
8
+ if (!existsSync(targetDir)) {
9
+ mkdirSync(targetDir, { recursive: true });
10
+ }
11
+ const pkgName = "translator";
12
+ const agentMdSrc = existsSync(join(pkgRoot, "AGENT.md")) ? join(pkgRoot, "AGENT.md") : join(pkgRoot, "CLAUDE.md");
13
+ const metaSrc = existsSync(join(pkgRoot, "metadata.json")) ? join(pkgRoot, "metadata.json") : join(pkgRoot, ".claude-meta.json");
14
+ if (existsSync(agentMdSrc)) {
15
+ copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));
16
+ }
17
+ if (existsSync(metaSrc)) {
18
+ copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));
19
+ }
20
+ console.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);
21
+ //# sourceMappingURL=claude-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-context.js","sources":["../../src/cli/claude-context.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI script to install agent context for @happyvertical/translator\n * Run the published context installer binary for this package.\n */\nimport { copyFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst Dirname = dirname(fileURLToPath(import.meta.url));\nconst pkgRoot = join(Dirname, '../..');\nconst targetDir = join(process.cwd(), '.claude');\n\nif (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n}\n\nconst pkgName = 'translator';\nconst agentMdSrc = existsSync(join(pkgRoot, 'AGENT.md'))\n ? join(pkgRoot, 'AGENT.md')\n : join(pkgRoot, 'CLAUDE.md');\nconst metaSrc = existsSync(join(pkgRoot, 'metadata.json'))\n ? join(pkgRoot, 'metadata.json')\n : join(pkgRoot, '.claude-meta.json');\n\nif (existsSync(agentMdSrc)) {\n copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));\n}\n\nif (existsSync(metaSrc)) {\n copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));\n}\n\nconsole.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);\n"],"names":[],"mappings":";;;;AASA,MAAM,UAAU,QAAQ,cAAc,YAAY,GAAG,CAAC;AACtD,MAAM,UAAU,KAAK,SAAS,OAAO;AACrC,MAAM,YAAY,KAAK,QAAQ,IAAA,GAAO,SAAS;AAE/C,IAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,YAAU,WAAW,EAAE,WAAW,KAAA,CAAM;AAC1C;AAEA,MAAM,UAAU;AAChB,MAAM,aAAa,WAAW,KAAK,SAAS,UAAU,CAAC,IACnD,KAAK,SAAS,UAAU,IACxB,KAAK,SAAS,WAAW;AAC7B,MAAM,UAAU,WAAW,KAAK,SAAS,eAAe,CAAC,IACrD,KAAK,SAAS,eAAe,IAC7B,KAAK,SAAS,mBAAmB;AAErC,IAAI,WAAW,UAAU,GAAG;AAC1B,eAAa,YAAY,KAAK,WAAW,QAAQ,OAAO,KAAK,CAAC;AAChE;AAEA,IAAI,WAAW,OAAO,GAAG;AACvB,eAAa,SAAS,KAAK,WAAW,QAAQ,OAAO,YAAY,CAAC;AACpE;AAEA,QAAQ,IAAI,8BAA8B,OAAO,sBAAsB;"}
@@ -0,0 +1,48 @@
1
+ import { Translator, TranslatorOptions } from './shared/types';
2
+ export * from './shared/types';
3
+ export * from './shared/utils';
4
+ /**
5
+ * Factory function to create a translator instance
6
+ *
7
+ * Supports environment variable configuration using HAVE_TRANSLATOR_* pattern:
8
+ * - HAVE_TRANSLATOR_PROVIDER: Translation provider ('google'|'deepl'|'libretranslate')
9
+ *
10
+ * Provider-specific API keys are loaded from their respective environment variables:
11
+ * - GOOGLE_TRANSLATE_API_KEY: Google Translate API key
12
+ * - DEEPL_API_KEY: DeepL API key
13
+ *
14
+ * @param options - Configuration options for the translation provider (optional if using env vars)
15
+ * @returns Promise resolving to a translator that implements Translator
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Using explicit options
20
+ * const translator = await getTranslator({
21
+ * provider: 'google',
22
+ * apiKey: process.env.GOOGLE_TRANSLATE_API_KEY!
23
+ * });
24
+ *
25
+ * // Using environment variables
26
+ * // Set: HAVE_TRANSLATOR_PROVIDER=deepl
27
+ * // DEEPL_API_KEY=your_key_here
28
+ * const translator = await getTranslator();
29
+ *
30
+ * // Mix of env vars and explicit options (explicit takes precedence)
31
+ * // Set: HAVE_TRANSLATOR_PROVIDER=google
32
+ * const translator = await getTranslator({
33
+ * apiKey: 'explicit_key' // Overrides env var
34
+ * });
35
+ *
36
+ * // Use the translator
37
+ * const result = await translator.translate('Hello, world!', 'es');
38
+ *
39
+ * // Use template function for repeated translations
40
+ * const t = translator.templateFunction('en', 'es');
41
+ * const greeting = await t('Hello');
42
+ * const farewell = await t('Goodbye');
43
+ * ```
44
+ */
45
+ export declare function getTranslator(options?: Partial<TranslatorOptions>): Promise<Translator>;
46
+ /** @internal */
47
+ export declare const PACKAGE_VERSION_INITIALIZED = true;
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAQV,UAAU,EACV,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAGxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AA4E/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACvC,OAAO,CAAC,UAAU,CAAC,CAkDrB;AAED,gBAAgB;AAChB,eAAO,MAAM,2BAA2B,OAAO,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,252 @@
1
+ import { loadEnvConfig } from "@happyvertical/utils";
2
+ class TranslationError extends Error {
3
+ constructor(message, code, provider) {
4
+ super(message);
5
+ this.code = code;
6
+ this.provider = provider;
7
+ this.name = "TranslationError";
8
+ }
9
+ }
10
+ class UnsupportedLanguageError extends TranslationError {
11
+ constructor(language, provider) {
12
+ super(
13
+ `Unsupported language: ${language}`,
14
+ "UNSUPPORTED_LANGUAGE",
15
+ provider
16
+ );
17
+ this.name = "UnsupportedLanguageError";
18
+ }
19
+ }
20
+ class QuotaExceededError extends TranslationError {
21
+ constructor(provider) {
22
+ super("Translation quota exceeded", "QUOTA_EXCEEDED", provider);
23
+ this.name = "QuotaExceededError";
24
+ }
25
+ }
26
+ class AuthenticationError extends TranslationError {
27
+ constructor(provider) {
28
+ super("Authentication failed", "AUTH_ERROR", provider);
29
+ this.name = "AuthenticationError";
30
+ }
31
+ }
32
+ class InvalidTextError extends TranslationError {
33
+ constructor(reason, provider) {
34
+ super(`Invalid text: ${reason}`, "INVALID_TEXT", provider);
35
+ this.name = "InvalidTextError";
36
+ }
37
+ }
38
+ const LANGUAGE_NAMES = {
39
+ en: "English",
40
+ es: "Spanish",
41
+ fr: "French",
42
+ de: "German",
43
+ it: "Italian",
44
+ pt: "Portuguese",
45
+ ru: "Russian",
46
+ ja: "Japanese",
47
+ ko: "Korean",
48
+ zh: "Chinese",
49
+ ar: "Arabic",
50
+ hi: "Hindi",
51
+ nl: "Dutch",
52
+ pl: "Polish",
53
+ tr: "Turkish",
54
+ sv: "Swedish",
55
+ da: "Danish",
56
+ no: "Norwegian",
57
+ fi: "Finnish",
58
+ cs: "Czech",
59
+ el: "Greek",
60
+ he: "Hebrew",
61
+ th: "Thai",
62
+ vi: "Vietnamese",
63
+ id: "Indonesian",
64
+ ro: "Romanian",
65
+ uk: "Ukrainian",
66
+ bg: "Bulgarian",
67
+ hr: "Croatian",
68
+ sk: "Slovak",
69
+ sl: "Slovenian",
70
+ lt: "Lithuanian",
71
+ lv: "Latvian",
72
+ et: "Estonian",
73
+ hu: "Hungarian",
74
+ ca: "Catalan",
75
+ gl: "Galician",
76
+ eu: "Basque",
77
+ fa: "Persian",
78
+ ur: "Urdu",
79
+ bn: "Bengali",
80
+ ta: "Tamil",
81
+ te: "Telugu",
82
+ mr: "Marathi",
83
+ ml: "Malayalam",
84
+ kn: "Kannada",
85
+ gu: "Gujarati",
86
+ pa: "Punjabi",
87
+ sw: "Swahili",
88
+ ms: "Malay",
89
+ tl: "Tagalog",
90
+ af: "Afrikaans",
91
+ sq: "Albanian",
92
+ am: "Amharic",
93
+ hy: "Armenian",
94
+ az: "Azerbaijani",
95
+ be: "Belarusian",
96
+ bs: "Bosnian",
97
+ my: "Burmese",
98
+ ceb: "Cebuano",
99
+ ny: "Chichewa",
100
+ co: "Corsican",
101
+ eo: "Esperanto",
102
+ fy: "Frisian",
103
+ ka: "Georgian",
104
+ haw: "Hawaiian",
105
+ hmn: "Hmong",
106
+ is: "Icelandic",
107
+ ig: "Igbo",
108
+ ga: "Irish",
109
+ jw: "Javanese",
110
+ kk: "Kazakh",
111
+ km: "Khmer",
112
+ ku: "Kurdish",
113
+ ky: "Kyrgyz",
114
+ lo: "Lao",
115
+ la: "Latin",
116
+ lb: "Luxembourgish",
117
+ mk: "Macedonian",
118
+ mg: "Malagasy",
119
+ mt: "Maltese",
120
+ mi: "Maori",
121
+ mn: "Mongolian",
122
+ ne: "Nepali",
123
+ ps: "Pashto",
124
+ sm: "Samoan",
125
+ gd: "Scots Gaelic",
126
+ sr: "Serbian",
127
+ st: "Sesotho",
128
+ sn: "Shona",
129
+ sd: "Sindhi",
130
+ si: "Sinhala",
131
+ so: "Somali",
132
+ su: "Sundanese",
133
+ tg: "Tajik",
134
+ tt: "Tatar",
135
+ tk: "Turkmen",
136
+ ug: "Uyghur",
137
+ uz: "Uzbek",
138
+ cy: "Welsh",
139
+ xh: "Xhosa",
140
+ yi: "Yiddish",
141
+ yo: "Yoruba",
142
+ zu: "Zulu"
143
+ };
144
+ function isValidLanguageCode(code) {
145
+ return code.length === 2 && /^[a-z]{2}$/.test(code);
146
+ }
147
+ function getLanguageName(code) {
148
+ return LANGUAGE_NAMES[code] || code;
149
+ }
150
+ function normalizeConfidence(score) {
151
+ if (score > 1 && score <= 100) {
152
+ return score / 100;
153
+ }
154
+ return Math.max(0, Math.min(1, score));
155
+ }
156
+ function isValidText(text) {
157
+ return typeof text === "string" && text.trim().length > 0;
158
+ }
159
+ function truncateText(text, maxLength) {
160
+ if (text.length <= maxLength) {
161
+ return text;
162
+ }
163
+ return `${text.substring(0, maxLength - 3)}...`;
164
+ }
165
+ class TranslatorWrapper {
166
+ constructor(provider) {
167
+ this.provider = provider;
168
+ }
169
+ async translate(text, targetLanguage, sourceLanguage) {
170
+ return this.provider.translate(text, targetLanguage, sourceLanguage);
171
+ }
172
+ async detectLanguage(text) {
173
+ return this.provider.detectLanguage(text);
174
+ }
175
+ async getSupportedLanguages() {
176
+ return this.provider.getSupportedLanguages();
177
+ }
178
+ async translateBatch(texts, targetLanguage, sourceLanguage) {
179
+ return this.provider.translateBatch(texts, targetLanguage, sourceLanguage);
180
+ }
181
+ /**
182
+ * Creates a pre-configured translation function
183
+ * This is the key ergonomic feature that makes repeated translations simple
184
+ */
185
+ templateFunction(sourceLanguage, targetLanguage = "en") {
186
+ return async (text) => {
187
+ const result = await this.provider.translate(
188
+ text,
189
+ targetLanguage,
190
+ sourceLanguage
191
+ );
192
+ return result.translatedText;
193
+ };
194
+ }
195
+ }
196
+ async function getTranslator(options = {}) {
197
+ const config = loadEnvConfig(
198
+ options,
199
+ {
200
+ packageName: "translator",
201
+ schema: {
202
+ provider: "string",
203
+ timeout: "number",
204
+ maxRetries: "number"
205
+ },
206
+ allowUnknown: true
207
+ }
208
+ );
209
+ if (!config.apiKey) {
210
+ if (config.provider === "google" && process.env.GOOGLE_TRANSLATE_API_KEY) {
211
+ config.apiKey = process.env.GOOGLE_TRANSLATE_API_KEY;
212
+ } else if (config.provider === "deepl" && process.env.DEEPL_API_KEY) {
213
+ config.apiKey = process.env.DEEPL_API_KEY;
214
+ }
215
+ }
216
+ if (!config.provider) {
217
+ throw new Error(
218
+ "Translation provider not specified. Set HAVE_TRANSLATOR_PROVIDER environment variable or pass provider in options."
219
+ );
220
+ }
221
+ let provider;
222
+ if (config.provider === "google") {
223
+ const { GoogleTranslateProvider } = await import("./chunks/google-KGGP9X0w.js");
224
+ provider = new GoogleTranslateProvider(config);
225
+ } else if (config.provider === "deepl") {
226
+ const { DeepLProvider } = await import("./chunks/deepl-CWEP0MSh.js");
227
+ provider = new DeepLProvider(config);
228
+ } else if (config.provider === "libretranslate") {
229
+ const { LibreTranslateProvider } = await import("./chunks/libretranslate-BeC38-0r.js");
230
+ provider = new LibreTranslateProvider(config);
231
+ } else {
232
+ throw new Error(`Unsupported provider: ${config.provider}`);
233
+ }
234
+ return new TranslatorWrapper(provider);
235
+ }
236
+ const PACKAGE_VERSION_INITIALIZED = true;
237
+ export {
238
+ AuthenticationError,
239
+ InvalidTextError,
240
+ LANGUAGE_NAMES,
241
+ PACKAGE_VERSION_INITIALIZED,
242
+ QuotaExceededError,
243
+ TranslationError,
244
+ UnsupportedLanguageError,
245
+ getLanguageName,
246
+ getTranslator,
247
+ isValidLanguageCode,
248
+ isValidText,
249
+ normalizeConfidence,
250
+ truncateText
251
+ };
252
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/shared/types.ts","../src/shared/utils.ts","../src/index.ts"],"sourcesContent":["/**\n * Core types and interfaces for the Translator library\n */\n\n/**\n * Standardized translation result\n */\nexport interface TranslationResult {\n /**\n * The translated text\n */\n translatedText: string;\n\n /**\n * The original source text\n */\n sourceText: string;\n\n /**\n * The detected or specified source language (ISO 639-1 code)\n */\n sourceLanguage: string;\n\n /**\n * The target language (ISO 639-1 code)\n */\n targetLanguage: string;\n\n /**\n * Confidence score for the translation (0-1), if available\n */\n confidence?: number;\n\n /**\n * Alternative translations, if available\n */\n alternatives?: string[];\n\n /**\n * Whether the source language was automatically detected\n */\n detectedSourceLanguage: boolean;\n\n /**\n * The original, raw response from the provider\n */\n raw: any;\n}\n\n/**\n * Language detection result\n */\nexport interface LanguageDetectionResult {\n /**\n * The detected language (ISO 639-1 code)\n */\n language: string;\n\n /**\n * Confidence score for the detection (0-1)\n */\n confidence: number;\n\n /**\n * Alternative detected languages with confidence scores, if available\n */\n alternatives?: Array<{\n language: string;\n confidence: number;\n }>;\n\n /**\n * The original, raw response from the provider\n */\n raw: any;\n}\n\n/**\n * Supported language information\n */\nexport interface SupportedLanguage {\n /**\n * ISO 639-1 language code (e.g., 'en', 'es', 'fr')\n */\n code: string;\n\n /**\n * Human-readable language name in English\n */\n name: string;\n\n /**\n * Native name of the language (optional)\n */\n nativeName?: string;\n}\n\n/**\n * Translation provider interface - all providers must implement this\n */\nexport interface TranslationProvider {\n /**\n * Translates text from source language to target language\n * @param text The text to translate\n * @param targetLanguage The target language code (ISO 639-1)\n * @param sourceLanguage Optional source language code. If not provided, auto-detect\n * @returns A promise that resolves to a TranslationResult object\n */\n translate(\n text: string,\n targetLanguage: string,\n sourceLanguage?: string,\n ): Promise<TranslationResult>;\n\n /**\n * Detects the language of the provided text\n * @param text The text to analyze\n * @returns A promise that resolves to a LanguageDetectionResult object\n */\n detectLanguage(text: string): Promise<LanguageDetectionResult>;\n\n /**\n * Gets the list of languages supported by this provider\n * @returns A promise that resolves to an array of SupportedLanguage objects\n */\n getSupportedLanguages(): Promise<SupportedLanguage[]>;\n\n /**\n * Translates multiple texts in a single batch operation\n * @param texts Array of texts to translate\n * @param targetLanguage The target language code (ISO 639-1)\n * @param sourceLanguage Optional source language code. If not provided, auto-detect\n * @returns A promise that resolves to an array of TranslationResult objects\n */\n translateBatch(\n texts: string[],\n targetLanguage: string,\n sourceLanguage?: string,\n ): Promise<TranslationResult[]>;\n}\n\n/**\n * Translator interface - extends provider interface with convenience methods\n */\nexport interface Translator extends TranslationProvider {\n /**\n * Creates a pre-configured translation function for repeated translations\n * @param sourceLanguage Optional source language code (ISO 639-1). If undefined, auto-detects\n * @param targetLanguage Optional target language code (ISO 639-1). Defaults to 'en'\n * @returns A function that translates text with the pre-configured languages\n */\n templateFunction(\n sourceLanguage?: string,\n targetLanguage?: string,\n ): (text: string) => Promise<string>;\n}\n\n/**\n * Base configuration options for all providers\n */\nexport interface BaseTranslatorOptions {\n /**\n * Request timeout in milliseconds\n */\n timeout?: number;\n\n /**\n * Maximum number of retries for failed requests\n */\n maxRetries?: number;\n}\n\n/**\n * Google Translate provider options\n */\nexport interface GoogleTranslateOptions extends BaseTranslatorOptions {\n provider: 'google';\n /**\n * Google Cloud API key or credentials\n */\n apiKey: string;\n /**\n * Optional Google Cloud project ID\n */\n projectId?: string;\n}\n\n/**\n * DeepL provider options\n */\nexport interface DeepLOptions extends BaseTranslatorOptions {\n provider: 'deepl';\n /**\n * DeepL API key\n */\n apiKey: string;\n /**\n * Whether to use free or pro API endpoint (default: false = pro)\n */\n freeApi?: boolean;\n}\n\n/**\n * LibreTranslate provider options\n */\nexport interface LibreTranslateOptions extends BaseTranslatorOptions {\n provider: 'libretranslate';\n /**\n * Custom instance URL (default: https://libretranslate.com)\n */\n apiUrl?: string;\n /**\n * Optional API key for some instances\n */\n apiKey?: string;\n}\n\n/**\n * Union type for all provider options\n */\nexport type TranslatorOptions =\n | GoogleTranslateOptions\n | DeepLOptions\n | LibreTranslateOptions;\n\n/**\n * Base error class for translation operations\n *\n * @param message - Human-readable error description\n * @param code - Machine-readable error code (e.g., 'QUOTA_EXCEEDED', 'AUTH_ERROR', 'TIMEOUT')\n * @param provider - The provider that produced the error (e.g., 'google', 'deepl', 'libretranslate')\n */\nexport class TranslationError extends Error {\n constructor(\n message: string,\n public code: string,\n public provider?: string,\n ) {\n super(message);\n this.name = 'TranslationError';\n }\n}\n\n/**\n * Thrown when a requested language code is not supported by the provider\n *\n * @param language - The unsupported ISO 639-1 language code\n * @param provider - The provider that rejected the language\n */\nexport class UnsupportedLanguageError extends TranslationError {\n constructor(language: string, provider?: string) {\n super(\n `Unsupported language: ${language}`,\n 'UNSUPPORTED_LANGUAGE',\n provider,\n );\n this.name = 'UnsupportedLanguageError';\n }\n}\n\n/**\n * Thrown when the provider's translation quota or rate limit has been exceeded\n *\n * @param provider - The provider that reported the quota limit\n */\nexport class QuotaExceededError extends TranslationError {\n constructor(provider?: string) {\n super('Translation quota exceeded', 'QUOTA_EXCEEDED', provider);\n this.name = 'QuotaExceededError';\n }\n}\n\n/**\n * Thrown when the provider rejects the API key or credentials\n *\n * @param provider - The provider that rejected authentication\n */\nexport class AuthenticationError extends TranslationError {\n constructor(provider?: string) {\n super('Authentication failed', 'AUTH_ERROR', provider);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown when the input text is empty or otherwise invalid for translation\n *\n * @param reason - Description of why the text is invalid\n * @param provider - The provider that rejected the text\n */\nexport class InvalidTextError extends TranslationError {\n constructor(reason: string, provider?: string) {\n super(`Invalid text: ${reason}`, 'INVALID_TEXT', provider);\n this.name = 'InvalidTextError';\n }\n}\n","/**\n * Utility functions for translation operations\n */\n\n/**\n * Common ISO 639-1 language codes and their names\n */\nexport const LANGUAGE_NAMES: Record<string, string> = {\n en: 'English',\n es: 'Spanish',\n fr: 'French',\n de: 'German',\n it: 'Italian',\n pt: 'Portuguese',\n ru: 'Russian',\n ja: 'Japanese',\n ko: 'Korean',\n zh: 'Chinese',\n ar: 'Arabic',\n hi: 'Hindi',\n nl: 'Dutch',\n pl: 'Polish',\n tr: 'Turkish',\n sv: 'Swedish',\n da: 'Danish',\n no: 'Norwegian',\n fi: 'Finnish',\n cs: 'Czech',\n el: 'Greek',\n he: 'Hebrew',\n th: 'Thai',\n vi: 'Vietnamese',\n id: 'Indonesian',\n ro: 'Romanian',\n uk: 'Ukrainian',\n bg: 'Bulgarian',\n hr: 'Croatian',\n sk: 'Slovak',\n sl: 'Slovenian',\n lt: 'Lithuanian',\n lv: 'Latvian',\n et: 'Estonian',\n hu: 'Hungarian',\n ca: 'Catalan',\n gl: 'Galician',\n eu: 'Basque',\n fa: 'Persian',\n ur: 'Urdu',\n bn: 'Bengali',\n ta: 'Tamil',\n te: 'Telugu',\n mr: 'Marathi',\n ml: 'Malayalam',\n kn: 'Kannada',\n gu: 'Gujarati',\n pa: 'Punjabi',\n sw: 'Swahili',\n ms: 'Malay',\n tl: 'Tagalog',\n af: 'Afrikaans',\n sq: 'Albanian',\n am: 'Amharic',\n hy: 'Armenian',\n az: 'Azerbaijani',\n be: 'Belarusian',\n bs: 'Bosnian',\n my: 'Burmese',\n ceb: 'Cebuano',\n ny: 'Chichewa',\n co: 'Corsican',\n eo: 'Esperanto',\n fy: 'Frisian',\n ka: 'Georgian',\n haw: 'Hawaiian',\n hmn: 'Hmong',\n is: 'Icelandic',\n ig: 'Igbo',\n ga: 'Irish',\n jw: 'Javanese',\n kk: 'Kazakh',\n km: 'Khmer',\n ku: 'Kurdish',\n ky: 'Kyrgyz',\n lo: 'Lao',\n la: 'Latin',\n lb: 'Luxembourgish',\n mk: 'Macedonian',\n mg: 'Malagasy',\n mt: 'Maltese',\n mi: 'Maori',\n mn: 'Mongolian',\n ne: 'Nepali',\n ps: 'Pashto',\n sm: 'Samoan',\n gd: 'Scots Gaelic',\n sr: 'Serbian',\n st: 'Sesotho',\n sn: 'Shona',\n sd: 'Sindhi',\n si: 'Sinhala',\n so: 'Somali',\n su: 'Sundanese',\n tg: 'Tajik',\n tt: 'Tatar',\n tk: 'Turkmen',\n ug: 'Uyghur',\n uz: 'Uzbek',\n cy: 'Welsh',\n xh: 'Xhosa',\n yi: 'Yiddish',\n yo: 'Yoruba',\n zu: 'Zulu',\n};\n\n/**\n * Validates an ISO 639-1 language code\n * @param code Language code to validate\n * @returns True if valid\n */\nexport function isValidLanguageCode(code: string): boolean {\n return code.length === 2 && /^[a-z]{2}$/.test(code);\n}\n\n/**\n * Gets the language name from a language code\n * @param code ISO 639-1 language code\n * @returns Language name or the code if not found\n */\nexport function getLanguageName(code: string): string {\n return LANGUAGE_NAMES[code] || code;\n}\n\n/**\n * Normalizes a confidence score to 0-1 range\n * @param score Confidence score (may be 0-1 or 0-100)\n * @returns Normalized score between 0 and 1\n */\nexport function normalizeConfidence(score: number): number {\n if (score > 1 && score <= 100) {\n return score / 100;\n }\n return Math.max(0, Math.min(1, score));\n}\n\n/**\n * Validates that text is not empty\n * @param text Text to validate\n * @returns True if text is valid\n */\nexport function isValidText(text: string): boolean {\n return typeof text === 'string' && text.trim().length > 0;\n}\n\n/**\n * Truncates text to a maximum length\n * @param text Text to truncate\n * @param maxLength Maximum length\n * @returns Truncated text\n */\nexport function truncateText(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n return `${text.substring(0, maxLength - 3)}...`;\n}\n","/**\n * Translator package entry point\n * Provides standardized translation interface\n */\n\nimport { loadEnvConfig } from '@happyvertical/utils';\nimport type {\n DeepLOptions,\n GoogleTranslateOptions,\n LanguageDetectionResult,\n LibreTranslateOptions,\n SupportedLanguage,\n TranslationProvider,\n TranslationResult,\n Translator,\n TranslatorOptions,\n} from './shared/types';\n\n// Export all types\nexport * from './shared/types';\nexport * from './shared/utils';\n\n/**\n * Type guard for Google Translate options\n */\nfunction _isGoogleTranslateOptions(\n options: TranslatorOptions,\n): options is GoogleTranslateOptions {\n return options.provider === 'google';\n}\n\n/**\n * Type guard for DeepL options\n */\nfunction _isDeepLOptions(options: TranslatorOptions): options is DeepLOptions {\n return options.provider === 'deepl';\n}\n\n/**\n * Type guard for LibreTranslate options\n */\nfunction _isLibreTranslateOptions(\n options: TranslatorOptions,\n): options is LibreTranslateOptions {\n return options.provider === 'libretranslate';\n}\n\n/**\n * Translator wrapper class that adds templateFunction to providers\n */\nclass TranslatorWrapper implements Translator {\n constructor(private provider: TranslationProvider) {}\n\n async translate(\n text: string,\n targetLanguage: string,\n sourceLanguage?: string,\n ): Promise<TranslationResult> {\n return this.provider.translate(text, targetLanguage, sourceLanguage);\n }\n\n async detectLanguage(text: string): Promise<LanguageDetectionResult> {\n return this.provider.detectLanguage(text);\n }\n\n async getSupportedLanguages(): Promise<SupportedLanguage[]> {\n return this.provider.getSupportedLanguages();\n }\n\n async translateBatch(\n texts: string[],\n targetLanguage: string,\n sourceLanguage?: string,\n ): Promise<TranslationResult[]> {\n return this.provider.translateBatch(texts, targetLanguage, sourceLanguage);\n }\n\n /**\n * Creates a pre-configured translation function\n * This is the key ergonomic feature that makes repeated translations simple\n */\n templateFunction(\n sourceLanguage?: string,\n targetLanguage: string = 'en',\n ): (text: string) => Promise<string> {\n return async (text: string): Promise<string> => {\n const result = await this.provider.translate(\n text,\n targetLanguage,\n sourceLanguage,\n );\n return result.translatedText;\n };\n }\n}\n\n/**\n * Factory function to create a translator instance\n *\n * Supports environment variable configuration using HAVE_TRANSLATOR_* pattern:\n * - HAVE_TRANSLATOR_PROVIDER: Translation provider ('google'|'deepl'|'libretranslate')\n *\n * Provider-specific API keys are loaded from their respective environment variables:\n * - GOOGLE_TRANSLATE_API_KEY: Google Translate API key\n * - DEEPL_API_KEY: DeepL API key\n *\n * @param options - Configuration options for the translation provider (optional if using env vars)\n * @returns Promise resolving to a translator that implements Translator\n *\n * @example\n * ```typescript\n * // Using explicit options\n * const translator = await getTranslator({\n * provider: 'google',\n * apiKey: process.env.GOOGLE_TRANSLATE_API_KEY!\n * });\n *\n * // Using environment variables\n * // Set: HAVE_TRANSLATOR_PROVIDER=deepl\n * // DEEPL_API_KEY=your_key_here\n * const translator = await getTranslator();\n *\n * // Mix of env vars and explicit options (explicit takes precedence)\n * // Set: HAVE_TRANSLATOR_PROVIDER=google\n * const translator = await getTranslator({\n * apiKey: 'explicit_key' // Overrides env var\n * });\n *\n * // Use the translator\n * const result = await translator.translate('Hello, world!', 'es');\n *\n * // Use template function for repeated translations\n * const t = translator.templateFunction('en', 'es');\n * const greeting = await t('Hello');\n * const farewell = await t('Goodbye');\n * ```\n */\nexport async function getTranslator(\n options: Partial<TranslatorOptions> = {},\n): Promise<Translator> {\n // Load configuration from environment variables with user options taking precedence\n const config: Record<string, any> = loadEnvConfig(\n options as Record<string, any>,\n {\n packageName: 'translator',\n schema: {\n provider: 'string',\n timeout: 'number',\n maxRetries: 'number',\n },\n allowUnknown: true,\n },\n );\n\n // Load provider-specific API keys from their respective env vars\n // This maintains backward compatibility with existing env var patterns\n if (!config.apiKey) {\n if (config.provider === 'google' && process.env.GOOGLE_TRANSLATE_API_KEY) {\n config.apiKey = process.env.GOOGLE_TRANSLATE_API_KEY;\n } else if (config.provider === 'deepl' && process.env.DEEPL_API_KEY) {\n config.apiKey = process.env.DEEPL_API_KEY;\n }\n }\n\n // Validate provider is set\n if (!config.provider) {\n throw new Error(\n 'Translation provider not specified. Set HAVE_TRANSLATOR_PROVIDER environment variable or pass provider in options.',\n );\n }\n\n let provider: TranslationProvider;\n\n if (config.provider === 'google') {\n const { GoogleTranslateProvider } = await import('./providers/google.js');\n provider = new GoogleTranslateProvider(config as GoogleTranslateOptions);\n } else if (config.provider === 'deepl') {\n const { DeepLProvider } = await import('./providers/deepl.js');\n provider = new DeepLProvider(config as DeepLOptions);\n } else if (config.provider === 'libretranslate') {\n const { LibreTranslateProvider } = await import(\n './providers/libretranslate.js'\n );\n provider = new LibreTranslateProvider(config as LibreTranslateOptions);\n } else {\n throw new Error(`Unsupported provider: ${config.provider}`);\n }\n\n return new TranslatorWrapper(provider);\n}\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { DeepLOptions, LanguageDetectionResult, SupportedLanguage, TranslationProvider, TranslationResult } from '../shared/types';
2
+ /**
3
+ * DeepL provider implementation with in-memory caching
4
+ */
5
+ export declare class DeepLProvider implements TranslationProvider {
6
+ private client;
7
+ private timeout;
8
+ private cache;
9
+ constructor(options: DeepLOptions);
10
+ /**
11
+ * Initializes the memory cache for translation results
12
+ */
13
+ private initCache;
14
+ /**
15
+ * Generates a cache key for translation requests
16
+ */
17
+ private getCacheKey;
18
+ /**
19
+ * Translates text from source language to target language
20
+ */
21
+ translate(text: string, targetLanguage: string, sourceLanguage?: string): Promise<TranslationResult>;
22
+ /**
23
+ * Detects the language of the provided text
24
+ * Note: DeepL doesn't have a dedicated language detection API,
25
+ * so we translate to English and use the detected source language
26
+ */
27
+ detectLanguage(text: string): Promise<LanguageDetectionResult>;
28
+ /**
29
+ * Gets the list of languages supported by this provider
30
+ */
31
+ getSupportedLanguages(): Promise<SupportedLanguage[]>;
32
+ /**
33
+ * Translates multiple texts in a single batch operation
34
+ */
35
+ translateBatch(texts: string[], targetLanguage: string, sourceLanguage?: string): Promise<TranslationResult[]>;
36
+ /**
37
+ * Maps DeepL errors to standardized error types
38
+ */
39
+ private mapError;
40
+ }
41
+ //# sourceMappingURL=deepl.d.ts.map