@lingo.dev/_sdk 0.7.12

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.
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
+ }