i18ntk 2.1.0 → 2.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.
Files changed (73) hide show
  1. package/README.md +87 -50
  2. package/main/i18ntk-analyze.js +63 -63
  3. package/main/i18ntk-backup-class.js +37 -41
  4. package/main/i18ntk-backup.js +28 -30
  5. package/main/i18ntk-complete.js +75 -74
  6. package/main/i18ntk-doctor.js +7 -6
  7. package/main/i18ntk-fixer.js +3 -3
  8. package/main/i18ntk-init.js +49 -13
  9. package/main/i18ntk-scanner.js +2 -2
  10. package/main/i18ntk-sizing.js +36 -37
  11. package/main/i18ntk-summary.js +4 -4
  12. package/main/i18ntk-ui.js +95 -96
  13. package/main/i18ntk-usage.js +31 -19
  14. package/main/i18ntk-validate.js +78 -27
  15. package/main/manage/commands/AnalyzeCommand.js +71 -73
  16. package/main/manage/commands/CommandRouter.js +15 -12
  17. package/main/manage/commands/FixerCommand.js +94 -38
  18. package/main/manage/commands/ScannerCommand.js +2 -2
  19. package/main/manage/commands/ValidateCommand.js +87 -36
  20. package/main/manage/index.js +165 -152
  21. package/main/manage/managers/DebugMenu.js +6 -6
  22. package/main/manage/managers/InteractiveMenu.js +6 -6
  23. package/main/manage/managers/LanguageMenu.js +12 -6
  24. package/main/manage/managers/SettingsMenu.js +6 -6
  25. package/main/manage/services/AuthenticationService.js +5 -6
  26. package/main/manage/services/ConfigurationService.js +22 -34
  27. package/main/manage/services/FileManagementService.js +6 -6
  28. package/main/manage/services/InitService.js +44 -8
  29. package/main/manage/services/UsageService.js +24 -12
  30. package/package.json +21 -42
  31. package/settings/settings-cli.js +5 -5
  32. package/settings/settings-manager.js +984 -968
  33. package/ui-locales/de.json +12 -11
  34. package/ui-locales/en.json +12 -11
  35. package/ui-locales/es.json +12 -11
  36. package/ui-locales/fr.json +12 -11
  37. package/ui-locales/ja.json +12 -11
  38. package/ui-locales/ru.json +12 -11
  39. package/ui-locales/zh.json +12 -11
  40. package/utils/config-helper.js +27 -16
  41. package/utils/config-manager.js +8 -7
  42. package/utils/i18n-helper.js +161 -166
  43. package/utils/init-helper.js +3 -2
  44. package/utils/json-output.js +11 -10
  45. package/{scripts → utils}/locale-optimizer.js +61 -60
  46. package/utils/logger.js +4 -4
  47. package/utils/safe-json.js +3 -3
  48. package/utils/secure-backup.js +8 -7
  49. package/utils/setup-enforcer.js +63 -98
  50. package/main/i18ntk-go.js +0 -283
  51. package/main/i18ntk-java.js +0 -380
  52. package/main/i18ntk-js.js +0 -512
  53. package/main/i18ntk-manage.js +0 -1694
  54. package/main/i18ntk-php.js +0 -462
  55. package/main/i18ntk-py.js +0 -379
  56. package/main/i18ntk-settings.js +0 -23
  57. package/main/manage/index-fixed.js +0 -1447
  58. package/scripts/build-lite.js +0 -279
  59. package/scripts/deprecate-versions.js +0 -317
  60. package/scripts/export-translations.js +0 -84
  61. package/scripts/fix-all-i18n.js +0 -236
  62. package/scripts/fix-and-purify-i18n.js +0 -233
  63. package/scripts/fix-locale-control-chars.js +0 -110
  64. package/scripts/lint-locales.js +0 -80
  65. package/scripts/prepublish-dev.js +0 -221
  66. package/scripts/prepublish.js +0 -362
  67. package/scripts/security-check.js +0 -117
  68. package/scripts/sync-translations.js +0 -151
  69. package/scripts/sync-ui-locales.js +0 -20
  70. package/scripts/validate-all-translations.js +0 -195
  71. package/scripts/verify-deprecations.js +0 -157
  72. package/scripts/verify-translations.js +0 -63
  73. package/utils/security-fixed.js +0 -609
@@ -1,39 +1,39 @@
1
1
  // utils/i18n-helper.js
2
- const path = require('path');
3
- const fs = require('fs');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
4
 
5
5
  // Lazy load SecurityUtils to prevent circular dependencies
6
6
  let securityUtils;
7
- function getSecurityUtils() {
8
- if (!securityUtils) {
9
- try {
10
- securityUtils = require('./security');
11
- } catch (error) {
12
- // Fallback: use basic fs operations if SecurityUtils is not available
13
- return {
14
- safeExistsSync: (targetPath) => {
15
- try {
16
- require('fs').accessSync(targetPath);
17
- return true;
18
- } catch {
19
- return false;
20
- }
21
- },
22
- safeWriteFileSync: (targetPath, data, _basePath, encoding = 'utf8') => {
23
- try {
24
- return require('fs').writeFileSync(targetPath, data, encoding);
25
- } catch {
26
- return null;
27
- }
28
- },
29
- safeReadFileSync: (targetPath, _basePath, encoding = 'utf8') => {
30
- try {
31
- return require('fs').readFileSync(targetPath, encoding);
32
- } catch {
33
- return null;
34
- }
35
- }
36
- };
7
+ function getSecurityUtils() {
8
+ if (!securityUtils) {
9
+ try {
10
+ securityUtils = require('./security');
11
+ } catch (error) {
12
+ // Fallback: use basic fs operations if SecurityUtils is not available
13
+ return {
14
+ safeExistsSync: (targetPath) => {
15
+ try {
16
+ require('fs').accessSync(targetPath);
17
+ return true;
18
+ } catch {
19
+ return false;
20
+ }
21
+ },
22
+ safeWriteFileSync: (targetPath, data, _basePath, encoding = 'utf8') => {
23
+ try {
24
+ return require('fs').writeFileSync(targetPath, data, encoding);
25
+ } catch {
26
+ return null;
27
+ }
28
+ },
29
+ safeReadFileSync: (targetPath, _basePath, encoding = 'utf8') => {
30
+ try {
31
+ return require('fs').readFileSync(targetPath, encoding);
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+ };
37
37
  }
38
38
  }
39
39
  return securityUtils;
@@ -50,105 +50,100 @@ function safeRequireConfig() {
50
50
  try { return require('./config-manager'); } catch { return null; }
51
51
  }
52
52
 
53
- function stripBOMAndComments(s) {
54
- if (s.charCodeAt(0) === 0xFEFF) s = s.slice(1);
55
- s = s.replace(/\/\*[\s\S]*?\*\//g, '');
56
- s = s.replace(/^\s*\/\/.*$/mg, '');
57
- return s;
58
- }
59
-
60
- function getValidationBase(targetPath) {
61
- const fallbackBase = path.resolve(__dirname, '..');
62
- if (!targetPath || typeof targetPath !== 'string') {
63
- return fallbackBase;
64
- }
65
-
66
- let current = path.resolve(path.dirname(targetPath));
67
- while (true) {
68
- try {
69
- if (fs.statSync(current).isDirectory()) {
70
- return current;
71
- }
72
- } catch {
73
- // Continue upward until we find an existing directory.
74
- }
75
-
76
- const parent = path.dirname(current);
77
- if (parent === current) {
78
- break;
79
- }
80
- current = parent;
81
- }
82
-
83
- return fallbackBase;
84
- }
85
-
86
- function safeReadFile(targetPath, encoding = 'utf8') {
87
- const SecurityUtils = getSecurityUtils();
88
- return SecurityUtils.safeReadFileSync(targetPath, getValidationBase(targetPath), encoding);
89
- }
90
-
91
- function readJsonSafe(file) {
92
- const raw = safeReadFile(file, 'utf8');
93
- if (raw === null) {
94
- throw new Error(`Unable to read JSON file: ${file}`);
95
- }
96
- return JSON.parse(stripBOMAndComments(raw));
97
- }
53
+ function stripBOMAndComments(s) {
54
+ if (s.charCodeAt(0) === 0xFEFF) s = s.slice(1);
55
+ s = s.replace(/\/\*[\s\S]*?\*\//g, '');
56
+ s = s.replace(/^\s*\/\/.*$/mg, '');
57
+ return s;
58
+ }
59
+
60
+ function getValidationBase(targetPath) {
61
+ const fallbackBase = path.resolve(__dirname, '..');
62
+ if (!targetPath || typeof targetPath !== 'string') {
63
+ return fallbackBase;
64
+ }
65
+
66
+ let current = path.resolve(path.dirname(targetPath));
67
+ while (true) {
68
+ try {
69
+ if (fs.statSync(current).isDirectory()) {
70
+ return current;
71
+ }
72
+ } catch {
73
+ // Continue upward until we find an existing directory.
74
+ }
75
+
76
+ const parent = path.dirname(current);
77
+ if (parent === current) {
78
+ break;
79
+ }
80
+ current = parent;
81
+ }
82
+
83
+ return fallbackBase;
84
+ }
85
+
86
+ function safeReadFile(targetPath, encoding = 'utf8') {
87
+ const SecurityUtils = getSecurityUtils();
88
+ return SecurityUtils.safeReadFileSync(targetPath, getValidationBase(targetPath), encoding);
89
+ }
90
+
91
+ function readJsonSafe(file) {
92
+ const raw = safeReadFile(file, 'utf8');
93
+ if (raw === null) {
94
+ throw new Error(`Unable to read JSON file: ${file}`);
95
+ }
96
+ return JSON.parse(stripBOMAndComments(raw));
97
+ }
98
98
 
99
99
  function pkgUiLocalesDirViaThisFile() {
100
100
  return path.resolve(__dirname, '..', 'ui-locales');
101
101
  }
102
102
 
103
- function pkgUiLocalesDirViaResolve() {
103
+ function pkgUiLocalesDirViaResolve() {
104
104
  try {
105
105
  // Try the new correct path first (ui-locales/en.json)
106
106
  const enJson = require.resolve('i18ntk/ui-locales/en.json');
107
107
  return path.dirname(enJson);
108
108
  } catch {
109
- try {
110
- // Fallback to the old incorrect path for backward compatibility
111
- const enJson = require.resolve('i18ntk/resources/i18n/ui-locales/en.json');
112
- return path.dirname(enJson);
113
- } catch { return null; }
114
- }
115
- }
116
-
117
- function legacyResourcesUiLocalesDir() {
118
- return path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
119
- }
120
-
121
- function resolveLocalesDirs(preferredDir) {
122
- const SecurityUtils = getSecurityUtils();
123
- const dirs = [];
124
- const addDir = (dir) => {
125
- if (typeof dir === 'string' && dir.trim()) {
126
- try {
127
- const normalized = path.normalize(path.resolve(dir.trim()));
128
- const stats = SecurityUtils.safeStatSync(normalized, getValidationBase(normalized));
129
- if (stats && stats.isDirectory()) {
130
- dirs.push(normalized);
131
- }
132
- } catch {
133
- // Silently ignore invalid paths
134
- }
135
- }
136
- };
137
-
138
- addDir(preferredDir);
139
-
140
- const pkgA = pkgUiLocalesDirViaThisFile();
141
- addDir(pkgA);
142
-
143
- const pkgB = pkgUiLocalesDirViaResolve();
144
- if (pkgB && pkgB !== pkgA) {
145
- addDir(pkgB);
146
- }
147
-
148
- const legacyDir = legacyResourcesUiLocalesDir();
149
- if (legacyDir !== pkgA && legacyDir !== pkgB) {
150
- addDir(legacyDir);
151
- }
109
+ try {
110
+ // Fallback to the old incorrect path for backward compatibility
111
+ const enJson = require.resolve('i18ntk/resources/i18n/ui-locales/en.json');
112
+ return path.dirname(enJson);
113
+ } catch { return null; }
114
+ }
115
+ }
116
+
117
+ // Removed legacyResourcesUiLocalesDir as resources/i18n/ui-locales is deprecated
118
+
119
+ function resolveLocalesDirs(preferredDir) {
120
+ const SecurityUtils = getSecurityUtils();
121
+ const dirs = [];
122
+ const addDir = (dir) => {
123
+ if (typeof dir === 'string' && dir.trim()) {
124
+ try {
125
+ const normalized = path.normalize(path.resolve(dir.trim()));
126
+ const stats = SecurityUtils.safeStatSync(normalized, getValidationBase(normalized));
127
+ if (stats && stats.isDirectory()) {
128
+ dirs.push(normalized);
129
+ }
130
+ } catch {
131
+ // Silently ignore invalid paths
132
+ }
133
+ }
134
+ };
135
+
136
+ addDir(preferredDir);
137
+
138
+ const pkgA = pkgUiLocalesDirViaThisFile();
139
+ addDir(pkgA);
140
+
141
+ const pkgB = pkgUiLocalesDirViaResolve();
142
+ if (pkgB && pkgB !== pkgA) {
143
+ addDir(pkgB);
144
+ }
145
+
146
+ // Removed legacy directory fallback
152
147
 
153
148
  // Deduplicate while preserving order
154
149
  const seen = new Set();
@@ -166,9 +161,9 @@ function candidatesForLang(dir, lang) {
166
161
  ];
167
162
  }
168
163
 
169
- function findLocaleFilesAllDirs(lang, preferredDir) {
170
- const SecurityUtils = getSecurityUtils();
171
- const dirs = resolveLocalesDirs(preferredDir);
164
+ function findLocaleFilesAllDirs(lang, preferredDir) {
165
+ const SecurityUtils = getSecurityUtils();
166
+ const dirs = resolveLocalesDirs(preferredDir);
172
167
 
173
168
  if (process.env.I18NTK_DEBUG_LOCALES === '1') {
174
169
  console.log('🔎 i18ntk locale search dirs:', dirs);
@@ -177,29 +172,29 @@ function findLocaleFilesAllDirs(lang, preferredDir) {
177
172
  const files = [];
178
173
  const errors = [];
179
174
 
180
- for (const dir of dirs) {
181
- for (const candidate of candidatesForLang(dir, lang)) {
182
- try {
183
- const stats = SecurityUtils.safeStatSync(candidate, getValidationBase(candidate));
184
- if (stats && stats.isFile() && stats.size > 0) {
185
- // Validate file is readable and parseable
186
- fs.accessSync(candidate, fs.constants.R_OK);
187
- // Quick JSON validation
188
- const content = safeReadFile(candidate, 'utf8');
189
- if (content) {
190
- if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
191
- files.push(candidate);
192
- } else {
193
- errors.push({ file: candidate, error: 'Invalid JSON format' });
194
- }
195
- } else {
196
- errors.push({ file: candidate, error: 'Empty or unreadable file' });
197
- }
198
- }
199
- } catch (error) {
200
- errors.push({ file: candidate, error: error.message });
201
- }
202
- }
175
+ for (const dir of dirs) {
176
+ for (const candidate of candidatesForLang(dir, lang)) {
177
+ try {
178
+ const stats = SecurityUtils.safeStatSync(candidate, getValidationBase(candidate));
179
+ if (stats && stats.isFile() && stats.size > 0) {
180
+ // Validate file is readable and parseable
181
+ fs.accessSync(candidate, fs.constants.R_OK);
182
+ // Quick JSON validation
183
+ const content = safeReadFile(candidate, 'utf8');
184
+ if (content) {
185
+ if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
186
+ files.push(candidate);
187
+ } else {
188
+ errors.push({ file: candidate, error: 'Invalid JSON format' });
189
+ }
190
+ } else {
191
+ errors.push({ file: candidate, error: 'Empty or unreadable file' });
192
+ }
193
+ }
194
+ } catch (error) {
195
+ errors.push({ file: candidate, error: error.message });
196
+ }
197
+ }
203
198
  }
204
199
 
205
200
  if (process.env.I18NTK_DEBUG_LOCALES === '1' && errors.length > 0) {
@@ -214,7 +209,7 @@ let currentLanguage = 'en';
214
209
  let isInitialized = false;
215
210
  const missingWarned = new Set();
216
211
 
217
- function loadTranslations(language) {
212
+ function loadTranslations(language) {
218
213
  const cfg = safeRequireConfig();
219
214
  const settings = cfg?.getConfig?.() || {};
220
215
  const configuredLanguage = settings.uiLanguage || settings.language;
@@ -226,10 +221,10 @@ function loadTranslations(language) {
226
221
 
227
222
  const loadErrors = [];
228
223
 
229
- const preferredDir = arguments.length > 1 ? arguments[1] : null;
230
-
231
- for (const lang of tryOrder) {
232
- const files = findLocaleFilesAllDirs(lang, preferredDir);
224
+ const preferredDir = arguments.length > 1 ? arguments[1] : null;
225
+
226
+ for (const lang of tryOrder) {
227
+ const files = findLocaleFilesAllDirs(lang, preferredDir);
233
228
 
234
229
  // Prioritize bundled locales over project ones
235
230
  const prioritizedFiles = files.sort((a, b) => Number(isBundledPath(b)) - Number(isBundledPath(a)));
@@ -402,20 +397,20 @@ function getCurrentLanguage() {
402
397
  function getAvailableLanguages() {
403
398
  const dirs = resolveLocalesDirs();
404
399
  const langs = new Set();
405
- for (const d of dirs) {
406
- try {
407
- const SecurityUtils = getSecurityUtils();
408
- if (!SecurityUtils.safeExistsSync(d, getValidationBase(d))) continue;
409
- for (const f of fs.readdirSync(d)) {
410
- if (f.endsWith('.json')) langs.add(path.basename(f, '.json'));
411
- }
412
- for (const f of fs.readdirSync(d, { withFileTypes: true })) {
413
- const nestedPath = path.join(d, f.name, `${f.name}.json`);
414
- if (f.isDirectory() && SecurityUtils.safeExistsSync(nestedPath, getValidationBase(nestedPath))) {
415
- langs.add(f.name);
416
- }
417
- }
418
- } catch {}
400
+ for (const d of dirs) {
401
+ try {
402
+ const SecurityUtils = getSecurityUtils();
403
+ if (!SecurityUtils.safeExistsSync(d, getValidationBase(d))) continue;
404
+ for (const f of fs.readdirSync(d)) {
405
+ if (f.endsWith('.json')) langs.add(path.basename(f, '.json'));
406
+ }
407
+ for (const f of fs.readdirSync(d, { withFileTypes: true })) {
408
+ const nestedPath = path.join(d, f.name, `${f.name}.json`);
409
+ if (f.isDirectory() && SecurityUtils.safeExistsSync(nestedPath, getValidationBase(nestedPath))) {
410
+ langs.add(f.name);
411
+ }
412
+ }
413
+ } catch {}
419
414
  }
420
415
  return Array.from(langs.size ? langs : new Set(['en']));
421
416
  }
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const configManager = require('./config-manager');
4
4
  const SecurityUtils = require('./security');
5
+ const packageJson = require('../package.json');
5
6
 
6
7
  function ensureDirectory(dirPath) {
7
8
  if (!dirPath || typeof dirPath !== 'string') return;
@@ -46,7 +47,7 @@ function hasSourceLanguageFiles(sourceDir, sourceLanguage) {
46
47
  */
47
48
  async function checkInitialized(options = {}) {
48
49
  const settings = configManager.getConfig ? configManager.getConfig() : {};
49
- const currentVersion = require('../package.json').version;
50
+ const currentVersion = packageJson.version;
50
51
  const projectConfigPath = configManager.CONFIG_PATH || path.join(process.cwd(), '.i18ntk-config');
51
52
  const configDir = path.dirname(projectConfigPath);
52
53
 
@@ -135,7 +136,7 @@ async function markAsInitialized(config) {
135
136
  const projectConfigPath = configManager.CONFIG_PATH || path.join(process.cwd(), '.i18ntk-config');
136
137
  const configDir = path.dirname(projectConfigPath);
137
138
  const initFilePath = path.join(configDir, 'initialization.json');
138
- const currentVersion = require('../package.json').version;
139
+ const currentVersion = packageJson.version;
139
140
  const now = new Date().toISOString();
140
141
  const sourceDir = config?.sourceDir || settings.sourceDir || './locales';
141
142
  const sourceLanguage = config?.sourceLanguage || settings.sourceLanguage || 'en';
@@ -1,6 +1,8 @@
1
- /**
2
- * JSON Output Utility for i18ntk commands
3
- * Provides consistent machine-readable output format for CI/CD integration
1
+ const packageJson = require('../package.json');
2
+
3
+ /**
4
+ * JSON Output Utility for i18ntk commands
5
+ * Provides consistent machine-readable output format for CI/CD integration
4
6
  */
5
7
 
6
8
  class JsonOutput {
@@ -21,12 +23,11 @@ class JsonOutput {
21
23
  };
22
24
  }
23
25
 
24
- getPackageVersion() {
25
- try {
26
- const packageJson = require('../package.json');
27
- return packageJson.version;
28
- } catch (error) {
29
- return '1.8.3';
26
+ getPackageVersion() {
27
+ try {
28
+ return packageJson.version;
29
+ } catch (error) {
30
+ return '1.8.3';
30
31
  }
31
32
  }
32
33
 
@@ -96,4 +97,4 @@ class JsonOutput {
96
97
  }
97
98
  }
98
99
 
99
- module.exports = JsonOutput;
100
+ module.exports = JsonOutput;