ai-l10n-sdk 1.5.0 → 1.6.0
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/CHANGELOG.md +25 -0
- package/README.md +139 -1
- package/dist/i18nProjectManager.d.ts +5 -0
- package/dist/i18nProjectManager.js +8 -0
- package/dist/index.d.ts +27 -1
- package/dist/index.js +21 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to the SDK package will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.6.0] - 2026-06-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **`I18nProjectManager.extractLanguageCodeFromPath(sourceFilePath)`** — New public method that returns the detected source language code from the file path (e.g., `"en"` for `locales/en.json`) or `null` if the structure is unrecognised. Used internally by `AiTranslator` to auto-populate `sourceLanguageCode` on translation requests.
|
|
12
|
+
- **Extended `TranslationConfig`** with four new optional fields:
|
|
13
|
+
- `sourceLanguageCode?: string | null` — BCP-47 source language; auto-detected from the file path when not provided
|
|
14
|
+
- `generateGlossary?: boolean` — generates and saves a glossary for this language pair for future translations (default: `false`)
|
|
15
|
+
- `glossary?: GlossaryEntry[] | null` — override the active glossary: omit/`null` = use active, `[]` = disable, entries = replace for this request
|
|
16
|
+
- `terminology?: TerminologyEntry[]` — terms for consistent translation with disallowed synonyms
|
|
17
|
+
- **`GlossaryEntry` and `TerminologyEntry`** re-exported from `ai-l10n-core` for convenience
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Updated to use `ai-l10n-core@1.6.0`
|
|
21
|
+
- `AiTranslator.translate()` now auto-detects the source language code from the project structure and passes it to the API as `sourceLanguageCode`
|
|
22
|
+
|
|
23
|
+
## [1.5.1] - 2026-04-17
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Updated to use `ai-l10n-core@1.5.1`
|
|
27
|
+
- Set default client identifier on `translate()` requests.
|
|
28
|
+
- Standardized the `reason` type used in API error responses to a fixed union of values.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- API documentation
|
|
32
|
+
|
|
8
33
|
## [1.5.0] - 2026-04-16
|
|
9
34
|
|
|
10
35
|
### Changed
|
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ const result = await translator.translate({
|
|
|
155
155
|
});
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
For the full list of supported formats see the [API documentation](https://l10n.dev/
|
|
158
|
+
For the full list of supported formats see the [API documentation](https://api.l10n.dev/doc/#tag/ai-translation/post/v2/translate).
|
|
159
159
|
|
|
160
160
|
### Multiple Files
|
|
161
161
|
|
|
@@ -170,6 +170,64 @@ for (const file of files) {
|
|
|
170
170
|
}
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
+
### Glossary & Terminology
|
|
174
|
+
|
|
175
|
+
#### Generate and Save a Glossary
|
|
176
|
+
|
|
177
|
+
When `generateGlossary` is enabled, a glossary is built from the source and translated target content and saved as the active glossary for this source/target language pair. It is then used automatically on future translations.
|
|
178
|
+
|
|
179
|
+
> **Balance note:** Balance is debited for the full source content upfront — even when `translateOnlyNewStrings` is `true`. When disabled (default), a temporary internal glossary is generated at no extra cost only for content that exceeds the AI chunk size.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const result = await translator.translate({
|
|
183
|
+
sourceFile: './locales/en.json',
|
|
184
|
+
targetLanguages: ['de', 'fr'],
|
|
185
|
+
generateGlossary: true,
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Manage your saved glossaries at [l10n.dev/ws/translation-glossary](https://l10n.dev/ws/translation-glossary).
|
|
190
|
+
|
|
191
|
+
#### Override the Glossary for a Single Request
|
|
192
|
+
|
|
193
|
+
Supply your own term mappings via `glossary`. Each `GlossaryEntry` maps a source term to the preferred target translation with an optional context note.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { AiTranslator, GlossaryEntry } from 'ai-l10n-sdk';
|
|
197
|
+
|
|
198
|
+
const glossary: GlossaryEntry[] = [
|
|
199
|
+
{ sourceTerm: 'Settings', targetTerm: 'Einstellungen' },
|
|
200
|
+
{ sourceTerm: 'bank', targetTerm: 'Bank', context: 'financial institution' },
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
const result = await translator.translate({
|
|
204
|
+
sourceFile: './locales/en.json',
|
|
205
|
+
targetLanguages: ['de'],
|
|
206
|
+
glossary,
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Pass an empty array (`glossary: []`) to disable the active glossary entirely for this request.
|
|
211
|
+
|
|
212
|
+
#### Terminology
|
|
213
|
+
|
|
214
|
+
Use `terminology` to enforce consistent term usage. Synonyms listed for each entry are replaced with the preferred `term`.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { AiTranslator, TerminologyEntry } from 'ai-l10n-sdk';
|
|
218
|
+
|
|
219
|
+
const terminology: TerminologyEntry[] = [
|
|
220
|
+
{ term: 'Settings', synonyms: ['Preferences', 'Options', 'Configuration'] },
|
|
221
|
+
{ term: 'Dashboard' },
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
const result = await translator.translate({
|
|
225
|
+
sourceFile: './locales/en.json',
|
|
226
|
+
targetLanguages: ['de', 'fr'],
|
|
227
|
+
terminology,
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
173
231
|
## Core API
|
|
174
232
|
|
|
175
233
|
`ILogger`, `ConsoleLogger`, `L10nTranslationService`, and all related types are part of the core library. See [ai-l10n-core](https://www.npmjs.com/package/ai-l10n-core) for full documentation. These are also re-exported from `ai-l10n-sdk` for convenience.
|
|
@@ -284,6 +342,32 @@ interface TranslationConfig {
|
|
|
284
342
|
* Enable verbose logging (default: false)
|
|
285
343
|
*/
|
|
286
344
|
verbose?: boolean;
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* BCP-47 code of the source language (e.g., "en", "en-US", "zh-Hans-CN").
|
|
348
|
+
* If not specified, auto-detected from the source file path.
|
|
349
|
+
*/
|
|
350
|
+
sourceLanguageCode?: string | null;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* When true, generates a glossary from source and translated target content and saves it as the
|
|
354
|
+
* active glossary for this language pair for future translations.
|
|
355
|
+
* Balance is debited for the full source content upfront — even when translateOnlyNewStrings is true.
|
|
356
|
+
* When false (default), an internal glossary is generated only for large content at no extra cost.
|
|
357
|
+
*/
|
|
358
|
+
generateGlossary?: boolean;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Glossary entries to apply during translation.
|
|
362
|
+
* null/omitted = use active glossary, [] = disable glossary, entries = replace active for this request.
|
|
363
|
+
* Manage saved glossaries at https://l10n.dev/ws/translation-glossary
|
|
364
|
+
*/
|
|
365
|
+
glossary?: GlossaryEntry[] | null;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* A list of terms for consistent translations. Synonyms are replaced by the preferred term.
|
|
369
|
+
*/
|
|
370
|
+
terminology?: TerminologyEntry[];
|
|
287
371
|
}
|
|
288
372
|
```
|
|
289
373
|
|
|
@@ -330,6 +414,37 @@ interface TranslationOutput {
|
|
|
330
414
|
}
|
|
331
415
|
```
|
|
332
416
|
|
|
417
|
+
#### GlossaryEntry
|
|
418
|
+
|
|
419
|
+
Maps a source term to a preferred target translation for use in `TranslationConfig.glossary`.
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
interface GlossaryEntry {
|
|
423
|
+
/** The term in the source language to be translated. Max length: 255. */
|
|
424
|
+
sourceTerm: string;
|
|
425
|
+
/** The preferred translation of the term in the target language. Max length: 255. */
|
|
426
|
+
targetTerm: string;
|
|
427
|
+
/**
|
|
428
|
+
* Optional context to clarify the meaning when the term is ambiguous. Max length: 500.
|
|
429
|
+
* Example: 'bank' could mean 'financial institution' or 'river bank'.
|
|
430
|
+
*/
|
|
431
|
+
context?: string | null;
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
#### TerminologyEntry
|
|
436
|
+
|
|
437
|
+
Specifies a preferred term and disallowed synonyms for use in `TranslationConfig.terminology`.
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
interface TerminologyEntry {
|
|
441
|
+
/** The preferred term to use in translations. */
|
|
442
|
+
term: string;
|
|
443
|
+
/** Synonyms that should be replaced by `term`. */
|
|
444
|
+
synonyms?: string[];
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
333
448
|
### I18nProjectManager
|
|
334
449
|
|
|
335
450
|
Utility class for managing i18n project structure detection, language code validation, and file path generation.
|
|
@@ -354,6 +469,25 @@ Creates an instance of I18nProjectManager.
|
|
|
354
469
|
|
|
355
470
|
#### Methods
|
|
356
471
|
|
|
472
|
+
##### `extractLanguageCodeFromPath(sourceFilePath: string): string | null`
|
|
473
|
+
|
|
474
|
+
Extracts the source language code from the file path using project structure detection.
|
|
475
|
+
|
|
476
|
+
**Parameters:**
|
|
477
|
+
- `sourceFilePath: string` - Path to the source file
|
|
478
|
+
|
|
479
|
+
**Returns:** `string | null` — The detected language code (e.g., `"en"`, `"en-US"`) or `null` if the structure is unrecognised
|
|
480
|
+
|
|
481
|
+
**Example:**
|
|
482
|
+
```typescript
|
|
483
|
+
const manager = new I18nProjectManager();
|
|
484
|
+
|
|
485
|
+
manager.extractLanguageCodeFromPath('./locales/en.json'); // Returns: 'en'
|
|
486
|
+
manager.extractLanguageCodeFromPath('./locales/en/common.json'); // Returns: 'en'
|
|
487
|
+
manager.extractLanguageCodeFromPath('./lib/l10n/app_en_US.arb'); // Returns: 'en_US'
|
|
488
|
+
manager.extractLanguageCodeFromPath('./strings.json'); // Returns: null
|
|
489
|
+
```
|
|
490
|
+
|
|
357
491
|
##### `detectLanguagesFromProject(sourceFilePath: string): string[]`
|
|
358
492
|
|
|
359
493
|
Detects target language codes from the project structure by scanning directories and files.
|
|
@@ -441,6 +575,10 @@ manager.getUniqueFilePath('./output.json');
|
|
|
441
575
|
// Returns: './output (2).json'
|
|
442
576
|
```
|
|
443
577
|
|
|
578
|
+
### I18nProjectManager.extractLanguageCodeFromPath usage in AiTranslator
|
|
579
|
+
|
|
580
|
+
`AiTranslator` calls `extractLanguageCodeFromPath()` automatically on each translation to populate `sourceLanguageCode` in the API request. You can override this by setting `sourceLanguageCode` explicitly in `TranslationConfig`.
|
|
581
|
+
|
|
444
582
|
## License
|
|
445
583
|
|
|
446
584
|
MIT
|
|
@@ -4,6 +4,11 @@ export declare class I18nProjectManager {
|
|
|
4
4
|
constructor(logger?: ILogger);
|
|
5
5
|
detectLanguagesFromProject(sourceFilePath: string): string[];
|
|
6
6
|
generateTargetFilePath(sourceFilePath: string, targetLanguage: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Extracts the source language code from the file path using project structure detection.
|
|
9
|
+
* Returns the language code (e.g., `"en"`, `"en-US"`) or `null` if it cannot be determined.
|
|
10
|
+
*/
|
|
11
|
+
extractLanguageCodeFromPath(sourceFilePath: string): string | null;
|
|
7
12
|
getUniqueFilePath(filePath: string): string;
|
|
8
13
|
private detectProjectStructure;
|
|
9
14
|
}
|
|
@@ -164,6 +164,14 @@ class I18nProjectManager {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Extracts the source language code from the file path using project structure detection.
|
|
169
|
+
* Returns the language code (e.g., `"en"`, `"en-US"`) or `null` if it cannot be determined.
|
|
170
|
+
*/
|
|
171
|
+
extractLanguageCodeFromPath(sourceFilePath) {
|
|
172
|
+
const structureInfo = this.detectProjectStructure(sourceFilePath);
|
|
173
|
+
return structureInfo.sourceLanguage ?? null;
|
|
174
|
+
}
|
|
167
175
|
getUniqueFilePath(filePath) {
|
|
168
176
|
if (!fs.existsSync(filePath)) {
|
|
169
177
|
return filePath;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ILogger } from "ai-l10n-core";
|
|
1
|
+
import { ILogger, GlossaryEntry, TerminologyEntry } from "ai-l10n-core";
|
|
2
2
|
/**
|
|
3
3
|
* Configuration options for translation
|
|
4
4
|
*/
|
|
@@ -53,6 +53,32 @@ export interface TranslationConfig {
|
|
|
53
53
|
* Enable verbose logging (default: false)
|
|
54
54
|
*/
|
|
55
55
|
verbose?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* BCP-47 code of the source language (e.g., "en", "en-US", "zh-Hans-CN").
|
|
58
|
+
* If not specified, auto-detected from the source file path.
|
|
59
|
+
*/
|
|
60
|
+
sourceLanguageCode?: string | null;
|
|
61
|
+
/**
|
|
62
|
+
* When true, generates a glossary from source and translated target content and saves it as the
|
|
63
|
+
* active glossary for this language pair for future translations.
|
|
64
|
+
* Balance is debited for the full source content upfront — even when `translateOnlyNewStrings` is true.
|
|
65
|
+
* When false (default), an internal glossary is generated only for large content that exceeds the AI
|
|
66
|
+
* chunk size, at no extra cost. (default: false)
|
|
67
|
+
*/
|
|
68
|
+
generateGlossary?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Glossary entries to apply during translation.
|
|
71
|
+
* - `null` or omitted: use the active glossary for this language pair.
|
|
72
|
+
* - Empty array `[]`: disable glossary translation entirely.
|
|
73
|
+
* - One or more entries: replace the active glossary for this request.
|
|
74
|
+
* Manage saved glossaries at https://l10n.dev/ws/translation-glossary
|
|
75
|
+
*/
|
|
76
|
+
glossary?: GlossaryEntry[] | null;
|
|
77
|
+
/**
|
|
78
|
+
* A list of terms to use for consistent translations.
|
|
79
|
+
* Synonyms listed per entry will be replaced by the preferred term.
|
|
80
|
+
*/
|
|
81
|
+
terminology?: TerminologyEntry[];
|
|
56
82
|
}
|
|
57
83
|
/**
|
|
58
84
|
* Result of a single translation operation
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,7 @@ class AiTranslator {
|
|
|
74
74
|
// Ensure API key is available
|
|
75
75
|
const apiKey = await this.apiKeyManager.ensureApiKey(config.apiKey);
|
|
76
76
|
// Determine target languages
|
|
77
|
+
let sourceLanguageCode = config.sourceLanguageCode;
|
|
77
78
|
let targetLanguages = config.targetLanguages;
|
|
78
79
|
if (!targetLanguages || targetLanguages.length === 0) {
|
|
79
80
|
// Auto-detect from project structure
|
|
@@ -82,6 +83,12 @@ class AiTranslator {
|
|
|
82
83
|
if (targetLanguages.length === 0) {
|
|
83
84
|
throw new Error("No target languages found. Please specify targetLanguages in config or ensure your project has the proper structure (e.g., folders named with language codes or files named with language codes).");
|
|
84
85
|
}
|
|
86
|
+
// Auto-detect source language code from file path if not provided
|
|
87
|
+
// If we can detect target languages from the project structure, we can also attempt to detect the source language code from the file path.
|
|
88
|
+
if (!sourceLanguageCode) {
|
|
89
|
+
sourceLanguageCode =
|
|
90
|
+
this.i18nProjectManager.extractLanguageCodeFromPath(sourceFilePath);
|
|
91
|
+
}
|
|
85
92
|
this.logger.logInfo(`✨ Auto-detected target languages from project structure: ${targetLanguages.join(", ")}`);
|
|
86
93
|
}
|
|
87
94
|
// Validate language codes
|
|
@@ -100,6 +107,9 @@ class AiTranslator {
|
|
|
100
107
|
const translateMetadata = config.translateMetadata ?? false;
|
|
101
108
|
const saveFilteredStrings = config.saveFilteredStrings ?? true;
|
|
102
109
|
const translateOnlyNewStrings = config.translateOnlyNewStrings ?? false;
|
|
110
|
+
const generateGlossary = config.generateGlossary ?? false;
|
|
111
|
+
const glossary = config.glossary;
|
|
112
|
+
const terminology = config.terminology;
|
|
103
113
|
if (verbose) {
|
|
104
114
|
this.logger.logInfo(`Configuration:
|
|
105
115
|
- Use contractions: ${useContractions}
|
|
@@ -107,7 +117,11 @@ class AiTranslator {
|
|
|
107
117
|
- Generate plural forms: ${generatePluralForms}
|
|
108
118
|
- Translate metadata: ${translateMetadata}
|
|
109
119
|
- Save filtered strings: ${saveFilteredStrings}
|
|
110
|
-
- Translate only new strings: ${translateOnlyNewStrings}
|
|
120
|
+
- Translate only new strings: ${translateOnlyNewStrings}
|
|
121
|
+
- Source language: ${sourceLanguageCode ?? "auto-detect"}
|
|
122
|
+
- Generate glossary: ${generateGlossary}
|
|
123
|
+
- Glossary entries: ${glossary === undefined ? "use active" : glossary === null ? "use active" : glossary.length === 0 ? "disabled" : `${glossary.length} entries`}
|
|
124
|
+
- Terminology entries: ${terminology?.length ?? 0}`);
|
|
111
125
|
}
|
|
112
126
|
// Perform translations in parallel
|
|
113
127
|
const totalLanguages = targetLanguages.length;
|
|
@@ -115,7 +129,7 @@ class AiTranslator {
|
|
|
115
129
|
const targetFilePath = this.i18nProjectManager.generateTargetFilePath(sourceFilePath, targetLanguage);
|
|
116
130
|
try {
|
|
117
131
|
this.logger.logInfo(`\n🌐 Translating (${i + 1}/${totalLanguages}) to ${targetLanguage}...`);
|
|
118
|
-
const result = await this.performTranslation(apiKey, sourceFilePath, targetLanguage, targetFilePath, translateOnlyNewStrings, useContractions, useShortening, generatePluralForms, translateMetadata, saveFilteredStrings, format, verbose);
|
|
132
|
+
const result = await this.performTranslation(apiKey, sourceFilePath, targetLanguage, targetFilePath, translateOnlyNewStrings, useContractions, useShortening, generatePluralForms, translateMetadata, saveFilteredStrings, format, verbose, sourceLanguageCode, generateGlossary, glossary, terminology);
|
|
119
133
|
return result;
|
|
120
134
|
}
|
|
121
135
|
catch (error) {
|
|
@@ -172,7 +186,7 @@ class AiTranslator {
|
|
|
172
186
|
};
|
|
173
187
|
}
|
|
174
188
|
}
|
|
175
|
-
async performTranslation(apiKey, sourceFilePath, targetLanguage, targetFilePath, translateOnlyNewStrings, useContractions, useShortening, generatePluralForms, translateMetadata, saveFilteredStrings, format, verbose) {
|
|
189
|
+
async performTranslation(apiKey, sourceFilePath, targetLanguage, targetFilePath, translateOnlyNewStrings, useContractions, useShortening, generatePluralForms, translateMetadata, saveFilteredStrings, format, verbose, sourceLanguageCode, generateGlossary, glossary, terminology) {
|
|
176
190
|
// Read source file
|
|
177
191
|
const sourceContent = fs.readFileSync(sourceFilePath, "utf8");
|
|
178
192
|
// Check if target file exists and read it if updating
|
|
@@ -189,6 +203,7 @@ class AiTranslator {
|
|
|
189
203
|
const request = {
|
|
190
204
|
sourceStrings: sourceContent,
|
|
191
205
|
targetLanguageCode: normalizedLanguage,
|
|
206
|
+
sourceLanguageCode: sourceLanguageCode ?? null,
|
|
192
207
|
useContractions,
|
|
193
208
|
useShortening,
|
|
194
209
|
generatePluralForms,
|
|
@@ -198,6 +213,9 @@ class AiTranslator {
|
|
|
198
213
|
targetStrings,
|
|
199
214
|
schema: format === "arb" ? ai_l10n_core_1.FileSchema.ARBFlutter : null,
|
|
200
215
|
format,
|
|
216
|
+
generateGlossary,
|
|
217
|
+
glossary,
|
|
218
|
+
terminology,
|
|
201
219
|
};
|
|
202
220
|
// Call translation service
|
|
203
221
|
const response = await this.translationService.translate(request, apiKey);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-l10n-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Lightweight SDK for AI-powered localization automation - programmatic API (no CLI)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"node": ">=14.0.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"ai-l10n-core": "^1.
|
|
49
|
+
"ai-l10n-core": "^1.6.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/mocha": "^10.0.10",
|