ai-l10n-sdk 1.4.1 → 1.5.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 +7 -0
- package/dist/apiKeyManager.js +1 -1
- package/dist/index.js +27 -29
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ 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.5.0] - 2026-04-16
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Updated to use `ai-l10n-core@1.5.0` — all API methods now return `ApiResponse<T>` and never throw
|
|
12
|
+
- `TranslationOutput.remainingBalance` is now sourced from `TranslationResponse.currentBalance` (unchanged behavior for consumers)
|
|
13
|
+
- Error results from `translate()` (previously thrown or null-returned) are now uniformly surfaced as failed `TranslationOutput` entries with the error `message` from `TranslationResponse`
|
|
14
|
+
|
|
8
15
|
## [1.4.0] - 2026-04-02
|
|
9
16
|
|
|
10
17
|
### Added
|
package/dist/apiKeyManager.js
CHANGED
|
@@ -41,7 +41,7 @@ const ai_l10n_core_1 = require("ai-l10n-core");
|
|
|
41
41
|
class ApiKeyManager {
|
|
42
42
|
constructor(logger = new ai_l10n_core_1.ConsoleLogger()) {
|
|
43
43
|
this.logger = logger;
|
|
44
|
-
this.noApiKeyMessage = "ℹ️
|
|
44
|
+
this.noApiKeyMessage = "ℹ️ API Key not found. Please provide it via:\n" +
|
|
45
45
|
"1. Configuration option (apiKey)\n" +
|
|
46
46
|
"2. Environment variable (L10N_API_KEY)\n" +
|
|
47
47
|
"3. Run 'ai-l10n config --api-key YOUR_KEY' to save it\n" +
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ class AiTranslator {
|
|
|
69
69
|
const fileExtension = path.extname(sourceFilePath);
|
|
70
70
|
const format = fileExtension.slice(1);
|
|
71
71
|
if (verbose) {
|
|
72
|
-
|
|
72
|
+
this.logger.logInfo(`📂 Source file: ${sourceFilePath}`);
|
|
73
73
|
}
|
|
74
74
|
// Ensure API key is available
|
|
75
75
|
const apiKey = await this.apiKeyManager.ensureApiKey(config.apiKey);
|
|
@@ -82,7 +82,7 @@ class AiTranslator {
|
|
|
82
82
|
if (targetLanguages.length === 0) {
|
|
83
83
|
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
84
|
}
|
|
85
|
-
|
|
85
|
+
this.logger.logInfo(`✨ Auto-detected target languages from project structure: ${targetLanguages.join(", ")}`);
|
|
86
86
|
}
|
|
87
87
|
// Validate language codes
|
|
88
88
|
for (const lang of targetLanguages) {
|
|
@@ -91,7 +91,7 @@ class AiTranslator {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
if (verbose) {
|
|
94
|
-
|
|
94
|
+
this.logger.logInfo(`🎯 Target languages: ${targetLanguages.join(", ")}`);
|
|
95
95
|
}
|
|
96
96
|
// Prepare configuration
|
|
97
97
|
const useContractions = config.useContractions ?? true;
|
|
@@ -101,7 +101,7 @@ class AiTranslator {
|
|
|
101
101
|
const saveFilteredStrings = config.saveFilteredStrings ?? true;
|
|
102
102
|
const translateOnlyNewStrings = config.translateOnlyNewStrings ?? false;
|
|
103
103
|
if (verbose) {
|
|
104
|
-
|
|
104
|
+
this.logger.logInfo(`Configuration:
|
|
105
105
|
- Use contractions: ${useContractions}
|
|
106
106
|
- Use shortening: ${useShortening}
|
|
107
107
|
- Generate plural forms: ${generatePluralForms}
|
|
@@ -114,17 +114,16 @@ class AiTranslator {
|
|
|
114
114
|
const translationPromises = targetLanguages.map(async (targetLanguage, i) => {
|
|
115
115
|
const targetFilePath = this.i18nProjectManager.generateTargetFilePath(sourceFilePath, targetLanguage);
|
|
116
116
|
try {
|
|
117
|
-
|
|
117
|
+
this.logger.logInfo(`\n🌐 Translating (${i + 1}/${totalLanguages}) to ${targetLanguage}...`);
|
|
118
118
|
const result = await this.performTranslation(apiKey, sourceFilePath, targetLanguage, targetFilePath, translateOnlyNewStrings, useContractions, useShortening, generatePluralForms, translateMetadata, saveFilteredStrings, format, verbose);
|
|
119
119
|
return result;
|
|
120
120
|
}
|
|
121
121
|
catch (error) {
|
|
122
|
-
|
|
123
|
-
console.error(`❌ Translation to ${targetLanguage} failed: ${errorMessage}`);
|
|
122
|
+
this.logger.showAndLogError(`Translation to ${targetLanguage} failed`, error);
|
|
124
123
|
return {
|
|
125
124
|
success: false,
|
|
126
125
|
language: targetLanguage,
|
|
127
|
-
error:
|
|
126
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
128
127
|
};
|
|
129
128
|
}
|
|
130
129
|
});
|
|
@@ -143,19 +142,19 @@ class AiTranslator {
|
|
|
143
142
|
}
|
|
144
143
|
// Summary
|
|
145
144
|
const successCount = results.filter((r) => r.success).length;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
this.logger.logInfo(`\n${"=".repeat(50)}`);
|
|
146
|
+
this.logger.logInfo(`📊 Translation Summary`);
|
|
147
|
+
this.logger.logInfo(`${"=".repeat(50)}`);
|
|
148
|
+
this.logger.logInfo(`✅ Successful: ${successCount}/${targetLanguages.length}`);
|
|
149
|
+
this.logger.logInfo(`📝 Total characters used: ${totalCharsUsed.toLocaleString()}`);
|
|
151
150
|
if (remainingBalance !== undefined && remainingBalance !== null) {
|
|
152
|
-
|
|
151
|
+
this.logger.logInfo(`💰 Remaining balance: ${remainingBalance.toLocaleString()} characters`);
|
|
153
152
|
}
|
|
154
153
|
if (successCount < targetLanguages.length) {
|
|
155
154
|
const failedLanguages = results
|
|
156
155
|
.filter((r) => !r.success)
|
|
157
156
|
.map((r) => r.language);
|
|
158
|
-
|
|
157
|
+
this.logger.logWarning(`Failed: ${failedLanguages.join(", ")}`);
|
|
159
158
|
}
|
|
160
159
|
return {
|
|
161
160
|
success: successCount > 0,
|
|
@@ -165,8 +164,7 @@ class AiTranslator {
|
|
|
165
164
|
};
|
|
166
165
|
}
|
|
167
166
|
catch (error) {
|
|
168
|
-
|
|
169
|
-
console.error(`❌ Translation failed: ${errorMessage}`);
|
|
167
|
+
this.logger.showAndLogError("Translation failed", error);
|
|
170
168
|
return {
|
|
171
169
|
success: false,
|
|
172
170
|
results: [],
|
|
@@ -182,7 +180,7 @@ class AiTranslator {
|
|
|
182
180
|
if (translateOnlyNewStrings && fs.existsSync(targetFilePath)) {
|
|
183
181
|
targetStrings = fs.readFileSync(targetFilePath, "utf8");
|
|
184
182
|
if (verbose) {
|
|
185
|
-
|
|
183
|
+
this.logger.logInfo(` 📄 Updating existing file: ${targetFilePath}`);
|
|
186
184
|
}
|
|
187
185
|
}
|
|
188
186
|
// Normalize language code for API
|
|
@@ -196,21 +194,21 @@ class AiTranslator {
|
|
|
196
194
|
generatePluralForms,
|
|
197
195
|
translateMetadata,
|
|
198
196
|
client: ai_l10n_core_1.CONFIG.CLIENT,
|
|
199
|
-
returnTranslationsAsString: true,
|
|
200
197
|
translateOnlyNewStrings,
|
|
201
198
|
targetStrings,
|
|
202
199
|
schema: format === "arb" ? ai_l10n_core_1.FileSchema.ARBFlutter : null,
|
|
203
200
|
format,
|
|
204
201
|
};
|
|
205
202
|
// Call translation service
|
|
206
|
-
const
|
|
207
|
-
if (!
|
|
203
|
+
const response = await this.translationService.translate(request, apiKey);
|
|
204
|
+
if (!response.success) {
|
|
208
205
|
return {
|
|
209
206
|
success: false,
|
|
210
207
|
language: targetLanguage,
|
|
211
|
-
error: "Translation service returned no result",
|
|
208
|
+
error: response.message ?? "Translation service returned no result",
|
|
212
209
|
};
|
|
213
210
|
}
|
|
211
|
+
const result = response.data;
|
|
214
212
|
if (!result.translations) {
|
|
215
213
|
return {
|
|
216
214
|
success: false,
|
|
@@ -230,14 +228,14 @@ class AiTranslator {
|
|
|
230
228
|
this.handleFilteredStrings(result, outputPath, saveFilteredStrings);
|
|
231
229
|
}
|
|
232
230
|
const charsUsed = result.usage.charsUsed || 0;
|
|
233
|
-
|
|
234
|
-
|
|
231
|
+
this.logger.logInfo(` ✅ Saved to: ${outputPath}`);
|
|
232
|
+
this.logger.logInfo(` 📊 Characters used: ${charsUsed.toLocaleString()}`);
|
|
235
233
|
return {
|
|
236
234
|
success: true,
|
|
237
235
|
language: targetLanguage,
|
|
238
236
|
outputPath,
|
|
239
237
|
charsUsed,
|
|
240
|
-
remainingBalance:
|
|
238
|
+
remainingBalance: response.currentBalance,
|
|
241
239
|
};
|
|
242
240
|
}
|
|
243
241
|
handleFilteredStrings(result, targetFilePath, saveFilteredStrings) {
|
|
@@ -252,9 +250,9 @@ class AiTranslator {
|
|
|
252
250
|
return;
|
|
253
251
|
}
|
|
254
252
|
const filteredStringsContent = result.filteredStrings ?? "";
|
|
255
|
-
|
|
253
|
+
this.logger.logWarning(`Some strings were excluded due to ${reasonMessage}`);
|
|
256
254
|
if (result.finishReason === ai_l10n_core_1.FinishReason.contentFilter) {
|
|
257
|
-
|
|
255
|
+
this.logger.logInfo(` ℹ️ View content policy at: ${ai_l10n_core_1.URLS.CONTENT_POLICY}`);
|
|
258
256
|
}
|
|
259
257
|
if (saveFilteredStrings) {
|
|
260
258
|
const ext = path.extname(targetFilePath);
|
|
@@ -262,10 +260,10 @@ class AiTranslator {
|
|
|
262
260
|
const dir = path.dirname(targetFilePath);
|
|
263
261
|
const filteredPath = path.join(dir, `${base}.filtered${ext}`);
|
|
264
262
|
fs.writeFileSync(filteredPath, filteredStringsContent, "utf8");
|
|
265
|
-
|
|
263
|
+
this.logger.logInfo(` 📝 Filtered strings saved to: ${filteredPath}`);
|
|
266
264
|
}
|
|
267
265
|
else {
|
|
268
|
-
|
|
266
|
+
this.logger.logInfo(` 📝 Filtered strings:\n${filteredStringsContent}`);
|
|
269
267
|
}
|
|
270
268
|
}
|
|
271
269
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-l10n-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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.5.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/mocha": "^10.0.10",
|