langie 1.9.25

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 @@
1
+ {"version":3,"sources":["../src/core.ts","../src/constants.ts"],"sourcesContent":["/**\n * Core translation functions\n */\nimport type {\n TranslateRequestBody,\n TranslateServiceResponse,\n TranslatorLanguage,\n TranslatorOptions\n} from './types'\nimport {\n API_FIELD_TEXT,\n API_FIELD_FROM,\n API_FIELD_TO,\n API_FIELD_CTX,\n API_FIELD_TRANSLATIONS,\n API_FIELD_ERROR\n} from './constants'\n\nconst DEFAULT_TRANSLATOR_HOST = 'http://localhost:8081'\n\n// --- Translation cache ---\nconst translationCache = new Map<string, Promise<TranslateServiceResponse[]>>()\n\nfunction getTranslationCacheKey(\n serviceTranslations: TranslateRequestBody[],\n options: TranslatorOptions\n): string {\n // Create a stable cache key based on translations and options\n return JSON.stringify({\n translations: serviceTranslations.map((t) => ({\n [API_FIELD_TEXT]: t[API_FIELD_TEXT],\n [API_FIELD_FROM]: t[API_FIELD_FROM],\n [API_FIELD_TO]: t[API_FIELD_TO],\n [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || 'ui'\n })),\n host: options.translatorHost || DEFAULT_TRANSLATOR_HOST,\n context: options[API_FIELD_CTX] || 'ui'\n })\n}\n\n/**\n * Translates a batch of texts\n *\n * @param translations Array of translation requests\n * @param options Configuration options\n * @returns Array of translated texts\n */\nexport async function translateBatch(\n translations: TranslateRequestBody[] = [],\n options: TranslatorOptions = {}\n): Promise<TranslateServiceResponse[]> {\n // const startTime = Date.now()\n\n // try {\n // console.debug('[translator-sdk] translateBatch called', {\n // count: translations.length,\n // sample: translations.slice(0, 3).map((t) => ({\n // text: t.text?.slice?.(0, 30) ?? '',\n // from: t.from_lang,\n // to: t.to_lang\n // })),\n // opts: { ...options }\n // })\n // } catch (_) {\n // // Ignore logging errors – never block translation\n // }\n\n if (!Array.isArray(translations) || translations.length === 0) {\n // console.error('[translator-sdk] Invalid translations input:', { translations })\n throw new Error('translations must be a non-empty array')\n }\n\n const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST\n const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY\n\n // console.debug('[translator-sdk] Configuration', {\n // translatorHost,\n // hasApiKey: !!apiKey,\n // apiKeyLength: apiKey?.length || 0\n // })\n\n // Partition translations: identical language pairs can be returned as-is\n const serviceTranslations: TranslateRequestBody[] = []\n const indexMap: number[] = []\n\n translations.forEach((tr, idx) => {\n const from = (tr[API_FIELD_FROM] || '').toLowerCase()\n const to = (tr[API_FIELD_TO] || '').toLowerCase()\n if (from === to) {\n // console.debug('[translator-sdk] Skipping translation (same language)', { from, to, text: tr.t?.slice(0, 20) })\n return // no need to translate\n }\n serviceTranslations.push(tr)\n indexMap.push(idx)\n })\n\n // console.debug('[translator-sdk] Translation partitioning', {\n // total: translations.length,\n // needsTranslation: serviceTranslations.length,\n // skipped: translations.length - serviceTranslations.length\n // })\n\n let serviceResults: TranslateServiceResponse[] = []\n if (serviceTranslations.length > 0) {\n // Check cache first\n const cacheKey = getTranslationCacheKey(serviceTranslations, options)\n if (translationCache.has(cacheKey)) {\n // console.debug('[translator-sdk] Cache hit for translation batch')\n serviceResults = await translationCache.get(cacheKey)!\n } else {\n // const requestStartTime = Date.now()\n // console.debug('[translator-sdk] Sending request to translator', {\n // host: translatorHost,\n // apiKeySnippet: apiKey ? `${apiKey.slice(0, 4)}…${apiKey.slice(-4)}` : 'none',\n // batchCount: serviceTranslations.length,\n // requestSize: JSON.stringify({ translations: serviceTranslations }).length\n // })\n\n const translationPromise = (async () => {\n try {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 5000) // 5s timeout\n\n const resp = await fetch(`${translatorHost}/translate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { Authorization: `Bearer ${apiKey}`, 'X-Api-Key': apiKey } : {})\n },\n body: JSON.stringify({\n [API_FIELD_TRANSLATIONS]: serviceTranslations.map((t) => ({\n [API_FIELD_TEXT]: t[API_FIELD_TEXT],\n [API_FIELD_FROM]: t[API_FIELD_FROM],\n [API_FIELD_TO]: t[API_FIELD_TO],\n [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || 'ui'\n })),\n [API_FIELD_CTX]: options[API_FIELD_CTX] || 'ui'\n }),\n signal: controller.signal\n })\n\n clearTimeout(timeoutId)\n // const requestDuration = Date.now() - requestStartTime\n\n // console.debug('[translator-sdk] Translator response received', {\n // status: resp.status,\n // statusText: resp.statusText,\n // duration: `${requestDuration}ms`,\n // contentType: resp.headers.get('content-type'),\n // contentLength: resp.headers.get('content-length')\n // })\n\n if (!resp.ok) {\n console.error(\n '[translator-sdk] Translator error response:',\n resp.status,\n resp.statusText\n )\n throw new Error(`Translator service error: ${resp.status} ${resp.statusText}`)\n }\n\n let parsed: {\n translations?: TranslateServiceResponse[]\n t?: string\n error?: string\n } | null\n try {\n parsed = await resp.clone().json()\n // console.debug('[translator-sdk] Response parsed successfully', {\n // hasTranslations: !!parsed?.translations,\n // translationCount: parsed?.translations?.length || 0,\n // hasLegacyT: !!parsed?.t\n // })\n } catch (jsonErr) {\n console.error('[translator-sdk] Failed to parse JSON response:', jsonErr)\n throw new Error(`Failed to parse JSON response: ${jsonErr}`)\n }\n\n // if (parsed) {\n // console.debug(\n // '[translator-sdk] Translator response sample',\n // JSON.stringify(parsed).slice(0, 500)\n // )\n // }\n\n const data = parsed || {}\n\n // Handle top-level error responses\n if (data[API_FIELD_ERROR]) {\n console.warn(`[translator-sdk] Top-level API error:`, data[API_FIELD_ERROR])\n\n // Create error responses for all translations\n return serviceTranslations.map((translation) => ({\n [API_FIELD_TEXT]: translation[API_FIELD_TEXT],\n [API_FIELD_ERROR]: data[API_FIELD_ERROR]\n }))\n }\n\n const results = Array.isArray(data[API_FIELD_TRANSLATIONS])\n ? data[API_FIELD_TRANSLATIONS].map((translation, index) => {\n const originalText = serviceTranslations[index]?.[API_FIELD_TEXT] || ''\n\n // Handle error responses\n if (translation[API_FIELD_ERROR]) {\n console.warn(\n `[translator-sdk] Translation error for \"${originalText}\":`,\n translation[API_FIELD_ERROR]\n )\n return {\n [API_FIELD_TEXT]: originalText, // Return original text on error\n [API_FIELD_ERROR]: translation[API_FIELD_ERROR]\n }\n }\n\n // Handle language detection response\n if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {\n return {\n [API_FIELD_TEXT]: originalText, // For detection, return original text\n [API_FIELD_FROM]: translation[API_FIELD_FROM]\n }\n }\n\n // Handle translation response\n return {\n [API_FIELD_TEXT]: translation[API_FIELD_TEXT] || originalText\n }\n })\n : data[API_FIELD_TEXT]\n ? [{ [API_FIELD_TEXT]: data[API_FIELD_TEXT] }]\n : []\n\n // console.debug('[translator-sdk] Service results processed', {\n // resultCount: results.length,\n // expectedCount: serviceTranslations.length\n // })\n\n return results\n } catch (error: unknown) {\n // Remove from cache on error\n translationCache.delete(cacheKey)\n\n if (error instanceof Error && error.name === 'AbortError') {\n const message = `Translator request to ${translatorHost} timed out after 5 seconds.`\n console.error(message)\n throw new Error(message)\n }\n const message = `Failed to connect to translator at ${translatorHost}. Is the service running?`\n console.error(message)\n throw new Error(message)\n }\n })()\n\n // Cache the promise\n translationCache.set(cacheKey, translationPromise)\n serviceResults = await translationPromise\n }\n }\n\n // Assemble final results in original order\n const final = translations.map((tr, idx) => {\n const from = (tr[API_FIELD_FROM] || '').toLowerCase()\n const to = (tr[API_FIELD_TO] || '').toLowerCase()\n if (from === to) return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] }\n\n const svcIdx = indexMap.indexOf(idx)\n if (svcIdx !== -1) return serviceResults[svcIdx] || { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] }\n return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] }\n })\n\n // const totalDuration = Date.now() - startTime\n // console.debug('[translator-sdk] translateBatch completed', {\n // totalDuration: `${totalDuration}ms`,\n // resultCount: final.length,\n // successCount: final.filter(r => r.translated && r.translated !== r.text).length\n // })\n\n return final as TranslateServiceResponse[]\n}\n\n// --- Cache management functions ---\nexport function clearTranslationCache(): void {\n translationCache.clear()\n}\n\nexport function getTranslationCacheSize(): number {\n return translationCache.size\n}\n\n/**\n * Fetches available languages from the translation service\n *\n * @param options Configuration options\n * @returns Array of available languages\n */\nexport async function fetchAvailableLanguages(\n options: TranslatorOptions = {}\n): Promise<TranslatorLanguage[]> {\n // const startTime = Date.now()\n\n // console.debug('[translator-sdk] fetchAvailableLanguages called', { options })\n\n const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST\n const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY\n const minPop =\n options.minPopularity !== undefined\n ? Number(options.minPopularity)\n : Number.parseFloat(process.env.MIN_LANGUAGE_POPULARITY || '0.1')\n\n // console.debug('[translator-sdk] Languages request config', {\n // translatorHost,\n // hasApiKey: !!apiKey,\n // minPopularity: minPop,\n // country: options.country,\n // region: options.region\n // })\n\n const queryParams = []\n if (options.country) queryParams.push(`country=${encodeURIComponent(options.country)}`)\n if (options.region) queryParams.push(`region=${encodeURIComponent(options.region)}`)\n const queryStr = queryParams.length ? `?${queryParams.join('&')}` : ''\n\n const requestUrl = `${translatorHost}/languages${queryStr}`\n\n try {\n const resp = await fetch(requestUrl, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { Authorization: `Bearer ${apiKey}`, 'X-Api-Key': apiKey } : {})\n }\n })\n\n if (!resp.ok) {\n throw new Error(`Translator languages error: ${resp.status}`)\n }\n\n const data = await resp.json()\n const languages = Array.isArray(data) ? data : data.languages || []\n\n const filtered = languages.filter((lang: TranslatorLanguage) => {\n if (lang.popularity === undefined || lang.popularity === null) return true\n return Number(lang.popularity) >= minPop\n })\n\n return filtered\n } catch (error) {\n console.error('[translator-sdk] Languages fetch error:', error)\n throw error\n }\n}\n","/**\n * Default API configuration constants\n */\nexport const DEFAULT_API_HOST = 'https://api.langie.uk/v1'\n\n/**\n * Development/Demo API host for local testing\n */\nexport const DEV_API_HOST = 'http://localhost:8081/v1'\n\n/**\n * Default translator options\n */\nexport const DEFAULT_TRANSLATOR_OPTIONS = {\n translatorHost: DEFAULT_API_HOST,\n defaultLanguage: 'en',\n fallbackLanguage: 'en'\n} as const\n\n/**\n * API field constants\n * Used throughout the SDK to ensure consistency\n */\n\n// Translation text field\nexport const API_FIELD_TEXT = 't'\n\n// Source language field\nexport const API_FIELD_FROM = 'from'\n\n// Target language field\nexport const API_FIELD_TO = 'to'\n\n// Context field\nexport const API_FIELD_CTX = 'ctx'\n\n// Translated text field (same as text field for response)\nexport const API_FIELD_TRANSLATED = 't'\n\n// Translations array field\nexport const API_FIELD_TRANSLATIONS = 'translations'\n\n// Error field for API responses\nexport const API_FIELD_ERROR = 'error'\n"],"mappings":"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","names":["message"]}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * API field constants
3
+ * Used throughout the SDK to ensure consistency
4
+ */
5
+ declare const API_FIELD_TEXT = "t";
6
+ declare const API_FIELD_FROM = "from";
7
+ declare const API_FIELD_TO = "to";
8
+ declare const API_FIELD_CTX = "ctx";
9
+ declare const API_FIELD_TRANSLATIONS = "translations";
10
+ declare const API_FIELD_ERROR = "error";
11
+
12
+ /**
13
+ * Type definitions for Vue Translator SDK
14
+ */
15
+ /**
16
+ * Language definition returned by the translation service
17
+ */
18
+ interface TranslatorLanguage {
19
+ code: string;
20
+ name: string;
21
+ native_name: string;
22
+ popularity?: number;
23
+ flag_country?: string;
24
+ value?: string;
25
+ }
26
+ /**
27
+ * Translation request body
28
+ */
29
+ interface TranslateRequestBody {
30
+ [API_FIELD_TEXT]: string;
31
+ [API_FIELD_FROM]?: string;
32
+ [API_FIELD_TO]?: string;
33
+ [API_FIELD_CTX]?: string;
34
+ }
35
+ /**
36
+ * Translation service response
37
+ */
38
+ interface TranslateServiceResponse {
39
+ [API_FIELD_TEXT]?: string;
40
+ [API_FIELD_FROM]?: string;
41
+ [API_FIELD_TRANSLATIONS]?: TranslateServiceResponse[];
42
+ [API_FIELD_ERROR]?: string;
43
+ }
44
+ /**
45
+ * Configuration options for the translator
46
+ */
47
+ interface TranslatorOptions {
48
+ translatorHost?: string;
49
+ apiKey?: string;
50
+ defaultLanguage?: string;
51
+ fallbackLanguage?: string;
52
+ /**
53
+ * Delay (ms) before the first batch of UI translations is sent.
54
+ * Allows collecting more keys into a single request on initial page load.
55
+ * Defaults to 100ms.
56
+ */
57
+ initialBatchDelay?: number;
58
+ /**
59
+ * Debounce delay (ms) for subsequent flushes after the initial batch.
60
+ * Defaults to 25ms.
61
+ */
62
+ followupBatchDelay?: number;
63
+ /**
64
+ * Maximum number of translations to send in a single batch request.
65
+ * Defaults to 50. Larger batches may be more efficient but could timeout.
66
+ */
67
+ maxBatchSize?: number;
68
+ /**
69
+ * Maximum time (ms) to wait before sending a batch.
70
+ * Defaults to 2000ms (2 seconds).
71
+ */
72
+ maxWaitTime?: number;
73
+ minPopularity?: number;
74
+ country?: string;
75
+ region?: string;
76
+ ctx?: string;
77
+ }
78
+
79
+ /**
80
+ * Core translation functions
81
+ */
82
+
83
+ /**
84
+ * Translates a batch of texts
85
+ *
86
+ * @param translations Array of translation requests
87
+ * @param options Configuration options
88
+ * @returns Array of translated texts
89
+ */
90
+ declare function translateBatch(translations?: TranslateRequestBody[], options?: TranslatorOptions): Promise<TranslateServiceResponse[]>;
91
+ declare function clearTranslationCache(): void;
92
+ declare function getTranslationCacheSize(): number;
93
+ /**
94
+ * Fetches available languages from the translation service
95
+ *
96
+ * @param options Configuration options
97
+ * @returns Array of available languages
98
+ */
99
+ declare function fetchAvailableLanguages(options?: TranslatorOptions): Promise<TranslatorLanguage[]>;
100
+
101
+ export { clearTranslationCache, fetchAvailableLanguages, getTranslationCacheSize, translateBatch };
package/dist/core.d.ts ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * API field constants
3
+ * Used throughout the SDK to ensure consistency
4
+ */
5
+ declare const API_FIELD_TEXT = "t";
6
+ declare const API_FIELD_FROM = "from";
7
+ declare const API_FIELD_TO = "to";
8
+ declare const API_FIELD_CTX = "ctx";
9
+ declare const API_FIELD_TRANSLATIONS = "translations";
10
+ declare const API_FIELD_ERROR = "error";
11
+
12
+ /**
13
+ * Type definitions for Vue Translator SDK
14
+ */
15
+ /**
16
+ * Language definition returned by the translation service
17
+ */
18
+ interface TranslatorLanguage {
19
+ code: string;
20
+ name: string;
21
+ native_name: string;
22
+ popularity?: number;
23
+ flag_country?: string;
24
+ value?: string;
25
+ }
26
+ /**
27
+ * Translation request body
28
+ */
29
+ interface TranslateRequestBody {
30
+ [API_FIELD_TEXT]: string;
31
+ [API_FIELD_FROM]?: string;
32
+ [API_FIELD_TO]?: string;
33
+ [API_FIELD_CTX]?: string;
34
+ }
35
+ /**
36
+ * Translation service response
37
+ */
38
+ interface TranslateServiceResponse {
39
+ [API_FIELD_TEXT]?: string;
40
+ [API_FIELD_FROM]?: string;
41
+ [API_FIELD_TRANSLATIONS]?: TranslateServiceResponse[];
42
+ [API_FIELD_ERROR]?: string;
43
+ }
44
+ /**
45
+ * Configuration options for the translator
46
+ */
47
+ interface TranslatorOptions {
48
+ translatorHost?: string;
49
+ apiKey?: string;
50
+ defaultLanguage?: string;
51
+ fallbackLanguage?: string;
52
+ /**
53
+ * Delay (ms) before the first batch of UI translations is sent.
54
+ * Allows collecting more keys into a single request on initial page load.
55
+ * Defaults to 100ms.
56
+ */
57
+ initialBatchDelay?: number;
58
+ /**
59
+ * Debounce delay (ms) for subsequent flushes after the initial batch.
60
+ * Defaults to 25ms.
61
+ */
62
+ followupBatchDelay?: number;
63
+ /**
64
+ * Maximum number of translations to send in a single batch request.
65
+ * Defaults to 50. Larger batches may be more efficient but could timeout.
66
+ */
67
+ maxBatchSize?: number;
68
+ /**
69
+ * Maximum time (ms) to wait before sending a batch.
70
+ * Defaults to 2000ms (2 seconds).
71
+ */
72
+ maxWaitTime?: number;
73
+ minPopularity?: number;
74
+ country?: string;
75
+ region?: string;
76
+ ctx?: string;
77
+ }
78
+
79
+ /**
80
+ * Core translation functions
81
+ */
82
+
83
+ /**
84
+ * Translates a batch of texts
85
+ *
86
+ * @param translations Array of translation requests
87
+ * @param options Configuration options
88
+ * @returns Array of translated texts
89
+ */
90
+ declare function translateBatch(translations?: TranslateRequestBody[], options?: TranslatorOptions): Promise<TranslateServiceResponse[]>;
91
+ declare function clearTranslationCache(): void;
92
+ declare function getTranslationCacheSize(): number;
93
+ /**
94
+ * Fetches available languages from the translation service
95
+ *
96
+ * @param options Configuration options
97
+ * @returns Array of available languages
98
+ */
99
+ declare function fetchAvailableLanguages(options?: TranslatorOptions): Promise<TranslatorLanguage[]>;
100
+
101
+ export { clearTranslationCache, fetchAvailableLanguages, getTranslationCacheSize, translateBatch };
package/dist/core.mjs ADDED
@@ -0,0 +1,196 @@
1
+ /**
2
+ * langie v1.9.25
3
+ * (c) 2026 nlit
4
+ * @license Apache-2.0
5
+ *
6
+ * Licensed under the Apache License, Version 2.0
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ */
9
+
10
+ // src/constants.ts
11
+ var API_FIELD_TEXT = "t";
12
+ var API_FIELD_FROM = "from";
13
+ var API_FIELD_TO = "to";
14
+ var API_FIELD_CTX = "ctx";
15
+ var API_FIELD_TRANSLATIONS = "translations";
16
+ var API_FIELD_ERROR = "error";
17
+
18
+ // src/core.ts
19
+ var DEFAULT_TRANSLATOR_HOST = "http://localhost:8081";
20
+ var translationCache = /* @__PURE__ */ new Map();
21
+ function getTranslationCacheKey(serviceTranslations, options) {
22
+ return JSON.stringify({
23
+ translations: serviceTranslations.map((t) => ({
24
+ [API_FIELD_TEXT]: t[API_FIELD_TEXT],
25
+ [API_FIELD_FROM]: t[API_FIELD_FROM],
26
+ [API_FIELD_TO]: t[API_FIELD_TO],
27
+ [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || "ui"
28
+ })),
29
+ host: options.translatorHost || DEFAULT_TRANSLATOR_HOST,
30
+ context: options[API_FIELD_CTX] || "ui"
31
+ });
32
+ }
33
+ async function translateBatch(translations = [], options = {}) {
34
+ if (!Array.isArray(translations) || translations.length === 0) {
35
+ throw new Error("translations must be a non-empty array");
36
+ }
37
+ const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST;
38
+ const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY;
39
+ const serviceTranslations = [];
40
+ const indexMap = [];
41
+ translations.forEach((tr, idx) => {
42
+ const from = (tr[API_FIELD_FROM] || "").toLowerCase();
43
+ const to = (tr[API_FIELD_TO] || "").toLowerCase();
44
+ if (from === to) {
45
+ return;
46
+ }
47
+ serviceTranslations.push(tr);
48
+ indexMap.push(idx);
49
+ });
50
+ let serviceResults = [];
51
+ if (serviceTranslations.length > 0) {
52
+ const cacheKey = getTranslationCacheKey(serviceTranslations, options);
53
+ if (translationCache.has(cacheKey)) {
54
+ serviceResults = await translationCache.get(cacheKey);
55
+ } else {
56
+ const translationPromise = (async () => {
57
+ try {
58
+ const controller = new AbortController();
59
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
60
+ const resp = await fetch(`${translatorHost}/translate`, {
61
+ method: "POST",
62
+ headers: {
63
+ "Content-Type": "application/json",
64
+ ...apiKey ? { Authorization: `Bearer ${apiKey}`, "X-Api-Key": apiKey } : {}
65
+ },
66
+ body: JSON.stringify({
67
+ [API_FIELD_TRANSLATIONS]: serviceTranslations.map((t) => ({
68
+ [API_FIELD_TEXT]: t[API_FIELD_TEXT],
69
+ [API_FIELD_FROM]: t[API_FIELD_FROM],
70
+ [API_FIELD_TO]: t[API_FIELD_TO],
71
+ [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || "ui"
72
+ })),
73
+ [API_FIELD_CTX]: options[API_FIELD_CTX] || "ui"
74
+ }),
75
+ signal: controller.signal
76
+ });
77
+ clearTimeout(timeoutId);
78
+ if (!resp.ok) {
79
+ console.error(
80
+ "[translator-sdk] Translator error response:",
81
+ resp.status,
82
+ resp.statusText
83
+ );
84
+ throw new Error(`Translator service error: ${resp.status} ${resp.statusText}`);
85
+ }
86
+ let parsed;
87
+ try {
88
+ parsed = await resp.clone().json();
89
+ } catch (jsonErr) {
90
+ console.error("[translator-sdk] Failed to parse JSON response:", jsonErr);
91
+ throw new Error(`Failed to parse JSON response: ${jsonErr}`);
92
+ }
93
+ const data = parsed || {};
94
+ if (data[API_FIELD_ERROR]) {
95
+ console.warn(`[translator-sdk] Top-level API error:`, data[API_FIELD_ERROR]);
96
+ return serviceTranslations.map((translation) => ({
97
+ [API_FIELD_TEXT]: translation[API_FIELD_TEXT],
98
+ [API_FIELD_ERROR]: data[API_FIELD_ERROR]
99
+ }));
100
+ }
101
+ const results = Array.isArray(data[API_FIELD_TRANSLATIONS]) ? data[API_FIELD_TRANSLATIONS].map((translation, index) => {
102
+ const originalText = serviceTranslations[index]?.[API_FIELD_TEXT] || "";
103
+ if (translation[API_FIELD_ERROR]) {
104
+ console.warn(
105
+ `[translator-sdk] Translation error for "${originalText}":`,
106
+ translation[API_FIELD_ERROR]
107
+ );
108
+ return {
109
+ [API_FIELD_TEXT]: originalText,
110
+ // Return original text on error
111
+ [API_FIELD_ERROR]: translation[API_FIELD_ERROR]
112
+ };
113
+ }
114
+ if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {
115
+ return {
116
+ [API_FIELD_TEXT]: originalText,
117
+ // For detection, return original text
118
+ [API_FIELD_FROM]: translation[API_FIELD_FROM]
119
+ };
120
+ }
121
+ return {
122
+ [API_FIELD_TEXT]: translation[API_FIELD_TEXT] || originalText
123
+ };
124
+ }) : data[API_FIELD_TEXT] ? [{ [API_FIELD_TEXT]: data[API_FIELD_TEXT] }] : [];
125
+ return results;
126
+ } catch (error) {
127
+ translationCache.delete(cacheKey);
128
+ if (error instanceof Error && error.name === "AbortError") {
129
+ const message2 = `Translator request to ${translatorHost} timed out after 5 seconds.`;
130
+ console.error(message2);
131
+ throw new Error(message2);
132
+ }
133
+ const message = `Failed to connect to translator at ${translatorHost}. Is the service running?`;
134
+ console.error(message);
135
+ throw new Error(message);
136
+ }
137
+ })();
138
+ translationCache.set(cacheKey, translationPromise);
139
+ serviceResults = await translationPromise;
140
+ }
141
+ }
142
+ const final = translations.map((tr, idx) => {
143
+ const from = (tr[API_FIELD_FROM] || "").toLowerCase();
144
+ const to = (tr[API_FIELD_TO] || "").toLowerCase();
145
+ if (from === to) return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
146
+ const svcIdx = indexMap.indexOf(idx);
147
+ if (svcIdx !== -1) return serviceResults[svcIdx] || { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
148
+ return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
149
+ });
150
+ return final;
151
+ }
152
+ function clearTranslationCache() {
153
+ translationCache.clear();
154
+ }
155
+ function getTranslationCacheSize() {
156
+ return translationCache.size;
157
+ }
158
+ async function fetchAvailableLanguages(options = {}) {
159
+ const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST;
160
+ const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY;
161
+ const minPop = options.minPopularity !== void 0 ? Number(options.minPopularity) : Number.parseFloat(process.env.MIN_LANGUAGE_POPULARITY || "0.1");
162
+ const queryParams = [];
163
+ if (options.country) queryParams.push(`country=${encodeURIComponent(options.country)}`);
164
+ if (options.region) queryParams.push(`region=${encodeURIComponent(options.region)}`);
165
+ const queryStr = queryParams.length ? `?${queryParams.join("&")}` : "";
166
+ const requestUrl = `${translatorHost}/languages${queryStr}`;
167
+ try {
168
+ const resp = await fetch(requestUrl, {
169
+ method: "GET",
170
+ headers: {
171
+ "Content-Type": "application/json",
172
+ ...apiKey ? { Authorization: `Bearer ${apiKey}`, "X-Api-Key": apiKey } : {}
173
+ }
174
+ });
175
+ if (!resp.ok) {
176
+ throw new Error(`Translator languages error: ${resp.status}`);
177
+ }
178
+ const data = await resp.json();
179
+ const languages = Array.isArray(data) ? data : data.languages || [];
180
+ const filtered = languages.filter((lang) => {
181
+ if (lang.popularity === void 0 || lang.popularity === null) return true;
182
+ return Number(lang.popularity) >= minPop;
183
+ });
184
+ return filtered;
185
+ } catch (error) {
186
+ console.error("[translator-sdk] Languages fetch error:", error);
187
+ throw error;
188
+ }
189
+ }
190
+ export {
191
+ clearTranslationCache,
192
+ fetchAvailableLanguages,
193
+ getTranslationCacheSize,
194
+ translateBatch
195
+ };
196
+ //# sourceMappingURL=core.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts","../src/core.ts"],"sourcesContent":["/**\n * Default API configuration constants\n */\nexport const DEFAULT_API_HOST = 'https://api.langie.uk/v1'\n\n/**\n * Development/Demo API host for local testing\n */\nexport const DEV_API_HOST = 'http://localhost:8081/v1'\n\n/**\n * Default translator options\n */\nexport const DEFAULT_TRANSLATOR_OPTIONS = {\n translatorHost: DEFAULT_API_HOST,\n defaultLanguage: 'en',\n fallbackLanguage: 'en'\n} as const\n\n/**\n * API field constants\n * Used throughout the SDK to ensure consistency\n */\n\n// Translation text field\nexport const API_FIELD_TEXT = 't'\n\n// Source language field\nexport const API_FIELD_FROM = 'from'\n\n// Target language field\nexport const API_FIELD_TO = 'to'\n\n// Context field\nexport const API_FIELD_CTX = 'ctx'\n\n// Translated text field (same as text field for response)\nexport const API_FIELD_TRANSLATED = 't'\n\n// Translations array field\nexport const API_FIELD_TRANSLATIONS = 'translations'\n\n// Error field for API responses\nexport const API_FIELD_ERROR = 'error'\n","/**\n * Core translation functions\n */\nimport type {\n TranslateRequestBody,\n TranslateServiceResponse,\n TranslatorLanguage,\n TranslatorOptions\n} from './types'\nimport {\n API_FIELD_TEXT,\n API_FIELD_FROM,\n API_FIELD_TO,\n API_FIELD_CTX,\n API_FIELD_TRANSLATIONS,\n API_FIELD_ERROR\n} from './constants'\n\nconst DEFAULT_TRANSLATOR_HOST = 'http://localhost:8081'\n\n// --- Translation cache ---\nconst translationCache = new Map<string, Promise<TranslateServiceResponse[]>>()\n\nfunction getTranslationCacheKey(\n serviceTranslations: TranslateRequestBody[],\n options: TranslatorOptions\n): string {\n // Create a stable cache key based on translations and options\n return JSON.stringify({\n translations: serviceTranslations.map((t) => ({\n [API_FIELD_TEXT]: t[API_FIELD_TEXT],\n [API_FIELD_FROM]: t[API_FIELD_FROM],\n [API_FIELD_TO]: t[API_FIELD_TO],\n [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || 'ui'\n })),\n host: options.translatorHost || DEFAULT_TRANSLATOR_HOST,\n context: options[API_FIELD_CTX] || 'ui'\n })\n}\n\n/**\n * Translates a batch of texts\n *\n * @param translations Array of translation requests\n * @param options Configuration options\n * @returns Array of translated texts\n */\nexport async function translateBatch(\n translations: TranslateRequestBody[] = [],\n options: TranslatorOptions = {}\n): Promise<TranslateServiceResponse[]> {\n // const startTime = Date.now()\n\n // try {\n // console.debug('[translator-sdk] translateBatch called', {\n // count: translations.length,\n // sample: translations.slice(0, 3).map((t) => ({\n // text: t.text?.slice?.(0, 30) ?? '',\n // from: t.from_lang,\n // to: t.to_lang\n // })),\n // opts: { ...options }\n // })\n // } catch (_) {\n // // Ignore logging errors – never block translation\n // }\n\n if (!Array.isArray(translations) || translations.length === 0) {\n // console.error('[translator-sdk] Invalid translations input:', { translations })\n throw new Error('translations must be a non-empty array')\n }\n\n const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST\n const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY\n\n // console.debug('[translator-sdk] Configuration', {\n // translatorHost,\n // hasApiKey: !!apiKey,\n // apiKeyLength: apiKey?.length || 0\n // })\n\n // Partition translations: identical language pairs can be returned as-is\n const serviceTranslations: TranslateRequestBody[] = []\n const indexMap: number[] = []\n\n translations.forEach((tr, idx) => {\n const from = (tr[API_FIELD_FROM] || '').toLowerCase()\n const to = (tr[API_FIELD_TO] || '').toLowerCase()\n if (from === to) {\n // console.debug('[translator-sdk] Skipping translation (same language)', { from, to, text: tr.t?.slice(0, 20) })\n return // no need to translate\n }\n serviceTranslations.push(tr)\n indexMap.push(idx)\n })\n\n // console.debug('[translator-sdk] Translation partitioning', {\n // total: translations.length,\n // needsTranslation: serviceTranslations.length,\n // skipped: translations.length - serviceTranslations.length\n // })\n\n let serviceResults: TranslateServiceResponse[] = []\n if (serviceTranslations.length > 0) {\n // Check cache first\n const cacheKey = getTranslationCacheKey(serviceTranslations, options)\n if (translationCache.has(cacheKey)) {\n // console.debug('[translator-sdk] Cache hit for translation batch')\n serviceResults = await translationCache.get(cacheKey)!\n } else {\n // const requestStartTime = Date.now()\n // console.debug('[translator-sdk] Sending request to translator', {\n // host: translatorHost,\n // apiKeySnippet: apiKey ? `${apiKey.slice(0, 4)}…${apiKey.slice(-4)}` : 'none',\n // batchCount: serviceTranslations.length,\n // requestSize: JSON.stringify({ translations: serviceTranslations }).length\n // })\n\n const translationPromise = (async () => {\n try {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 5000) // 5s timeout\n\n const resp = await fetch(`${translatorHost}/translate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { Authorization: `Bearer ${apiKey}`, 'X-Api-Key': apiKey } : {})\n },\n body: JSON.stringify({\n [API_FIELD_TRANSLATIONS]: serviceTranslations.map((t) => ({\n [API_FIELD_TEXT]: t[API_FIELD_TEXT],\n [API_FIELD_FROM]: t[API_FIELD_FROM],\n [API_FIELD_TO]: t[API_FIELD_TO],\n [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || 'ui'\n })),\n [API_FIELD_CTX]: options[API_FIELD_CTX] || 'ui'\n }),\n signal: controller.signal\n })\n\n clearTimeout(timeoutId)\n // const requestDuration = Date.now() - requestStartTime\n\n // console.debug('[translator-sdk] Translator response received', {\n // status: resp.status,\n // statusText: resp.statusText,\n // duration: `${requestDuration}ms`,\n // contentType: resp.headers.get('content-type'),\n // contentLength: resp.headers.get('content-length')\n // })\n\n if (!resp.ok) {\n console.error(\n '[translator-sdk] Translator error response:',\n resp.status,\n resp.statusText\n )\n throw new Error(`Translator service error: ${resp.status} ${resp.statusText}`)\n }\n\n let parsed: {\n translations?: TranslateServiceResponse[]\n t?: string\n error?: string\n } | null\n try {\n parsed = await resp.clone().json()\n // console.debug('[translator-sdk] Response parsed successfully', {\n // hasTranslations: !!parsed?.translations,\n // translationCount: parsed?.translations?.length || 0,\n // hasLegacyT: !!parsed?.t\n // })\n } catch (jsonErr) {\n console.error('[translator-sdk] Failed to parse JSON response:', jsonErr)\n throw new Error(`Failed to parse JSON response: ${jsonErr}`)\n }\n\n // if (parsed) {\n // console.debug(\n // '[translator-sdk] Translator response sample',\n // JSON.stringify(parsed).slice(0, 500)\n // )\n // }\n\n const data = parsed || {}\n\n // Handle top-level error responses\n if (data[API_FIELD_ERROR]) {\n console.warn(`[translator-sdk] Top-level API error:`, data[API_FIELD_ERROR])\n\n // Create error responses for all translations\n return serviceTranslations.map((translation) => ({\n [API_FIELD_TEXT]: translation[API_FIELD_TEXT],\n [API_FIELD_ERROR]: data[API_FIELD_ERROR]\n }))\n }\n\n const results = Array.isArray(data[API_FIELD_TRANSLATIONS])\n ? data[API_FIELD_TRANSLATIONS].map((translation, index) => {\n const originalText = serviceTranslations[index]?.[API_FIELD_TEXT] || ''\n\n // Handle error responses\n if (translation[API_FIELD_ERROR]) {\n console.warn(\n `[translator-sdk] Translation error for \"${originalText}\":`,\n translation[API_FIELD_ERROR]\n )\n return {\n [API_FIELD_TEXT]: originalText, // Return original text on error\n [API_FIELD_ERROR]: translation[API_FIELD_ERROR]\n }\n }\n\n // Handle language detection response\n if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {\n return {\n [API_FIELD_TEXT]: originalText, // For detection, return original text\n [API_FIELD_FROM]: translation[API_FIELD_FROM]\n }\n }\n\n // Handle translation response\n return {\n [API_FIELD_TEXT]: translation[API_FIELD_TEXT] || originalText\n }\n })\n : data[API_FIELD_TEXT]\n ? [{ [API_FIELD_TEXT]: data[API_FIELD_TEXT] }]\n : []\n\n // console.debug('[translator-sdk] Service results processed', {\n // resultCount: results.length,\n // expectedCount: serviceTranslations.length\n // })\n\n return results\n } catch (error: unknown) {\n // Remove from cache on error\n translationCache.delete(cacheKey)\n\n if (error instanceof Error && error.name === 'AbortError') {\n const message = `Translator request to ${translatorHost} timed out after 5 seconds.`\n console.error(message)\n throw new Error(message)\n }\n const message = `Failed to connect to translator at ${translatorHost}. Is the service running?`\n console.error(message)\n throw new Error(message)\n }\n })()\n\n // Cache the promise\n translationCache.set(cacheKey, translationPromise)\n serviceResults = await translationPromise\n }\n }\n\n // Assemble final results in original order\n const final = translations.map((tr, idx) => {\n const from = (tr[API_FIELD_FROM] || '').toLowerCase()\n const to = (tr[API_FIELD_TO] || '').toLowerCase()\n if (from === to) return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] }\n\n const svcIdx = indexMap.indexOf(idx)\n if (svcIdx !== -1) return serviceResults[svcIdx] || { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] }\n return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] }\n })\n\n // const totalDuration = Date.now() - startTime\n // console.debug('[translator-sdk] translateBatch completed', {\n // totalDuration: `${totalDuration}ms`,\n // resultCount: final.length,\n // successCount: final.filter(r => r.translated && r.translated !== r.text).length\n // })\n\n return final as TranslateServiceResponse[]\n}\n\n// --- Cache management functions ---\nexport function clearTranslationCache(): void {\n translationCache.clear()\n}\n\nexport function getTranslationCacheSize(): number {\n return translationCache.size\n}\n\n/**\n * Fetches available languages from the translation service\n *\n * @param options Configuration options\n * @returns Array of available languages\n */\nexport async function fetchAvailableLanguages(\n options: TranslatorOptions = {}\n): Promise<TranslatorLanguage[]> {\n // const startTime = Date.now()\n\n // console.debug('[translator-sdk] fetchAvailableLanguages called', { options })\n\n const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST\n const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY\n const minPop =\n options.minPopularity !== undefined\n ? Number(options.minPopularity)\n : Number.parseFloat(process.env.MIN_LANGUAGE_POPULARITY || '0.1')\n\n // console.debug('[translator-sdk] Languages request config', {\n // translatorHost,\n // hasApiKey: !!apiKey,\n // minPopularity: minPop,\n // country: options.country,\n // region: options.region\n // })\n\n const queryParams = []\n if (options.country) queryParams.push(`country=${encodeURIComponent(options.country)}`)\n if (options.region) queryParams.push(`region=${encodeURIComponent(options.region)}`)\n const queryStr = queryParams.length ? `?${queryParams.join('&')}` : ''\n\n const requestUrl = `${translatorHost}/languages${queryStr}`\n\n try {\n const resp = await fetch(requestUrl, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { Authorization: `Bearer ${apiKey}`, 'X-Api-Key': apiKey } : {})\n }\n })\n\n if (!resp.ok) {\n throw new Error(`Translator languages error: ${resp.status}`)\n }\n\n const data = await resp.json()\n const languages = Array.isArray(data) ? data : data.languages || []\n\n const filtered = languages.filter((lang: TranslatorLanguage) => {\n if (lang.popularity === undefined || lang.popularity === null) return true\n return Number(lang.popularity) >= minPop\n })\n\n return filtered\n } catch (error) {\n console.error('[translator-sdk] Languages fetch error:', error)\n throw error\n }\n}\n"],"mappings":"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","names":["message"]}