i18ntk 4.2.2 → 4.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 +6 -1
- package/README.md +11 -8
- package/main/i18ntk-fixer.js +16 -11
- package/main/i18ntk-translate.js +29 -3
- package/main/manage/commands/CommandRouter.js +7 -4
- package/main/manage/commands/FixerCommand.js +144 -55
- package/main/manage/commands/TranslateCommand.js +36 -24
- package/package.json +9 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,10 +5,15 @@ 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
|
-
## [4.
|
|
8
|
+
## [4.3.0] - 2026-05-31
|
|
9
9
|
|
|
10
10
|
### Fixed
|
|
11
11
|
- Auto Translate now treats single-word uppercase target-language placeholders such as `[AR] Email` and `[AR] Password` as untranslated target values, matching the existing multi-word `[AR] What We Offer` detection.
|
|
12
|
+
- Auto Translate now treats bracketed target-language placeholders case-insensitively, so `[zh] Email` and `[TR] Password` are both retried for the matching target language.
|
|
13
|
+
- Managed Auto Translate now checks every selected source file for a target language before reporting leftover failures, instead of stopping after the first failed file.
|
|
14
|
+
- Auto Translate no longer fails a run when a provider legitimately returns a short all-caps acronym or code unchanged, such as `XP`.
|
|
15
|
+
- Manager option 7 and `i18ntk-fixer --check-placeholders` now run an English source placeholder audit, reporting any `[LANG] ...` values left in English locale files; a clean project reports `0` placeholders.
|
|
16
|
+
- The management command router no longer prints a generic operation-success message when a command returns `{ success: false }`.
|
|
12
17
|
|
|
13
18
|
## [4.2.1] - 2026-05-31
|
|
14
19
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# i18ntk v4.
|
|
1
|
+
# i18ntk v4.3.0
|
|
2
2
|
|
|
3
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
|
|
|
@@ -9,7 +9,7 @@ A i18n toolkit - A zero-dependency internationalization toolkit for setup, scann
|
|
|
9
9
|
[](https://nodejs.org)
|
|
10
10
|
[](https://www.npmjs.com/package/i18ntk)
|
|
11
11
|
[](LICENSE)
|
|
12
|
-
[](https://socket.dev/npm/package/i18ntk/overview/4.3.0)
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -30,14 +30,17 @@ Requirements:
|
|
|
30
30
|
- npm `>=8.0.0`
|
|
31
31
|
- No runtime dependencies
|
|
32
32
|
|
|
33
|
-
## What's New in 4.
|
|
33
|
+
## What's New in 4.3.0
|
|
34
34
|
|
|
35
|
-
- **AUTO TRANSLATE**: Existing target values like `[AR] What We Offer`, `[AR] Email`, and `[
|
|
35
|
+
- **AUTO TRANSLATE**: Existing target values like `[AR] What We Offer`, `[AR] Email`, `[zh] Email`, and `[TR] Password` are now treated as untranslated placeholders for the matching target language and are translated from the source text.
|
|
36
36
|
- **AUTO TRANSLATE**: Before writing each output file, Auto Translate now performs a final leftover check and retries any placeholder-prefixed or source-copy values once.
|
|
37
37
|
- **AUTO TRANSLATE**: If leftovers remain after the final retry, the command warns, includes them in the report, recommends rerunning Auto Translate, and exits with validation failure instead of reporting a clean completion.
|
|
38
|
+
- **AUTO TRANSLATE**: Managed Auto Translate now checks every selected source file for a target language before reporting leftovers, so one failed file does not prevent the rest from being audited.
|
|
39
|
+
- **AUTO TRANSLATE**: Short all-caps acronyms and codes such as `XP` are allowed to remain unchanged instead of failing the final leftover check.
|
|
40
|
+
- **FIX PLACEHOLDERS**: Menu option 7 now runs an English source placeholder audit and reports how many `[LANG] ...` values remain in English locale files. A clean project reports `0`.
|
|
38
41
|
- **SIZING/USAGE**: Usage analysis no longer writes its inferred app source fallback back into the shared locale config, so running usage before sizing no longer makes sizing analyze the wrong directory.
|
|
39
42
|
- **VALIDATION REPORTS**: Validation summary files now include warning and error details, including English-content warning payloads, instead of only totals.
|
|
40
|
-
- **DOCS**: Versioned docs and migration guidance now reflect the current 4.
|
|
43
|
+
- **DOCS**: Versioned docs and migration guidance now reflect the current 4.3.0 command surface.
|
|
41
44
|
|
|
42
45
|
## What's New in 4.1.0
|
|
43
46
|
|
|
@@ -144,7 +147,7 @@ Note: manager route `i18ntk --command=backup` is disabled in current builds. Use
|
|
|
144
147
|
| `i18ntk --command=translate` / `i18ntk-translate` | Auto-translates locale JSON using configured provider behavior. | Missing, empty, untranslated-marker, source-copy, likely-English, or visibly corrupt target values by default. | Target locale JSON files and translation reports. Existing translated values are kept unless `--translate-all` is used. |
|
|
145
148
|
| `i18ntk --command=sizing` / `i18ntk-sizing` | Estimates translated string length expansion and layout risk. | Text length, expansion ratios, placeholder-bearing strings. | Sizing report. Does not edit locale files. |
|
|
146
149
|
| `i18ntk --command=summary` / `i18ntk-summary` | Shows project translation status. | Configured locales, reports, completeness status. | Console/report output only. |
|
|
147
|
-
| `i18ntk-fixer` | Fixes placeholder and missing-marker issues
|
|
150
|
+
| `i18ntk-fixer` | Fixes placeholder and missing-marker issues, and can audit English source files with `--check-placeholders`. | Placeholder corruption, missing translation markers, configured language files, `[LANG] ...` leftovers in English locales. | Locale JSON files when fixes are applied. Use dry-run options where available before bulk edits. |
|
|
148
151
|
| `i18ntk-backup` | Creates, verifies, restores, and cleans locale backups. | Locale JSON files and backup manifests. | Backup archives/manifests, or restored locale files when using restore. |
|
|
149
152
|
|
|
150
153
|
## Common Options
|
|
@@ -503,7 +506,7 @@ Example:
|
|
|
503
506
|
|
|
504
507
|
```json
|
|
505
508
|
{
|
|
506
|
-
"version": "4.
|
|
509
|
+
"version": "4.3.0",
|
|
507
510
|
"sourceDir": "./locales",
|
|
508
511
|
"i18nDir": "./locales",
|
|
509
512
|
"outputDir": "./i18ntk-reports",
|
|
@@ -565,7 +568,7 @@ The public package manifest includes `readmeFilename: "README.md"`, and the rele
|
|
|
565
568
|
- [Auto Translate Guide](./docs/auto-translate.md)
|
|
566
569
|
- [Scanner Guide](./docs/scanner-guide.md)
|
|
567
570
|
- [Environment Variables](./docs/environment-variables.md)
|
|
568
|
-
- [Migration Guide v4.
|
|
571
|
+
- [Migration Guide v4.3.0](./docs/migration-guide-v4.3.0.md)
|
|
569
572
|
|
|
570
573
|
## Security
|
|
571
574
|
|
package/main/i18ntk-fixer.js
CHANGED
|
@@ -13,9 +13,10 @@ const fs = require('fs');
|
|
|
13
13
|
const SecurityUtils = require('../utils/security');
|
|
14
14
|
const cliHelper = require('../utils/cli-helper');
|
|
15
15
|
const { loadTranslations, t } = require('../utils/i18n-helper');
|
|
16
|
-
const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
|
|
17
|
-
const JsonOutput = require('../utils/json-output');
|
|
18
|
-
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
16
|
+
const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
|
|
17
|
+
const JsonOutput = require('../utils/json-output');
|
|
18
|
+
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
19
|
+
const FixerCommand = require('./manage/commands/FixerCommand');
|
|
19
20
|
|
|
20
21
|
// Ensure setup is complete before running
|
|
21
22
|
(async () => {
|
|
@@ -94,13 +95,17 @@ class I18nFixer {
|
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
async run() {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
98
|
+
async run(options = {}) {
|
|
99
|
+
const command = new FixerCommand(this.config, null);
|
|
100
|
+
const result = await command.execute(options);
|
|
101
|
+
|
|
102
|
+
if (result && result.success === false) {
|
|
103
|
+
throw new Error(result.error || 'Fixer command failed');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
104
109
|
|
|
105
110
|
module.exports = I18nFixer;
|
|
106
111
|
|
|
@@ -110,4 +115,4 @@ if (require.main === module) {
|
|
|
110
115
|
console.error('I18n Fixer failed:', error);
|
|
111
116
|
process.exit(1);
|
|
112
117
|
});
|
|
113
|
-
}
|
|
118
|
+
}
|
package/main/i18ntk-translate.js
CHANGED
|
@@ -344,7 +344,7 @@ function normalizeLanguageCode(language) {
|
|
|
344
344
|
|
|
345
345
|
function parseLanguagePrefix(value) {
|
|
346
346
|
if (typeof value !== 'string') return null;
|
|
347
|
-
const match = value.match(/^\s*\[([A-Z]{2,3}(?:[-_][A-Z0-9]{2,4})?)\]\s*(.+)$/);
|
|
347
|
+
const match = value.match(/^\s*\[([A-Z]{2,3}(?:[-_][A-Z0-9]{2,4})?)\]\s*(.+)$/i);
|
|
348
348
|
if (!match) return null;
|
|
349
349
|
return {
|
|
350
350
|
raw: match[1],
|
|
@@ -368,6 +368,28 @@ function isLanguagePrefixedEnglish(value, args = {}) {
|
|
|
368
368
|
return hasLatinWordContent(prefix.text, 1) || isLikelyEnglish(prefix.text, args);
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
+
function isSafeUnchangedSourceCopy(value, args = {}) {
|
|
372
|
+
const text = String(value ?? '').trim();
|
|
373
|
+
if (!text) return false;
|
|
374
|
+
|
|
375
|
+
const normalized = text.toLowerCase();
|
|
376
|
+
const allowedTerms = new Set(['api']);
|
|
377
|
+
if (Array.isArray(args.allowedEnglishTerms)) {
|
|
378
|
+
args.allowedEnglishTerms
|
|
379
|
+
.filter(term => typeof term === 'string' && term.trim())
|
|
380
|
+
.forEach(term => allowedTerms.add(term.trim().toLowerCase()));
|
|
381
|
+
}
|
|
382
|
+
if (allowedTerms.has(normalized)) return true;
|
|
383
|
+
|
|
384
|
+
const tokens = text.match(/[A-Za-z0-9]+/g) || [];
|
|
385
|
+
if (tokens.length === 0) return true;
|
|
386
|
+
|
|
387
|
+
return tokens.every((token) => {
|
|
388
|
+
if (!/[A-Za-z]/.test(token)) return true;
|
|
389
|
+
return token.length <= 8 && /^[A-Z0-9]{2,}$/.test(token);
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
371
393
|
function isBrokenTranslationValue(value) {
|
|
372
394
|
if (typeof value !== 'string') return false;
|
|
373
395
|
const text = value.trim();
|
|
@@ -399,7 +421,9 @@ function shouldTranslateTargetValue(sourceValue, targetValue, args) {
|
|
|
399
421
|
if (isUntranslatedMarker(targetValue)) return true;
|
|
400
422
|
if (isBrokenTranslationValue(targetValue)) return true;
|
|
401
423
|
if (isLanguagePrefixedEnglish(targetValue, args)) return true;
|
|
402
|
-
if (targetValue.trim() === String(sourceValue ?? '').trim())
|
|
424
|
+
if (targetValue.trim() === String(sourceValue ?? '').trim()) {
|
|
425
|
+
return !isSafeUnchangedSourceCopy(targetValue, args);
|
|
426
|
+
}
|
|
403
427
|
if (args.onlyMissingOrEnglish !== false && isLikelyEnglish(targetValue, args)) return true;
|
|
404
428
|
return args.onlyMissingOrEnglish === false;
|
|
405
429
|
}
|
|
@@ -410,7 +434,9 @@ function getResidualUntranslatedReason(sourceValue, targetValue, args) {
|
|
|
410
434
|
if (isUntranslatedMarker(targetValue)) return 'marker';
|
|
411
435
|
if (isBrokenTranslationValue(targetValue)) return 'broken';
|
|
412
436
|
if (isLanguagePrefixedEnglish(targetValue, args)) return 'language_prefix';
|
|
413
|
-
if (targetValue.trim() === String(sourceValue ?? '').trim())
|
|
437
|
+
if (targetValue.trim() === String(sourceValue ?? '').trim()) {
|
|
438
|
+
return isSafeUnchangedSourceCopy(targetValue, args) ? null : 'source_copy';
|
|
439
|
+
}
|
|
414
440
|
return null;
|
|
415
441
|
}
|
|
416
442
|
|
|
@@ -157,10 +157,13 @@ class CommandRouter {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
try {
|
|
160
|
-
// Route command to appropriate handler
|
|
161
|
-
const result = await this.routeCommand(command, options, executionContext);
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
// Route command to appropriate handler
|
|
161
|
+
const result = await this.routeCommand(command, options, executionContext);
|
|
162
|
+
if (result && result.success === false) {
|
|
163
|
+
throw new Error(result.error || result.message || `${command} command failed`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Handle command completion based on execution context
|
|
164
167
|
console.log('\n' + t('operations.completed'));
|
|
165
168
|
|
|
166
169
|
if (isManagerExecution && !this.isNonInteractiveMode && this.prompt) {
|
|
@@ -12,9 +12,10 @@ const fs = require('fs');
|
|
|
12
12
|
const SecurityUtils = require('../../../utils/security');
|
|
13
13
|
const cliHelper = require('../../../utils/cli-helper');
|
|
14
14
|
const { loadTranslations, t } = require('../../../utils/i18n-helper');
|
|
15
|
-
const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../../../utils/config-helper');
|
|
16
|
-
const JsonOutput = require('../../../utils/json-output');
|
|
17
|
-
const SetupEnforcer = require('../../../utils/setup-enforcer');
|
|
15
|
+
const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../../../utils/config-helper');
|
|
16
|
+
const JsonOutput = require('../../../utils/json-output');
|
|
17
|
+
const SetupEnforcer = require('../../../utils/setup-enforcer');
|
|
18
|
+
const { scanEnglishPlaceholders } = require('../../../utils/english-placeholder-checker');
|
|
18
19
|
|
|
19
20
|
class FixerCommand {
|
|
20
21
|
constructor(config = {}, ui = null) {
|
|
@@ -60,10 +61,11 @@ class FixerCommand {
|
|
|
60
61
|
const args = this.parseArgs();
|
|
61
62
|
if (args.help) {
|
|
62
63
|
displayHelp('i18ntk-fixer', {
|
|
63
|
-
'source-dir': 'Source directory to scan (default: ./locales)',
|
|
64
|
-
'languages': 'Comma separated list of languages to fix',
|
|
65
|
-
'markers': 'Comma separated markers to treat as untranslated',
|
|
66
|
-
'
|
|
64
|
+
'source-dir': 'Source directory to scan (default: ./locales)',
|
|
65
|
+
'languages': 'Comma separated list of languages to fix',
|
|
66
|
+
'markers': 'Comma separated markers to treat as untranslated',
|
|
67
|
+
'check-placeholders': 'Only check English locale files for [LANG] placeholder leftovers',
|
|
68
|
+
'no-backup': 'Skip automatic backup creation',
|
|
67
69
|
'dry-run': 'Show what would be fixed without making changes',
|
|
68
70
|
'force': 'Force fixes without confirmation',
|
|
69
71
|
'output-dir': 'Output directory for fixed files'
|
|
@@ -104,11 +106,13 @@ class FixerCommand {
|
|
|
104
106
|
|
|
105
107
|
if (sanitizedKey === 'source-dir') {
|
|
106
108
|
parsed.sourceDir = sanitizedValue;
|
|
107
|
-
} else if (sanitizedKey === 'languages') {
|
|
108
|
-
parsed.languages = sanitizedValue.split(',').map(l => l.trim());
|
|
109
|
-
} else if (sanitizedKey === 'markers') {
|
|
110
|
-
parsed.markers = sanitizedValue.split(',').map(m => m.trim());
|
|
111
|
-
} else if (sanitizedKey === '
|
|
109
|
+
} else if (sanitizedKey === 'languages') {
|
|
110
|
+
parsed.languages = sanitizedValue.split(',').map(l => l.trim());
|
|
111
|
+
} else if (sanitizedKey === 'markers') {
|
|
112
|
+
parsed.markers = sanitizedValue.split(',').map(m => m.trim());
|
|
113
|
+
} else if (sanitizedKey === 'check-placeholders') {
|
|
114
|
+
parsed.checkPlaceholders = true;
|
|
115
|
+
} else if (sanitizedKey === 'no-backup') {
|
|
112
116
|
parsed.noBackup = true;
|
|
113
117
|
} else if (sanitizedKey === 'dry-run') {
|
|
114
118
|
parsed.dryRun = true;
|
|
@@ -462,11 +466,11 @@ class FixerCommand {
|
|
|
462
466
|
}
|
|
463
467
|
|
|
464
468
|
// Fix translation issues for a language
|
|
465
|
-
async fixLanguage(language) {
|
|
466
|
-
const fixes = {
|
|
467
|
-
language,
|
|
468
|
-
files: {},
|
|
469
|
-
totalIssues: 0,
|
|
469
|
+
async fixLanguage(language) {
|
|
470
|
+
const fixes = {
|
|
471
|
+
language,
|
|
472
|
+
files: {},
|
|
473
|
+
totalIssues: 0,
|
|
470
474
|
fixedIssues: 0
|
|
471
475
|
};
|
|
472
476
|
|
|
@@ -516,20 +520,100 @@ class FixerCommand {
|
|
|
516
520
|
}
|
|
517
521
|
}
|
|
518
522
|
}
|
|
519
|
-
|
|
520
|
-
return fixes;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
523
|
+
|
|
524
|
+
return fixes;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
checkEnglishPlaceholders(options = {}) {
|
|
528
|
+
const result = scanEnglishPlaceholders({
|
|
529
|
+
sourceDir: this.sourceDir || this.config.sourceDir,
|
|
530
|
+
sourceLanguage: this.config.sourceLanguage || 'en'
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
if (options.print === false) {
|
|
534
|
+
return result;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
console.log('\nEnglish placeholder check');
|
|
538
|
+
console.log('-'.repeat(50));
|
|
539
|
+
console.log(`Source directory: ${result.sourceDir}`);
|
|
540
|
+
console.log(`Source language: ${result.sourceLanguage}`);
|
|
541
|
+
console.log(`Files scanned: ${result.fileCount}`);
|
|
542
|
+
console.log(`Keys scanned: ${result.keyCount}`);
|
|
543
|
+
console.log(`Language-code placeholders found: ${result.placeholderCount}`);
|
|
544
|
+
|
|
545
|
+
if (result.errors.length > 0) {
|
|
546
|
+
console.log('\nParse errors:');
|
|
547
|
+
for (const item of result.errors.slice(0, 20)) {
|
|
548
|
+
console.log(` ${item.file}: ${item.error}`);
|
|
549
|
+
}
|
|
550
|
+
if (result.errors.length > 20) {
|
|
551
|
+
console.log(` ... and ${result.errors.length - 20} more parse errors.`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (result.placeholders.length > 0) {
|
|
556
|
+
console.log('\nWARNING: English locale files still contain language-code placeholders.');
|
|
557
|
+
for (const item of result.placeholders.slice(0, 20)) {
|
|
558
|
+
console.log(` ${item.file} :: ${item.key} = ${JSON.stringify(item.value)}`);
|
|
559
|
+
}
|
|
560
|
+
if (result.placeholders.length > 20) {
|
|
561
|
+
console.log(` ... and ${result.placeholders.length - 20} more placeholders.`);
|
|
562
|
+
}
|
|
563
|
+
console.log('Recommendation: translate or replace these English source values before fixing target locales.');
|
|
564
|
+
} else if (result.errors.length === 0) {
|
|
565
|
+
console.log('OK: English placeholder count is 0.');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return result;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Main fixing process
|
|
572
|
+
async fix() {
|
|
525
573
|
try {
|
|
526
574
|
const args = this.parseArgs();
|
|
527
575
|
const jsonOutput = new JsonOutput('fixer');
|
|
528
576
|
|
|
529
|
-
// Set options from args
|
|
530
|
-
this.dryRun = args.dryRun || false;
|
|
531
|
-
this.force = args.force || false;
|
|
532
|
-
|
|
577
|
+
// Set options from args
|
|
578
|
+
this.dryRun = args.dryRun || false;
|
|
579
|
+
this.force = args.force || false;
|
|
580
|
+
|
|
581
|
+
const placeholderCheck = this.checkEnglishPlaceholders({ print: !args.json });
|
|
582
|
+
if (args.checkPlaceholders) {
|
|
583
|
+
if (args.json) {
|
|
584
|
+
jsonOutput.setStatus(placeholderCheck.success ? 'ok' : 'error');
|
|
585
|
+
jsonOutput.setStats({
|
|
586
|
+
files: placeholderCheck.fileCount,
|
|
587
|
+
keys: placeholderCheck.keyCount,
|
|
588
|
+
placeholders: placeholderCheck.placeholderCount,
|
|
589
|
+
parseErrors: placeholderCheck.errors.length
|
|
590
|
+
});
|
|
591
|
+
jsonOutput.data.message = 'English placeholder check completed';
|
|
592
|
+
jsonOutput.data.placeholders = placeholderCheck.placeholders;
|
|
593
|
+
jsonOutput.data.errors = placeholderCheck.errors;
|
|
594
|
+
console.log(JSON.stringify(jsonOutput.data, null, args.indent || 2));
|
|
595
|
+
}
|
|
596
|
+
return placeholderCheck;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (!placeholderCheck.success) {
|
|
600
|
+
const error = `English locale placeholder check failed: ${placeholderCheck.placeholderCount} placeholder(s), ${placeholderCheck.errors.length} parse error(s).`;
|
|
601
|
+
if (args.json) {
|
|
602
|
+
jsonOutput.setStatus('error');
|
|
603
|
+
jsonOutput.setStats({
|
|
604
|
+
placeholders: placeholderCheck.placeholderCount,
|
|
605
|
+
parseErrors: placeholderCheck.errors.length
|
|
606
|
+
});
|
|
607
|
+
jsonOutput.data.message = error;
|
|
608
|
+
jsonOutput.data.placeholders = placeholderCheck.placeholders;
|
|
609
|
+
jsonOutput.data.errors = placeholderCheck.errors;
|
|
610
|
+
console.log(JSON.stringify(jsonOutput.data, null, args.indent || 2));
|
|
611
|
+
return { ...placeholderCheck, success: false, error };
|
|
612
|
+
}
|
|
613
|
+
console.log(`\n${error}`);
|
|
614
|
+
return { ...placeholderCheck, success: false, error };
|
|
615
|
+
}
|
|
616
|
+
|
|
533
617
|
const languages = this.getAvailableLanguages();
|
|
534
618
|
|
|
535
619
|
if (!args.json) {
|
|
@@ -544,15 +628,16 @@ class FixerCommand {
|
|
|
544
628
|
}
|
|
545
629
|
|
|
546
630
|
if (languages.length === 0) {
|
|
547
|
-
const error = t('fixer.noLanguages') || 'No target languages found.';
|
|
548
|
-
if (args.json) {
|
|
549
|
-
jsonOutput.setStatus('error'
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
631
|
+
const error = t('fixer.noLanguages') || 'No target languages found.';
|
|
632
|
+
if (args.json) {
|
|
633
|
+
jsonOutput.setStatus('error');
|
|
634
|
+
jsonOutput.data.message = error;
|
|
635
|
+
console.log(JSON.stringify(jsonOutput.data, null, args.indent || 2));
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
console.log(error);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
556
641
|
|
|
557
642
|
if (!args.json) {
|
|
558
643
|
console.log(t('fixer.foundLanguages', { count: languages.length, languages: languages.join(', ') }));
|
|
@@ -585,17 +670,18 @@ class FixerCommand {
|
|
|
585
670
|
}
|
|
586
671
|
|
|
587
672
|
// Prepare JSON output
|
|
588
|
-
if (args.json) {
|
|
589
|
-
jsonOutput.setStatus(totalFixed > 0 ? 'ok' : 'info'
|
|
590
|
-
jsonOutput.
|
|
591
|
-
issues: totalIssues,
|
|
592
|
-
fixed: totalFixed,
|
|
593
|
-
languages: languages.length
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
673
|
+
if (args.json) {
|
|
674
|
+
jsonOutput.setStatus(totalFixed > 0 ? 'ok' : 'info');
|
|
675
|
+
jsonOutput.setStats({
|
|
676
|
+
issues: totalIssues,
|
|
677
|
+
fixed: totalFixed,
|
|
678
|
+
languages: languages.length
|
|
679
|
+
});
|
|
680
|
+
jsonOutput.data.message = 'Fixer completed';
|
|
681
|
+
|
|
682
|
+
console.log(JSON.stringify(jsonOutput.data, null, args.indent || 2));
|
|
683
|
+
return { success: true, totalIssues, totalFixed, results };
|
|
684
|
+
}
|
|
599
685
|
|
|
600
686
|
// Summary
|
|
601
687
|
console.log(t('fixer.summary'));
|
|
@@ -659,14 +745,17 @@ class FixerCommand {
|
|
|
659
745
|
/**
|
|
660
746
|
* Execute the fixer command
|
|
661
747
|
*/
|
|
662
|
-
async execute(options = {}) {
|
|
663
|
-
try {
|
|
664
|
-
await this.initialize();
|
|
665
|
-
await this.run(options);
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
748
|
+
async execute(options = {}) {
|
|
749
|
+
try {
|
|
750
|
+
await this.initialize();
|
|
751
|
+
const result = await this.run(options);
|
|
752
|
+
if (result && result.success === false) {
|
|
753
|
+
return result;
|
|
754
|
+
}
|
|
755
|
+
return { success: true, command: 'fix', result };
|
|
756
|
+
} catch (error) {
|
|
757
|
+
console.error(`Fixer command failed: ${error.message}`);
|
|
758
|
+
throw error;
|
|
670
759
|
}
|
|
671
760
|
}
|
|
672
761
|
|
|
@@ -488,34 +488,46 @@ class TranslateCommand {
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
async runTranslate(sourceFiles, targetLang, opts = {}) {
|
|
491
|
-
const
|
|
492
|
-
const settings = this.autoTranslateSettings || this.getAutoTranslateSettings();
|
|
493
|
-
|
|
491
|
+
const failures = [];
|
|
494
492
|
for (const src of sourceFiles) {
|
|
495
|
-
const
|
|
496
|
-
args.noConfirm = true;
|
|
497
|
-
args.sourceLang = this.sourceLang || 'en';
|
|
498
|
-
args.dryRun = opts.dryRun === true;
|
|
499
|
-
args.onlyMissingOrEnglish = settings.onlyMissingOrEnglish;
|
|
500
|
-
args.reportStdout = settings.reportStdout;
|
|
501
|
-
args.bom = settings.bom;
|
|
502
|
-
args.concurrency = settings.concurrency;
|
|
503
|
-
args.batchSize = settings.batchSize;
|
|
504
|
-
args.progressInterval = settings.progressInterval;
|
|
505
|
-
args.retryCount = settings.retryCount;
|
|
506
|
-
args.retryDelay = settings.retryDelay;
|
|
507
|
-
args.timeout = settings.timeout;
|
|
508
|
-
args.protectionEnabled = settings.protectionEnabled;
|
|
509
|
-
args.protectionFile = settings.protectionFile;
|
|
510
|
-
args.preservePlaceholders = settings.placeholderMode === 'preserve';
|
|
511
|
-
args.skipPlaceholders = settings.placeholderMode === 'skip';
|
|
512
|
-
args.sendPlaceholders = settings.placeholderMode === 'send';
|
|
513
|
-
|
|
514
|
-
const result = await run(args);
|
|
493
|
+
const result = await this.runSingleTranslate(src, targetLang, opts);
|
|
515
494
|
if (!result || result.success !== true) {
|
|
516
|
-
|
|
495
|
+
failures.push({
|
|
496
|
+
src,
|
|
497
|
+
error: result?.error || `Translation failed for ${src}`
|
|
498
|
+
});
|
|
517
499
|
}
|
|
518
500
|
}
|
|
501
|
+
|
|
502
|
+
if (failures.length > 0) {
|
|
503
|
+
throw new Error(failures.map(item => item.error).join('; '));
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async runSingleTranslate(src, targetLang, opts = {}) {
|
|
508
|
+
const { parseArgs, run } = require('../../i18ntk-translate');
|
|
509
|
+
const settings = this.autoTranslateSettings || this.getAutoTranslateSettings();
|
|
510
|
+
|
|
511
|
+
const args = parseArgs(['node', 'i18ntk-translate', src, targetLang]);
|
|
512
|
+
args.noConfirm = true;
|
|
513
|
+
args.sourceLang = this.sourceLang || 'en';
|
|
514
|
+
args.dryRun = opts.dryRun === true;
|
|
515
|
+
args.onlyMissingOrEnglish = settings.onlyMissingOrEnglish;
|
|
516
|
+
args.reportStdout = settings.reportStdout;
|
|
517
|
+
args.bom = settings.bom;
|
|
518
|
+
args.concurrency = settings.concurrency;
|
|
519
|
+
args.batchSize = settings.batchSize;
|
|
520
|
+
args.progressInterval = settings.progressInterval;
|
|
521
|
+
args.retryCount = settings.retryCount;
|
|
522
|
+
args.retryDelay = settings.retryDelay;
|
|
523
|
+
args.timeout = settings.timeout;
|
|
524
|
+
args.protectionEnabled = settings.protectionEnabled;
|
|
525
|
+
args.protectionFile = settings.protectionFile;
|
|
526
|
+
args.preservePlaceholders = settings.placeholderMode === 'preserve';
|
|
527
|
+
args.skipPlaceholders = settings.placeholderMode === 'skip';
|
|
528
|
+
args.sendPlaceholders = settings.placeholderMode === 'send';
|
|
529
|
+
|
|
530
|
+
return await run(args);
|
|
519
531
|
}
|
|
520
532
|
}
|
|
521
533
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18ntk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.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": [
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
},
|
|
169
169
|
"preferGlobal": true,
|
|
170
170
|
"versionInfo": {
|
|
171
|
-
"version": "4.
|
|
171
|
+
"version": "4.3.0",
|
|
172
172
|
"releaseDate": "31/05/2026",
|
|
173
173
|
"lastUpdated": "31/05/2026",
|
|
174
174
|
"maintainer": "Vlad Noskov",
|
|
@@ -176,8 +176,11 @@
|
|
|
176
176
|
"documentation": "./README.md",
|
|
177
177
|
"apiReference": "./docs/api/API_REFERENCE.md",
|
|
178
178
|
"majorChanges": [
|
|
179
|
-
"AUTO TRANSLATE:
|
|
179
|
+
"AUTO TRANSLATE: Target-language placeholders such as [AR] What We Offer, [AR] Email, [zh] Email, and [TR] Password are detected, translated from source, and checked again before completion.",
|
|
180
180
|
"AUTO TRANSLATE: Runs now warn, report, and exit with validation failure if placeholder-prefixed or source-copy leftovers remain after the final retry.",
|
|
181
|
+
"AUTO TRANSLATE: Managed runs audit every selected file for a language before reporting leftover failures.",
|
|
182
|
+
"AUTO TRANSLATE: Short all-caps acronyms and codes such as XP may remain unchanged without failing the final leftover check.",
|
|
183
|
+
"FIX PLACEHOLDERS: Option 7 and i18ntk-fixer --check-placeholders audit English locale files for [LANG] placeholder leftovers and report a clean count of 0.",
|
|
181
184
|
"SIZING: Manager sizing uses the configured i18n directory unless --source-dir is explicitly provided, so usage analysis no longer poisons the next sizing run.",
|
|
182
185
|
"VALIDATION: Validation summary reports now include warning and error details, not only totals.",
|
|
183
186
|
"USAGE: Usage fallback source detection no longer writes its inferred app source directory back into the shared locale configuration."
|
|
@@ -186,7 +189,7 @@
|
|
|
186
189
|
"i18ntk/runtime module-level helpers keep the first initialized runtime configuration for compatibility instead of being overwritten by later initRuntime() calls.",
|
|
187
190
|
"utils/watch-locales.js returns a callable watcher object with EventEmitter methods and stop(); existing bare stop-function usage remains supported."
|
|
188
191
|
],
|
|
189
|
-
"nextVersion": "4.
|
|
192
|
+
"nextVersion": "4.3.1",
|
|
190
193
|
"supportedNodeVersions": ">=16.0.0",
|
|
191
194
|
"supportedFrameworks": {
|
|
192
195
|
"react-i18next": ">=11.0.0",
|
|
@@ -208,7 +211,7 @@
|
|
|
208
211
|
"spring-boot": ">=2.5.0",
|
|
209
212
|
"laravel": ">=8.0.0"
|
|
210
213
|
},
|
|
211
|
-
"supportPolicy": "Versions earlier than 4.
|
|
214
|
+
"supportPolicy": "Versions earlier than 4.3.0 may be unstable or insecure. Upgrade to 4.3.0 or newer."
|
|
212
215
|
},
|
|
213
|
-
"readme": "# i18ntk v4.2.2\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\n\n[](https://www.npmjs.com/package/i18ntk)\n[](https://www.npmjs.com/package/i18ntk)\n[](https://nodejs.org)\n[](https://www.npmjs.com/package/i18ntk)\n[](LICENSE)\n[](https://socket.dev/npm/package/i18ntk/overview/4.2.2)\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 4.2.2\n\n- **AUTO TRANSLATE**: Existing target values like `[AR] What We Offer`, `[AR] Email`, and `[AR] Password` are now treated as untranslated placeholders for the matching target language and are translated from the source text.\n- **AUTO TRANSLATE**: Before writing each output file, Auto Translate now performs a final leftover check and retries any placeholder-prefixed or source-copy values once.\n- **AUTO TRANSLATE**: If leftovers remain after the final retry, the command warns, includes them in the report, recommends rerunning Auto Translate, and exits with validation failure instead of reporting a clean completion.\n- **SIZING/USAGE**: Usage analysis no longer writes its inferred app source fallback back into the shared locale config, so running usage before sizing no longer makes sizing analyze the wrong directory.\n- **VALIDATION REPORTS**: Validation summary files now include warning and error details, including English-content warning payloads, instead of only totals.\n- **DOCS**: Versioned docs and migration guidance now reflect the current 4.2.2 command surface.\n\n## What's New in 4.1.0\n\n- **FIX**: Critical and high-impact bugs resolved across the v4.0.0 feature set — runtime staleness crashes, backup hash-chain verification, sizing adminAuth crash, scanner `--source-language` propagation, watch callback subscriptions, dead key detection performance, validator key style enforcement, and protection Unicode boundary handling. See [CHANGELOG.md](./CHANGELOG.md) for complete details.\n\n## What's New in 4.0.0\n\n- **SIZING**: `--predict-expansion` flag computes per-key expansion ratios across languages with Safe/Warning/Critical risk tiers for UI layout planning.\n- **WATCH**: `watchLocales()` now returns an EventEmitter-compatible watcher with debounced `change`/`add`/`unlink`/`error` events and SHA-256 hash tracking.\n- **USAGE**: `--cleanup` and `--dry-run-delete` flags identify dead translation keys with confidence scores.\n- **VALIDATOR**: `--enforce-key-style` enforces dot.notation, snake_case, camelCase, kebab-case, or flat naming conventions.\n- **SCANNER**: `--source-language` supports multi-language hardcoded text detection with 12+ language profiles.\n- **BACKUP**: `--incremental` flag creates differential backups with SHA-256 hashing and chained restores.\n- **RUNTIME**: `lazy: true` option defers locale file loading until first key access for lower memory usage.\n- **PROTECTION**: Context-aware rules (`after:word`, `before:word`, `standalone`, `surrounded:left,right`) for precise term masking.\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\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## Command Reference\n\n| Command | What it does | Looks for | Writes or changes |\n| --- | --- | --- | --- |\n| `i18ntk` | Opens the interactive management menu. | Project config, setup state, available commands. | Only changes files after you choose a command that writes. |\n| `i18ntk --command=init` / `i18ntk-init` | Sets up locale folders and missing target-language files. | Source language files and selected target languages. | Locale JSON files, `.i18ntk-config`, optional reports/backups. |\n| `i18ntk --command=analyze` / `i18ntk-analyze` | Compares source and target translation coverage. | Missing keys, extra keys, untranslated markers, completion by language. | Markdown/JSON/text reports when report output is enabled. |\n| `i18ntk --command=validate` / `i18ntk-validate` | Validates structure and translation quality risks. | Placeholder mismatches, missing keys, risky URLs/emails/secrets, likely English target text. | Validation summary report. Does not edit locale files. |\n| `i18ntk --command=usage` / `i18ntk-usage` | Maps translation keys to source files and finds unused/missing keys. | Direct i18n calls, literal known-key references, bounded dynamic templates/object maps, unresolved dynamic expressions, hardcoded text candidates, namespace/file naming mismatches. | Usage report with key locations, namespace recommendations, unresolved dynamic expressions, hardcoded text suggestions, and optional dead-key report. Does not delete unless cleanup deletion is explicitly enabled. |\n| `i18ntk --command=scanner` / `i18ntk-scanner` | Scans source for i18n issues and hardcoded user-facing text. | JSX/template text, common text attributes, i18n usage patterns, source-language text profiles. | Scanner report. Does not edit files. |\n| `i18ntk --command=complete` / `i18ntk-complete` | Adds missing keys to target language files for 100% key coverage. | Source-language keys missing from targets. | Target locale JSON files, using missing translation markers/prefixes. |\n| `i18ntk --command=translate` / `i18ntk-translate` | Auto-translates locale JSON using configured provider behavior. | Missing, empty, untranslated-marker, source-copy, likely-English, or visibly corrupt target values by default. | Target locale JSON files and translation reports. Existing translated values are kept unless `--translate-all` is used. |\n| `i18ntk --command=sizing` / `i18ntk-sizing` | Estimates translated string length expansion and layout risk. | Text length, expansion ratios, placeholder-bearing strings. | Sizing report. Does not edit locale files. |\n| `i18ntk --command=summary` / `i18ntk-summary` | Shows project translation status. | Configured locales, reports, completeness status. | Console/report output only. |\n| `i18ntk-fixer` | Fixes placeholder and missing-marker issues. | Placeholder corruption, missing translation markers, configured language files. | Locale JSON files when fixes are applied. Use dry-run options where available before bulk edits. |\n| `i18ntk-backup` | Creates, verifies, restores, and cleans locale backups. | Locale JSON files and backup manifests. | Backup archives/manifests, or restored locale files when using restore. |\n\n## Common Options\n\nMany 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- `--help`\n\nCommand-specific tools add their own flags such as `--dry-run`, `--output-report`, `--cleanup`, `--predict-expansion`, or Auto Translate provider options.\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\"\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\nAuto Translate is target-aware by default. When a target file already exists, it keeps translated target values and only sends values that are missing, empty, marked as untranslated, still identical to the source, likely still English, or visibly corrupt from encoding damage such as `?????`, replacement characters, or common mojibake. Use `--translate-all` when you intentionally want to re-translate every source string.\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- `{count, plural, one {# item} other {# items}}`\n- `$t(common.save)`\n- `%(total).2f`\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- `--only-missing`: keep existing translated target values and translate only missing/source-copy/likely English values (default)\n- `--translate-all`: re-translate every source string\n\nProgress output is stage-aware for large files. Normal keys are reported as `Translating strings`, while preserve-mode placeholder work is reported as `Translating placeholder-safe text segments`; each progress update includes the current key path when available.\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\": [\n \"BrandName\",\n \"PRODUCT_CODE\",\n { \"value\": \"OK\", \"context\": \"after:Click|Press|Tap\" },\n { \"value\": \"API\", \"context\": \"standalone\" }\n ],\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 - **Plain strings**: masked everywhere (backward compatible).\n - **Context objects**: masked only in specific contexts (`after:word`, `before:word`, `standalone`, `surrounded:left,right`).\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` to edit defaults for placeholder mode, translate-only-needed 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\nValidation warning types are 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### Expansion Prediction (New in 4.0.0)\n\nPredict UI layout overflow risk by analyzing per-key character-count expansion across languages:\n\n```bash\ni18ntk-sizing --source-dir ./locales --predict-expansion --output-report\n```\n\nExpansion ratios are classified into risk tiers:\n\n- **Safe** (<30% expansion): no UI impact expected\n- **Warning** (30–50%): may overflow in tight layouts — test on target languages\n- **Critical** (>50%): high risk of truncation — review UI element sizing\n\nThe report includes a built-in language-pair expansion reference table (EN→DE +35%, EN→RU +50%, EN→JA −40%, etc.) and lists the top-30 most-expanded keys.\n\n## Scanner: Multi-Language Detection (New in 4.0.0)\n\n`i18ntk-scanner` now supports detecting hardcoded text in multiple source languages beyond English:\n\n```bash\ni18ntk-scanner --source-dir ./src --source-language de\ni18ntk-scanner --source-dir ./src --source-language ja --output-report\n```\n\nSupported language profiles (12+): English, German, French, Spanish, Japanese, Chinese, Russian, Korean, Arabic, Hindi, and more. Each profile includes language-specific character ranges, stopword lists for false-positive filtering, and transliteration rules for key generation.\n\n## Usage: Dead Key Detection (New in 4.0.0)\n\n`i18ntk-usage` can identify translation keys that are defined but never referenced in source code:\n\n```bash\ni18ntk-usage --source-dir ./src --i18n-dir ./locales --cleanup\ni18ntk-usage --source-dir ./src --i18n-dir ./locales --cleanup --dry-run-delete\n```\n\nEach dead key receives a confidence score (0.0–1.0) factoring:\n- Unresolved dynamic key patterns (e.g., `` t(`prefix.${dynamic}`) ``) — lower score and listed in the usage report; simple consts, bounded arrays, object maps, and ternaries are expanded to exact keys where possible\n- Key appears in source code comments or JSDoc — medium score\n- Parent file recently modified (<30 days) — medium score\n- No references found anywhere — high score (>0.8)\n\nThe `--dry-run-delete` flag writes a `.dead-keys.json` report for review before any destructive action.\n\n## Validator: Key Naming Conventions (New in 4.0.0)\n\nEnforce consistent translation key naming across your project:\n\n```bash\ni18ntk-validate --enforce-key-style\n```\n\nConfigure the expected style in `.i18ntk-config`:\n\n```json\n{\n \"keyStyle\": \"dot.notation\"\n}\n```\n\nSupported styles: `dot.notation`, `snake_case`, `camelCase`, `kebab-case`, `flat`. Violations are reported as warnings with suggested canonical forms.\n\n## Watch: Hot Reload (New in 4.0.0)\n\n`utils/watch-locales.js` now provides debounced file watching with EventEmitter support:\n\n```js\nconst watchLocales = require('i18ntk/utils/watch-locales');\nconst watcher = watchLocales('./locales');\n\nwatcher.on('change', (filePath) => {\n console.log('Locale changed:', filePath);\n});\n\nwatcher.on('add', (filePath) => {\n console.log('Locale added:', filePath);\n});\n\n// Later:\nwatcher.stop();\n```\n\nFeatures: 300ms debounce (configurable), SHA-256 hash tracking to skip no-change saves, and a maximum of 50 watched directories.\n\n### Migration\n\nThe `watchLocales` return value gained EventEmitter methods in v4.0.0. Existing stop-function usage still works:\n\n```js\nconst stop = watchLocales('./locales', onChange);\n```\n\nCan be updated to:\n\n```js\nconst watcher = watchLocales('./locales');\nwatcher.on('change', onChange);\nwatcher.stop();\n```\n\nPassing a callback as the second argument is still supported — it auto-subscribes to `change` and `add` events.\n\n## Backup: Incremental Mode (New in 4.0.0)\n\nCreate differential backups that only include changed files:\n\n```bash\ni18ntk-backup create ./locales --incremental\n```\n\nIncremental backups store SHA-256 hashes per file and a parent-chain reference. Restoring an incremental backup automatically chains from the oldest full backup through each incremental diff in order. Chain depth is capped at 10 increments. Use `verify` to validate the hash chain.\n\n## Runtime: Lazy Loading (New in 4.0.0)\n\nReduce memory usage by deferring locale file loads until first key access:\n\n```js\nconst runtime = require('i18ntk/runtime');\n\nconst i18n = runtime.initRuntime({\n baseDir: './locales',\n language: 'en',\n lazy: true\n});\n\nconsole.log(i18n.t('common.hello')); // loads common.json on first access\n```\n\nWhen `lazy: true`, the runtime builds a key-to-file manifest on first access and loads individual files on demand. Files are loaded once and cached. If the manifest is missing or incomplete, the runtime falls back to full eager loading for that language. Manifest size is capped at 100KB with path containment validation.\n\nProduction guidance:\n\n- Prefer the object returned from `initRuntime()` instead of module-level `runtime.t()` in apps with multiple tenants, projects, or locale roots.\n- Use `lazy: true` for large modular locale folders where lower steady-state memory matters more than a small first-key lookup cost.\n- Use `preload: true` without `lazy` for small locale sets or latency-sensitive startup paths.\n- Call `refresh(language)` after deploying or writing changed locale files so cached data and lazy manifests are rebuilt.\n- Use per-call language overrides when rendering one-off alternate-language strings: `i18n.t('common.hello', {}, { language: 'de' })`.\n- Use `translateBatch()` for small groups of labels and `clearCache()` / `getCacheInfo()` for cache maintenance and diagnostics.\n- `i18ntk/runtime/enhanced` remains available for compatibility with existing async/encryption users, but new production integrations should start with `i18ntk/runtime`.\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\nconst i18n = runtime.initRuntime({\n baseDir: './locales',\n language: 'en',\n fallbackLanguage: 'en',\n keySeparator: '.',\n preload: true\n});\n\nconsole.log(i18n.t('common.hello'));\ni18n.setLanguage('fr');\nconsole.log(i18n.getLanguage());\nconsole.log(i18n.getAvailableLanguages());\ni18n.refresh('fr');\n```\n\nUseful production helpers:\n\n```js\ni18n.t('common.hello', {}, { language: 'de' }); // per-call language override\ni18n.translateBatch(['menu.home', 'menu.settings']);\ni18n.clearCache('fr');\nconsole.log(i18n.getCacheInfo());\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\": \"4.2.2\",\n \"sourceDir\": \"./locales\",\n \"i18nDir\": \"./locales\",\n \"outputDir\": \"./i18ntk-reports\",\n \"sourceLanguage\": \"en\",\n \"defaultLanguages\": [\"en\", \"de\", \"es\", \"fr\", \"ru\"],\n \"reports\": {\n \"format\": \"markdown\"\n },\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\", \"PRODUCT_CODE\"],\n \"autoTranslate\": {\n \"placeholderMode\": \"preserve\",\n \"concurrency\": 12,\n \"batchSize\": 100,\n \"progressInterval\": 25,\n \"retryCount\": 3,\n \"retryDelay\": 1000,\n \"timeout\": 15000,\n \"dryRunFirst\": true,\n \"onlyMissingOrEnglish\": 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 v4.2.2](./docs/migration-guide-v4.2.2.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"
|
|
216
|
+
"readme": "# i18ntk v4.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\n\n[](https://www.npmjs.com/package/i18ntk)\n[](https://www.npmjs.com/package/i18ntk)\n[](https://nodejs.org)\n[](https://www.npmjs.com/package/i18ntk)\n[](LICENSE)\n[](https://socket.dev/npm/package/i18ntk/overview/4.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 4.3.0\n\n- **AUTO TRANSLATE**: Existing target values like `[AR] What We Offer`, `[AR] Email`, `[zh] Email`, and `[TR] Password` are now treated as untranslated placeholders for the matching target language and are translated from the source text.\n- **AUTO TRANSLATE**: Before writing each output file, Auto Translate now performs a final leftover check and retries any placeholder-prefixed or source-copy values once.\n- **AUTO TRANSLATE**: If leftovers remain after the final retry, the command warns, includes them in the report, recommends rerunning Auto Translate, and exits with validation failure instead of reporting a clean completion.\n- **AUTO TRANSLATE**: Managed Auto Translate now checks every selected source file for a target language before reporting leftovers, so one failed file does not prevent the rest from being audited.\n- **AUTO TRANSLATE**: Short all-caps acronyms and codes such as `XP` are allowed to remain unchanged instead of failing the final leftover check.\n- **FIX PLACEHOLDERS**: Menu option 7 now runs an English source placeholder audit and reports how many `[LANG] ...` values remain in English locale files. A clean project reports `0`.\n- **SIZING/USAGE**: Usage analysis no longer writes its inferred app source fallback back into the shared locale config, so running usage before sizing no longer makes sizing analyze the wrong directory.\n- **VALIDATION REPORTS**: Validation summary files now include warning and error details, including English-content warning payloads, instead of only totals.\n- **DOCS**: Versioned docs and migration guidance now reflect the current 4.3.0 command surface.\n\n## What's New in 4.1.0\n\n- **FIX**: Critical and high-impact bugs resolved across the v4.0.0 feature set — runtime staleness crashes, backup hash-chain verification, sizing adminAuth crash, scanner `--source-language` propagation, watch callback subscriptions, dead key detection performance, validator key style enforcement, and protection Unicode boundary handling. See [CHANGELOG.md](./CHANGELOG.md) for complete details.\n\n## What's New in 4.0.0\n\n- **SIZING**: `--predict-expansion` flag computes per-key expansion ratios across languages with Safe/Warning/Critical risk tiers for UI layout planning.\n- **WATCH**: `watchLocales()` now returns an EventEmitter-compatible watcher with debounced `change`/`add`/`unlink`/`error` events and SHA-256 hash tracking.\n- **USAGE**: `--cleanup` and `--dry-run-delete` flags identify dead translation keys with confidence scores.\n- **VALIDATOR**: `--enforce-key-style` enforces dot.notation, snake_case, camelCase, kebab-case, or flat naming conventions.\n- **SCANNER**: `--source-language` supports multi-language hardcoded text detection with 12+ language profiles.\n- **BACKUP**: `--incremental` flag creates differential backups with SHA-256 hashing and chained restores.\n- **RUNTIME**: `lazy: true` option defers locale file loading until first key access for lower memory usage.\n- **PROTECTION**: Context-aware rules (`after:word`, `before:word`, `standalone`, `surrounded:left,right`) for precise term masking.\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\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## Command Reference\n\n| Command | What it does | Looks for | Writes or changes |\n| --- | --- | --- | --- |\n| `i18ntk` | Opens the interactive management menu. | Project config, setup state, available commands. | Only changes files after you choose a command that writes. |\n| `i18ntk --command=init` / `i18ntk-init` | Sets up locale folders and missing target-language files. | Source language files and selected target languages. | Locale JSON files, `.i18ntk-config`, optional reports/backups. |\n| `i18ntk --command=analyze` / `i18ntk-analyze` | Compares source and target translation coverage. | Missing keys, extra keys, untranslated markers, completion by language. | Markdown/JSON/text reports when report output is enabled. |\n| `i18ntk --command=validate` / `i18ntk-validate` | Validates structure and translation quality risks. | Placeholder mismatches, missing keys, risky URLs/emails/secrets, likely English target text. | Validation summary report. Does not edit locale files. |\n| `i18ntk --command=usage` / `i18ntk-usage` | Maps translation keys to source files and finds unused/missing keys. | Direct i18n calls, literal known-key references, bounded dynamic templates/object maps, unresolved dynamic expressions, hardcoded text candidates, namespace/file naming mismatches. | Usage report with key locations, namespace recommendations, unresolved dynamic expressions, hardcoded text suggestions, and optional dead-key report. Does not delete unless cleanup deletion is explicitly enabled. |\n| `i18ntk --command=scanner` / `i18ntk-scanner` | Scans source for i18n issues and hardcoded user-facing text. | JSX/template text, common text attributes, i18n usage patterns, source-language text profiles. | Scanner report. Does not edit files. |\n| `i18ntk --command=complete` / `i18ntk-complete` | Adds missing keys to target language files for 100% key coverage. | Source-language keys missing from targets. | Target locale JSON files, using missing translation markers/prefixes. |\n| `i18ntk --command=translate` / `i18ntk-translate` | Auto-translates locale JSON using configured provider behavior. | Missing, empty, untranslated-marker, source-copy, likely-English, or visibly corrupt target values by default. | Target locale JSON files and translation reports. Existing translated values are kept unless `--translate-all` is used. |\n| `i18ntk --command=sizing` / `i18ntk-sizing` | Estimates translated string length expansion and layout risk. | Text length, expansion ratios, placeholder-bearing strings. | Sizing report. Does not edit locale files. |\n| `i18ntk --command=summary` / `i18ntk-summary` | Shows project translation status. | Configured locales, reports, completeness status. | Console/report output only. |\n| `i18ntk-fixer` | Fixes placeholder and missing-marker issues, and can audit English source files with `--check-placeholders`. | Placeholder corruption, missing translation markers, configured language files, `[LANG] ...` leftovers in English locales. | Locale JSON files when fixes are applied. Use dry-run options where available before bulk edits. |\n| `i18ntk-backup` | Creates, verifies, restores, and cleans locale backups. | Locale JSON files and backup manifests. | Backup archives/manifests, or restored locale files when using restore. |\n\n## Common Options\n\nMany 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- `--help`\n\nCommand-specific tools add their own flags such as `--dry-run`, `--output-report`, `--cleanup`, `--predict-expansion`, or Auto Translate provider options.\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\"\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\nAuto Translate is target-aware by default. When a target file already exists, it keeps translated target values and only sends values that are missing, empty, marked as untranslated, still identical to the source, likely still English, or visibly corrupt from encoding damage such as `?????`, replacement characters, or common mojibake. Use `--translate-all` when you intentionally want to re-translate every source string.\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- `{count, plural, one {# item} other {# items}}`\n- `$t(common.save)`\n- `%(total).2f`\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- `--only-missing`: keep existing translated target values and translate only missing/source-copy/likely English values (default)\n- `--translate-all`: re-translate every source string\n\nProgress output is stage-aware for large files. Normal keys are reported as `Translating strings`, while preserve-mode placeholder work is reported as `Translating placeholder-safe text segments`; each progress update includes the current key path when available.\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\": [\n \"BrandName\",\n \"PRODUCT_CODE\",\n { \"value\": \"OK\", \"context\": \"after:Click|Press|Tap\" },\n { \"value\": \"API\", \"context\": \"standalone\" }\n ],\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 - **Plain strings**: masked everywhere (backward compatible).\n - **Context objects**: masked only in specific contexts (`after:word`, `before:word`, `standalone`, `surrounded:left,right`).\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` to edit defaults for placeholder mode, translate-only-needed 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\nValidation warning types are 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### Expansion Prediction (New in 4.0.0)\n\nPredict UI layout overflow risk by analyzing per-key character-count expansion across languages:\n\n```bash\ni18ntk-sizing --source-dir ./locales --predict-expansion --output-report\n```\n\nExpansion ratios are classified into risk tiers:\n\n- **Safe** (<30% expansion): no UI impact expected\n- **Warning** (30–50%): may overflow in tight layouts — test on target languages\n- **Critical** (>50%): high risk of truncation — review UI element sizing\n\nThe report includes a built-in language-pair expansion reference table (EN→DE +35%, EN→RU +50%, EN→JA −40%, etc.) and lists the top-30 most-expanded keys.\n\n## Scanner: Multi-Language Detection (New in 4.0.0)\n\n`i18ntk-scanner` now supports detecting hardcoded text in multiple source languages beyond English:\n\n```bash\ni18ntk-scanner --source-dir ./src --source-language de\ni18ntk-scanner --source-dir ./src --source-language ja --output-report\n```\n\nSupported language profiles (12+): English, German, French, Spanish, Japanese, Chinese, Russian, Korean, Arabic, Hindi, and more. Each profile includes language-specific character ranges, stopword lists for false-positive filtering, and transliteration rules for key generation.\n\n## Usage: Dead Key Detection (New in 4.0.0)\n\n`i18ntk-usage` can identify translation keys that are defined but never referenced in source code:\n\n```bash\ni18ntk-usage --source-dir ./src --i18n-dir ./locales --cleanup\ni18ntk-usage --source-dir ./src --i18n-dir ./locales --cleanup --dry-run-delete\n```\n\nEach dead key receives a confidence score (0.0–1.0) factoring:\n- Unresolved dynamic key patterns (e.g., `` t(`prefix.${dynamic}`) ``) — lower score and listed in the usage report; simple consts, bounded arrays, object maps, and ternaries are expanded to exact keys where possible\n- Key appears in source code comments or JSDoc — medium score\n- Parent file recently modified (<30 days) — medium score\n- No references found anywhere — high score (>0.8)\n\nThe `--dry-run-delete` flag writes a `.dead-keys.json` report for review before any destructive action.\n\n## Validator: Key Naming Conventions (New in 4.0.0)\n\nEnforce consistent translation key naming across your project:\n\n```bash\ni18ntk-validate --enforce-key-style\n```\n\nConfigure the expected style in `.i18ntk-config`:\n\n```json\n{\n \"keyStyle\": \"dot.notation\"\n}\n```\n\nSupported styles: `dot.notation`, `snake_case`, `camelCase`, `kebab-case`, `flat`. Violations are reported as warnings with suggested canonical forms.\n\n## Watch: Hot Reload (New in 4.0.0)\n\n`utils/watch-locales.js` now provides debounced file watching with EventEmitter support:\n\n```js\nconst watchLocales = require('i18ntk/utils/watch-locales');\nconst watcher = watchLocales('./locales');\n\nwatcher.on('change', (filePath) => {\n console.log('Locale changed:', filePath);\n});\n\nwatcher.on('add', (filePath) => {\n console.log('Locale added:', filePath);\n});\n\n// Later:\nwatcher.stop();\n```\n\nFeatures: 300ms debounce (configurable), SHA-256 hash tracking to skip no-change saves, and a maximum of 50 watched directories.\n\n### Migration\n\nThe `watchLocales` return value gained EventEmitter methods in v4.0.0. Existing stop-function usage still works:\n\n```js\nconst stop = watchLocales('./locales', onChange);\n```\n\nCan be updated to:\n\n```js\nconst watcher = watchLocales('./locales');\nwatcher.on('change', onChange);\nwatcher.stop();\n```\n\nPassing a callback as the second argument is still supported — it auto-subscribes to `change` and `add` events.\n\n## Backup: Incremental Mode (New in 4.0.0)\n\nCreate differential backups that only include changed files:\n\n```bash\ni18ntk-backup create ./locales --incremental\n```\n\nIncremental backups store SHA-256 hashes per file and a parent-chain reference. Restoring an incremental backup automatically chains from the oldest full backup through each incremental diff in order. Chain depth is capped at 10 increments. Use `verify` to validate the hash chain.\n\n## Runtime: Lazy Loading (New in 4.0.0)\n\nReduce memory usage by deferring locale file loads until first key access:\n\n```js\nconst runtime = require('i18ntk/runtime');\n\nconst i18n = runtime.initRuntime({\n baseDir: './locales',\n language: 'en',\n lazy: true\n});\n\nconsole.log(i18n.t('common.hello')); // loads common.json on first access\n```\n\nWhen `lazy: true`, the runtime builds a key-to-file manifest on first access and loads individual files on demand. Files are loaded once and cached. If the manifest is missing or incomplete, the runtime falls back to full eager loading for that language. Manifest size is capped at 100KB with path containment validation.\n\nProduction guidance:\n\n- Prefer the object returned from `initRuntime()` instead of module-level `runtime.t()` in apps with multiple tenants, projects, or locale roots.\n- Use `lazy: true` for large modular locale folders where lower steady-state memory matters more than a small first-key lookup cost.\n- Use `preload: true` without `lazy` for small locale sets or latency-sensitive startup paths.\n- Call `refresh(language)` after deploying or writing changed locale files so cached data and lazy manifests are rebuilt.\n- Use per-call language overrides when rendering one-off alternate-language strings: `i18n.t('common.hello', {}, { language: 'de' })`.\n- Use `translateBatch()` for small groups of labels and `clearCache()` / `getCacheInfo()` for cache maintenance and diagnostics.\n- `i18ntk/runtime/enhanced` remains available for compatibility with existing async/encryption users, but new production integrations should start with `i18ntk/runtime`.\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\nconst i18n = runtime.initRuntime({\n baseDir: './locales',\n language: 'en',\n fallbackLanguage: 'en',\n keySeparator: '.',\n preload: true\n});\n\nconsole.log(i18n.t('common.hello'));\ni18n.setLanguage('fr');\nconsole.log(i18n.getLanguage());\nconsole.log(i18n.getAvailableLanguages());\ni18n.refresh('fr');\n```\n\nUseful production helpers:\n\n```js\ni18n.t('common.hello', {}, { language: 'de' }); // per-call language override\ni18n.translateBatch(['menu.home', 'menu.settings']);\ni18n.clearCache('fr');\nconsole.log(i18n.getCacheInfo());\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\": \"4.3.0\",\n \"sourceDir\": \"./locales\",\n \"i18nDir\": \"./locales\",\n \"outputDir\": \"./i18ntk-reports\",\n \"sourceLanguage\": \"en\",\n \"defaultLanguages\": [\"en\", \"de\", \"es\", \"fr\", \"ru\"],\n \"reports\": {\n \"format\": \"markdown\"\n },\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\", \"PRODUCT_CODE\"],\n \"autoTranslate\": {\n \"placeholderMode\": \"preserve\",\n \"concurrency\": 12,\n \"batchSize\": 100,\n \"progressInterval\": 25,\n \"retryCount\": 3,\n \"retryDelay\": 1000,\n \"timeout\": 15000,\n \"dryRunFirst\": true,\n \"onlyMissingOrEnglish\": 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 v4.3.0](./docs/migration-guide-v4.3.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"
|
|
214
217
|
}
|