ai-l10n-core 1.4.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 +19 -0
- package/LICENSE +21 -0
- package/NOTICE +27 -0
- package/README.md +487 -0
- package/dist/consoleLogger.d.ts +26 -0
- package/dist/consoleLogger.js +62 -0
- package/dist/constants.d.ts +17 -0
- package/dist/constants.js +22 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -0
- package/dist/languageUtils.d.ts +27 -0
- package/dist/languageUtils.js +79 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.js +2 -0
- package/dist/test/consoleLogger.test.d.ts +1 -0
- package/dist/test/consoleLogger.test.js +242 -0
- package/dist/test/translationService.test.d.ts +1 -0
- package/dist/test/translationService.test.js +454 -0
- package/dist/translationService.d.ts +94 -0
- package/dist/translationService.js +155 -0
- package/package.json +51 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog - ai-l10n-core
|
|
2
|
+
|
|
3
|
+
All notable changes to the core package will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.4.0] - 2026-04-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Initial Package Release** - Core library extracted from `ai-l10n` as independent package
|
|
12
|
+
- `L10nTranslationService` — low-level translation API client
|
|
13
|
+
- `TranslationRequest` now includes optional `format?: string` field for specifying localization file format (e.g., `"json"`, `"arb"`, `"po"`, `"yaml"`, `"xliff"`)
|
|
14
|
+
- `ILogger` interface — dependency injection for logging
|
|
15
|
+
- `ConsoleLogger` — default console-based logger implementation
|
|
16
|
+
- `languageUtils` — BCP 47 language code normalization and validation
|
|
17
|
+
- `validateLanguageCode(code)` and `normalizeLanguageCode(code)` are now the canonical location for these utilities (previously also exposed as wrapper methods on `I18nProjectManager` in `ai-l10n-sdk`)
|
|
18
|
+
- `constants` — `URLS` and `CONFIG` shared configuration
|
|
19
|
+
- Complete TypeScript type definitions
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anton Antonov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
ai-l10n-core
|
|
2
|
+
Copyright (C) 2025 Antonov Anton Yurevich, Individual Entrepreneur. <https://l10n.dev/>
|
|
3
|
+
|
|
4
|
+
This package is part of the ai-l10n project and provides the platform-independent
|
|
5
|
+
core for AI-powered localization — translation API client, language utilities, and
|
|
6
|
+
shared types.
|
|
7
|
+
|
|
8
|
+
License:
|
|
9
|
+
--------
|
|
10
|
+
This software is licensed under the MIT License.
|
|
11
|
+
See the LICENSE file for the complete license text.
|
|
12
|
+
|
|
13
|
+
Usage of l10n.dev service:
|
|
14
|
+
--------------------------
|
|
15
|
+
This package communicates with the l10n.dev platform, which provides AI-powered
|
|
16
|
+
translation for internationalization (i18n) files. The l10n.dev backend service is
|
|
17
|
+
proprietary and not covered by the MIT License. A valid l10n.dev account and API key
|
|
18
|
+
are required to use the translation features of this package.
|
|
19
|
+
|
|
20
|
+
You may freely use, modify, and distribute this package under the terms of the MIT License.
|
|
21
|
+
However, this does not grant any rights to the l10n.dev backend services, which remain
|
|
22
|
+
the exclusive property of Antonov Anton Yurevich, Individual Entrepreneur.
|
|
23
|
+
|
|
24
|
+
Trademark Notice:
|
|
25
|
+
-----------------
|
|
26
|
+
"l10n.dev" and associated branding are trademarks of Antonov Anton Yurevich,
|
|
27
|
+
Individual Entrepreneur. Use of these names and branding is subject to applicable law.
|
package/README.md
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
# ai-l10n-core
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/ai-l10n-core)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Platform-independent core for AI-powered localization — translation API client, language utilities, and shared types.
|
|
7
|
+
|
|
8
|
+
This is the foundational library for the [ai-l10n](https://www.npmjs.com/package/ai-l10n) ecosystem. It provides the low-level translation API client, logger interface, and language utilities used by the SDK and CLI.
|
|
9
|
+
|
|
10
|
+
Powered by [l10n.dev](https://l10n.dev).
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install ai-l10n-core
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Related Packages
|
|
19
|
+
|
|
20
|
+
- [ai-l10n-sdk](https://www.npmjs.com/package/ai-l10n-sdk) — High-level SDK with `AiTranslator`, file management, and project structure detection
|
|
21
|
+
- [ai-l10n](https://www.npmjs.com/package/ai-l10n) — CLI tool built on the SDK
|
|
22
|
+
|
|
23
|
+
## Custom Logger Integration
|
|
24
|
+
|
|
25
|
+
By default, `L10nTranslationService` uses the built-in `ConsoleLogger` which outputs to the console. For integration in other environments (VS Code extensions, web applications, custom UIs), implement a custom logger.
|
|
26
|
+
|
|
27
|
+
### Logger Interface
|
|
28
|
+
|
|
29
|
+
Implement the `ILogger` interface to create your own logger:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { ILogger } from 'ai-l10n-core';
|
|
33
|
+
|
|
34
|
+
class MyLogger implements ILogger {
|
|
35
|
+
logInfo(message: string): void {
|
|
36
|
+
myLoggingSystem.info(message);
|
|
37
|
+
}
|
|
38
|
+
logWarning(message: string, error?: unknown): void {
|
|
39
|
+
myLoggingSystem.warn(message, error);
|
|
40
|
+
}
|
|
41
|
+
logError(message: string, error?: unknown): void {
|
|
42
|
+
myLoggingSystem.error(message, error);
|
|
43
|
+
}
|
|
44
|
+
showAndLogError(
|
|
45
|
+
message: string,
|
|
46
|
+
error?: unknown,
|
|
47
|
+
context?: string,
|
|
48
|
+
linkBtnText?: string,
|
|
49
|
+
url?: string
|
|
50
|
+
): void {
|
|
51
|
+
myLoggingSystem.error(message, { error, context, linkBtnText, url });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Default ConsoleLogger
|
|
57
|
+
|
|
58
|
+
The built-in `ConsoleLogger` implementation:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { ConsoleLogger } from 'ai-l10n-core';
|
|
62
|
+
|
|
63
|
+
const logger = new ConsoleLogger();
|
|
64
|
+
|
|
65
|
+
// Outputs with emojis and formatting
|
|
66
|
+
logger.logInfo('Translation started'); // Plain console.log
|
|
67
|
+
logger.logWarning('File not found', error); // ⚠️ with error details
|
|
68
|
+
logger.logError('Translation failed', error); // ❌ with error details
|
|
69
|
+
logger.showAndLogError(
|
|
70
|
+
'API request failed',
|
|
71
|
+
error,
|
|
72
|
+
'Authentication',
|
|
73
|
+
'Get API Key',
|
|
74
|
+
'https://l10n.dev/ws/keys'
|
|
75
|
+
);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API Reference
|
|
79
|
+
|
|
80
|
+
### ConsoleLogger
|
|
81
|
+
|
|
82
|
+
Default console-based implementation of `ILogger`.
|
|
83
|
+
|
|
84
|
+
#### Constructor
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { ConsoleLogger } from 'ai-l10n-core';
|
|
88
|
+
|
|
89
|
+
const logger = new ConsoleLogger();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Methods
|
|
93
|
+
|
|
94
|
+
- `logInfo(message: string): void` — Outputs plain `console.log`
|
|
95
|
+
- `logWarning(message: string, error?: unknown): void` — Outputs `⚠️ message` via `console.warn`
|
|
96
|
+
- `logError(message: string, error?: unknown): void` — Outputs `❌ message` via `console.error`
|
|
97
|
+
- `showAndLogError(message, error?, context?, linkBtnText?, url?): void` — Outputs full error details including stack trace, context, and optional link
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### L10nTranslationService
|
|
102
|
+
|
|
103
|
+
Low-level client for interacting with the l10n.dev translation API.
|
|
104
|
+
|
|
105
|
+
#### Constructor
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { L10nTranslationService, ConsoleLogger, ILogger } from 'ai-l10n-core';
|
|
109
|
+
|
|
110
|
+
// Default: uses ConsoleLogger
|
|
111
|
+
const service = new L10nTranslationService();
|
|
112
|
+
|
|
113
|
+
// With a custom logger
|
|
114
|
+
const customLogger: ILogger = new ConsoleLogger();
|
|
115
|
+
const service = new L10nTranslationService(customLogger);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Methods
|
|
119
|
+
|
|
120
|
+
##### `translate(request: TranslationRequest, apiKey: string): Promise<TranslationResult | null>`
|
|
121
|
+
|
|
122
|
+
Translates localization content using the l10n.dev API.
|
|
123
|
+
|
|
124
|
+
**Parameters:**
|
|
125
|
+
- `request: TranslationRequest` — Translation request configuration
|
|
126
|
+
- `apiKey: string` — API key for authentication
|
|
127
|
+
|
|
128
|
+
**Returns:** `Promise<TranslationResult | null>` — Translation result, or `null` for 401/402 responses
|
|
129
|
+
|
|
130
|
+
**Throws:** Error for 400, 413, 500, and other failure conditions
|
|
131
|
+
|
|
132
|
+
##### `predictLanguages(input: string, limit?: number): Promise<Language[]>`
|
|
133
|
+
|
|
134
|
+
Predicts possible language codes from a text input (language name in English or native language, region, or script).
|
|
135
|
+
|
|
136
|
+
**Parameters:**
|
|
137
|
+
- `input: string` — Text to analyze
|
|
138
|
+
- `limit?: number` — Maximum number of predictions (default: 10)
|
|
139
|
+
|
|
140
|
+
**Returns:** `Promise<Language[]>` — Array of predicted languages with codes and names
|
|
141
|
+
|
|
142
|
+
##### `getLanguages(options?: { codes?: string[]; proficiencyLevels?: LanguageProficiencyLevel[] }): Promise<SupportedLanguagesResponse>`
|
|
143
|
+
|
|
144
|
+
Retrieves a list of supported languages, optionally filtered by language codes or proficiency levels.
|
|
145
|
+
|
|
146
|
+
**Parameters:**
|
|
147
|
+
- `options?` — Optional filter object
|
|
148
|
+
- `codes?: string[]` — Filter by specific language codes (e.g., `["en", "es", "fr"]`)
|
|
149
|
+
- `proficiencyLevels?: LanguageProficiencyLevel[]` — Filter by proficiency level: `"strong"`, `"high"`, `"moderate"`, or `"limited"`
|
|
150
|
+
|
|
151
|
+
**Returns:** `Promise<SupportedLanguagesResponse>` — Object containing a `languages` array of `SupportedLanguage` entries
|
|
152
|
+
|
|
153
|
+
**Throws:** Error if the API request fails (non-2xx response)
|
|
154
|
+
|
|
155
|
+
**Example:**
|
|
156
|
+
```typescript
|
|
157
|
+
// Get all supported languages
|
|
158
|
+
const { languages } = await service.getLanguages();
|
|
159
|
+
|
|
160
|
+
// Filter by proficiency level
|
|
161
|
+
const { languages } = await service.getLanguages({
|
|
162
|
+
proficiencyLevels: ['strong', 'high'],
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Filter by specific codes
|
|
166
|
+
const { languages } = await service.getLanguages({
|
|
167
|
+
codes: ['en', 'es', 'fr'],
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### Types
|
|
172
|
+
|
|
173
|
+
##### TranslationRequest
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
interface TranslationRequest {
|
|
177
|
+
/** Source strings as JSON string */
|
|
178
|
+
sourceStrings: string;
|
|
179
|
+
|
|
180
|
+
/** Target language code (e.g., "es", "fr-FR") */
|
|
181
|
+
targetLanguageCode: string;
|
|
182
|
+
|
|
183
|
+
/** Use contractions (e.g., "don't" vs "do not") */
|
|
184
|
+
useContractions?: boolean;
|
|
185
|
+
|
|
186
|
+
/** Use shortened forms when translation is longer than source text */
|
|
187
|
+
useShortening?: boolean;
|
|
188
|
+
|
|
189
|
+
/** Generate plural forms for i18next */
|
|
190
|
+
generatePluralForms?: boolean;
|
|
191
|
+
|
|
192
|
+
/** Translate metadata along with UI strings (e.g., ARB `@key` descriptions) */
|
|
193
|
+
translateMetadata?: boolean;
|
|
194
|
+
|
|
195
|
+
/** Return translations as JSON string */
|
|
196
|
+
returnTranslationsAsString: boolean;
|
|
197
|
+
|
|
198
|
+
/** Client identifier */
|
|
199
|
+
client: string;
|
|
200
|
+
|
|
201
|
+
/** Only translate new strings */
|
|
202
|
+
translateOnlyNewStrings?: boolean;
|
|
203
|
+
|
|
204
|
+
/** Existing target strings (for incremental updates) */
|
|
205
|
+
targetStrings?: string;
|
|
206
|
+
|
|
207
|
+
/** File schema format */
|
|
208
|
+
schema: FileSchema | null;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Localization file format (e.g., "json", "arb", "po", "yaml", "xml").
|
|
212
|
+
* If not specified, auto-detected from sourceStrings content.
|
|
213
|
+
* See the [supported formats table](https://l10n.dev/ws/translate-i18n-files#supported-formats).
|
|
214
|
+
*/
|
|
215
|
+
format?: string;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
##### TranslationResult
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
interface TranslationResult {
|
|
223
|
+
/** Target language code */
|
|
224
|
+
targetLanguageCode: string;
|
|
225
|
+
|
|
226
|
+
/** Translated content as JSON string */
|
|
227
|
+
translations?: string;
|
|
228
|
+
|
|
229
|
+
/** Usage statistics */
|
|
230
|
+
usage: TranslationUsage;
|
|
231
|
+
|
|
232
|
+
/** Reason translation finished */
|
|
233
|
+
finishReason?: FinishReason;
|
|
234
|
+
|
|
235
|
+
/** Number of chunks completed */
|
|
236
|
+
completedChunks: number;
|
|
237
|
+
|
|
238
|
+
/** Total number of chunks */
|
|
239
|
+
totalChunks: number;
|
|
240
|
+
|
|
241
|
+
/** Remaining character balance */
|
|
242
|
+
remainingBalance?: number;
|
|
243
|
+
|
|
244
|
+
/** Strings filtered due to content policy, in the same format as the input */
|
|
245
|
+
filteredStrings?: string;
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
##### TranslationUsage
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
interface TranslationUsage {
|
|
253
|
+
/** Number of characters used */
|
|
254
|
+
charsUsed?: number;
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
##### FileSchema
|
|
259
|
+
|
|
260
|
+
Supported file schema formats for translation requests.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
enum FileSchema {
|
|
264
|
+
/** OpenAPI specification format */
|
|
265
|
+
OpenAPI = "openApi",
|
|
266
|
+
|
|
267
|
+
/** Flutter ARB (Application Resource Bundle) format */
|
|
268
|
+
ARBFlutter = "arbFlutter"
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
##### FinishReason
|
|
273
|
+
|
|
274
|
+
Reasons why a translation finished.
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
enum FinishReason {
|
|
278
|
+
/** Translation completed successfully */
|
|
279
|
+
stop = "stop",
|
|
280
|
+
|
|
281
|
+
/** Translation stopped due to length/context limits */
|
|
282
|
+
length = "length",
|
|
283
|
+
|
|
284
|
+
/** Some content was filtered due to content policy */
|
|
285
|
+
contentFilter = "contentFilter",
|
|
286
|
+
|
|
287
|
+
/** Insufficient character balance */
|
|
288
|
+
insufficientBalance = "insufficientBalance",
|
|
289
|
+
|
|
290
|
+
/** Translation failed with error */
|
|
291
|
+
error = "error"
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
##### Language
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
interface Language {
|
|
299
|
+
/** Language code (e.g., "es", "fr-FR") */
|
|
300
|
+
code: string;
|
|
301
|
+
|
|
302
|
+
/** Human-readable language name */
|
|
303
|
+
name: string;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
##### SupportedLanguage
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
interface SupportedLanguage {
|
|
311
|
+
code: string | null;
|
|
312
|
+
name: string | null;
|
|
313
|
+
nativeName: string | null;
|
|
314
|
+
level?: "strong" | "high" | "moderate" | "limited";
|
|
315
|
+
regions?: Region[] | null;
|
|
316
|
+
scripts?: Script[] | null;
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
#### Error Handling
|
|
321
|
+
|
|
322
|
+
The service throws errors for:
|
|
323
|
+
|
|
324
|
+
- **400 Bad Request** — Validation error with details
|
|
325
|
+
- **413 Request Too Large** — `"Request too large. Maximum request size is 5 MB."`
|
|
326
|
+
- **500 Server Error** — `"An internal server error occurred (Error code: ...)"`
|
|
327
|
+
|
|
328
|
+
Returns `null` with error logged for:
|
|
329
|
+
|
|
330
|
+
- **Missing API Key** — `"API Key not set. Please configure your API Key first."`
|
|
331
|
+
- **401 Unauthorized** — `"Unauthorized. Please check your API Key."`
|
|
332
|
+
- **402 Payment Required** — `"Not enough characters remaining for this translation..."`
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### ILogger Interface
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
interface ILogger {
|
|
340
|
+
logInfo(message: string): void;
|
|
341
|
+
logWarning(message: string, error?: unknown): void;
|
|
342
|
+
logError(message: string, error?: unknown): void;
|
|
343
|
+
showAndLogError(
|
|
344
|
+
message: string,
|
|
345
|
+
error?: unknown,
|
|
346
|
+
context?: string,
|
|
347
|
+
linkBtnText?: string,
|
|
348
|
+
url?: string
|
|
349
|
+
): void;
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
### Constants
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import { URLS, CONFIG } from 'ai-l10n-core';
|
|
359
|
+
|
|
360
|
+
// API endpoints
|
|
361
|
+
URLS.API_BASE // Base URL for l10n.dev API
|
|
362
|
+
|
|
363
|
+
// Configuration keys
|
|
364
|
+
CONFIG.API_KEY // Key name for stored API key
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
### Language Utilities
|
|
370
|
+
|
|
371
|
+
Pure utility functions for working with BCP 47 language codes.
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import { validateLanguageCode, normalizeLanguageCode, extractLanguageCode } from 'ai-l10n-core';
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
##### `validateLanguageCode(code: string): boolean`
|
|
378
|
+
|
|
379
|
+
Validates whether a string is a valid BCP 47 language code.
|
|
380
|
+
|
|
381
|
+
**Parameters:**
|
|
382
|
+
- `code: string` — Language code to validate
|
|
383
|
+
|
|
384
|
+
**Returns:** `boolean` — `true` if valid, `false` otherwise
|
|
385
|
+
|
|
386
|
+
**Validation Rules:**
|
|
387
|
+
- **Case-insensitive validation**: Accepts language codes in any case (e.g., `"EN"`, `"en"`, `"EN-US"`)
|
|
388
|
+
- Language: 2-3 letters (e.g., `en`, `eng`, `EN`)
|
|
389
|
+
- Script (optional): 4 letters (e.g., `Hans`, `Latn`, `hans`)
|
|
390
|
+
- Region (optional): 2-3 letters or 3 digits (e.g., `US`, `us`, `419`)
|
|
391
|
+
- Separators: hyphens `-` or underscores `_`
|
|
392
|
+
|
|
393
|
+
**Important:** This function only validates the format. For proper BCP 47 compliance, use `normalizeLanguageCode()` to convert validated codes to the correct case format (language lowercase, Script title case, REGION uppercase).
|
|
394
|
+
|
|
395
|
+
**Example:**
|
|
396
|
+
```typescript
|
|
397
|
+
// All valid (case-insensitive)
|
|
398
|
+
validateLanguageCode('en'); // true
|
|
399
|
+
validateLanguageCode('EN'); // true
|
|
400
|
+
validateLanguageCode('en-US'); // true
|
|
401
|
+
validateLanguageCode('en-us'); // true
|
|
402
|
+
validateLanguageCode('zh-Hans-CN'); // true
|
|
403
|
+
validateLanguageCode('ZH-HANS-CN'); // true
|
|
404
|
+
|
|
405
|
+
// Invalid
|
|
406
|
+
validateLanguageCode('invalid'); // false
|
|
407
|
+
validateLanguageCode(''); // false
|
|
408
|
+
|
|
409
|
+
// Normalize after validation for proper BCP 47 format
|
|
410
|
+
const code = 'EN-US';
|
|
411
|
+
if (validateLanguageCode(code)) {
|
|
412
|
+
const normalized = normalizeLanguageCode(code);
|
|
413
|
+
console.log(normalized); // 'en-US'
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
##### `normalizeLanguageCode(code: string): string`
|
|
420
|
+
|
|
421
|
+
Normalizes language codes to a consistent BCP 47 format.
|
|
422
|
+
|
|
423
|
+
**Parameters:**
|
|
424
|
+
- `code: string` — Language code to normalize (accepts hyphens or underscores)
|
|
425
|
+
|
|
426
|
+
**Returns:** `string` — Normalized language code in format: `language[-Script][-REGION]`
|
|
427
|
+
|
|
428
|
+
**Normalization Rules:**
|
|
429
|
+
- Language: lowercase (e.g., `"EN"` → `"en"`)
|
|
430
|
+
- Script: Title case (e.g., `"hans"` → `"Hans"`)
|
|
431
|
+
- Region: uppercase (e.g., `"us"` → `"US"`)
|
|
432
|
+
- Separator: always hyphen `-` (underscores converted to hyphens)
|
|
433
|
+
|
|
434
|
+
**Example:**
|
|
435
|
+
```typescript
|
|
436
|
+
normalizeLanguageCode('en'); // 'en'
|
|
437
|
+
normalizeLanguageCode('en-us'); // 'en-US'
|
|
438
|
+
normalizeLanguageCode('en_US'); // 'en-US'
|
|
439
|
+
normalizeLanguageCode('zh_hans'); // 'zh-Hans'
|
|
440
|
+
normalizeLanguageCode('zh-Hans-CN'); // 'zh-Hans-CN'
|
|
441
|
+
normalizeLanguageCode('ZH_HANS_CN'); // 'zh-Hans-CN'
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
##### `extractLanguageCode(fileName: string): string | null`
|
|
447
|
+
|
|
448
|
+
Extracts a language code from a file name, handling various localization file patterns.
|
|
449
|
+
|
|
450
|
+
**Parameters:**
|
|
451
|
+
- `fileName: string` — File name (with or without extension)
|
|
452
|
+
|
|
453
|
+
**Returns:** `string | null` — Extracted language code, or `null` if none found
|
|
454
|
+
|
|
455
|
+
**Supported Patterns:**
|
|
456
|
+
- **JSON/JSONC**: `en.json`, `es-ES.json`, `en.jsonc`
|
|
457
|
+
- **ARB**: `app_en.arb`, `app_en_US.arb`, `my_app_fr.arb`
|
|
458
|
+
- **Shopify**: `en.default.schema.json`, `es-ES.schema.json`
|
|
459
|
+
|
|
460
|
+
**Example:**
|
|
461
|
+
```typescript
|
|
462
|
+
// JSON files
|
|
463
|
+
extractLanguageCode('en.json'); // 'en'
|
|
464
|
+
extractLanguageCode('es-ES.json'); // 'es-ES'
|
|
465
|
+
extractLanguageCode('fr.jsonc'); // 'fr'
|
|
466
|
+
|
|
467
|
+
// ARB files
|
|
468
|
+
extractLanguageCode('app_en.arb'); // 'en'
|
|
469
|
+
extractLanguageCode('app_en_US.arb'); // 'en_US'
|
|
470
|
+
extractLanguageCode('my_app_fr.arb'); // 'fr'
|
|
471
|
+
|
|
472
|
+
// Shopify theme
|
|
473
|
+
extractLanguageCode('en.default.schema.json'); // 'en'
|
|
474
|
+
extractLanguageCode('es-ES.schema.json'); // 'es-ES'
|
|
475
|
+
|
|
476
|
+
// Invalid files
|
|
477
|
+
extractLanguageCode('readme.md'); // null
|
|
478
|
+
extractLanguageCode('invalid.json'); // null
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## License
|
|
482
|
+
|
|
483
|
+
MIT
|
|
484
|
+
|
|
485
|
+
## Credits
|
|
486
|
+
|
|
487
|
+
Powered by [l10n.dev](https://l10n.dev) — AI-powered localization service
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ILogger } from "./logger";
|
|
2
|
+
/**
|
|
3
|
+
* Console-based logger implementation
|
|
4
|
+
*/
|
|
5
|
+
export declare class ConsoleLogger implements ILogger {
|
|
6
|
+
/**
|
|
7
|
+
* Show and log error message
|
|
8
|
+
*/
|
|
9
|
+
showAndLogError(message: string, error?: unknown, context?: string, linkBtnText?: string, url?: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Log informational message
|
|
12
|
+
*/
|
|
13
|
+
logInfo(message: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Log warning message
|
|
16
|
+
*/
|
|
17
|
+
logWarning(message: string, error?: unknown): void;
|
|
18
|
+
/**
|
|
19
|
+
* Log error message
|
|
20
|
+
*/
|
|
21
|
+
logError(message: string, error?: unknown): void;
|
|
22
|
+
/**
|
|
23
|
+
* Log error details to console
|
|
24
|
+
*/
|
|
25
|
+
private logErrorDetails;
|
|
26
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConsoleLogger = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Console-based logger implementation
|
|
6
|
+
*/
|
|
7
|
+
class ConsoleLogger {
|
|
8
|
+
/**
|
|
9
|
+
* Show and log error message
|
|
10
|
+
*/
|
|
11
|
+
showAndLogError(message, error, context, linkBtnText, url) {
|
|
12
|
+
console.error(`❌ ${message}`);
|
|
13
|
+
if (context) {
|
|
14
|
+
console.error(`Context: ${context}`);
|
|
15
|
+
}
|
|
16
|
+
if (error) {
|
|
17
|
+
this.logErrorDetails(error, console.error);
|
|
18
|
+
}
|
|
19
|
+
if (linkBtnText && url) {
|
|
20
|
+
console.error(`${linkBtnText}: ${url}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Log informational message
|
|
25
|
+
*/
|
|
26
|
+
logInfo(message) {
|
|
27
|
+
console.log(message);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Log warning message
|
|
31
|
+
*/
|
|
32
|
+
logWarning(message, error) {
|
|
33
|
+
console.warn(`⚠️ ${message}`);
|
|
34
|
+
if (error) {
|
|
35
|
+
this.logErrorDetails(error, console.warn);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Log error message
|
|
40
|
+
*/
|
|
41
|
+
logError(message, error) {
|
|
42
|
+
console.error(`❌ ${message}`);
|
|
43
|
+
if (error) {
|
|
44
|
+
this.logErrorDetails(error, console.error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Log error details to console
|
|
49
|
+
*/
|
|
50
|
+
logErrorDetails(error, logFn) {
|
|
51
|
+
if (error instanceof Error) {
|
|
52
|
+
logFn(`Error: ${error.message}`);
|
|
53
|
+
if (error.stack) {
|
|
54
|
+
logFn(error.stack);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
logFn(`Error: ${String(error)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.ConsoleLogger = ConsoleLogger;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const CONFIG: {
|
|
2
|
+
readonly CLIENT: "ai-l10n-npmjs";
|
|
3
|
+
readonly KEYS: {
|
|
4
|
+
readonly API_KEY: "apiKey";
|
|
5
|
+
readonly USE_CONTRACTIONS: "useContractions";
|
|
6
|
+
readonly USE_SHORTENING: "useShortening";
|
|
7
|
+
readonly GENERATE_PLURAL_FORMS: "generatePluralForms";
|
|
8
|
+
readonly SAVE_FILTERED_STRINGS: "saveFilteredStrings";
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export declare const URLS: {
|
|
12
|
+
readonly BASE: "https://l10n.dev";
|
|
13
|
+
readonly API_BASE: "https://api.l10n.dev";
|
|
14
|
+
readonly API_KEYS: "https://l10n.dev/ws/keys";
|
|
15
|
+
readonly PRICING: "https://l10n.dev/#pricing";
|
|
16
|
+
readonly CONTENT_POLICY: "https://l10n.dev/terms-of-service#content-policy";
|
|
17
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.URLS = exports.CONFIG = void 0;
|
|
4
|
+
// Configuration constants shared across the package
|
|
5
|
+
exports.CONFIG = {
|
|
6
|
+
CLIENT: "ai-l10n-npmjs",
|
|
7
|
+
KEYS: {
|
|
8
|
+
API_KEY: "apiKey",
|
|
9
|
+
USE_CONTRACTIONS: "useContractions",
|
|
10
|
+
USE_SHORTENING: "useShortening",
|
|
11
|
+
GENERATE_PLURAL_FORMS: "generatePluralForms",
|
|
12
|
+
SAVE_FILTERED_STRINGS: "saveFilteredStrings",
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
// URL constants for l10n.dev service
|
|
16
|
+
exports.URLS = {
|
|
17
|
+
BASE: "https://l10n.dev",
|
|
18
|
+
API_BASE: "https://api.l10n.dev",
|
|
19
|
+
API_KEYS: "https://l10n.dev/ws/keys",
|
|
20
|
+
PRICING: "https://l10n.dev/#pricing",
|
|
21
|
+
CONTENT_POLICY: "https://l10n.dev/terms-of-service#content-policy",
|
|
22
|
+
};
|