@lingo.dev/_sdk 0.7.12

Sign up to get free protection for your applications and to get access to all the features.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # Lingo.dev SDK
2
+
3
+ Official SDK for Lingo.dev.
4
+
5
+ ### Installation
6
+
7
+ ```bash
8
+ npm i lingo.dev
9
+ ```
10
+
11
+ ### Usage
12
+
13
+ ```
14
+ import {} from 'lingo.dev/sdk';
15
+ ```
16
+
17
+ ### Documentation
18
+
19
+ [Documentation](https://lingo.dev/go/docs)
@@ -0,0 +1,326 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/index.ts
2
+ var _zod = require('zod'); var _zod2 = _interopRequireDefault(_zod);
3
+ var __spec = require('@lingo.dev/_spec');
4
+ var _cuid2 = require('@paralleldrive/cuid2');
5
+ var engineParamsSchema = _zod2.default.object({
6
+ apiKey: _zod2.default.string(),
7
+ apiUrl: _zod2.default.string().url().default("https://engine.lingo.dev"),
8
+ batchSize: _zod2.default.number().int().gt(0).lte(250).default(25),
9
+ idealBatchItemSize: _zod2.default.number().int().gt(0).lte(2500).default(250)
10
+ }).passthrough();
11
+ var payloadSchema = _zod2.default.record(_zod2.default.string(), _zod2.default.any());
12
+ var localizationParamsSchema = _zod2.default.object({
13
+ sourceLocale: __spec.localeCodeSchema,
14
+ targetLocale: __spec.localeCodeSchema,
15
+ fast: _zod2.default.boolean().optional()
16
+ });
17
+ var referenceSchema = _zod2.default.record(__spec.localeCodeSchema, payloadSchema);
18
+ var ReplexicaEngine = class {
19
+
20
+ /**
21
+ * Create a new ReplexicaEngine instance
22
+ * @param config - Configuration options for the Engine
23
+ */
24
+ constructor(config) {
25
+ this.config = engineParamsSchema.parse(config);
26
+ }
27
+ /**
28
+ * Localize content using the Lingo.dev API
29
+ * @param payload - The content to be localized
30
+ * @param params - Localization parameters including source/target locales and fast mode option
31
+ * @param reference - Optional reference translations to maintain consistency
32
+ * @param progressCallback - Optional callback function to report progress (0-100)
33
+ * @returns Localized content
34
+ * @internal
35
+ */
36
+ async _localizeRaw(payload, params, reference, progressCallback) {
37
+ const finalPayload = payloadSchema.parse(payload);
38
+ const finalParams = localizationParamsSchema.parse(params);
39
+ const chunkedPayload = this.extractPayloadChunks(finalPayload);
40
+ const processedPayloadChunks = [];
41
+ const workflowId = _cuid2.createId.call(void 0, );
42
+ for (let i = 0; i < chunkedPayload.length; i++) {
43
+ const chunk = chunkedPayload[i];
44
+ const percentageCompleted = Math.round((i + 1) / chunkedPayload.length * 100);
45
+ const processedPayloadChunk = await this.localizeChunk(
46
+ finalParams.sourceLocale,
47
+ finalParams.targetLocale,
48
+ { data: chunk, reference },
49
+ workflowId,
50
+ params.fast || false
51
+ );
52
+ if (progressCallback) {
53
+ progressCallback(percentageCompleted, chunk, processedPayloadChunk);
54
+ }
55
+ processedPayloadChunks.push(processedPayloadChunk);
56
+ }
57
+ return Object.assign({}, ...processedPayloadChunks);
58
+ }
59
+ /**
60
+ * Localize a single chunk of content
61
+ * @param sourceLocale - Source locale
62
+ * @param targetLocale - Target locale
63
+ * @param payload - Payload containing the chunk to be localized
64
+ * @returns Localized chunk
65
+ */
66
+ async localizeChunk(sourceLocale, targetLocale, payload, workflowId, fast) {
67
+ const res = await fetch(`${this.config.apiUrl}/i18n`, {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ Authorization: `Bearer ${this.config.apiKey}`
72
+ },
73
+ body: JSON.stringify(
74
+ {
75
+ params: { workflowId, fast },
76
+ locale: {
77
+ source: sourceLocale,
78
+ target: targetLocale
79
+ },
80
+ data: payload.data,
81
+ reference: payload.reference
82
+ },
83
+ null,
84
+ 2
85
+ )
86
+ });
87
+ if (!res.ok) {
88
+ if (res.status === 400) {
89
+ throw new Error(`Invalid request: ${res.statusText}`);
90
+ } else {
91
+ const errorText = await res.text();
92
+ throw new Error(errorText);
93
+ }
94
+ }
95
+ const jsonResponse = await res.json();
96
+ return jsonResponse.data || {};
97
+ }
98
+ /**
99
+ * Extract payload chunks based on the ideal chunk size
100
+ * @param payload - The payload to be chunked
101
+ * @returns An array of payload chunks
102
+ */
103
+ extractPayloadChunks(payload) {
104
+ const result = [];
105
+ let currentChunk = {};
106
+ let currentChunkItemCount = 0;
107
+ const payloadEntries = Object.entries(payload);
108
+ for (let i = 0; i < payloadEntries.length; i++) {
109
+ const [key, value] = payloadEntries[i];
110
+ currentChunk[key] = value;
111
+ currentChunkItemCount++;
112
+ const currentChunkSize = this.countWordsInRecord(currentChunk);
113
+ if (currentChunkSize > this.config.idealBatchItemSize || currentChunkItemCount >= this.config.batchSize || i === payloadEntries.length - 1) {
114
+ result.push(currentChunk);
115
+ currentChunk = {};
116
+ currentChunkItemCount = 0;
117
+ }
118
+ }
119
+ return result;
120
+ }
121
+ /**
122
+ * Count words in a record or array
123
+ * @param payload - The payload to count words in
124
+ * @returns The total number of words
125
+ */
126
+ countWordsInRecord(payload) {
127
+ if (Array.isArray(payload)) {
128
+ return payload.reduce((acc, item) => acc + this.countWordsInRecord(item), 0);
129
+ } else if (typeof payload === "object" && payload !== null) {
130
+ return Object.values(payload).reduce((acc, item) => acc + this.countWordsInRecord(item), 0);
131
+ } else if (typeof payload === "string") {
132
+ return payload.trim().split(/\s+/).filter(Boolean).length;
133
+ } else {
134
+ return 0;
135
+ }
136
+ }
137
+ /**
138
+ * Localize a typical JavaScript object
139
+ * @param obj - The object to be localized (strings will be extracted and translated)
140
+ * @param params - Localization parameters:
141
+ * - sourceLocale: The source language code (e.g., 'en')
142
+ * - targetLocale: The target language code (e.g., 'es')
143
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
144
+ * @param progressCallback - Optional callback function to report progress (0-100)
145
+ * @returns A new object with the same structure but localized string values
146
+ */
147
+ async localizeObject(obj, params, progressCallback) {
148
+ return this._localizeRaw(obj, params, void 0, progressCallback);
149
+ }
150
+ /**
151
+ * Localize a single text string
152
+ * @param text - The text string to be localized
153
+ * @param params - Localization parameters:
154
+ * - sourceLocale: The source language code (e.g., 'en')
155
+ * - targetLocale: The target language code (e.g., 'es')
156
+ * - fast: Optional boolean to enable fast mode (faster for bigger batches)
157
+ * @param progressCallback - Optional callback function to report progress (0-100)
158
+ * @returns The localized text string
159
+ */
160
+ async localizeText(text, params, progressCallback) {
161
+ const response = await this._localizeRaw({ text }, params, void 0, progressCallback);
162
+ return response.text || "";
163
+ }
164
+ /**
165
+ * Localize a text string to multiple target locales
166
+ * @param text - The text string to be localized
167
+ * @param params - Localization parameters:
168
+ * - sourceLocale: The source language code (e.g., 'en')
169
+ * - targetLocales: An array of target language codes (e.g., ['es', 'fr'])
170
+ * - fast: Optional boolean to enable fast mode (for bigger batches)
171
+ * @returns An array of localized text strings
172
+ */
173
+ async batchLocalizeText(text, params) {
174
+ const responses = await Promise.all(
175
+ params.targetLocales.map(
176
+ (targetLocale) => this.localizeText(text, {
177
+ sourceLocale: params.sourceLocale,
178
+ targetLocale,
179
+ fast: params.fast
180
+ })
181
+ )
182
+ );
183
+ return responses;
184
+ }
185
+ /**
186
+ * Localize a chat sequence while preserving speaker names
187
+ * @param chat - Array of chat messages, each with 'name' and 'text' properties
188
+ * @param params - Localization parameters:
189
+ * - sourceLocale: The source language code (e.g., 'en')
190
+ * - targetLocale: The target language code (e.g., 'es')
191
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
192
+ * @param progressCallback - Optional callback function to report progress (0-100)
193
+ * @returns Array of localized chat messages with preserved structure
194
+ */
195
+ async localizeChat(chat, params, progressCallback) {
196
+ const localized = await this._localizeRaw({ chat }, params, void 0, progressCallback);
197
+ return Object.entries(localized).map(([key, value]) => ({
198
+ name: chat[parseInt(key.split("_")[1])].name,
199
+ text: value
200
+ }));
201
+ }
202
+ /**
203
+ * Localize an HTML document while preserving structure and formatting
204
+ * Handles both text content and localizable attributes (alt, title, placeholder, meta content)
205
+ * @param html - The HTML document string to be localized
206
+ * @param params - Localization parameters:
207
+ * - sourceLocale: The source language code (e.g., 'en')
208
+ * - targetLocale: The target language code (e.g., 'es')
209
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
210
+ * @param progressCallback - Optional callback function to report progress (0-100)
211
+ * @returns The localized HTML document as a string, with updated lang attribute
212
+ */
213
+ async localizeHtml(html, params, progressCallback) {
214
+ const jsdomPackage = await Promise.resolve().then(() => _interopRequireWildcard(require("jsdom")));
215
+ const { JSDOM } = jsdomPackage;
216
+ const dom = new JSDOM(html);
217
+ const document = dom.window.document;
218
+ const LOCALIZABLE_ATTRIBUTES = {
219
+ meta: ["content"],
220
+ img: ["alt"],
221
+ input: ["placeholder"],
222
+ a: ["title"]
223
+ };
224
+ const UNLOCALIZABLE_TAGS = ["script", "style"];
225
+ const extractedContent = {};
226
+ const getPath = (node, attribute) => {
227
+ const indices = [];
228
+ let current = node;
229
+ let rootParent = "";
230
+ while (current) {
231
+ const parent = current.parentElement;
232
+ if (!parent) break;
233
+ if (parent === document.documentElement) {
234
+ rootParent = current.nodeName.toLowerCase();
235
+ break;
236
+ }
237
+ const siblings = Array.from(parent.childNodes).filter(
238
+ (n) => n.nodeType === 1 || n.nodeType === 3 && _optionalChain([n, 'access', _ => _.textContent, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()])
239
+ );
240
+ const index = siblings.indexOf(current);
241
+ if (index !== -1) {
242
+ indices.unshift(index);
243
+ }
244
+ current = parent;
245
+ }
246
+ const basePath = rootParent ? `${rootParent}/${indices.join("/")}` : indices.join("/");
247
+ return attribute ? `${basePath}#${attribute}` : basePath;
248
+ };
249
+ const processNode = (node) => {
250
+ let parent = node.parentElement;
251
+ while (parent) {
252
+ if (UNLOCALIZABLE_TAGS.includes(parent.tagName.toLowerCase())) {
253
+ return;
254
+ }
255
+ parent = parent.parentElement;
256
+ }
257
+ if (node.nodeType === 3) {
258
+ const text = _optionalChain([node, 'access', _4 => _4.textContent, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]) || "";
259
+ if (text) {
260
+ extractedContent[getPath(node)] = text;
261
+ }
262
+ } else if (node.nodeType === 1) {
263
+ const element = node;
264
+ const tagName = element.tagName.toLowerCase();
265
+ const attributes = LOCALIZABLE_ATTRIBUTES[tagName] || [];
266
+ attributes.forEach((attr) => {
267
+ const value = element.getAttribute(attr);
268
+ if (value) {
269
+ extractedContent[getPath(element, attr)] = value;
270
+ }
271
+ });
272
+ Array.from(element.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && _optionalChain([n, 'access', _7 => _7.textContent, 'optionalAccess', _8 => _8.trim, 'call', _9 => _9()])).forEach(processNode);
273
+ }
274
+ };
275
+ Array.from(document.head.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && _optionalChain([n, 'access', _10 => _10.textContent, 'optionalAccess', _11 => _11.trim, 'call', _12 => _12()])).forEach(processNode);
276
+ Array.from(document.body.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && _optionalChain([n, 'access', _13 => _13.textContent, 'optionalAccess', _14 => _14.trim, 'call', _15 => _15()])).forEach(processNode);
277
+ const localizedContent = await this._localizeRaw(extractedContent, params, void 0, progressCallback);
278
+ document.documentElement.setAttribute("lang", params.targetLocale);
279
+ Object.entries(localizedContent).forEach(([path, value]) => {
280
+ const [nodePath, attribute] = path.split("#");
281
+ const [rootTag, ...indices] = nodePath.split("/");
282
+ let parent = rootTag === "head" ? document.head : document.body;
283
+ let current = parent;
284
+ for (const index of indices) {
285
+ const siblings = Array.from(parent.childNodes).filter(
286
+ (n) => n.nodeType === 1 || n.nodeType === 3 && _optionalChain([n, 'access', _16 => _16.textContent, 'optionalAccess', _17 => _17.trim, 'call', _18 => _18()])
287
+ );
288
+ current = siblings[parseInt(index)] || null;
289
+ if (_optionalChain([current, 'optionalAccess', _19 => _19.nodeType]) === 1) {
290
+ parent = current;
291
+ }
292
+ }
293
+ if (current) {
294
+ if (attribute) {
295
+ current.setAttribute(attribute, value);
296
+ } else {
297
+ current.textContent = value;
298
+ }
299
+ }
300
+ });
301
+ return dom.serialize();
302
+ }
303
+ /**
304
+ * Detect the language of a given text
305
+ * @param text - The text to analyze
306
+ * @returns Promise resolving to a locale code (e.g., 'en', 'es', 'fr')
307
+ */
308
+ async recognizeLocale(text) {
309
+ const response = await fetch(`${this.config.apiUrl}/recognize`, {
310
+ method: "POST",
311
+ headers: {
312
+ "Content-Type": "application/json",
313
+ Authorization: `Bearer ${this.config.apiKey}`
314
+ },
315
+ body: JSON.stringify({ text })
316
+ });
317
+ if (!response.ok) {
318
+ throw new Error(`Error recognizing locale: ${response.statusText}`);
319
+ }
320
+ const jsonResponse = await response.json();
321
+ return jsonResponse.locale;
322
+ }
323
+ };
324
+
325
+
326
+ exports.ReplexicaEngine = ReplexicaEngine;
@@ -0,0 +1,150 @@
1
+ import Z from 'zod';
2
+ import { LocaleCode } from '@lingo.dev/_spec';
3
+
4
+ declare const engineParamsSchema: Z.ZodObject<{
5
+ apiKey: Z.ZodString;
6
+ apiUrl: Z.ZodDefault<Z.ZodString>;
7
+ batchSize: Z.ZodDefault<Z.ZodNumber>;
8
+ idealBatchItemSize: Z.ZodDefault<Z.ZodNumber>;
9
+ }, "passthrough", Z.ZodTypeAny, Z.objectOutputType<{
10
+ apiKey: Z.ZodString;
11
+ apiUrl: Z.ZodDefault<Z.ZodString>;
12
+ batchSize: Z.ZodDefault<Z.ZodNumber>;
13
+ idealBatchItemSize: Z.ZodDefault<Z.ZodNumber>;
14
+ }, Z.ZodTypeAny, "passthrough">, Z.objectInputType<{
15
+ apiKey: Z.ZodString;
16
+ apiUrl: Z.ZodDefault<Z.ZodString>;
17
+ batchSize: Z.ZodDefault<Z.ZodNumber>;
18
+ idealBatchItemSize: Z.ZodDefault<Z.ZodNumber>;
19
+ }, Z.ZodTypeAny, "passthrough">>;
20
+ declare const payloadSchema: Z.ZodRecord<Z.ZodString, Z.ZodAny>;
21
+ declare const localizationParamsSchema: Z.ZodObject<{
22
+ sourceLocale: Z.ZodEffects<Z.ZodString, string, string>;
23
+ targetLocale: Z.ZodEffects<Z.ZodString, string, string>;
24
+ fast: Z.ZodOptional<Z.ZodBoolean>;
25
+ }, "strip", Z.ZodTypeAny, {
26
+ sourceLocale: string;
27
+ targetLocale: string;
28
+ fast?: boolean | undefined;
29
+ }, {
30
+ sourceLocale: string;
31
+ targetLocale: string;
32
+ fast?: boolean | undefined;
33
+ }>;
34
+ declare const referenceSchema: Z.ZodRecord<Z.ZodEffects<Z.ZodString, string, string>, Z.ZodRecord<Z.ZodString, Z.ZodAny>>;
35
+ /**
36
+ * ReplexicaEngine class for interacting with the Lingo.dev API
37
+ * A powerful localization engine that supports various content types including
38
+ * plain text, objects, chat sequences, and HTML documents.
39
+ */
40
+ declare class ReplexicaEngine {
41
+ private config;
42
+ /**
43
+ * Create a new ReplexicaEngine instance
44
+ * @param config - Configuration options for the Engine
45
+ */
46
+ constructor(config: Partial<Z.infer<typeof engineParamsSchema>>);
47
+ /**
48
+ * Localize content using the Lingo.dev API
49
+ * @param payload - The content to be localized
50
+ * @param params - Localization parameters including source/target locales and fast mode option
51
+ * @param reference - Optional reference translations to maintain consistency
52
+ * @param progressCallback - Optional callback function to report progress (0-100)
53
+ * @returns Localized content
54
+ * @internal
55
+ */
56
+ _localizeRaw(payload: Z.infer<typeof payloadSchema>, params: Z.infer<typeof localizationParamsSchema>, reference?: Z.infer<typeof referenceSchema>, progressCallback?: (progress: number, sourceChunk: Record<string, string>, processedChunk: Record<string, string>) => void): Promise<Record<string, string>>;
57
+ /**
58
+ * Localize a single chunk of content
59
+ * @param sourceLocale - Source locale
60
+ * @param targetLocale - Target locale
61
+ * @param payload - Payload containing the chunk to be localized
62
+ * @returns Localized chunk
63
+ */
64
+ private localizeChunk;
65
+ /**
66
+ * Extract payload chunks based on the ideal chunk size
67
+ * @param payload - The payload to be chunked
68
+ * @returns An array of payload chunks
69
+ */
70
+ private extractPayloadChunks;
71
+ /**
72
+ * Count words in a record or array
73
+ * @param payload - The payload to count words in
74
+ * @returns The total number of words
75
+ */
76
+ private countWordsInRecord;
77
+ /**
78
+ * Localize a typical JavaScript object
79
+ * @param obj - The object to be localized (strings will be extracted and translated)
80
+ * @param params - Localization parameters:
81
+ * - sourceLocale: The source language code (e.g., 'en')
82
+ * - targetLocale: The target language code (e.g., 'es')
83
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
84
+ * @param progressCallback - Optional callback function to report progress (0-100)
85
+ * @returns A new object with the same structure but localized string values
86
+ */
87
+ localizeObject(obj: Record<string, any>, params: Z.infer<typeof localizationParamsSchema>, progressCallback?: (progress: number, sourceChunk: Record<string, string>, processedChunk: Record<string, string>) => void): Promise<Record<string, any>>;
88
+ /**
89
+ * Localize a single text string
90
+ * @param text - The text string to be localized
91
+ * @param params - Localization parameters:
92
+ * - sourceLocale: The source language code (e.g., 'en')
93
+ * - targetLocale: The target language code (e.g., 'es')
94
+ * - fast: Optional boolean to enable fast mode (faster for bigger batches)
95
+ * @param progressCallback - Optional callback function to report progress (0-100)
96
+ * @returns The localized text string
97
+ */
98
+ localizeText(text: string, params: Z.infer<typeof localizationParamsSchema>, progressCallback?: (progress: number) => void): Promise<string>;
99
+ /**
100
+ * Localize a text string to multiple target locales
101
+ * @param text - The text string to be localized
102
+ * @param params - Localization parameters:
103
+ * - sourceLocale: The source language code (e.g., 'en')
104
+ * - targetLocales: An array of target language codes (e.g., ['es', 'fr'])
105
+ * - fast: Optional boolean to enable fast mode (for bigger batches)
106
+ * @returns An array of localized text strings
107
+ */
108
+ batchLocalizeText(text: string, params: {
109
+ sourceLocale: LocaleCode;
110
+ targetLocales: LocaleCode[];
111
+ fast?: boolean;
112
+ }): Promise<string[]>;
113
+ /**
114
+ * Localize a chat sequence while preserving speaker names
115
+ * @param chat - Array of chat messages, each with 'name' and 'text' properties
116
+ * @param params - Localization parameters:
117
+ * - sourceLocale: The source language code (e.g., 'en')
118
+ * - targetLocale: The target language code (e.g., 'es')
119
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
120
+ * @param progressCallback - Optional callback function to report progress (0-100)
121
+ * @returns Array of localized chat messages with preserved structure
122
+ */
123
+ localizeChat(chat: Array<{
124
+ name: string;
125
+ text: string;
126
+ }>, params: Z.infer<typeof localizationParamsSchema>, progressCallback?: (progress: number) => void): Promise<Array<{
127
+ name: string;
128
+ text: string;
129
+ }>>;
130
+ /**
131
+ * Localize an HTML document while preserving structure and formatting
132
+ * Handles both text content and localizable attributes (alt, title, placeholder, meta content)
133
+ * @param html - The HTML document string to be localized
134
+ * @param params - Localization parameters:
135
+ * - sourceLocale: The source language code (e.g., 'en')
136
+ * - targetLocale: The target language code (e.g., 'es')
137
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
138
+ * @param progressCallback - Optional callback function to report progress (0-100)
139
+ * @returns The localized HTML document as a string, with updated lang attribute
140
+ */
141
+ localizeHtml(html: string, params: Z.infer<typeof localizationParamsSchema>, progressCallback?: (progress: number) => void): Promise<string>;
142
+ /**
143
+ * Detect the language of a given text
144
+ * @param text - The text to analyze
145
+ * @returns Promise resolving to a locale code (e.g., 'en', 'es', 'fr')
146
+ */
147
+ recognizeLocale(text: string): Promise<LocaleCode>;
148
+ }
149
+
150
+ export { ReplexicaEngine };
@@ -0,0 +1,150 @@
1
+ import Z from 'zod';
2
+ import { LocaleCode } from '@lingo.dev/_spec';
3
+
4
+ declare const engineParamsSchema: Z.ZodObject<{
5
+ apiKey: Z.ZodString;
6
+ apiUrl: Z.ZodDefault<Z.ZodString>;
7
+ batchSize: Z.ZodDefault<Z.ZodNumber>;
8
+ idealBatchItemSize: Z.ZodDefault<Z.ZodNumber>;
9
+ }, "passthrough", Z.ZodTypeAny, Z.objectOutputType<{
10
+ apiKey: Z.ZodString;
11
+ apiUrl: Z.ZodDefault<Z.ZodString>;
12
+ batchSize: Z.ZodDefault<Z.ZodNumber>;
13
+ idealBatchItemSize: Z.ZodDefault<Z.ZodNumber>;
14
+ }, Z.ZodTypeAny, "passthrough">, Z.objectInputType<{
15
+ apiKey: Z.ZodString;
16
+ apiUrl: Z.ZodDefault<Z.ZodString>;
17
+ batchSize: Z.ZodDefault<Z.ZodNumber>;
18
+ idealBatchItemSize: Z.ZodDefault<Z.ZodNumber>;
19
+ }, Z.ZodTypeAny, "passthrough">>;
20
+ declare const payloadSchema: Z.ZodRecord<Z.ZodString, Z.ZodAny>;
21
+ declare const localizationParamsSchema: Z.ZodObject<{
22
+ sourceLocale: Z.ZodEffects<Z.ZodString, string, string>;
23
+ targetLocale: Z.ZodEffects<Z.ZodString, string, string>;
24
+ fast: Z.ZodOptional<Z.ZodBoolean>;
25
+ }, "strip", Z.ZodTypeAny, {
26
+ sourceLocale: string;
27
+ targetLocale: string;
28
+ fast?: boolean | undefined;
29
+ }, {
30
+ sourceLocale: string;
31
+ targetLocale: string;
32
+ fast?: boolean | undefined;
33
+ }>;
34
+ declare const referenceSchema: Z.ZodRecord<Z.ZodEffects<Z.ZodString, string, string>, Z.ZodRecord<Z.ZodString, Z.ZodAny>>;
35
+ /**
36
+ * ReplexicaEngine class for interacting with the Lingo.dev API
37
+ * A powerful localization engine that supports various content types including
38
+ * plain text, objects, chat sequences, and HTML documents.
39
+ */
40
+ declare class ReplexicaEngine {
41
+ private config;
42
+ /**
43
+ * Create a new ReplexicaEngine instance
44
+ * @param config - Configuration options for the Engine
45
+ */
46
+ constructor(config: Partial<Z.infer<typeof engineParamsSchema>>);
47
+ /**
48
+ * Localize content using the Lingo.dev API
49
+ * @param payload - The content to be localized
50
+ * @param params - Localization parameters including source/target locales and fast mode option
51
+ * @param reference - Optional reference translations to maintain consistency
52
+ * @param progressCallback - Optional callback function to report progress (0-100)
53
+ * @returns Localized content
54
+ * @internal
55
+ */
56
+ _localizeRaw(payload: Z.infer<typeof payloadSchema>, params: Z.infer<typeof localizationParamsSchema>, reference?: Z.infer<typeof referenceSchema>, progressCallback?: (progress: number, sourceChunk: Record<string, string>, processedChunk: Record<string, string>) => void): Promise<Record<string, string>>;
57
+ /**
58
+ * Localize a single chunk of content
59
+ * @param sourceLocale - Source locale
60
+ * @param targetLocale - Target locale
61
+ * @param payload - Payload containing the chunk to be localized
62
+ * @returns Localized chunk
63
+ */
64
+ private localizeChunk;
65
+ /**
66
+ * Extract payload chunks based on the ideal chunk size
67
+ * @param payload - The payload to be chunked
68
+ * @returns An array of payload chunks
69
+ */
70
+ private extractPayloadChunks;
71
+ /**
72
+ * Count words in a record or array
73
+ * @param payload - The payload to count words in
74
+ * @returns The total number of words
75
+ */
76
+ private countWordsInRecord;
77
+ /**
78
+ * Localize a typical JavaScript object
79
+ * @param obj - The object to be localized (strings will be extracted and translated)
80
+ * @param params - Localization parameters:
81
+ * - sourceLocale: The source language code (e.g., 'en')
82
+ * - targetLocale: The target language code (e.g., 'es')
83
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
84
+ * @param progressCallback - Optional callback function to report progress (0-100)
85
+ * @returns A new object with the same structure but localized string values
86
+ */
87
+ localizeObject(obj: Record<string, any>, params: Z.infer<typeof localizationParamsSchema>, progressCallback?: (progress: number, sourceChunk: Record<string, string>, processedChunk: Record<string, string>) => void): Promise<Record<string, any>>;
88
+ /**
89
+ * Localize a single text string
90
+ * @param text - The text string to be localized
91
+ * @param params - Localization parameters:
92
+ * - sourceLocale: The source language code (e.g., 'en')
93
+ * - targetLocale: The target language code (e.g., 'es')
94
+ * - fast: Optional boolean to enable fast mode (faster for bigger batches)
95
+ * @param progressCallback - Optional callback function to report progress (0-100)
96
+ * @returns The localized text string
97
+ */
98
+ localizeText(text: string, params: Z.infer<typeof localizationParamsSchema>, progressCallback?: (progress: number) => void): Promise<string>;
99
+ /**
100
+ * Localize a text string to multiple target locales
101
+ * @param text - The text string to be localized
102
+ * @param params - Localization parameters:
103
+ * - sourceLocale: The source language code (e.g., 'en')
104
+ * - targetLocales: An array of target language codes (e.g., ['es', 'fr'])
105
+ * - fast: Optional boolean to enable fast mode (for bigger batches)
106
+ * @returns An array of localized text strings
107
+ */
108
+ batchLocalizeText(text: string, params: {
109
+ sourceLocale: LocaleCode;
110
+ targetLocales: LocaleCode[];
111
+ fast?: boolean;
112
+ }): Promise<string[]>;
113
+ /**
114
+ * Localize a chat sequence while preserving speaker names
115
+ * @param chat - Array of chat messages, each with 'name' and 'text' properties
116
+ * @param params - Localization parameters:
117
+ * - sourceLocale: The source language code (e.g., 'en')
118
+ * - targetLocale: The target language code (e.g., 'es')
119
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
120
+ * @param progressCallback - Optional callback function to report progress (0-100)
121
+ * @returns Array of localized chat messages with preserved structure
122
+ */
123
+ localizeChat(chat: Array<{
124
+ name: string;
125
+ text: string;
126
+ }>, params: Z.infer<typeof localizationParamsSchema>, progressCallback?: (progress: number) => void): Promise<Array<{
127
+ name: string;
128
+ text: string;
129
+ }>>;
130
+ /**
131
+ * Localize an HTML document while preserving structure and formatting
132
+ * Handles both text content and localizable attributes (alt, title, placeholder, meta content)
133
+ * @param html - The HTML document string to be localized
134
+ * @param params - Localization parameters:
135
+ * - sourceLocale: The source language code (e.g., 'en')
136
+ * - targetLocale: The target language code (e.g., 'es')
137
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
138
+ * @param progressCallback - Optional callback function to report progress (0-100)
139
+ * @returns The localized HTML document as a string, with updated lang attribute
140
+ */
141
+ localizeHtml(html: string, params: Z.infer<typeof localizationParamsSchema>, progressCallback?: (progress: number) => void): Promise<string>;
142
+ /**
143
+ * Detect the language of a given text
144
+ * @param text - The text to analyze
145
+ * @returns Promise resolving to a locale code (e.g., 'en', 'es', 'fr')
146
+ */
147
+ recognizeLocale(text: string): Promise<LocaleCode>;
148
+ }
149
+
150
+ export { ReplexicaEngine };
@@ -0,0 +1,326 @@
1
+ // src/index.ts
2
+ import Z from "zod";
3
+ import { localeCodeSchema } from "@lingo.dev/_spec";
4
+ import { createId } from "@paralleldrive/cuid2";
5
+ var engineParamsSchema = Z.object({
6
+ apiKey: Z.string(),
7
+ apiUrl: Z.string().url().default("https://engine.lingo.dev"),
8
+ batchSize: Z.number().int().gt(0).lte(250).default(25),
9
+ idealBatchItemSize: Z.number().int().gt(0).lte(2500).default(250)
10
+ }).passthrough();
11
+ var payloadSchema = Z.record(Z.string(), Z.any());
12
+ var localizationParamsSchema = Z.object({
13
+ sourceLocale: localeCodeSchema,
14
+ targetLocale: localeCodeSchema,
15
+ fast: Z.boolean().optional()
16
+ });
17
+ var referenceSchema = Z.record(localeCodeSchema, payloadSchema);
18
+ var ReplexicaEngine = class {
19
+ config;
20
+ /**
21
+ * Create a new ReplexicaEngine instance
22
+ * @param config - Configuration options for the Engine
23
+ */
24
+ constructor(config) {
25
+ this.config = engineParamsSchema.parse(config);
26
+ }
27
+ /**
28
+ * Localize content using the Lingo.dev API
29
+ * @param payload - The content to be localized
30
+ * @param params - Localization parameters including source/target locales and fast mode option
31
+ * @param reference - Optional reference translations to maintain consistency
32
+ * @param progressCallback - Optional callback function to report progress (0-100)
33
+ * @returns Localized content
34
+ * @internal
35
+ */
36
+ async _localizeRaw(payload, params, reference, progressCallback) {
37
+ const finalPayload = payloadSchema.parse(payload);
38
+ const finalParams = localizationParamsSchema.parse(params);
39
+ const chunkedPayload = this.extractPayloadChunks(finalPayload);
40
+ const processedPayloadChunks = [];
41
+ const workflowId = createId();
42
+ for (let i = 0; i < chunkedPayload.length; i++) {
43
+ const chunk = chunkedPayload[i];
44
+ const percentageCompleted = Math.round((i + 1) / chunkedPayload.length * 100);
45
+ const processedPayloadChunk = await this.localizeChunk(
46
+ finalParams.sourceLocale,
47
+ finalParams.targetLocale,
48
+ { data: chunk, reference },
49
+ workflowId,
50
+ params.fast || false
51
+ );
52
+ if (progressCallback) {
53
+ progressCallback(percentageCompleted, chunk, processedPayloadChunk);
54
+ }
55
+ processedPayloadChunks.push(processedPayloadChunk);
56
+ }
57
+ return Object.assign({}, ...processedPayloadChunks);
58
+ }
59
+ /**
60
+ * Localize a single chunk of content
61
+ * @param sourceLocale - Source locale
62
+ * @param targetLocale - Target locale
63
+ * @param payload - Payload containing the chunk to be localized
64
+ * @returns Localized chunk
65
+ */
66
+ async localizeChunk(sourceLocale, targetLocale, payload, workflowId, fast) {
67
+ const res = await fetch(`${this.config.apiUrl}/i18n`, {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ Authorization: `Bearer ${this.config.apiKey}`
72
+ },
73
+ body: JSON.stringify(
74
+ {
75
+ params: { workflowId, fast },
76
+ locale: {
77
+ source: sourceLocale,
78
+ target: targetLocale
79
+ },
80
+ data: payload.data,
81
+ reference: payload.reference
82
+ },
83
+ null,
84
+ 2
85
+ )
86
+ });
87
+ if (!res.ok) {
88
+ if (res.status === 400) {
89
+ throw new Error(`Invalid request: ${res.statusText}`);
90
+ } else {
91
+ const errorText = await res.text();
92
+ throw new Error(errorText);
93
+ }
94
+ }
95
+ const jsonResponse = await res.json();
96
+ return jsonResponse.data || {};
97
+ }
98
+ /**
99
+ * Extract payload chunks based on the ideal chunk size
100
+ * @param payload - The payload to be chunked
101
+ * @returns An array of payload chunks
102
+ */
103
+ extractPayloadChunks(payload) {
104
+ const result = [];
105
+ let currentChunk = {};
106
+ let currentChunkItemCount = 0;
107
+ const payloadEntries = Object.entries(payload);
108
+ for (let i = 0; i < payloadEntries.length; i++) {
109
+ const [key, value] = payloadEntries[i];
110
+ currentChunk[key] = value;
111
+ currentChunkItemCount++;
112
+ const currentChunkSize = this.countWordsInRecord(currentChunk);
113
+ if (currentChunkSize > this.config.idealBatchItemSize || currentChunkItemCount >= this.config.batchSize || i === payloadEntries.length - 1) {
114
+ result.push(currentChunk);
115
+ currentChunk = {};
116
+ currentChunkItemCount = 0;
117
+ }
118
+ }
119
+ return result;
120
+ }
121
+ /**
122
+ * Count words in a record or array
123
+ * @param payload - The payload to count words in
124
+ * @returns The total number of words
125
+ */
126
+ countWordsInRecord(payload) {
127
+ if (Array.isArray(payload)) {
128
+ return payload.reduce((acc, item) => acc + this.countWordsInRecord(item), 0);
129
+ } else if (typeof payload === "object" && payload !== null) {
130
+ return Object.values(payload).reduce((acc, item) => acc + this.countWordsInRecord(item), 0);
131
+ } else if (typeof payload === "string") {
132
+ return payload.trim().split(/\s+/).filter(Boolean).length;
133
+ } else {
134
+ return 0;
135
+ }
136
+ }
137
+ /**
138
+ * Localize a typical JavaScript object
139
+ * @param obj - The object to be localized (strings will be extracted and translated)
140
+ * @param params - Localization parameters:
141
+ * - sourceLocale: The source language code (e.g., 'en')
142
+ * - targetLocale: The target language code (e.g., 'es')
143
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
144
+ * @param progressCallback - Optional callback function to report progress (0-100)
145
+ * @returns A new object with the same structure but localized string values
146
+ */
147
+ async localizeObject(obj, params, progressCallback) {
148
+ return this._localizeRaw(obj, params, void 0, progressCallback);
149
+ }
150
+ /**
151
+ * Localize a single text string
152
+ * @param text - The text string to be localized
153
+ * @param params - Localization parameters:
154
+ * - sourceLocale: The source language code (e.g., 'en')
155
+ * - targetLocale: The target language code (e.g., 'es')
156
+ * - fast: Optional boolean to enable fast mode (faster for bigger batches)
157
+ * @param progressCallback - Optional callback function to report progress (0-100)
158
+ * @returns The localized text string
159
+ */
160
+ async localizeText(text, params, progressCallback) {
161
+ const response = await this._localizeRaw({ text }, params, void 0, progressCallback);
162
+ return response.text || "";
163
+ }
164
+ /**
165
+ * Localize a text string to multiple target locales
166
+ * @param text - The text string to be localized
167
+ * @param params - Localization parameters:
168
+ * - sourceLocale: The source language code (e.g., 'en')
169
+ * - targetLocales: An array of target language codes (e.g., ['es', 'fr'])
170
+ * - fast: Optional boolean to enable fast mode (for bigger batches)
171
+ * @returns An array of localized text strings
172
+ */
173
+ async batchLocalizeText(text, params) {
174
+ const responses = await Promise.all(
175
+ params.targetLocales.map(
176
+ (targetLocale) => this.localizeText(text, {
177
+ sourceLocale: params.sourceLocale,
178
+ targetLocale,
179
+ fast: params.fast
180
+ })
181
+ )
182
+ );
183
+ return responses;
184
+ }
185
+ /**
186
+ * Localize a chat sequence while preserving speaker names
187
+ * @param chat - Array of chat messages, each with 'name' and 'text' properties
188
+ * @param params - Localization parameters:
189
+ * - sourceLocale: The source language code (e.g., 'en')
190
+ * - targetLocale: The target language code (e.g., 'es')
191
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
192
+ * @param progressCallback - Optional callback function to report progress (0-100)
193
+ * @returns Array of localized chat messages with preserved structure
194
+ */
195
+ async localizeChat(chat, params, progressCallback) {
196
+ const localized = await this._localizeRaw({ chat }, params, void 0, progressCallback);
197
+ return Object.entries(localized).map(([key, value]) => ({
198
+ name: chat[parseInt(key.split("_")[1])].name,
199
+ text: value
200
+ }));
201
+ }
202
+ /**
203
+ * Localize an HTML document while preserving structure and formatting
204
+ * Handles both text content and localizable attributes (alt, title, placeholder, meta content)
205
+ * @param html - The HTML document string to be localized
206
+ * @param params - Localization parameters:
207
+ * - sourceLocale: The source language code (e.g., 'en')
208
+ * - targetLocale: The target language code (e.g., 'es')
209
+ * - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
210
+ * @param progressCallback - Optional callback function to report progress (0-100)
211
+ * @returns The localized HTML document as a string, with updated lang attribute
212
+ */
213
+ async localizeHtml(html, params, progressCallback) {
214
+ const jsdomPackage = await import("jsdom");
215
+ const { JSDOM } = jsdomPackage;
216
+ const dom = new JSDOM(html);
217
+ const document = dom.window.document;
218
+ const LOCALIZABLE_ATTRIBUTES = {
219
+ meta: ["content"],
220
+ img: ["alt"],
221
+ input: ["placeholder"],
222
+ a: ["title"]
223
+ };
224
+ const UNLOCALIZABLE_TAGS = ["script", "style"];
225
+ const extractedContent = {};
226
+ const getPath = (node, attribute) => {
227
+ const indices = [];
228
+ let current = node;
229
+ let rootParent = "";
230
+ while (current) {
231
+ const parent = current.parentElement;
232
+ if (!parent) break;
233
+ if (parent === document.documentElement) {
234
+ rootParent = current.nodeName.toLowerCase();
235
+ break;
236
+ }
237
+ const siblings = Array.from(parent.childNodes).filter(
238
+ (n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()
239
+ );
240
+ const index = siblings.indexOf(current);
241
+ if (index !== -1) {
242
+ indices.unshift(index);
243
+ }
244
+ current = parent;
245
+ }
246
+ const basePath = rootParent ? `${rootParent}/${indices.join("/")}` : indices.join("/");
247
+ return attribute ? `${basePath}#${attribute}` : basePath;
248
+ };
249
+ const processNode = (node) => {
250
+ let parent = node.parentElement;
251
+ while (parent) {
252
+ if (UNLOCALIZABLE_TAGS.includes(parent.tagName.toLowerCase())) {
253
+ return;
254
+ }
255
+ parent = parent.parentElement;
256
+ }
257
+ if (node.nodeType === 3) {
258
+ const text = node.textContent?.trim() || "";
259
+ if (text) {
260
+ extractedContent[getPath(node)] = text;
261
+ }
262
+ } else if (node.nodeType === 1) {
263
+ const element = node;
264
+ const tagName = element.tagName.toLowerCase();
265
+ const attributes = LOCALIZABLE_ATTRIBUTES[tagName] || [];
266
+ attributes.forEach((attr) => {
267
+ const value = element.getAttribute(attr);
268
+ if (value) {
269
+ extractedContent[getPath(element, attr)] = value;
270
+ }
271
+ });
272
+ Array.from(element.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()).forEach(processNode);
273
+ }
274
+ };
275
+ Array.from(document.head.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()).forEach(processNode);
276
+ Array.from(document.body.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()).forEach(processNode);
277
+ const localizedContent = await this._localizeRaw(extractedContent, params, void 0, progressCallback);
278
+ document.documentElement.setAttribute("lang", params.targetLocale);
279
+ Object.entries(localizedContent).forEach(([path, value]) => {
280
+ const [nodePath, attribute] = path.split("#");
281
+ const [rootTag, ...indices] = nodePath.split("/");
282
+ let parent = rootTag === "head" ? document.head : document.body;
283
+ let current = parent;
284
+ for (const index of indices) {
285
+ const siblings = Array.from(parent.childNodes).filter(
286
+ (n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()
287
+ );
288
+ current = siblings[parseInt(index)] || null;
289
+ if (current?.nodeType === 1) {
290
+ parent = current;
291
+ }
292
+ }
293
+ if (current) {
294
+ if (attribute) {
295
+ current.setAttribute(attribute, value);
296
+ } else {
297
+ current.textContent = value;
298
+ }
299
+ }
300
+ });
301
+ return dom.serialize();
302
+ }
303
+ /**
304
+ * Detect the language of a given text
305
+ * @param text - The text to analyze
306
+ * @returns Promise resolving to a locale code (e.g., 'en', 'es', 'fr')
307
+ */
308
+ async recognizeLocale(text) {
309
+ const response = await fetch(`${this.config.apiUrl}/recognize`, {
310
+ method: "POST",
311
+ headers: {
312
+ "Content-Type": "application/json",
313
+ Authorization: `Bearer ${this.config.apiKey}`
314
+ },
315
+ body: JSON.stringify({ text })
316
+ });
317
+ if (!response.ok) {
318
+ throw new Error(`Error recognizing locale: ${response.statusText}`);
319
+ }
320
+ const jsonResponse = await response.json();
321
+ return jsonResponse.locale;
322
+ }
323
+ };
324
+ export {
325
+ ReplexicaEngine
326
+ };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@lingo.dev/_sdk",
3
+ "version": "0.7.12",
4
+ "description": "Lingo.dev JS SDK",
5
+ "private": false,
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "sideEffects": false,
11
+ "types": "build/index.d.ts",
12
+ "module": "build/index.mjs",
13
+ "main": "build/index.cjs",
14
+ "files": [
15
+ "build"
16
+ ],
17
+ "scripts": {
18
+ "dev": "tsup --watch",
19
+ "build": "tsc --noEmit && tsup",
20
+ "test": "vitest run"
21
+ },
22
+ "keywords": [],
23
+ "author": "",
24
+ "license": "Apache-2.0",
25
+ "dependencies": {
26
+ "@paralleldrive/cuid2": "^2.2.2",
27
+ "@lingo.dev/_spec": "workspace:*",
28
+ "jsdom": "^25.0.1",
29
+ "typescript": "^5.7.2",
30
+ "vitest": "^2.1.8",
31
+ "zod": "^3.24.1"
32
+ },
33
+ "devDependencies": {
34
+ "@types/jsdom": "^21.1.7",
35
+ "tsup": "^8.3.5"
36
+ }
37
+ }