i18ntk 4.3.0 → 4.3.2
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 +13 -0
- package/README.md +26 -4
- package/main/i18ntk-go.js +283 -0
- package/main/i18ntk-java.js +380 -0
- package/main/i18ntk-js.js +512 -0
- package/main/i18ntk-php.js +462 -0
- package/main/i18ntk-py.js +379 -0
- package/package.json +16 -5
- package/utils/english-placeholder-checker.js +162 -0
- package/utils/mini-commander.js +179 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ 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.3.2] - 2026-05-31
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Documentation, README badges, and migration guidance now reference the current 4.3.2 release.
|
|
12
|
+
- Release metadata now marks 4.3.0 for npm deprecation because its npm tarball is unavailable.
|
|
13
|
+
|
|
14
|
+
## [4.3.1] - 2026-05-31
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Published tarball now includes `utils/english-placeholder-checker.js`, resolving `MODULE_NOT_FOUND` at startup for `i18ntk-fixer --check-placeholders` and manager option 7.
|
|
18
|
+
- Language-specific CLI entry points (`main/i18ntk-go.js`, `main/i18ntk-java.js`, `main/i18ntk-js.js`, `main/i18ntk-php.js`, `main/i18ntk-py.js`) and their shared `utils/mini-commander.js` dependency are now included in the published package.
|
|
19
|
+
- Removed inconsistent `.js` extension suffixes from require paths in `main/i18ntk-js.js`.
|
|
20
|
+
|
|
8
21
|
## [4.3.0] - 2026-05-31
|
|
9
22
|
|
|
10
23
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# i18ntk v4.3.
|
|
1
|
+
# i18ntk v4.3.2
|
|
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,18 @@ 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.2)
|
|
13
|
+
## VS Code Extensions
|
|
14
|
+
|
|
15
|
+
[](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-workbench)
|
|
16
|
+
[](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-lens)
|
|
17
|
+
|
|
18
|
+
Official VS Code extensions powered by i18ntk:
|
|
19
|
+
|
|
20
|
+
- **i18ntk Workbench**: setup, scanning, validation, usage checks, reports, key management, and CLI-backed Auto Translate inside VS Code.
|
|
21
|
+
- **i18ntk Lens**: lightweight inline hovers, CodeLens, missing-key warnings, unused-key diagnostics, key navigation, and settings.
|
|
22
|
+
|
|
23
|
+
Install both when you want the full sidebar plus inline editor feedback.
|
|
13
24
|
|
|
14
25
|
## Install
|
|
15
26
|
|
|
@@ -30,6 +41,17 @@ Requirements:
|
|
|
30
41
|
- npm `>=8.0.0`
|
|
31
42
|
- No runtime dependencies
|
|
32
43
|
|
|
44
|
+
## What's New in 4.3.2
|
|
45
|
+
|
|
46
|
+
- **DOCS**: README, API docs, and migration guidance now reflect the current 4.3.2 package version.
|
|
47
|
+
- **RELEASE**: 4.3.0 is marked for npm deprecation because its npm tarball is unavailable; install 4.3.1 or newer.
|
|
48
|
+
|
|
49
|
+
## What's New in 4.3.1
|
|
50
|
+
|
|
51
|
+
- **FIX**: Published tarball now includes `utils/english-placeholder-checker.js`, resolving `MODULE_NOT_FOUND` at startup for `i18ntk-fixer --check-placeholders` and manager option 7.
|
|
52
|
+
- **FIX**: Language-specific CLI entry points (`i18ntk-go`, `i18ntk-java`, `i18ntk-js`, `i18ntk-php`, `i18ntk-py`) and their shared `utils/mini-commander.js` dependency are now included in the published package.
|
|
53
|
+
- **FIX**: Removed inconsistent `.js` extension suffixes from require paths in `main/i18ntk-js.js`.
|
|
54
|
+
|
|
33
55
|
## What's New in 4.3.0
|
|
34
56
|
|
|
35
57
|
- **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.
|
|
@@ -506,7 +528,7 @@ Example:
|
|
|
506
528
|
|
|
507
529
|
```json
|
|
508
530
|
{
|
|
509
|
-
"version": "4.3.
|
|
531
|
+
"version": "4.3.2",
|
|
510
532
|
"sourceDir": "./locales",
|
|
511
533
|
"i18nDir": "./locales",
|
|
512
534
|
"outputDir": "./i18ntk-reports",
|
|
@@ -568,7 +590,7 @@ The public package manifest includes `readmeFilename: "README.md"`, and the rele
|
|
|
568
590
|
- [Auto Translate Guide](./docs/auto-translate.md)
|
|
569
591
|
- [Scanner Guide](./docs/scanner-guide.md)
|
|
570
592
|
- [Environment Variables](./docs/environment-variables.md)
|
|
571
|
-
- [Migration Guide v4.3.
|
|
593
|
+
- [Migration Guide v4.3.2](./docs/migration-guide-v4.3.2.md)
|
|
572
594
|
|
|
573
595
|
## Security
|
|
574
596
|
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* i18ntk-go.js - Go Language I18n Management Command
|
|
5
|
+
*
|
|
6
|
+
* Supports:
|
|
7
|
+
* - Standard Go i18n patterns
|
|
8
|
+
* - go-i18n library
|
|
9
|
+
* - Custom Go i18n implementations
|
|
10
|
+
* - Resource file (.json, .toml) management
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const SecurityUtils = require('../utils/security');
|
|
16
|
+
const { getConfig, saveConfig } = require('../utils/config-helper');
|
|
17
|
+
const I18nHelper = require('../utils/i18n-helper');
|
|
18
|
+
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
19
|
+
const { program } = require('../utils/mini-commander');
|
|
20
|
+
|
|
21
|
+
(async () => {
|
|
22
|
+
try {
|
|
23
|
+
await SetupEnforcer.checkSetupCompleteAsync();
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('Setup check failed:', error.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
})();
|
|
29
|
+
|
|
30
|
+
class GoI18nManager {
|
|
31
|
+
constructor() {
|
|
32
|
+
this.supportedPatterns = [
|
|
33
|
+
'T("key")',
|
|
34
|
+
'Localize("key")',
|
|
35
|
+
'i18n.T("key")',
|
|
36
|
+
'tr("key")',
|
|
37
|
+
'message.Printer.Printf',
|
|
38
|
+
'i18n.MustLocalize'
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
this.fileExtensions = ['.go', '.mod', '.sum'];
|
|
42
|
+
this.resourceFormats = ['.json', '.toml', '.yaml', '.yml'];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async detectFramework(sourceDir) {
|
|
46
|
+
const goModPath = path.join(sourceDir, 'go.mod');
|
|
47
|
+
if (SecurityUtils.safeExistsSync(goModPath)) {
|
|
48
|
+
const content = SecurityUtils.safeReadFileSync(goModPath, sourceDir, 'utf8') || '';
|
|
49
|
+
|
|
50
|
+
if (content.includes('go-i18n')) return 'go-i18n-v2';
|
|
51
|
+
if (/x\/text\b/.test(content)) return 'golang-text';
|
|
52
|
+
|
|
53
|
+
return 'standard-go';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check for Go files
|
|
57
|
+
const goFiles = this.findFiles(sourceDir, '.go');
|
|
58
|
+
if (goFiles.length > 0) {
|
|
59
|
+
return 'standard-go';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return 'generic';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async extractTranslations(sourceDir, options = {}) {
|
|
66
|
+
const framework = await this.detectFramework(sourceDir);
|
|
67
|
+
const translations = new Set();
|
|
68
|
+
|
|
69
|
+
const goFiles = this.findFiles(sourceDir, '.go');
|
|
70
|
+
|
|
71
|
+
for (const file of goFiles) {
|
|
72
|
+
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8') || '';
|
|
73
|
+
|
|
74
|
+
// Extract Go i18n patterns
|
|
75
|
+
const patterns = [
|
|
76
|
+
/T\("([^"]+)"\)/g,
|
|
77
|
+
/Localize\("([^"]+)"\)/g,
|
|
78
|
+
/i18n\.T\("([^"]+)"\)/g,
|
|
79
|
+
/tr\("([^"]+)"\)/g,
|
|
80
|
+
/message\.Printer\.Printf\("([^"]+)"/g,
|
|
81
|
+
/i18n\.MustLocalize\(&i18n\.LocalizeConfig\{MessageID:\s*"([^"]+)"/g
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
for (const pattern of patterns) {
|
|
85
|
+
let match;
|
|
86
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
87
|
+
translations.add(match[1]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
framework,
|
|
94
|
+
translations: Array.from(translations),
|
|
95
|
+
files: goFiles.length,
|
|
96
|
+
patterns: this.supportedPatterns
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async createLocaleStructure(outputDir, languages = ['en']) {
|
|
101
|
+
const localesDir = path.join(outputDir, 'locales');
|
|
102
|
+
|
|
103
|
+
for (const lang of languages) {
|
|
104
|
+
const langDir = path.join(localesDir, lang);
|
|
105
|
+
fs.mkdirSync(langDir, { recursive: true });
|
|
106
|
+
|
|
107
|
+
// Create Go i18n format files
|
|
108
|
+
SecurityUtils.safeWriteFileSync(path.join(langDir, 'active.en.toml'), `# Go i18n translations for ${lang}
|
|
109
|
+
[hello]
|
|
110
|
+
other = "Hello, World!"
|
|
111
|
+
|
|
112
|
+
[items]
|
|
113
|
+
one = "{{.Count}} item"
|
|
114
|
+
other = "{{.Count}} items"
|
|
115
|
+
`);
|
|
116
|
+
|
|
117
|
+
SecurityUtils.safeWriteFileSync(path.join(langDir, 'active.en.json'), JSON.stringify({
|
|
118
|
+
hello: "Hello, World!",
|
|
119
|
+
items: {
|
|
120
|
+
one: "{{.Count}} item",
|
|
121
|
+
other: "{{.Count}} items"
|
|
122
|
+
}
|
|
123
|
+
}, null, 2));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return localesDir;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
findFiles(dir, extension) {
|
|
130
|
+
const files = [];
|
|
131
|
+
|
|
132
|
+
function traverse(currentDir) {
|
|
133
|
+
const items = fs.readdirSync(currentDir);
|
|
134
|
+
|
|
135
|
+
for (const item of items) {
|
|
136
|
+
const fullPath = path.join(currentDir, item);
|
|
137
|
+
const stat = fs.statSync(fullPath);
|
|
138
|
+
|
|
139
|
+
if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
|
|
140
|
+
traverse(fullPath);
|
|
141
|
+
} else if (stat.isFile() && path.extname(item) === extension) {
|
|
142
|
+
files.push(fullPath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
traverse(dir);
|
|
148
|
+
return files;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async generateReport(results, outputDir) {
|
|
152
|
+
const report = {
|
|
153
|
+
timestamp: new Date().toISOString(),
|
|
154
|
+
framework: results.framework,
|
|
155
|
+
totalTranslations: results.translations.length,
|
|
156
|
+
translations: results.translations,
|
|
157
|
+
filesProcessed: results.files,
|
|
158
|
+
patterns: results.patterns,
|
|
159
|
+
recommendations: this.getRecommendations(results)
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const reportPath = path.join(outputDir, 'i18ntk-go-report.json');
|
|
163
|
+
SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
164
|
+
|
|
165
|
+
return reportPath;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getRecommendations(results) {
|
|
169
|
+
const recommendations = [];
|
|
170
|
+
|
|
171
|
+
if (results.translations.length === 0) {
|
|
172
|
+
recommendations.push('No translations found. Check your Go i18n patterns');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (results.files === 0) {
|
|
176
|
+
recommendations.push('No Go files found in source directory');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (results.framework === 'generic') {
|
|
180
|
+
recommendations.push('Consider using go-i18n library for better i18n support');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return recommendations;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// CLI Implementation
|
|
188
|
+
program
|
|
189
|
+
.name('i18ntk-go')
|
|
190
|
+
.description('Go language i18n management tool')
|
|
191
|
+
.version('1.10.1');
|
|
192
|
+
|
|
193
|
+
program
|
|
194
|
+
.command('init')
|
|
195
|
+
.description('Initialize Go i18n structure')
|
|
196
|
+
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
197
|
+
.option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
|
|
198
|
+
.option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
|
|
199
|
+
.action(async (options) => {
|
|
200
|
+
try {
|
|
201
|
+
const manager = new GoI18nManager();
|
|
202
|
+
const languages = options.languages.split(',');
|
|
203
|
+
|
|
204
|
+
const localesDir = await manager.createLocaleStructure(options.outputDir, languages);
|
|
205
|
+
|
|
206
|
+
console.log(`✅ Go i18n structure initialized in: ${localesDir}`);
|
|
207
|
+
console.log(`📊 Languages: ${languages.join(', ')}`);
|
|
208
|
+
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error('❌ Error initializing Go i18n:', error.message);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
program
|
|
216
|
+
.command('analyze')
|
|
217
|
+
.description('Analyze Go i18n usage')
|
|
218
|
+
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
219
|
+
.option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
|
|
220
|
+
.option('--dry-run', 'Preview without making changes')
|
|
221
|
+
.action(async (options) => {
|
|
222
|
+
try {
|
|
223
|
+
SecurityUtils.validatePath(options.sourceDir);
|
|
224
|
+
|
|
225
|
+
const manager = new GoI18nManager();
|
|
226
|
+
const results = await manager.extractTranslations(options.sourceDir);
|
|
227
|
+
|
|
228
|
+
console.log(`🔍 Framework detected: ${results.framework}`);
|
|
229
|
+
console.log(`📊 Files processed: ${results.files}`);
|
|
230
|
+
console.log(`📝 Translations found: ${results.translations.length}`);
|
|
231
|
+
|
|
232
|
+
if (!options.dryRun) {
|
|
233
|
+
const reportPath = await manager.generateReport(results, options.outputDir);
|
|
234
|
+
console.log(`📄 Report saved: ${reportPath}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error('❌ Error analyzing Go i18n:', error.message);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
program
|
|
244
|
+
.command('extract')
|
|
245
|
+
.description('Extract Go translations')
|
|
246
|
+
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
247
|
+
.option('-o, --output-dir <dir>', 'Output directory', './locales')
|
|
248
|
+
.option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
|
|
249
|
+
.action(async (options) => {
|
|
250
|
+
try {
|
|
251
|
+
SecurityUtils.validatePath(options.sourceDir);
|
|
252
|
+
|
|
253
|
+
const manager = new GoI18nManager();
|
|
254
|
+
const results = await manager.extractTranslations(options.sourceDir);
|
|
255
|
+
|
|
256
|
+
await manager.createLocaleStructure(options.outputDir, options.languages.split(','));
|
|
257
|
+
|
|
258
|
+
console.log(`✅ Extracted ${results.translations.length} translations`);
|
|
259
|
+
console.log(`📁 Locale structure created in: ${options.outputDir}`);
|
|
260
|
+
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error('❌ Error extracting Go translations:', error.message);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Handle uncaught errors
|
|
268
|
+
process.on('uncaughtException', (error) => {
|
|
269
|
+
console.error('❌ Uncaught exception:', error.message);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
process.on('unhandledRejection', (reason) => {
|
|
274
|
+
console.error('❌ Unhandled rejection:', reason);
|
|
275
|
+
process.exit(1);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Export for programmatic use
|
|
279
|
+
module.exports = { GoI18nManager };
|
|
280
|
+
|
|
281
|
+
if (require.main === module) {
|
|
282
|
+
program.parse();
|
|
283
|
+
}
|