i18ntk 2.0.4 ā 2.2.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/README.md +38 -60
- package/main/i18ntk-analyze.js +49 -44
- package/main/i18ntk-complete.js +75 -74
- package/main/i18ntk-fixer.js +3 -3
- package/main/i18ntk-init.js +5 -5
- package/main/i18ntk-scanner.js +2 -2
- package/main/i18ntk-sizing.js +35 -35
- package/main/i18ntk-summary.js +4 -4
- package/main/i18ntk-ui.js +54 -8
- package/main/i18ntk-usage.js +14 -14
- package/main/i18ntk-validate.js +6 -5
- package/main/manage/commands/AnalyzeCommand.js +40 -35
- package/main/manage/commands/FixerCommand.js +2 -2
- package/main/manage/commands/ScannerCommand.js +2 -2
- package/main/manage/commands/ValidateCommand.js +9 -9
- package/main/manage/index.js +147 -75
- package/main/manage/managers/LanguageMenu.js +7 -2
- package/main/manage/services/UsageService.js +7 -7
- package/package.json +269 -290
- package/settings/settings-cli.js +3 -3
- package/ui-locales/de.json +161 -166
- package/ui-locales/en.json +13 -18
- package/ui-locales/es.json +171 -184
- package/ui-locales/fr.json +155 -161
- package/ui-locales/ja.json +192 -243
- package/ui-locales/ru.json +145 -196
- package/ui-locales/zh.json +179 -185
- package/utils/cli-helper.js +26 -98
- package/utils/extractors/regex.js +39 -12
- package/utils/i18n-helper.js +88 -40
- package/{scripts ā utils}/locale-optimizer.js +61 -60
- package/utils/security-check-improved.js +16 -13
- package/utils/security.js +6 -4
- package/main/i18ntk-go.js +0 -283
- package/main/i18ntk-java.js +0 -380
- package/main/i18ntk-js.js +0 -512
- package/main/i18ntk-manage.js +0 -1694
- package/main/i18ntk-php.js +0 -462
- package/main/i18ntk-py.js +0 -379
- package/main/i18ntk-settings.js +0 -23
- package/main/manage/index-fixed.js +0 -1447
- package/main/manage/services/ConfigurationService-fixed.js +0 -449
- package/scripts/build-lite.js +0 -279
- package/scripts/deprecate-versions.js +0 -317
- package/scripts/export-translations.js +0 -84
- package/scripts/fix-all-i18n.js +0 -215
- package/scripts/fix-and-purify-i18n.js +0 -213
- package/scripts/fix-locale-control-chars.js +0 -110
- package/scripts/lint-locales.js +0 -80
- package/scripts/prepublish.js +0 -348
- package/scripts/security-check.js +0 -117
- package/scripts/sync-translations.js +0 -151
- package/scripts/sync-ui-locales.js +0 -20
- package/scripts/validate-all-translations.js +0 -139
- package/scripts/verify-deprecations.js +0 -157
- package/scripts/verify-translations.js +0 -63
- package/utils/security-fixed.js +0 -607
package/README.md
CHANGED
|
@@ -1,74 +1,64 @@
|
|
|
1
|
-
#
|
|
1
|
+
# i18ntk v2.2.0
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Zero-dependency i18n toolkit for initialization, scanning, analysis, validation, usage tracking, and translation completion.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
**The fastest and most comprehensive i18n toolkit ever built.**
|
|
8
|
-
|
|
9
7
|
[](https://www.npmjs.com/package/i18ntk)
|
|
10
|
-
[](https://opensource.org/licenses/MIT)
|
|
11
|
-
[](https://github.com/vladnoskv/i18ntk#performance)
|
|
12
|
-
[](https://github.com/vladnoskv/i18ntk#features)
|
|
13
8
|
[](https://www.npmjs.com/package/i18ntk)
|
|
14
|
-
[](https://nodejs.org)
|
|
10
|
+
[](https://www.npmjs.com/package/i18ntk)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://socket.dev/npm/package/i18ntk/overview/2.2.0)
|
|
20
13
|
|
|
21
|
-
##
|
|
14
|
+
## Upgrade Notice
|
|
22
15
|
|
|
23
|
-
|
|
16
|
+
Versions earlier than `2.2.0` may contain known stability and security issues.
|
|
17
|
+
They are considered unsupported for production use. Upgrade to `2.2.0` or newer.
|
|
24
18
|
|
|
25
|
-
|
|
19
|
+
## Why i18ntk
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
- Zero runtime dependencies
|
|
22
|
+
- Works across JS/TS, React, Vue, Angular, and generic projects
|
|
23
|
+
- Supports non-interactive CI runs (`--no-prompt`)
|
|
24
|
+
- Includes usage/coverage validation and missing-key completion
|
|
25
|
+
- Ships with runtime translation helpers via `i18ntk/runtime`
|
|
28
26
|
|
|
29
|
-
##
|
|
27
|
+
## Install
|
|
30
28
|
|
|
31
29
|
```bash
|
|
32
|
-
#
|
|
30
|
+
# global (recommended for CLI use)
|
|
33
31
|
npm install -g i18ntk
|
|
34
32
|
|
|
35
|
-
#
|
|
36
|
-
|
|
33
|
+
# local
|
|
34
|
+
npm install --save-dev i18ntk
|
|
37
35
|
|
|
38
|
-
#
|
|
39
|
-
|
|
36
|
+
# one-off
|
|
37
|
+
npx i18ntk --help
|
|
40
38
|
```
|
|
41
39
|
|
|
42
|
-
##
|
|
43
|
-
|
|
44
|
-
Get your i18n project up and running in **60 seconds**:
|
|
40
|
+
## Quick Start
|
|
45
41
|
|
|
46
42
|
```bash
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# 2. Initialize your project
|
|
51
|
-
i18ntk init
|
|
52
|
-
|
|
53
|
-
# 3. Analyze your translations
|
|
54
|
-
i18ntk analyze
|
|
43
|
+
# initialize locales/project settings
|
|
44
|
+
i18ntk --command=init
|
|
55
45
|
|
|
56
|
-
#
|
|
57
|
-
i18ntk
|
|
46
|
+
# analyze translation completeness
|
|
47
|
+
i18ntk --command=analyze
|
|
58
48
|
|
|
59
|
-
#
|
|
60
|
-
i18ntk validate
|
|
49
|
+
# validate translation structure/content
|
|
50
|
+
i18ntk --command=validate
|
|
61
51
|
|
|
62
|
-
#
|
|
52
|
+
# complete missing keys
|
|
53
|
+
i18ntk --command=complete
|
|
63
54
|
```
|
|
64
55
|
|
|
65
|
-
|
|
56
|
+
## Command Model (v2)
|
|
66
57
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Primary CLI commands:
|
|
58
|
+
Primary CLI:
|
|
70
59
|
|
|
71
60
|
```bash
|
|
61
|
+
i18ntk
|
|
72
62
|
i18ntk --command=init
|
|
73
63
|
i18ntk --command=analyze
|
|
74
64
|
i18ntk --command=validate
|
|
@@ -80,7 +70,7 @@ i18ntk --command=summary
|
|
|
80
70
|
i18ntk --command=debug
|
|
81
71
|
```
|
|
82
72
|
|
|
83
|
-
Standalone
|
|
73
|
+
Standalone executables:
|
|
84
74
|
|
|
85
75
|
```bash
|
|
86
76
|
i18ntk-init
|
|
@@ -96,19 +86,8 @@ i18ntk-fixer
|
|
|
96
86
|
i18ntk-backup
|
|
97
87
|
```
|
|
98
88
|
|
|
99
|
-
Backup helper:
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
i18ntk-backup --help
|
|
103
|
-
i18ntk-backup create ./locales
|
|
104
|
-
i18ntk-backup list
|
|
105
|
-
i18ntk-backup restore <backup-file>
|
|
106
|
-
```
|
|
107
|
-
|
|
108
89
|
## Common Flags
|
|
109
90
|
|
|
110
|
-
Most commands support:
|
|
111
|
-
|
|
112
91
|
- `--source-dir <path>`
|
|
113
92
|
- `--i18n-dir <path>`
|
|
114
93
|
- `--output-dir <path>`
|
|
@@ -120,13 +99,13 @@ Most commands support:
|
|
|
120
99
|
|
|
121
100
|
## Configuration
|
|
122
101
|
|
|
123
|
-
i18ntk reads project settings from `.i18ntk-config` in
|
|
102
|
+
i18ntk reads project settings from `.i18ntk-config` in your project root.
|
|
124
103
|
|
|
125
104
|
Example:
|
|
126
105
|
|
|
127
106
|
```json
|
|
128
107
|
{
|
|
129
|
-
"version": "2.
|
|
108
|
+
"version": "2.2.0",
|
|
130
109
|
"sourceDir": "./locales",
|
|
131
110
|
"i18nDir": "./locales",
|
|
132
111
|
"outputDir": "./i18ntk-reports",
|
|
@@ -155,7 +134,7 @@ setLanguage('fr');
|
|
|
155
134
|
console.log(getLanguage());
|
|
156
135
|
```
|
|
157
136
|
|
|
158
|
-
##
|
|
137
|
+
## Documentation
|
|
159
138
|
|
|
160
139
|
- [Documentation Index](docs/README.md)
|
|
161
140
|
- [API Reference](docs/api/API_REFERENCE.md)
|
|
@@ -163,8 +142,7 @@ console.log(getLanguage());
|
|
|
163
142
|
- [Runtime API Guide](docs/runtime.md)
|
|
164
143
|
- [Scanner Guide](docs/scanner-guide.md)
|
|
165
144
|
- [Environment Variables](docs/environment-variables.md)
|
|
166
|
-
- [Migration Guide v2.
|
|
167
|
-
- [Release Runbook](DEVUPDATE.md)
|
|
145
|
+
- [Migration Guide v2.2.0](docs/migration-guide-v2.2.0.md)
|
|
168
146
|
|
|
169
147
|
## License
|
|
170
148
|
|
package/main/i18ntk-analyze.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* I18NTK TRANSLATION ANALYSIS SCRIPT
|
|
4
4
|
*
|
|
@@ -27,7 +27,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
27
27
|
}
|
|
28
28
|
})();
|
|
29
29
|
|
|
30
|
-
loadTranslations(
|
|
30
|
+
loadTranslations('en', path.resolve(__dirname, '..', 'ui-locales'));
|
|
31
31
|
|
|
32
32
|
const PROJECT_ROOT = process.cwd();
|
|
33
33
|
|
|
@@ -67,7 +67,7 @@ class I18nAnalyzer {
|
|
|
67
67
|
this.config = { ...baseConfig, ...(this.config || {}) };
|
|
68
68
|
|
|
69
69
|
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
70
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
70
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
71
71
|
|
|
72
72
|
this.sourceDir = this.config.sourceDir;
|
|
73
73
|
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
@@ -143,6 +143,17 @@ class I18nAnalyzer {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// Get all available languages
|
|
146
|
+
isValidLanguageCode(code) {
|
|
147
|
+
if (!code || typeof code !== 'string') return false;
|
|
148
|
+
return /^[a-z]{2}(?:-[A-Za-z0-9]{2,8})*$/i.test(code.trim());
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
isExcludedLanguageDirectory(name) {
|
|
152
|
+
if (!name || typeof name !== 'string') return true;
|
|
153
|
+
const lowered = name.toLowerCase();
|
|
154
|
+
return lowered.startsWith('backup-') || lowered === 'backup' || lowered === 'reports' || lowered === 'i18ntk-reports';
|
|
155
|
+
}
|
|
156
|
+
|
|
146
157
|
getAvailableLanguages() {
|
|
147
158
|
try {
|
|
148
159
|
const items = SecurityUtils.safeReaddirSync(this.sourceDir, process.cwd(), { withFileTypes: true });
|
|
@@ -157,7 +168,18 @@ class I18nAnalyzer {
|
|
|
157
168
|
const directories = items
|
|
158
169
|
.filter(item => item.isDirectory())
|
|
159
170
|
.map(item => item.name)
|
|
160
|
-
.filter(name =>
|
|
171
|
+
.filter(name =>
|
|
172
|
+
name !== 'node_modules' &&
|
|
173
|
+
!name.startsWith('.') &&
|
|
174
|
+
name !== this.config.sourceLanguage &&
|
|
175
|
+
!this.isExcludedLanguageDirectory(name) &&
|
|
176
|
+
this.isValidLanguageCode(name)
|
|
177
|
+
)
|
|
178
|
+
.filter(name => {
|
|
179
|
+
const dirPath = path.join(this.sourceDir, name);
|
|
180
|
+
const dirItems = SecurityUtils.safeReaddirSync(dirPath, process.cwd(), { withFileTypes: true }) || [];
|
|
181
|
+
return dirItems.some(item => item.isFile() && item.name.endsWith('.json'));
|
|
182
|
+
});
|
|
161
183
|
|
|
162
184
|
// Check for monolith files (language.json files)
|
|
163
185
|
const files = items
|
|
@@ -170,31 +192,14 @@ class I18nAnalyzer {
|
|
|
170
192
|
// Add monolith files as languages (without .json extension)
|
|
171
193
|
const monolithLanguages = files
|
|
172
194
|
.map(file => file.replace('.json', ''))
|
|
173
|
-
.filter(lang =>
|
|
195
|
+
.filter(lang =>
|
|
196
|
+
!languages.includes(lang) &&
|
|
197
|
+
lang !== this.config.sourceLanguage &&
|
|
198
|
+
!this.isExcludedLanguageDirectory(lang) &&
|
|
199
|
+
this.isValidLanguageCode(lang)
|
|
200
|
+
);
|
|
174
201
|
languages.push(...monolithLanguages);
|
|
175
|
-
|
|
176
|
-
// Check for nested structures
|
|
177
|
-
for (const dir of directories) {
|
|
178
|
-
const dirPath = path.join(this.sourceDir, dir);
|
|
179
|
-
try {
|
|
180
|
-
const dirItems = SecurityUtils.safeReaddirSync(dirPath, process.cwd(), { withFileTypes: true });
|
|
181
|
-
if (dirItems) {
|
|
182
|
-
const jsonFiles = dirItems
|
|
183
|
-
.filter(item => item.isFile() && item.name.endsWith('.json'))
|
|
184
|
-
.map(item => item.name.replace('.json', ''));
|
|
185
|
-
|
|
186
|
-
// If directory contains JSON files, it's likely a language directory
|
|
187
|
-
if (jsonFiles.length > 0) {
|
|
188
|
-
if (!languages.includes(dir)) {
|
|
189
|
-
languages.push(dir);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
} catch (error) {
|
|
194
|
-
// Skip directories we can't read
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
202
|
+
|
|
198
203
|
return [...new Set(languages)].sort();
|
|
199
204
|
} catch (error) {
|
|
200
205
|
console.error('Error reading source directory:', error.message);
|
|
@@ -707,7 +712,6 @@ try {
|
|
|
707
712
|
throw new Error(t('analyze.failedToWriteReportFile') || 'Failed to write report file securely');
|
|
708
713
|
}
|
|
709
714
|
|
|
710
|
-
console.log(`Report saved to: ${reportPath}`);
|
|
711
715
|
return reportPath;
|
|
712
716
|
|
|
713
717
|
} catch (error) {
|
|
@@ -775,20 +779,21 @@ try {
|
|
|
775
779
|
console.log(t('analyze.analyzing', { language }) || `\nš Analyzing ${language}...`);
|
|
776
780
|
}
|
|
777
781
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
782
|
+
const analysis = this.analyzeLanguage(language);
|
|
783
|
+
const report = this.generateLanguageReport(analysis);
|
|
784
|
+
|
|
785
|
+
// Save report
|
|
786
|
+
const reportPath = await this.saveReport(language, report);
|
|
787
|
+
const processedCount = results.length + 1;
|
|
788
|
+
|
|
789
|
+
if (!args.json) {
|
|
790
|
+
console.log(t('analyze.completed', { language }) || `ā
Analysis completed for ${language}`);
|
|
791
|
+
console.log(t('analyze.progress', {
|
|
792
|
+
translated: processedCount,
|
|
793
|
+
total: languages.length
|
|
794
|
+
}) || ` Progress: ${processedCount}/${languages.length} languages processed`);
|
|
795
|
+
console.log(t('analyze.reportSaved', { reportPath }) || ` Report saved: ${reportPath}`);
|
|
796
|
+
}
|
|
792
797
|
|
|
793
798
|
results.push({
|
|
794
799
|
language,
|
|
@@ -884,7 +889,7 @@ try {
|
|
|
884
889
|
this.config = { ...baseConfig, ...this.config };
|
|
885
890
|
|
|
886
891
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
887
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
892
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
888
893
|
|
|
889
894
|
this.sourceDir = this.config.sourceDir;
|
|
890
895
|
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
package/main/i18ntk-complete.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* I18NTK TRANSLATION COMPLETION SCRIPT
|
|
4
4
|
*
|
|
@@ -19,15 +19,15 @@ const { loadTranslations, t } = require('../utils/i18n-helper');
|
|
|
19
19
|
const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
|
|
20
20
|
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
21
21
|
|
|
22
|
-
// Ensure setup is complete before running, except for help output.
|
|
23
|
-
(async () => {
|
|
24
|
-
const isHelpRequest = process.argv.slice(2).some(arg => arg === '--help' || arg === '-h');
|
|
25
|
-
if (isHelpRequest) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
await SetupEnforcer.checkSetupCompleteAsync();
|
|
30
|
-
} catch (error) {
|
|
22
|
+
// Ensure setup is complete before running, except for help output.
|
|
23
|
+
(async () => {
|
|
24
|
+
const isHelpRequest = process.argv.slice(2).some(arg => arg === '--help' || arg === '-h');
|
|
25
|
+
if (isHelpRequest) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
await SetupEnforcer.checkSetupCompleteAsync();
|
|
30
|
+
} catch (error) {
|
|
31
31
|
console.error('Setup check failed:', error.message);
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
@@ -37,7 +37,7 @@ loadTranslations();
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
class I18nCompletionTool {
|
|
40
|
+
class I18nCompletionTool {
|
|
41
41
|
constructor(config = {}) {
|
|
42
42
|
this.config = config;
|
|
43
43
|
this.sourceDir = null;
|
|
@@ -109,57 +109,57 @@ class I18nCompletionTool {
|
|
|
109
109
|
parsed.autoTranslate = true;
|
|
110
110
|
} else if (key === 'dry-run') {
|
|
111
111
|
parsed.dryRun = true;
|
|
112
|
-
} else if (key === 'no-prompt') {
|
|
113
|
-
parsed.noPrompt = true;
|
|
114
|
-
} else if (key === 'help' || key === 'h') {
|
|
115
|
-
parsed.help = true;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
});
|
|
112
|
+
} else if (key === 'no-prompt') {
|
|
113
|
+
parsed.noPrompt = true;
|
|
114
|
+
} else if (key === 'help' || key === 'h') {
|
|
115
|
+
parsed.help = true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
119
|
|
|
120
120
|
return parsed;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
// Get all available languages
|
|
124
|
-
getAvailableLanguages() {
|
|
123
|
+
// Get all available languages
|
|
124
|
+
getAvailableLanguages() {
|
|
125
125
|
if (!SecurityUtils.safeExistsSync(this.sourceDir, this.config.projectRoot)) {
|
|
126
126
|
throw new Error(`Source directory not found: ${this.sourceDir}`);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// Check for monolith JSON files (en.json, es.json, etc.)
|
|
130
|
-
const files = SecurityUtils.safeReaddirSync(this.sourceDir, this.config.projectRoot);
|
|
131
|
-
const languagesFromFiles = files
|
|
132
|
-
.filter(file => file.endsWith('.json'))
|
|
133
|
-
.map(file => path.basename(file, '.json'))
|
|
134
|
-
.filter(code => this.isValidLanguageCode(code));
|
|
135
|
-
|
|
136
|
-
// Also check for directory-based structure for backward compatibility
|
|
137
|
-
const directories = SecurityUtils.safeReaddirSync(this.sourceDir, this.config.projectRoot)
|
|
138
|
-
.filter(item => {
|
|
139
|
-
if (this.isExcludedLanguageDirectory(item)) return false;
|
|
140
|
-
if (!this.isValidLanguageCode(item)) return false;
|
|
141
|
-
const itemPath = path.join(this.sourceDir, item);
|
|
142
|
-
const stat = SecurityUtils.safeStatSync(itemPath, this.config.projectRoot);
|
|
143
|
-
if (!stat || !stat.isDirectory()) return false;
|
|
144
|
-
|
|
145
|
-
const localeFiles = SecurityUtils.safeReaddirSync(itemPath, this.config.projectRoot)
|
|
146
|
-
.filter(name => name.endsWith('.json'));
|
|
147
|
-
return localeFiles.length > 0;
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
return [...new Set([...languagesFromFiles, ...directories])];
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
isValidLanguageCode(code) {
|
|
154
|
-
if (!code || typeof code !== 'string') return false;
|
|
155
|
-
return /^[a-z]{2}(?:-[A-Za-z0-9]{2,8})*$/i.test(code.trim());
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
isExcludedLanguageDirectory(name) {
|
|
159
|
-
if (!name || typeof name !== 'string') return true;
|
|
160
|
-
const lowered = name.toLowerCase();
|
|
161
|
-
return lowered.startsWith('backup-') || lowered === 'backup' || lowered === 'reports' || lowered === 'i18ntk-reports';
|
|
162
|
-
}
|
|
130
|
+
const files = SecurityUtils.safeReaddirSync(this.sourceDir, this.config.projectRoot);
|
|
131
|
+
const languagesFromFiles = files
|
|
132
|
+
.filter(file => file.endsWith('.json'))
|
|
133
|
+
.map(file => path.basename(file, '.json'))
|
|
134
|
+
.filter(code => this.isValidLanguageCode(code));
|
|
135
|
+
|
|
136
|
+
// Also check for directory-based structure for backward compatibility
|
|
137
|
+
const directories = SecurityUtils.safeReaddirSync(this.sourceDir, this.config.projectRoot)
|
|
138
|
+
.filter(item => {
|
|
139
|
+
if (this.isExcludedLanguageDirectory(item)) return false;
|
|
140
|
+
if (!this.isValidLanguageCode(item)) return false;
|
|
141
|
+
const itemPath = path.join(this.sourceDir, item);
|
|
142
|
+
const stat = SecurityUtils.safeStatSync(itemPath, this.config.projectRoot);
|
|
143
|
+
if (!stat || !stat.isDirectory()) return false;
|
|
144
|
+
|
|
145
|
+
const localeFiles = SecurityUtils.safeReaddirSync(itemPath, this.config.projectRoot)
|
|
146
|
+
.filter(name => name.endsWith('.json'));
|
|
147
|
+
return localeFiles.length > 0;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return [...new Set([...languagesFromFiles, ...directories])];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
isValidLanguageCode(code) {
|
|
154
|
+
if (!code || typeof code !== 'string') return false;
|
|
155
|
+
return /^[a-z]{2}(?:-[A-Za-z0-9]{2,8})*$/i.test(code.trim());
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
isExcludedLanguageDirectory(name) {
|
|
159
|
+
if (!name || typeof name !== 'string') return true;
|
|
160
|
+
const lowered = name.toLowerCase();
|
|
161
|
+
return lowered.startsWith('backup-') || lowered === 'backup' || lowered === 'reports' || lowered === 'i18ntk-reports';
|
|
162
|
+
}
|
|
163
163
|
|
|
164
164
|
// Get all JSON files from a language directory
|
|
165
165
|
getLanguageFiles(language) {
|
|
@@ -481,7 +481,8 @@ class I18nCompletionTool {
|
|
|
481
481
|
this.config = { ...baseConfig, ...(this.config || {}) };
|
|
482
482
|
|
|
483
483
|
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
484
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
484
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
485
|
+
this.sourceDir = this.config.sourceDir;
|
|
485
486
|
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
486
487
|
} else {
|
|
487
488
|
await this.initialize();
|
|
@@ -552,28 +553,28 @@ class I18nCompletionTool {
|
|
|
552
553
|
console.log(t("complete.languagesProcessed", { languagesProcessed: languages.length }));
|
|
553
554
|
console.log(t("complete.missingKeysAdded", { missingKeysAdded: missingKeys.length }));
|
|
554
555
|
|
|
555
|
-
if (!args.dryRun && allChanges.length > 0 && !args.noPrompt) {
|
|
556
|
-
const rl = this.rl || this.initReadline();
|
|
557
|
-
const answer = await this.prompt('\n' + t('complete.generateReportPrompt') + ' (Y/N): ');
|
|
558
|
-
|
|
559
|
-
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
560
|
-
await this.generateReport(allChanges, languages);
|
|
556
|
+
if (!args.dryRun && allChanges.length > 0 && !args.noPrompt) {
|
|
557
|
+
const rl = this.rl || this.initReadline();
|
|
558
|
+
const answer = await this.prompt('\n' + t('complete.generateReportPrompt') + ' (Y/N): ');
|
|
559
|
+
|
|
560
|
+
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
561
|
+
await this.generateReport(allChanges, languages);
|
|
561
562
|
}
|
|
562
563
|
}
|
|
563
564
|
|
|
564
|
-
if (!args.dryRun) {
|
|
565
|
-
console.log('\n' + t("complete.nextStepsTitle"));
|
|
566
|
-
console.log(t("complete.separator"));
|
|
567
|
-
console.log('1. Run usage analysis:');
|
|
568
|
-
console.log(' i18ntk --command=usage');
|
|
569
|
-
console.log('2. Validate translations:');
|
|
570
|
-
console.log(' i18ntk --command=validate');
|
|
571
|
-
console.log('3. Analyze translation status:');
|
|
572
|
-
console.log(' i18ntk --command=analyze');
|
|
573
|
-
console.log('\n' + t("complete.allKeysAvailable"));
|
|
574
|
-
} else {
|
|
575
|
-
console.log('\n' + t("complete.runWithoutDryRun"));
|
|
576
|
-
}
|
|
565
|
+
if (!args.dryRun) {
|
|
566
|
+
console.log('\n' + t("complete.nextStepsTitle"));
|
|
567
|
+
console.log(t("complete.separator"));
|
|
568
|
+
console.log('1. Run usage analysis:');
|
|
569
|
+
console.log(' i18ntk --command=usage');
|
|
570
|
+
console.log('2. Validate translations:');
|
|
571
|
+
console.log(' i18ntk --command=validate');
|
|
572
|
+
console.log('3. Analyze translation status:');
|
|
573
|
+
console.log(' i18ntk --command=analyze');
|
|
574
|
+
console.log('\n' + t("complete.allKeysAvailable"));
|
|
575
|
+
} else {
|
|
576
|
+
console.log('\n' + t("complete.runWithoutDryRun"));
|
|
577
|
+
}
|
|
577
578
|
|
|
578
579
|
// Only prompt when run from the menu (i.e., when a callback or menu context is present)
|
|
579
580
|
if (typeof this.prompt === "function" && args.fromMenu) {
|
|
@@ -605,4 +606,4 @@ if (require.main === module) {
|
|
|
605
606
|
});
|
|
606
607
|
}
|
|
607
608
|
|
|
608
|
-
module.exports = I18nCompletionTool;
|
|
609
|
+
module.exports = I18nCompletionTool;
|
package/main/i18ntk-fixer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* I18NTK TRANSLATION FIXER SCRIPT
|
|
@@ -27,7 +27,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
27
27
|
}
|
|
28
28
|
})();
|
|
29
29
|
|
|
30
|
-
loadTranslations('en', path.resolve(__dirname, '..', '
|
|
30
|
+
loadTranslations('en', path.resolve(__dirname, '..', 'ui-locales'));
|
|
31
31
|
|
|
32
32
|
class I18nFixer {
|
|
33
33
|
constructor(config = {}) {
|
|
@@ -51,7 +51,7 @@ class I18nFixer {
|
|
|
51
51
|
this.config = { ...baseConfig, ...(this.config || {}) };
|
|
52
52
|
|
|
53
53
|
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
54
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
54
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
55
55
|
|
|
56
56
|
this.sourceDir = this.config.sourceDir;
|
|
57
57
|
this.outputDir = this.config.outputDir;
|
package/main/i18ntk-init.js
CHANGED
|
@@ -1122,7 +1122,7 @@ class I18nInitializer {
|
|
|
1122
1122
|
|
|
1123
1123
|
try {
|
|
1124
1124
|
// Import locale optimizer directly
|
|
1125
|
-
const LocaleOptimizer = require('../
|
|
1125
|
+
const LocaleOptimizer = require('../utils/locale-optimizer');
|
|
1126
1126
|
|
|
1127
1127
|
// First run dry run to show current state
|
|
1128
1128
|
console.log('\nš Running locale optimization preview...');
|
|
@@ -1139,12 +1139,12 @@ class I18nInitializer {
|
|
|
1139
1139
|
console.log('\nā
Package optimization completed!');
|
|
1140
1140
|
} else {
|
|
1141
1141
|
console.log('\nš” You can run locale optimization later with:');
|
|
1142
|
-
console.log(' node
|
|
1142
|
+
console.log(' node utils/locale-optimizer.js --interactive');
|
|
1143
1143
|
}
|
|
1144
1144
|
} catch (error) {
|
|
1145
1145
|
console.log('\nā ļø Could not offer locale optimization:', error.message);
|
|
1146
1146
|
console.log('\nš” You can run locale optimization later with:');
|
|
1147
|
-
console.log(' node
|
|
1147
|
+
console.log(' node utils/locale-optimizer.js --interactive');
|
|
1148
1148
|
}
|
|
1149
1149
|
} catch (error) {
|
|
1150
1150
|
console.log('\nā ļø Could not offer locale optimization:', error.message);
|
|
@@ -1178,7 +1178,7 @@ class I18nInitializer {
|
|
|
1178
1178
|
// Load translations for UI messages
|
|
1179
1179
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
1180
1180
|
const { loadTranslations } = require('../utils/i18n-helper');
|
|
1181
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
1181
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
1182
1182
|
}
|
|
1183
1183
|
|
|
1184
1184
|
// Setup is now handled centrally by config manager
|
|
@@ -1261,7 +1261,7 @@ class I18nInitializer {
|
|
|
1261
1261
|
// Load translations for UI messages
|
|
1262
1262
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
1263
1263
|
const { loadTranslations } = require('../utils/i18n-helper');
|
|
1264
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
1264
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
1265
1265
|
|
|
1266
1266
|
// Skip i18n framework check in non-interactive mode
|
|
1267
1267
|
console.log('Running initialization in non-interactive mode...');
|
package/main/i18ntk-scanner.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* I18NTK TEXT SCANNER
|
|
@@ -47,7 +47,7 @@ class I18nTextScanner {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
loadLocale() {
|
|
50
|
-
const uiLocalesDir = path.join(__dirname, '..', '
|
|
50
|
+
const uiLocalesDir = path.join(__dirname, '..', 'ui-locales');
|
|
51
51
|
const localeFile = path.join(uiLocalesDir, 'en.json');
|
|
52
52
|
|
|
53
53
|
try {
|