i18ntk 3.2.0 → 3.3.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 CHANGED
@@ -5,7 +5,40 @@ All notable changes to this project 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
- ## [3.2.0] - 2026-05-16
8
+ ## [3.3.0] - 2026-05-20
9
+
10
+ ### Changed
11
+ - Auto Translate now supports `--provider google|deepl|libretranslate`; DeepL uses `DEEPL_API_KEY`, while LibreTranslate supports `LIBRETRANSLATE_URL` and optional `LIBRETRANSLATE_API_KEY`.
12
+ - Auto Translate provider networking now keeps HTTPS, host allowlist, response-size, private-network, and redacted security logging protections in place for additional providers.
13
+
14
+ ### Fixed
15
+ - `i18ntk-complete` now fills missing target-language keys from the English source value with a language prefix such as `[DE] Home` instead of writing `NOT_TRANSLATED`; this works for both `locales/en/*.json` and monolith `locales/en.json` layouts.
16
+
17
+ ### Security
18
+ - Eliminated all 21 dynamic `require()` calls flagged by Socket.dev: 20 `require(path.join(__dirname, ...))` patterns in `i18ntk-js.js`, `i18ntk-py.js`, `i18ntk-java.js`, `i18ntk-php.js`, and `i18ntk-go.js` converted to static string literal requires.
19
+ - Added `SecurityUtils.validatePath()` gate around the remaining dynamic `require()` in `i18ntk-translate.js` `loadCustomTranslateFn`.
20
+ - Created `utils/translate/safe-network.js` — a secure HTTPS wrapper with URL host/path allowlist validation, response size limits (100KB), suspicious query parameter detection, and security event logging. All outbound network access now flows through this validated layer.
21
+ - Replaced direct `https.get` call in `utils/translate/api.js` with `safeHttpGet` from the safe-network wrapper.
22
+
23
+ ### Docs
24
+ - README.md updated for v3.3.0 Auto Translate providers and secure provider operations.
25
+ - SECURITY.md updated with Socket.dev analysis disclaimer and guidance on expected alerts for a CLI/i18n toolkit.
26
+ - CHANGELOG.md and `package.json` versionInfo updated for v3.3.0.
27
+
28
+ ### Socket.dev Analysis Disclaimer
29
+
30
+ This package is a developer CLI and runtime helper that performs file I/O, network access (translation provider APIs on user request), and environment variable access. As such, Socket.dev will flag the following alerts that are **expected and by design**:
31
+
32
+ | Alert | Why it's expected |
33
+ |---|---|
34
+ | Network access | Only contacts configured translation providers via HTTPS when user invokes auto-translate. All outbound calls flow through `safe-network.js` with host/path allowlist validation, response size limits, private-network blocking, and redacted security event logging. No telemetry, no unexpected outbound calls. |
35
+ | Environment variable access | Centralized through `env-manager.js` with a strict allowlist. Blocks `SECRET`, `PASSWORD`, `KEY`, `TOKEN`, `AWS_*`, `NPM_*`, and 15+ other patterns. |
36
+ | Filesystem access | Reads/writes only project locale files and reports within validated paths. All FS operations gated by `SecurityUtils.validatePath`. |
37
+ | URL strings | Hardcoded default provider URLs for Google, DeepL, and LibreTranslate used only for auto-translation. No external resource loading. |
38
+
39
+ The v3.3.0 release resolves the actionable dynamic-require alert by eliminating all 21 instances.
40
+
41
+ ## [3.2.0] - 2026-05-16
9
42
 
10
43
  ### Security
11
44
  - **CRITICAL**: Fixed invalid `crypto.createCipherGCM`/`createDecipherGCM` API calls in `admin-pin.js` — replaced with `crypto.createCipheriv`/`createDecipheriv`.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # i18ntk v3.2.0
1
+ # i18ntk v3.3.0
2
2
 
3
- Zero-dependency internationalization toolkit for setup, scanning, analysis, validation, usage tracking, translation completion, automatic JSON locale translation, reporting, and runtime translation loading.
3
+ A i18n toolkit - A zero-dependency internationalization toolkit for setup, scanning, analysis, validation, usage tracking, translation completion, automatic JSON locale translation, reporting, and runtime translation loading.
4
4
 
5
5
  ![i18ntk Logo](https://raw.githubusercontent.com/vladnoskv/i18ntk/main/docs/screenshots/i18ntk-logo-public.PNG)
6
6
 
@@ -9,7 +9,7 @@ Zero-dependency internationalization toolkit for setup, scanning, analysis, vali
9
9
  [![node](https://img.shields.io/badge/node-%3E%3D16-339933)](https://nodejs.org)
10
10
  [![dependencies](https://img.shields.io/badge/dependencies-0-success)](https://www.npmjs.com/package/i18ntk)
11
11
  [![license](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
12
- [![socket](https://socket.dev/api/badge/npm/package/i18ntk/3.2.0)](https://socket.dev/npm/package/i18ntk/overview/3.2.0)
12
+ [![socket](https://socket.dev/api/badge/npm/package/i18ntk/3.3.0)](https://socket.dev/npm/package/i18ntk/overview/3.3.0)
13
13
 
14
14
  ## Install
15
15
 
@@ -30,14 +30,14 @@ Requirements:
30
30
  - npm `>=8.0.0`
31
31
  - No runtime dependencies
32
32
 
33
- ## What's New in 3.2.0
33
+ ## What's New in 3.3.0
34
34
 
35
- - **SECURITY**: Fixed 4 critical runtime-crash bugs (invalid crypto APIs, missing imports) across admin-pin.js, security-config.js, and scripts/security-check.js.
36
- - **SECURITY**: Removed encryption key stored alongside ciphertext in admin-pin.js; encryption key is now derived via HKDF.
37
- - **SECURITY**: Enforced HTTPS-only for Google Translate API requests; fixed http.get timeout for Node.js <16.14 compatibility.
38
- - **SECURITY**: Added path validation to backup restore/verify operations; locked down FileManagementService PIN verification stubs.
35
+ - **SECURITY**: Eliminated all 21 dynamic `require()` calls flagged by Socket.dev; 20 converted to static string literals, 1 gated with `SecurityUtils.validatePath`.
36
+ - **AUTO TRANSLATE**: Added provider selection for Google, DeepL, and LibreTranslate.
37
+ - **FIX**: `i18ntk-complete` now fills missing target-language keys from English values with language prefixes instead of `NOT_TRANSLATED`.
38
+ - **DOCS**: SECURITY.md updated with Socket.dev analysis disclaimer explaining expected alerts for a CLI/i18n toolkit.
39
39
 
40
- See [CHANGELOG.md](./CHANGELOG.md) and [docs/migration-guide-v3.2.0.md](./docs/migration-guide-v3.2.0.md) for release details.
40
+ See [CHANGELOG.md](./CHANGELOG.md) for more release details.
41
41
 
42
42
  ## Quick Start
43
43
 
@@ -45,7 +45,7 @@ Initialize a project:
45
45
 
46
46
  ```bash
47
47
  i18ntk
48
- # or
48
+ # or with explicit command
49
49
  i18ntk --command=init
50
50
  ```
51
51
 
@@ -111,8 +111,8 @@ i18ntk-fixer
111
111
  i18ntk-backup
112
112
  i18ntk-translate
113
113
  ```
114
-
115
- Note: manager route `i18ntk --command=backup` is disabled in current builds. Use `i18ntk-backup` directly for backup operations.
114
+ `n
115
+ Note: manager route `i18ntk --command=backup` is disabled in current builds. Use `i18ntk-backup` (or legacy `i18ntk-backup`) directly for backup operations.
116
116
 
117
117
  ## Common Options
118
118
 
@@ -150,6 +150,21 @@ i18ntk-translate locales/en/common.json fr --dry-run --report-stdout
150
150
  i18ntk-translate locales/en es --source-dir locales/en --files "*.json" --no-confirm --preserve-placeholders
151
151
  ```
152
152
 
153
+ Provider examples:
154
+
155
+ ```bash
156
+ export DEEPL_API_KEY="your-deepl-api-key"
157
+ i18ntk-translate locales/en/common.json de --provider deepl --no-confirm --preserve-placeholders
158
+
159
+ export LIBRETRANSLATE_URL="https://libretranslate.com/translate"
160
+ export LIBRETRANSLATE_API_KEY="optional-api-key"
161
+ i18ntk-translate locales/en/common.json es --provider libretranslate --no-confirm --preserve-placeholders
162
+ ```
163
+
164
+ `google` remains the default provider. You can also set `I18NTK_TRANSLATE_PROVIDER=deepl` or `I18NTK_TRANSLATE_PROVIDER=libretranslate`.
165
+
166
+ Provider requests are HTTPS-only and response-size limited, and security logs redact provider query strings and response bodies. DeepL is pinned to official DeepL hosts by default; set `I18NTK_ALLOW_CUSTOM_TRANSLATE_HOSTS=1` only for a trusted DeepL-compatible proxy. Custom LibreTranslate URLs are blocked for localhost/private IP ranges unless `I18NTK_ALLOW_PRIVATE_TRANSLATE_URLS=1` is set for trusted local testing. Keep provider API keys in environment variables or a secret manager.
167
+
153
168
  The manager flow asks for:
154
169
 
155
170
  - source locale directory, either the folder with JSON files or a locale root such as `./locales`
@@ -289,7 +304,7 @@ Example:
289
304
 
290
305
  ```json
291
306
  {
292
- "version": "3.2.0",
307
+ "version": "3.3.0",
293
308
  "sourceDir": "./locales",
294
309
  "i18nDir": "./locales",
295
310
  "outputDir": "./i18ntk-reports",
@@ -350,8 +365,6 @@ The public package manifest includes `readmeFilename: "README.md"`, and the rele
350
365
  - [Migration Guide v3.2.0](./docs/migration-guide-v3.2.0.md)
351
366
  - [Migration Guide v3.1.1](./docs/migration-guide-v3.1.1.md)
352
367
  - [Migration Guide v3.0.0](./docs/migration-guide-v3.0.0.md)
353
- - [Migration Guide v2.6.0](./docs/migration-guide-v2.6.0.md)
354
- - [Migration Guide v2.5.1](./docs/migration-guide-v2.5.1.md)
355
368
 
356
369
  ## Security
357
370
 
package/SECURITY.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## Supported Versions
4
4
 
5
- The supported production line is `2.5.x`.
5
+ The supported production line is `3.x`.
6
6
 
7
- Versions earlier than `2.5.0` are not recommended for production use because later releases include package, filesystem, environment, and admin-auth hardening.
7
+ Versions earlier than `3.3.0` are not recommended for production use because later releases include Auto Translate provider hardening, dynamic-require elimination, and path-validation hardening.
8
8
 
9
9
  ## Security Model
10
10
 
@@ -14,8 +14,8 @@ Security priorities:
14
14
 
15
15
  - zero runtime dependencies
16
16
  - no install-time lifecycle commands in the public package manifest
17
- - no shipped local setup state, admin PINs, backups, reports, logs, credentials, or generated artifacts
18
- - centralized environment-variable access through `utils/env-manager.js`
17
+ - no shipped local setup state, admin PINs, backup files, reports, logs, credentials, or generated artifacts
18
+ - centralized environment-variable access through `utils/env-manager.js` with a strict allowlist
19
19
  - path containment checks based on resolved paths and `path.relative()`
20
20
  - timing-safe comparison for authentication hashes or tokens
21
21
  - silent-by-default logging for production-like contexts
@@ -24,6 +24,20 @@ Security priorities:
24
24
 
25
25
  The npm package uses a stripped public manifest. It must not contain install-time lifecycle commands, dependency fields, local setup state, or development tooling.
26
26
 
27
+ ## Socket.dev Analysis Disclaimer
28
+
29
+ Socket.dev scans the published npm package and may flag the following alerts. These are **expected behaviors** for a developer CLI/i18n toolkit and are mitigated as described:
30
+
31
+ | Alert | Design Rationale | Mitigation |
32
+ |---|---|---|
33
+ | **Network access** | Contacts configured translation providers via HTTPS only when user explicitly invokes auto-translate. No telemetry, no beaconing, no background network activity. | `utils/translate/safe-network.js` enforces HTTPS, host/path allowlists, response-size limits, private-network blocking, and redacted security logging for Google, DeepL, and LibreTranslate provider requests. |
34
+ | **Environment variable access** | Reads env vars for logging level, output directory, UI language, and project paths. Required for CLI configuration. | `utils/env-manager.js` uses a fixed allowlist (28 vars). Blocks `SECRET`, `PASSWORD`, `KEY`, `TOKEN`, `AWS_*`, `GITHUB_*`, `NPM_*`, `NODE_*`, `PATH`, `HOME`, `USER`, `SHELL`, and 8 more patterns. |
35
+ | **Filesystem access** | Reads/writes locale JSON files, config files, and reports within the user's project. Core function of the toolkit. | All FS operations are gated by `SecurityUtils.validatePath()` with path-traversal detection, symlink resolution, and base-path containment checks. |
36
+ | **URL strings** | Contains hardcoded translation provider endpoint URLs for Google, DeepL, and LibreTranslate defaults. | Only used when user invokes `i18ntk-translate`. Custom provider URLs remain HTTPS-only and are validated before use. |
37
+
38
+ The v3.3.0 release **resolved** the previously actionable Socket.dev alert:
39
+ - **Dynamic require** — all 21 instances eliminated (20 converted to static string literals, 1 gated with `SecurityUtils.validatePath`).
40
+
27
41
  ## Reporting Vulnerabilities
28
42
 
29
43
  Do not report security vulnerabilities in public GitHub issues.
@@ -46,7 +60,7 @@ Security reports are reviewed privately first. Confirmed issues should receive:
46
60
 
47
61
  ## User Guidance
48
62
 
49
- - Keep i18ntk updated to `2.5.0` or newer.
63
+ - Keep i18ntk updated to `3.3.0` or newer.
50
64
  - Do not commit `.i18ntk-config`, admin PIN files, backup directories, generated reports, logs, npm credentials, or secret material.
51
65
  - Run i18ntk only in project directories you trust.
52
66
  - Review generated translation changes before committing them.
@@ -161,9 +161,14 @@ class I18nCompletionTool {
161
161
  return lowered.startsWith('backup-') || lowered === 'backup' || lowered === 'reports' || lowered === 'i18ntk-reports';
162
162
  }
163
163
 
164
- // Get all JSON files from a language directory
165
- getLanguageFiles(language) {
166
- const languageDir = path.join(this.sourceDir, language);
164
+ // Get all JSON files from a language directory
165
+ getLanguageFiles(language) {
166
+ const monolithFile = path.join(this.sourceDir, `${language}.json`);
167
+ if (SecurityUtils.safeExistsSync(monolithFile, this.config.projectRoot)) {
168
+ return [`${language}.json`];
169
+ }
170
+
171
+ const languageDir = path.join(this.sourceDir, language);
167
172
 
168
173
  if (!SecurityUtils.safeExistsSync(languageDir, this.config.projectRoot)) {
169
174
  return [];
@@ -173,8 +178,20 @@ class I18nCompletionTool {
173
178
  .filter(file => {
174
179
  return file.endsWith('.json') &&
175
180
  !this.config.excludeFiles.includes(file);
176
- });
177
- }
181
+ });
182
+ }
183
+
184
+ getLanguageFilePath(language, fileName) {
185
+ if (fileName === `${language}.json`) {
186
+ return path.join(this.sourceDir, fileName);
187
+ }
188
+
189
+ return path.join(this.sourceDir, language, fileName);
190
+ }
191
+
192
+ usesMonolithFile(language) {
193
+ return SecurityUtils.safeExistsSync(path.join(this.sourceDir, `${language}.json`), this.config.projectRoot);
194
+ }
178
195
 
179
196
  // Parse key path and determine which file it belongs to
180
197
  parseKeyPath(keyPath) {
@@ -238,25 +255,30 @@ class I18nCompletionTool {
238
255
  }
239
256
 
240
257
  // Add missing keys to a language
241
- addMissingKeysToLanguage(language, missingKeys, dryRun = false) {
242
- const languageDir = path.join(this.sourceDir, language);
243
- const changes = [];
258
+ addMissingKeysToLanguage(language, missingKeys, dryRun = false) {
259
+ const languageDir = path.join(this.sourceDir, language);
260
+ const changes = [];
261
+ const usesMonolith = this.usesMonolithFile(language);
244
262
 
245
263
  // Group keys by file
246
264
  const keysByFile = {};
247
265
 
248
- missingKeys.forEach(keyPath => {
249
- const { file, key } = this.parseKeyPath(keyPath);
250
- if (!keysByFile[file]) {
251
- keysByFile[file] = [];
252
- }
266
+ missingKeys.forEach(keyPath => {
267
+ const { file, key } = usesMonolith
268
+ ? { file: `${language}.json`, key: keyPath }
269
+ : this.parseKeyPath(keyPath);
270
+ if (!keysByFile[file]) {
271
+ keysByFile[file] = [];
272
+ }
253
273
  keysByFile[file].push({ keyPath, key });
254
274
  });
255
275
 
256
- // Process each file
257
- for (const [fileName, keys] of Object.entries(keysByFile)) {
258
- const filePath = path.join(languageDir, fileName);
259
- let fileContent = {};
276
+ // Process each file
277
+ for (const [fileName, keys] of Object.entries(keysByFile)) {
278
+ const filePath = usesMonolith
279
+ ? path.join(this.sourceDir, fileName)
280
+ : path.join(languageDir, fileName);
281
+ let fileContent = {};
260
282
 
261
283
  // Load existing file or create new
262
284
  if (SecurityUtils.safeExistsSync(filePath, this.config.projectRoot)) {
@@ -266,12 +288,12 @@ class I18nCompletionTool {
266
288
  console.warn(t("completeTranslations.warning_could_not_parse_filepa", { filePath })); ;
267
289
  fileContent = {};
268
290
  }
269
- } else {
270
- // Create directory if it doesn't exist
271
- if (!SecurityUtils.safeExistsSync(languageDir, this.config.projectRoot)) {
272
- if (!dryRun) {
273
- SecurityUtils.safeMkdirSync(languageDir, this.config.projectRoot, { recursive: true });
274
- }
291
+ } else {
292
+ // Create directory if it doesn't exist
293
+ if (!usesMonolith && !SecurityUtils.safeExistsSync(languageDir, this.config.projectRoot)) {
294
+ if (!dryRun) {
295
+ SecurityUtils.safeMkdirSync(languageDir, this.config.projectRoot, { recursive: true });
296
+ }
275
297
  }
276
298
  }
277
299
 
@@ -303,19 +325,66 @@ class I18nCompletionTool {
303
325
  return changes;
304
326
  }
305
327
 
306
- // Generate appropriate translation value based on key and language
307
- generateTranslationValue(keyPath, language) {
308
- // Generate value from key path for source language
309
- const baseValue = this.generateValueFromKey(keyPath);
310
-
311
- // For source language, use the generated value
312
- if (language === this.config.sourceLanguage) {
313
- return baseValue;
314
- }
315
-
316
- // For other languages, use the not translated marker
317
- return this.config.notTranslatedMarker || 'NOT_TRANSLATED';
318
- }
328
+ // Generate appropriate translation value based on key and language
329
+ generateTranslationValue(keyPath, language) {
330
+ const sourceValue = this.getSourceValueForKeyPath(keyPath);
331
+ const baseValue = typeof sourceValue === 'string' && sourceValue.trim() !== ''
332
+ ? sourceValue
333
+ : this.generateValueFromKey(keyPath);
334
+
335
+ // For source language, use the generated value
336
+ if (language === this.config.sourceLanguage) {
337
+ return baseValue;
338
+ }
339
+
340
+ return `[${language.toUpperCase()}] ${baseValue}`;
341
+ }
342
+
343
+ getNestedValue(obj, keyPath) {
344
+ const keys = String(keyPath || '').split('.');
345
+ let current = obj;
346
+
347
+ for (const key of keys) {
348
+ if (!current || typeof current !== 'object' || !(key in current)) {
349
+ return undefined;
350
+ }
351
+ current = current[key];
352
+ }
353
+
354
+ return current;
355
+ }
356
+
357
+ getSourceValueForKeyPath(keyPath) {
358
+ if (!this.sourceLanguageDir && !this.usesMonolithFile(this.config.sourceLanguage)) {
359
+ return undefined;
360
+ }
361
+
362
+ const sourceFiles = this.getLanguageFiles(this.config.sourceLanguage);
363
+ const keyPathStr = String(keyPath || '');
364
+ const parsed = this.parseKeyPath(keyPathStr);
365
+
366
+ for (const fileName of sourceFiles) {
367
+ const sourceFilePath = this.getLanguageFilePath(this.config.sourceLanguage, fileName);
368
+ try {
369
+ const sourceContent = SecurityUtils.safeParseJSON(SecurityUtils.safeReadFileSync(sourceFilePath, this.config.projectRoot, 'utf8'));
370
+ if (!sourceContent || typeof sourceContent !== 'object') continue;
371
+
372
+ const candidates = [keyPathStr];
373
+ if (fileName === parsed.file) {
374
+ candidates.push(parsed.key);
375
+ }
376
+
377
+ for (const candidate of candidates) {
378
+ const value = this.getNestedValue(sourceContent, candidate);
379
+ if (value !== undefined) return value;
380
+ }
381
+ } catch (error) {
382
+ console.warn(t("complete.couldNotParseSource", { file: sourceFilePath }));
383
+ }
384
+ }
385
+
386
+ return undefined;
387
+ }
319
388
 
320
389
  // Generate a readable value from a key path
321
390
  generateValueFromKey(keyPath) {
@@ -351,18 +420,18 @@ class I18nCompletionTool {
351
420
  }
352
421
 
353
422
  // Get missing keys by comparing source language with target languages
354
- getMissingKeysFromComparison() {
355
- const sourceFiles = this.getLanguageFiles(this.config.sourceLanguage);
356
- const missingKeys = [];
357
-
358
- if (!SecurityUtils.safeExistsSync(this.sourceLanguageDir, this.config.projectRoot)) {
359
- console.log(t("complete.sourceLanguageNotFound", { sourceLanguage: this.config.sourceLanguage }));
360
- return [];
361
- }
362
-
363
- // Process each file in source language
364
- for (const fileName of sourceFiles) {
365
- const sourceFilePath = path.join(this.sourceLanguageDir, fileName);
423
+ getMissingKeysFromComparison() {
424
+ const sourceFiles = this.getLanguageFiles(this.config.sourceLanguage);
425
+ const missingKeys = [];
426
+
427
+ if (sourceFiles.length === 0) {
428
+ console.log(t("complete.sourceLanguageNotFound", { sourceLanguage: this.config.sourceLanguage }));
429
+ return [];
430
+ }
431
+
432
+ // Process each file in source language
433
+ for (const fileName of sourceFiles) {
434
+ const sourceFilePath = this.getLanguageFilePath(this.config.sourceLanguage, fileName);
366
435
 
367
436
  try {
368
437
  const sourceContent = SecurityUtils.safeParseJSON(SecurityUtils.safeReadFileSync(sourceFilePath, this.config.projectRoot, 'utf8'));
@@ -373,7 +442,9 @@ class I18nCompletionTool {
373
442
  for (const language of languages) {
374
443
  if (language === this.config.sourceLanguage) continue;
375
444
 
376
- const targetFilePath = path.join(this.sourceDir, language, fileName);
445
+ const targetFilePath = fileName === `${this.config.sourceLanguage}.json` || this.usesMonolithFile(language)
446
+ ? path.join(this.sourceDir, `${language}.json`)
447
+ : path.join(this.sourceDir, language, fileName);
377
448
  let targetKeys = [];
378
449
 
379
450
  if (SecurityUtils.safeExistsSync(targetFilePath, this.config.projectRoot)) {
@@ -15,6 +15,7 @@
15
15
  * Options:
16
16
  * --source-dir <dir> Source directory (default: ./locales/en)
17
17
  * --output-dir <dir> Output directory (default: ./locales/<lang>)
18
+ * --provider <name> Translation provider: google, deepl, libretranslate
18
19
  * --custom-regex <regex> Additional placeholder regex pattern
19
20
  * --no-confirm Skip all confirmation dialogs
20
21
  * --preserve-placeholders Translate text around placeholders and reinsert tokens
@@ -90,6 +91,7 @@ function printHelp() {
90
91
  'Options:',
91
92
  ' --source-dir <dir> Source directory containing locale files',
92
93
  ' --output-dir <dir> Output directory for translated files',
94
+ ' --provider <name> Provider: google (default), deepl, libretranslate',
93
95
  ' --source-lang <code> Source language code (default: en)',
94
96
  ' --custom-regex <regex> Additional placeholder regex pattern',
95
97
  ' --no-confirm Automate: skip confirmation dialogs',
@@ -110,6 +112,15 @@ function printHelp() {
110
112
  ' --retry-count <n> Max retries per failed request (default: 3)',
111
113
  ' --retry-delay <ms> Base backoff delay in ms (default: 1000)',
112
114
  ' --timeout <ms> HTTP request timeout in ms (default: 15000)',
115
+ '',
116
+ 'Environment:',
117
+ ' I18NTK_TRANSLATE_PROVIDER Default provider when --provider is omitted',
118
+ ' DEEPL_API_KEY Required for --provider deepl',
119
+ ' DEEPL_API_URL Optional, defaults to https://api-free.deepl.com/v2/translate',
120
+ ' I18NTK_ALLOW_CUSTOM_TRANSLATE_HOSTS=1 Allow custom DeepL-compatible HTTPS hosts',
121
+ ' LIBRETRANSLATE_URL Optional, defaults to https://libretranslate.com/translate',
122
+ ' LIBRETRANSLATE_API_KEY Optional API key for LibreTranslate servers that require one',
123
+ ' I18NTK_ALLOW_PRIVATE_TRANSLATE_URLS=1 Allow localhost/private provider URLs for trusted testing',
113
124
  ' -h, --help Show this help',
114
125
  ].join('\n'));
115
126
  }
@@ -121,6 +132,7 @@ function parseArgs(argv) {
121
132
  sourceDir: null,
122
133
  outputDir: null,
123
134
  sourceLang: 'en',
135
+ provider: process.env.I18NTK_TRANSLATE_PROVIDER || 'google',
124
136
  customRegex: [],
125
137
  noConfirm: false,
126
138
  preservePlaceholders: false,
@@ -161,6 +173,7 @@ function parseArgs(argv) {
161
173
  else if (arg === '--source-dir' && i + 1 < argv.length) { args.sourceDir = argv[++i]; }
162
174
  else if (arg === '--output-dir' && i + 1 < argv.length) { args.outputDir = argv[++i]; }
163
175
  else if (arg === '--source-lang' && i + 1 < argv.length) { args.sourceLang = argv[++i]; }
176
+ else if (arg === '--provider' && i + 1 < argv.length) { args.provider = argv[++i]; }
164
177
  else if (arg === '--custom-regex' && i + 1 < argv.length) { args.customRegex.push(argv[++i]); }
165
178
  else if (arg === '--protection-file' && i + 1 < argv.length) { args.protectionFile = argv[++i]; }
166
179
  else if (arg === '--concurrency' && i + 1 < argv.length) { args.concurrency = parseInt(argv[++i], 10) || 3; }
@@ -206,7 +219,17 @@ function loadCustomTranslateFn(modulePath) {
206
219
  if (!modulePath) return null;
207
220
  try {
208
221
  const resolved = path.isAbsolute(modulePath) ? modulePath : path.resolve(process.cwd(), modulePath);
209
- const mod = require(resolved);
222
+ const validated = SecurityUtils.validatePath(resolved);
223
+ if (!validated) {
224
+ SecurityUtils.logSecurityEvent('Blocked unsafe custom translate module path', 'warn', {
225
+ modulePath,
226
+ resolved,
227
+ source: 'user'
228
+ });
229
+ console.error(`Error: Custom translate module path "${modulePath}" failed security validation.`);
230
+ process.exit(1);
231
+ }
232
+ const mod = require(validated);
210
233
  if (typeof mod === 'function') return mod;
211
234
  if (mod && typeof mod.translate === 'function') return mod.translate;
212
235
  if (mod && typeof mod.default === 'function') return mod.default;
@@ -676,6 +699,7 @@ async function processFile(sourcePath, targetLang, args) {
676
699
 
677
700
  const translateOptions = {
678
701
  sourceLang: args.sourceLang,
702
+ provider: args.provider,
679
703
  concurrency: args.concurrency,
680
704
  batchSize: args.batchSize,
681
705
  retryCount: args.retryCount,
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "i18ntk",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "i18n Tool Kit - Zero-dependency internationalization toolkit for setup, scanning, analysis, validation, auto translation, fixing, reporting, and runtime translation loading.",
5
5
  "readmeFilename": "README.md",
6
6
  "keywords": [
7
7
  "i18ntk",
8
- "i18n-toolkit",
9
8
  "i18n",
10
9
  "internationalization",
11
10
  "localization",
@@ -123,6 +122,7 @@
123
122
  "utils/formats/json.js",
124
123
  "utils/translate/placeholder.js",
125
124
  "utils/translate/api.js",
125
+ "utils/translate/safe-network.js",
126
126
  "utils/translate/traverse.js",
127
127
  "utils/translate/report.js",
128
128
  "utils/translate/cli.js",
@@ -161,5 +161,5 @@
161
161
  "access": "public"
162
162
  },
163
163
  "preferGlobal": true,
164
- "readme": "# i18ntk v3.2.0\n\nZero-dependency internationalization toolkit for setup, scanning, analysis, validation, usage tracking, translation completion, automatic JSON locale translation, reporting, and runtime translation loading.\n\n![i18ntk Logo](https://raw.githubusercontent.com/vladnoskv/i18ntk/main/docs/screenshots/i18ntk-logo-public.PNG)\n\n[![npm version](https://img.shields.io/npm/v/i18ntk.svg?color=brightgreen)](https://www.npmjs.com/package/i18ntk)\n[![npm downloads](https://img.shields.io/npm/dt/i18ntk.svg)](https://www.npmjs.com/package/i18ntk)\n[![node](https://img.shields.io/badge/node-%3E%3D16-339933)](https://nodejs.org)\n[![dependencies](https://img.shields.io/badge/dependencies-0-success)](https://www.npmjs.com/package/i18ntk)\n[![license](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)\n[![socket](https://socket.dev/api/badge/npm/package/i18ntk/3.2.0)](https://socket.dev/npm/package/i18ntk/overview/3.2.0)\n\n## Install\n\n```bash\n# global CLI use\nnpm install -g i18ntk\n\n# local project use\nnpm install --save-dev i18ntk\n\n# one-off execution\nnpx i18ntk --help\n```\n\nRequirements:\n\n- Node.js `>=16.0.0`\n- npm `>=8.0.0`\n- No runtime dependencies\n\n## What's New in 3.2.0\n\n- **SECURITY**: Fixed 4 critical runtime-crash bugs (invalid crypto APIs, missing imports) across admin-pin.js, security-config.js, and scripts/security-check.js.\n- **SECURITY**: Removed encryption key stored alongside ciphertext in admin-pin.js; encryption key is now derived via HKDF.\n- **SECURITY**: Enforced HTTPS-only for Google Translate API requests; fixed http.get timeout for Node.js <16.14 compatibility.\n- **SECURITY**: Added path validation to backup restore/verify operations; locked down FileManagementService PIN verification stubs.\n\nSee [CHANGELOG.md](./CHANGELOG.md) and [docs/migration-guide-v3.2.0.md](./docs/migration-guide-v3.2.0.md) for release details.\n\n## Quick Start\n\nInitialize a project:\n\n```bash\ni18ntk\n# or\ni18ntk --command=init\n```\n\nRun common checks:\n\n```bash\ni18ntk --command=analyze\ni18ntk --command=validate\ni18ntk --command=usage\ni18ntk --command=sizing\ni18ntk --command=summary\n```\n\nComplete or fix translation files:\n\n```bash\ni18ntk --command=complete\ni18ntk-fixer --help\n```\n\nAuto-translate locale JSON:\n\n```bash\ni18ntk --command=translate\n# or\ni18ntk-translate locales/en/common.json de --report-stdout\n```\n\nThe full onboarding guide is in [docs/getting-started.md](./docs/getting-started.md).\n\n## Main Commands\n\nPrimary CLI:\n\n```bash\ni18ntk\ni18ntk --help\ni18ntk --command=init\ni18ntk --command=analyze\ni18ntk --command=validate\ni18ntk --command=usage\ni18ntk --command=scanner\ni18ntk --command=sizing\ni18ntk --command=complete\ni18ntk --command=translate\ni18ntk --command=summary\ni18ntk --command=debug\n```\n\nStandalone executables:\n\n```bash\ni18ntk-init\ni18ntk-analyze\ni18ntk-validate\ni18ntk-usage\ni18ntk-scanner\ni18ntk-sizing\ni18ntk-complete\ni18ntk-summary\ni18ntk-doctor\ni18ntk-fixer\ni18ntk-backup\ni18ntk-translate\n```\n\nNote: manager route `i18ntk --command=backup` is disabled in current builds. Use `i18ntk-backup` directly for backup operations.\n\n## Common Options\n\nMost commands support:\n\n- `--source-dir <path>`\n- `--i18n-dir <path>`\n- `--output-dir <path>`\n- `--source-language <code>`\n- `--ui-language <code>`\n- `--no-prompt`\n- `--dry-run`\n- `--help`\n\nExample:\n\n```bash\ni18ntk --command=analyze --source-dir=./src --i18n-dir=./locales --output-dir=./i18ntk-reports\n```\n\n## Auto Translate\n\nInteractive manager flow:\n\n```bash\ni18ntk\n# choose \"Auto Translate (Beta)\"\n```\n\nDirect CLI examples:\n\n```bash\ni18ntk-translate locales/en/common.json de\ni18ntk-translate locales/en/common.json fr --dry-run --report-stdout\ni18ntk-translate locales/en es --source-dir locales/en --files \"*.json\" --no-confirm --preserve-placeholders\n```\n\nThe manager flow asks for:\n\n- source locale directory, either the folder with JSON files or a locale root such as `./locales`\n- source language code\n- one or more target languages, or `all`\n- one JSON file or all JSON files in the source directory\n\nIf you select a locale root such as `./locales` and choose source language `en`, the manager automatically uses `./locales/en` when that folder contains the source JSON files.\n\nBefore writing files, the manager can run a dry-run preview. After confirmation it writes translated files under sibling target-language folders, for example:\n\n```text\nlocales/en/common.json\nlocales/de/common.json\nlocales/fr/common.json\n```\n\n### Placeholder Handling\n\nAuto Translate detects common placeholders such as:\n\n- `{name}`\n- `{{count}}`\n- `%s`\n- `%d`\n- `:id`\n- `%{name}`\n- `${value}`\n\nUseful flags:\n\n- `--preserve-placeholders`: translate text around placeholders and reinsert original tokens\n- `--skip-placeholders`: copy placeholder-bearing strings unchanged\n- `--send-placeholders`: send placeholder-bearing strings through translation after masking\n- `--custom-regex <regex>`: add project-specific placeholder detection\n\n### Protected Terms and Keys\n\nAuto Translate can create and use a project-local protection file:\n\n```bash\ni18ntk-translate locales/en/common.json de --create-protection-file --protection-file ./i18ntk-auto-translate.json\n```\n\nExample `i18ntk-auto-translate.json`:\n\n```json\n{\n \"version\": 1,\n \"terms\": [\"BrandName\", \"PRODUCT_CODE\", \"API\"],\n \"keys\": [\"app.brandName\", \"legal.companyName\", \"product.*.symbol\"],\n \"values\": [\"BrandName Ltd\", \"support@example.com\"],\n \"patterns\": [\"[A-Z]{2,}-\\\\d+\"]\n}\n```\n\n- `terms` are masked before translation and restored exactly afterward.\n- `keys` are exact key paths or `*` wildcard paths copied unchanged.\n- `values` are exact source values copied unchanged.\n- `patterns` are JavaScript regex strings for advanced protected substrings.\n\nUseful flags:\n\n- `--protection-file <path>`\n- `--create-protection-file`\n- `--no-protection`\n\nOpen Settings and choose `Auto Translate Beta` to edit defaults for placeholder mode, concurrency, batch size, retry settings, report output, BOM output, protection file path, first-run setup prompt, and update prompt.\n\nSee [docs/auto-translate.md](./docs/auto-translate.md) for the full Auto Translate guide.\n\n## Validation\n\nValidation checks locale structure, completeness, placeholders, and content risks.\n\nIn 3.1.2, warning types are more specific:\n\n- `Potential risky content`: URL, email address, or secret-like value\n- `Possible untranslated English content`: target-language value appears to contain too much English\n\nEnglish-content warnings include:\n\n- detected English percentage\n- configured threshold\n- matched word count\n- sample matched words\n\nTune warnings in `.i18ntk-config`:\n\n```json\n{\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\", \"PRODUCT_CODE\"]\n}\n```\n\n## Sizing Analysis\n\n`i18ntk-sizing` reports translation file sizes, key counts, average value length, and file-set mismatches across language folders.\n\n```bash\ni18ntk-sizing --source-dir ./locales --format table\ni18ntk-sizing --source-dir ./locales --detailed --output-dir ./i18ntk-reports\n```\n\nUse `--detailed` to print per-file rows in the terminal.\n\n## Runtime API\n\nUse `i18ntk/runtime` when an application needs to read locale JSON files at runtime.\n\n```js\nconst runtime = require('i18ntk/runtime');\n\nruntime.initRuntime({\n baseDir: './locales',\n language: 'en',\n fallbackLanguage: 'en',\n keySeparator: '.',\n preload: true\n});\n\nconsole.log(runtime.t('common.hello'));\nruntime.setLanguage('fr');\nconsole.log(runtime.getLanguage());\nconsole.log(runtime.getAvailableLanguages());\nruntime.refresh('fr');\n```\n\nSee [docs/runtime.md](./docs/runtime.md) for runtime details.\n\n## Configuration\n\ni18ntk uses a project-local `.i18ntk-config` file.\n\nExample:\n\n```json\n{\n \"version\": \"3.2.0\",\n \"sourceDir\": \"./locales\",\n \"i18nDir\": \"./locales\",\n \"outputDir\": \"./i18ntk-reports\",\n \"sourceLanguage\": \"en\",\n \"defaultLanguages\": [\"de\", \"es\", \"fr\", \"ru\"],\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\", \"PRODUCT_CODE\"],\n \"autoTranslate\": {\n \"placeholderMode\": \"preserve\",\n \"concurrency\": 6,\n \"batchSize\": 100,\n \"progressInterval\": 25,\n \"retryCount\": 3,\n \"retryDelay\": 1000,\n \"timeout\": 15000,\n \"dryRunFirst\": true,\n \"reportStdout\": true,\n \"bom\": false,\n \"protectionEnabled\": true,\n \"protectionFile\": \"./i18ntk-auto-translate.json\",\n \"promptProtectionSetup\": true,\n \"promptProtectionUpdate\": true\n },\n \"setup\": {\n \"completed\": true\n }\n}\n```\n\nSee [docs/api/CONFIGURATION.md](./docs/api/CONFIGURATION.md) for the full configuration model.\n\n## Public Package Contents\n\nThe public package intentionally ships runtime and CLI files only. The publish staging script excludes development-only content such as tests, scripts, docs, release staging folders, local config files, and generated protection files.\n\nThe package includes:\n\n- CLI entry points under `main/`\n- manager commands and services\n- runtime API files under `runtime/`\n- settings UI files required at runtime\n- bundled internal UI locales\n- shared utilities required by the shipped commands\n- `README.md`, `CHANGELOG.md`, `LICENSE`, and policy files\n\nThe public package manifest includes `readmeFilename: \"README.md\"`, and the release staging script fails if `README.md` is missing or empty.\n\n## Documentation\n\n- [Documentation Index](./docs/README.md)\n- [Getting Started](./docs/getting-started.md)\n- [API Reference](./docs/api/API_REFERENCE.md)\n- [Configuration Guide](./docs/api/CONFIGURATION.md)\n- [Runtime API Guide](./docs/runtime.md)\n- [Auto Translate Guide](./docs/auto-translate.md)\n- [Scanner Guide](./docs/scanner-guide.md)\n- [Environment Variables](./docs/environment-variables.md)\n- [Migration Guide v3.2.0](./docs/migration-guide-v3.2.0.md)\n- [Migration Guide v3.1.1](./docs/migration-guide-v3.1.1.md)\n- [Migration Guide v3.0.0](./docs/migration-guide-v3.0.0.md)\n- [Migration Guide v2.6.0](./docs/migration-guide-v2.6.0.md)\n- [Migration Guide v2.5.1](./docs/migration-guide-v2.5.1.md)\n\n## Security\n\n- No API key is required for the default Auto Translate flow.\n- Do not store secrets in locale files, `.i18ntk-config`, or protection files.\n- Project-specific brand/product terms should be configured by the user, not hardcoded into the package.\n- Report security issues using [SECURITY.md](./SECURITY.md).\n\n## Community\n\n- [Contributing](./CONTRIBUTING.md)\n- [Code of Conduct](./CODE_OF_CONDUCT.md)\n- [Funding](./FUNDING.md)\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n"
164
+ "readme": "# i18ntk v3.3.0\n\nA i18n toolkit - A zero-dependency internationalization toolkit for setup, scanning, analysis, validation, usage tracking, translation completion, automatic JSON locale translation, reporting, and runtime translation loading.\n\n![i18ntk Logo](https://raw.githubusercontent.com/vladnoskv/i18ntk/main/docs/screenshots/i18ntk-logo-public.PNG)\n\n[![npm version](https://img.shields.io/npm/v/i18ntk.svg?color=brightgreen)](https://www.npmjs.com/package/i18ntk)\n[![npm downloads](https://img.shields.io/npm/dt/i18ntk.svg)](https://www.npmjs.com/package/i18ntk)\n[![node](https://img.shields.io/badge/node-%3E%3D16-339933)](https://nodejs.org)\n[![dependencies](https://img.shields.io/badge/dependencies-0-success)](https://www.npmjs.com/package/i18ntk)\n[![license](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)\n[![socket](https://socket.dev/api/badge/npm/package/i18ntk/3.3.0)](https://socket.dev/npm/package/i18ntk/overview/3.3.0)\n\n## Install\n\n```bash\n# global CLI use\nnpm install -g i18ntk\n\n# local project use\nnpm install --save-dev i18ntk\n\n# one-off execution\nnpx i18ntk --help\n```\n\nRequirements:\n\n- Node.js `>=16.0.0`\n- npm `>=8.0.0`\n- No runtime dependencies\n\n## What's New in 3.3.0\n\n- **SECURITY**: Eliminated all 21 dynamic `require()` calls flagged by Socket.dev; 20 converted to static string literals, 1 gated with `SecurityUtils.validatePath`.\n- **AUTO TRANSLATE**: Added provider selection for Google, DeepL, and LibreTranslate.\n- **FIX**: `i18ntk-complete` now fills missing target-language keys from English values with language prefixes instead of `NOT_TRANSLATED`.\n- **DOCS**: SECURITY.md updated with Socket.dev analysis disclaimer explaining expected alerts for a CLI/i18n toolkit.\n\nSee [CHANGELOG.md](./CHANGELOG.md) for more release details.\n\n## Quick Start\n\nInitialize a project:\n\n```bash\ni18ntk\n# or with explicit command\ni18ntk --command=init\n```\n\nRun common checks:\n\n```bash\ni18ntk --command=analyze\ni18ntk --command=validate\ni18ntk --command=usage\ni18ntk --command=sizing\ni18ntk --command=summary\n```\n\nComplete or fix translation files:\n\n```bash\ni18ntk --command=complete\ni18ntk-fixer --help\n```\n\nAuto-translate locale JSON:\n\n```bash\ni18ntk --command=translate\n# or\ni18ntk-translate locales/en/common.json de --report-stdout\n```\n\nThe full onboarding guide is in [docs/getting-started.md](./docs/getting-started.md).\n\n## Main Commands\n\nPrimary CLI:\n\n```bash\ni18ntk\ni18ntk --help\ni18ntk --command=init\ni18ntk --command=analyze\ni18ntk --command=validate\ni18ntk --command=usage\ni18ntk --command=scanner\ni18ntk --command=sizing\ni18ntk --command=complete\ni18ntk --command=translate\ni18ntk --command=summary\ni18ntk --command=debug\n```\n\nStandalone executables:\n\n```bash\ni18ntk-init\ni18ntk-analyze\ni18ntk-validate\ni18ntk-usage\ni18ntk-scanner\ni18ntk-sizing\ni18ntk-complete\ni18ntk-summary\ni18ntk-doctor\ni18ntk-fixer\ni18ntk-backup\ni18ntk-translate\n```\n`n\nNote: manager route `i18ntk --command=backup` is disabled in current builds. Use `i18ntk-backup` (or legacy `i18ntk-backup`) directly for backup operations.\n\n## Common Options\n\nMost commands support:\n\n- `--source-dir <path>`\n- `--i18n-dir <path>`\n- `--output-dir <path>`\n- `--source-language <code>`\n- `--ui-language <code>`\n- `--no-prompt`\n- `--dry-run`\n- `--help`\n\nExample:\n\n```bash\ni18ntk --command=analyze --source-dir=./src --i18n-dir=./locales --output-dir=./i18ntk-reports\n```\n\n## Auto Translate\n\nInteractive manager flow:\n\n```bash\ni18ntk\n# choose \"Auto Translate (Beta)\"\n```\n\nDirect CLI examples:\n\n```bash\ni18ntk-translate locales/en/common.json de\ni18ntk-translate locales/en/common.json fr --dry-run --report-stdout\ni18ntk-translate locales/en es --source-dir locales/en --files \"*.json\" --no-confirm --preserve-placeholders\n```\n\nProvider examples:\n\n```bash\nexport DEEPL_API_KEY=\"your-deepl-api-key\"\ni18ntk-translate locales/en/common.json de --provider deepl --no-confirm --preserve-placeholders\n\nexport LIBRETRANSLATE_URL=\"https://libretranslate.com/translate\"\nexport LIBRETRANSLATE_API_KEY=\"optional-api-key\"\ni18ntk-translate locales/en/common.json es --provider libretranslate --no-confirm --preserve-placeholders\n```\n\n`google` remains the default provider. You can also set `I18NTK_TRANSLATE_PROVIDER=deepl` or `I18NTK_TRANSLATE_PROVIDER=libretranslate`.\n\nProvider requests are HTTPS-only and response-size limited, and security logs redact provider query strings and response bodies. DeepL is pinned to official DeepL hosts by default; set `I18NTK_ALLOW_CUSTOM_TRANSLATE_HOSTS=1` only for a trusted DeepL-compatible proxy. Custom LibreTranslate URLs are blocked for localhost/private IP ranges unless `I18NTK_ALLOW_PRIVATE_TRANSLATE_URLS=1` is set for trusted local testing. Keep provider API keys in environment variables or a secret manager.\n\nThe manager flow asks for:\n\n- source locale directory, either the folder with JSON files or a locale root such as `./locales`\n- source language code\n- one or more target languages, or `all`\n- one JSON file or all JSON files in the source directory\n\nIf you select a locale root such as `./locales` and choose source language `en`, the manager automatically uses `./locales/en` when that folder contains the source JSON files.\n\nBefore writing files, the manager can run a dry-run preview. After confirmation it writes translated files under sibling target-language folders, for example:\n\n```text\nlocales/en/common.json\nlocales/de/common.json\nlocales/fr/common.json\n```\n\n### Placeholder Handling\n\nAuto Translate detects common placeholders such as:\n\n- `{name}`\n- `{{count}}`\n- `%s`\n- `%d`\n- `:id`\n- `%{name}`\n- `${value}`\n\nUseful flags:\n\n- `--preserve-placeholders`: translate text around placeholders and reinsert original tokens\n- `--skip-placeholders`: copy placeholder-bearing strings unchanged\n- `--send-placeholders`: send placeholder-bearing strings through translation after masking\n- `--custom-regex <regex>`: add project-specific placeholder detection\n\n### Protected Terms and Keys\n\nAuto Translate can create and use a project-local protection file:\n\n```bash\ni18ntk-translate locales/en/common.json de --create-protection-file --protection-file ./i18ntk-auto-translate.json\n```\n\nExample `i18ntk-auto-translate.json`:\n\n```json\n{\n \"version\": 1,\n \"terms\": [\"BrandName\", \"PRODUCT_CODE\", \"API\"],\n \"keys\": [\"app.brandName\", \"legal.companyName\", \"product.*.symbol\"],\n \"values\": [\"BrandName Ltd\", \"support@example.com\"],\n \"patterns\": [\"[A-Z]{2,}-\\\\d+\"]\n}\n```\n\n- `terms` are masked before translation and restored exactly afterward.\n- `keys` are exact key paths or `*` wildcard paths copied unchanged.\n- `values` are exact source values copied unchanged.\n- `patterns` are JavaScript regex strings for advanced protected substrings.\n\nUseful flags:\n\n- `--protection-file <path>`\n- `--create-protection-file`\n- `--no-protection`\n\nOpen Settings and choose `Auto Translate Beta` to edit defaults for placeholder mode, concurrency, batch size, retry settings, report output, BOM output, protection file path, first-run setup prompt, and update prompt.\n\nSee [docs/auto-translate.md](./docs/auto-translate.md) for the full Auto Translate guide.\n\n## Validation\n\nValidation checks locale structure, completeness, placeholders, and content risks.\n\nIn 3.1.2, warning types are more specific:\n\n- `Potential risky content`: URL, email address, or secret-like value\n- `Possible untranslated English content`: target-language value appears to contain too much English\n\nEnglish-content warnings include:\n\n- detected English percentage\n- configured threshold\n- matched word count\n- sample matched words\n\nTune warnings in `.i18ntk-config`:\n\n```json\n{\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\", \"PRODUCT_CODE\"]\n}\n```\n\n## Sizing Analysis\n\n`i18ntk-sizing` reports translation file sizes, key counts, average value length, and file-set mismatches across language folders.\n\n```bash\ni18ntk-sizing --source-dir ./locales --format table\ni18ntk-sizing --source-dir ./locales --detailed --output-dir ./i18ntk-reports\n```\n\nUse `--detailed` to print per-file rows in the terminal.\n\n## Runtime API\n\nUse `i18ntk/runtime` when an application needs to read locale JSON files at runtime.\n\n```js\nconst runtime = require('i18ntk/runtime');\n\nruntime.initRuntime({\n baseDir: './locales',\n language: 'en',\n fallbackLanguage: 'en',\n keySeparator: '.',\n preload: true\n});\n\nconsole.log(runtime.t('common.hello'));\nruntime.setLanguage('fr');\nconsole.log(runtime.getLanguage());\nconsole.log(runtime.getAvailableLanguages());\nruntime.refresh('fr');\n```\n\nSee [docs/runtime.md](./docs/runtime.md) for runtime details.\n\n## Configuration\n\ni18ntk uses a project-local `.i18ntk-config` file.\n\nExample:\n\n```json\n{\n \"version\": \"3.3.0\",\n \"sourceDir\": \"./locales\",\n \"i18nDir\": \"./locales\",\n \"outputDir\": \"./i18ntk-reports\",\n \"sourceLanguage\": \"en\",\n \"defaultLanguages\": [\"de\", \"es\", \"fr\", \"ru\"],\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\", \"PRODUCT_CODE\"],\n \"autoTranslate\": {\n \"placeholderMode\": \"preserve\",\n \"concurrency\": 6,\n \"batchSize\": 100,\n \"progressInterval\": 25,\n \"retryCount\": 3,\n \"retryDelay\": 1000,\n \"timeout\": 15000,\n \"dryRunFirst\": true,\n \"reportStdout\": true,\n \"bom\": false,\n \"protectionEnabled\": true,\n \"protectionFile\": \"./i18ntk-auto-translate.json\",\n \"promptProtectionSetup\": true,\n \"promptProtectionUpdate\": true\n },\n \"setup\": {\n \"completed\": true\n }\n}\n```\n\nSee [docs/api/CONFIGURATION.md](./docs/api/CONFIGURATION.md) for the full configuration model.\n\n## Public Package Contents\n\nThe public package intentionally ships runtime and CLI files only. The publish staging script excludes development-only content such as tests, scripts, docs, release staging folders, local config files, and generated protection files.\n\nThe package includes:\n\n- CLI entry points under `main/`\n- manager commands and services\n- runtime API files under `runtime/`\n- settings UI files required at runtime\n- bundled internal UI locales\n- shared utilities required by the shipped commands\n- `README.md`, `CHANGELOG.md`, `LICENSE`, and policy files\n\nThe public package manifest includes `readmeFilename: \"README.md\"`, and the release staging script fails if `README.md` is missing or empty.\n\n## Documentation\n\n- [Documentation Index](./docs/README.md)\n- [Getting Started](./docs/getting-started.md)\n- [API Reference](./docs/api/API_REFERENCE.md)\n- [Configuration Guide](./docs/api/CONFIGURATION.md)\n- [Runtime API Guide](./docs/runtime.md)\n- [Auto Translate Guide](./docs/auto-translate.md)\n- [Scanner Guide](./docs/scanner-guide.md)\n- [Environment Variables](./docs/environment-variables.md)\n- [Migration Guide v3.2.0](./docs/migration-guide-v3.2.0.md)\n- [Migration Guide v3.1.1](./docs/migration-guide-v3.1.1.md)\n- [Migration Guide v3.0.0](./docs/migration-guide-v3.0.0.md)\n\n## Security\n\n- No API key is required for the default Auto Translate flow.\n- Do not store secrets in locale files, `.i18ntk-config`, or protection files.\n- Project-specific brand/product terms should be configured by the user, not hardcoded into the package.\n- Report security issues using [SECURITY.md](./SECURITY.md).\n\n## Community\n\n- [Contributing](./CONTRIBUTING.md)\n- [Code of Conduct](./CODE_OF_CONDUCT.md)\n- [Funding](./FUNDING.md)\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n"
165
165
  }