i18ntk 1.7.0 → 1.7.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/README.md CHANGED
@@ -2,19 +2,18 @@
2
2
 
3
3
  ![i18ntk Logo](docs/screenshots/i18ntk-logo-public.PNG)
4
4
 
5
- **Version:** 1.7.0
5
+ **Version:** 1.7.2
6
6
  **Last Updated:** 2025-08-10
7
7
  **GitHub Repository:** [vladnoskv/i18ntk](https://github.com/vladnoskv/i18ntk)
8
8
 
9
- [![npm](https://img.shields.io/npm/dt/i18ntk.svg)](https://www.npmjs.com/package/i18ntk) [![npm version](https://badge.fury.io/js/i18ntk.svg)](https://badge.fury.io/js/i18ntk) [![Node.js Version](https://img.shields.io/badge/node-%3E%3D16.0.0-brightgreen.svg)](https://nodejs.org/) [![Downloads](https://img.shields.io/npm/dm/i18ntk.svg)](https://www.npmjs.com/package/i18ntk) [![GitHub stars](https://img.shields.io/github/stars/vladnoskv/i18ntk?style=social)](https://github.com/vladnoskv/i18ntk)
9
+ [![npm](https://img.shields.io/npm/dt/i18ntk.svg)](https://www.npmjs.com/package/i18ntk) [![npm version](https://badge.fury.io/js/i18ntk.svg)](https://badge.fury.io/js/i18ntk) [![Node.js Version](https://img.shields.io/badge/node-%3E%3D16.0.0-brightgreen.svg)](https://nodejs.org/) [![Downloads](https://img.shields.io/npm/dm/i18ntk.svg)](https://www.npmjs.com/package/i18ntk) [![GitHub stars](https://img.shields.io/github/stars/vladnoskv/i18ntk?style=social)](https://github.com/vladnoskv/i18ntk)
10
+ [![Socket Badge](https://socket.dev/api/badge/npm/package/i18ntk/1.7.1)](https://socket.dev/npm/package/i18ntk/overview/1.7.2)
10
11
 
11
12
  **🚀 The fastest way to manage translations across any framework or vanilla JavaScript projects**
12
13
 
13
- **Framework Support:** Auto-detects popular libraries (React i18next, Vue i18n, i18next, Nuxt i18n, Svelte i18n) or works without a framework. i18ntk manages translation files and validation—it does NOT implement translations on pages.
14
+ **Framework Support:** Auto-detects popular libraries (React i18next, Vue i18n, i18next, Nuxt i18n, Svelte i18n) or works without a framework. i18ntk manages translation files and validation—it does NOT implement translation logic like i18next or Vue i18n.
14
15
 
15
- > **Zero dependencies** | **Works with any framework** | **Enterprise-grade security**
16
-
17
- > **v1.7.0** – 97% speed improvement (**15.38ms** for 200k keys), new watch helper, and a lite English-only UI locale framework; up to 86% package size reduction, zero runtime dependencies, enhanced security with PIN protection, and comprehensive edge case handling.
16
+ > **v1.7.2** Enhanced security logging, flexible 4-6 digit PIN authentication, configuration stability improvements, **fixed framework detection prompt**, and CI/CD silent mode support; maintains 97% speed improvement (**15.38ms** for 200k keys up to 5/M keys per second).
18
17
 
19
18
  ## 🚀 Quick Start
20
19
 
@@ -54,6 +53,7 @@ i18ntk validate --source ./locales
54
53
  - **Framework Support**: Auto-detects React i18next, Vue i18n, Angular, Next i18next, Nuxt i18next, Svelte i18n
55
54
  - **Memory Optimization**: 67% memory reduction with streaming processing
56
55
  - **Scalability**: Linear scaling up to 5M keys with ultra-extreme settings
56
+ - **Smart Framework Detection**: Automatically skips unnecessary prompts when i18n frameworks are detected
57
57
 
58
58
  ### 📸 Screenshots
59
59
 
@@ -88,7 +88,7 @@ Configuration is managed through the `settings/i18ntk-config.json` file:
88
88
 
89
89
  ```json
90
90
  {
91
- "version": "1.7.0",
91
+ "version": "1.7.2",
92
92
  "sourceDir": "./locales",
93
93
  "outputDir": "./i18ntk-reports",
94
94
  "defaultLanguage": "en",
@@ -176,6 +176,7 @@ const i18n = createI18n({ locale: 'en', messages: translations });
176
176
  - **Encrypted Backups**: AES-256 encrypted backup storage
177
177
 
178
178
  ### 🎯 **NEW INTERACTIVE LOCALE OPTIMIZER** - up to 86% Package Size Reduction
179
+
179
180
  - **Package Size**: 830.4KB → 115.3KB (86% reduction for English only)
180
181
  - **Smart Management**: Interactive selection with automatic backups
181
182
  - **Zero Breaking Changes**: Safe restoration from backups
@@ -199,51 +200,10 @@ your-project/
199
200
 
200
201
  - **Locale files are backed up automatically** before optimization
201
202
  - **Use interactive optimizer** for safe locale management
202
- - **Zero breaking changes** from v1.6.x to v1.7.0
203
- - **All versions prior to 1.7.0 are deprecated**
203
+ - **Zero breaking changes** from v1.6.x to v1.7.1
204
+ - **All versions prior to 1.7.1 are deprecated**
204
205
  - **All improvements applied automatically** on update
205
206
 
206
- ## 📞 Support
207
-
208
- - **Issues**: [GitHub Issues](https://github.com/vladnoskv/i18ntk/issues)
209
- - **Documentation**: [Complete docs](./docs)
210
- - **Performance**: [Benchmark results](./benchmarks/results)
211
- - **Version**: `i18ntk --version`
212
-
213
- ---
214
-
215
- **Made for the global development community** ❤️
216
-
217
- ## Migration Guide
218
-
219
- ### Upgrading from Deprecated Versions
220
-
221
- #### From any version < 1.7.0 (DEPRECATED - use latest version)
222
- 1. **Backup your current configuration**:
223
- ```bash
224
- cp -r ./.i18ntk ./.i18ntk-backup-$(date +%Y%m%d)
225
- ```
226
-
227
- 2. **Install the latest version**:
228
- ```bash
229
- npm install i18ntk@1.7.0
230
- ```
231
-
232
- 3. **Verify installation**:
233
- ```bash
234
- i18ntk --version
235
- i18ntk doctor
236
- ```
237
-
238
- 3. **Run configuration migration**:
239
- ```bash
240
- npx i18ntk@1.6.3 --migrate
241
- ```
242
-
243
- 4. **Verify installation**:
244
- ```bash
245
- npx i18ntk@1.6.3--validate
246
- ```
247
207
 
248
208
  #### Preserved Features from 1.6.3
249
209
  - ✅ Ultra-extreme performance improvements
@@ -256,9 +216,12 @@ your-project/
256
216
  #### Breaking Changes
257
217
  - **None** - 1.6.3 is fully backward compatible
258
218
 
259
- ### Migration Support
260
- If you encounter issues during migration:
261
- 1. Check the [troubleshooting guide](docs/TROUBLESHOOTING.md)
262
- 2. Open an issue on [GitHub](https://github.com/vladnoskv/i18ntk/issues)
263
- 3. Join our [Discord community](https://discord.gg/i18ntk)
219
+ ## 📞 Support
220
+
221
+ - **Issues**: [GitHub Issues](https://github.com/vladnoskv/i18ntk/issues)
222
+ - **Documentation**: [Complete docs](./docs)
223
+ - **Performance**: [Benchmark results](./benchmarks/results)
224
+ - **Version**: `i18ntk --version`
225
+ ---
264
226
 
227
+ **Made for the global development community** ❤️
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * I18N TRANSLATION ANALYSIS SCRIPT
4
4
  *
@@ -11,7 +11,7 @@ const fs = require('fs');
11
11
  const path = require('path');
12
12
  const cliHelper = require('../utils/cli-helper');
13
13
  const { loadTranslations, t } = require('../utils/i18n-helper');
14
- loadTranslations(process.env.I18NTK_LANG || 'en');
14
+ loadTranslations(process.env.I18NTK_LANG);
15
15
  const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
16
16
  const SecurityUtils = require('../utils/security');
17
17
  const AdminCLI = require('../utils/admin-cli');
@@ -14,7 +14,7 @@ const fs = require('fs');
14
14
  const path = require('path');
15
15
  const { spawnSync } = require('child_process');
16
16
  const { loadTranslations, t } = require('../utils/i18n-helper');
17
- loadTranslations(process.env.I18NTK_LANG || 'en');
17
+ loadTranslations(process.env.I18NTK_LANG);
18
18
  const { getUnifiedConfig, parseCommonArgs, displayHelp, ensureInitialized } = require('../utils/config-helper');
19
19
  const SecurityUtils = require('../utils/security');
20
20
  const configManager = require('../utils/config-manager');
@@ -17,7 +17,7 @@ const { execSync } = require('child_process');
17
17
  const SecurityUtils = require('../utils/security');
18
18
  const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
19
19
  const { loadTranslations, t } = require('../utils/i18n-helper');
20
- loadTranslations(process.env.I18NTK_LANG || 'en');
20
+ loadTranslations(process.env.I18NTK_LANG);
21
21
  const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
22
22
 
23
23
 
@@ -20,7 +20,7 @@ const AdminAuth = require('../utils/admin-auth');
20
20
  const { loadTranslations, t } = require('../utils/i18n-helper');
21
21
  // Ensure UIi18n is available for this initializer class
22
22
  const UIi18n = require('./i18ntk-ui');
23
- loadTranslations(process.env.I18NTK_LANG || 'en');
23
+ loadTranslations(process.env.I18NTK_LANG);
24
24
  const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
25
25
  const { showFrameworkWarningOnce } = require('../utils/cli-helper');
26
26
 
@@ -66,15 +66,16 @@ class I18nInitializer {
66
66
  // No longer create readline interface here - use CLI helpers
67
67
  this.rl = null;
68
68
  this.shouldCloseRL = false;
69
+ this.announcedExistingDir = false;
69
70
  }
70
71
 
71
- // Add the missing checkI18nDependencies method
72
+ // Updated checkI18nDependencies method that uses configuration
72
73
  async checkI18nDependencies(noPrompt = false) {
73
74
  const packageJsonPath = path.resolve('./package.json');
74
75
 
75
76
  if (!fs.existsSync(packageJsonPath)) {
76
77
  console.log(t('init.noPackageJson'));
77
- return await this.promptContinueWithoutI18n(noPrompt);
78
+ return true; // Allow to continue without framework
78
79
  }
79
80
 
80
81
  try {
@@ -100,14 +101,33 @@ class I18nInitializer {
100
101
 
101
102
  if (installedFrameworks.length > 0) {
102
103
  console.log(t('init.detectedI18nFrameworks', { frameworks: installedFrameworks.join(', ') }));
104
+ const cfg = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
105
+ cfg.framework = cfg.framework || {};
106
+ cfg.framework.detected = true;
107
+ cfg.framework.installed = installedFrameworks;
108
+ if (configManager.saveSettings) {
109
+ configManager.saveSettings(cfg);
110
+ } else if (configManager.saveConfig) {
111
+ configManager.saveConfig(cfg);
112
+ }
103
113
  return true;
104
114
  } else {
105
- showFrameworkWarningOnce(this.ui);
106
- return await this.promptContinueWithoutI18n(noPrompt);
115
+ const cfg = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
116
+ if (cfg.framework) {
117
+ cfg.framework.detected = false;
118
+ if (configManager.saveSettings) {
119
+ configManager.saveSettings(cfg);
120
+ } else if (configManager.saveConfig) {
121
+ configManager.saveConfig(cfg);
122
+ }
123
+ }
124
+ // Framework detection is now handled by maybePromptFramework in i18ntk-manage.js
125
+ // Skip prompting here to avoid double prompts
126
+ return true;
107
127
  }
108
128
  } catch (error) {
109
129
  console.log(t('init.errors.packageJsonRead'));
110
- return await this.promptContinueWithoutI18n(noPrompt);
130
+ return true; // Allow to continue on error
111
131
  }
112
132
  }
113
133
 
@@ -239,7 +259,10 @@ class I18nInitializer {
239
259
 
240
260
  if (selectedIndex >= 0 && selectedIndex < existingLocations.length) {
241
261
  const selectedDir = existingLocations[selectedIndex];
242
- console.log(t('init.usingExistingDirectory', { dir: selectedDir }));
262
+ if (!this.announcedExistingDir) {
263
+ console.log(t('init.usingExistingDirectory', { dir: selectedDir }));
264
+ this.announcedExistingDir = true;
265
+ }
243
266
 
244
267
  this.config.sourceDir = selectedDir;
245
268
  this.sourceDir = path.resolve(selectedDir);
@@ -733,7 +756,10 @@ class I18nInitializer {
733
756
  this.config.sourceDir = selectedDir;
734
757
  this.sourceDir = path.resolve(selectedDir);
735
758
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
736
- console.log(t('init.usingExistingDirectory', { dir: selectedDir }));
759
+ if (!this.announcedExistingDir) {
760
+ console.log(t('init.usingExistingDirectory', { dir: selectedDir }));
761
+ this.announcedExistingDir = true;
762
+ }
737
763
  } else {
738
764
  await this.setupInitialStructure(args.noPrompt);
739
765
  }
@@ -907,7 +933,14 @@ class I18nInitializer {
907
933
  try {
908
934
  // Parse command line arguments
909
935
  const args = this.parseArgs();
910
-
936
+
937
+ // On first run, prompt user for preferred UI language
938
+ if (!fs.existsSync(configManager.CONFIG_PATH)) {
939
+ const { getGlobalReadline } = require('../utils/cli');
940
+ getGlobalReadline();
941
+ const selectedLang = await this.ui.selectLanguage();
942
+ loadTranslations(selectedLang);
943
+ }
911
944
  // Initialize configuration properly when called from menu
912
945
  if (fromMenu && !this.sourceDir) {
913
946
  const baseConfig = await getUnifiedConfig('init', args);
@@ -34,9 +34,174 @@ const I18nSizingAnalyzer = require('./i18ntk-sizing');
34
34
  const SettingsCLI = require('../settings/settings-cli');
35
35
  const I18nDebugger = require('../scripts/debug/debugger');
36
36
 
37
- const { loadTranslations, t } = require('../utils/i18n-helper');
38
- loadTranslations(process.env.I18NTK_LANG || 'en');
37
+ const { loadTranslations, t, refreshLanguageFromSettings} = require('../utils/i18n-helper');
39
38
  const cliHelper = require('../utils/cli-helper');
39
+ const { loadConfig, saveConfig, ensureConfigDefaults } = require('../utils/config');
40
+ const pkg = require('../package.json');
41
+
42
+ async function runInitFlow(rl) {
43
+ const initializer = new I18nInitializer();
44
+ await initializer.run({ fromMenu: true });
45
+ const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
46
+ return { i18nDir: settings.i18nDir, sourceDir: settings.sourceDir };
47
+ }
48
+
49
+ function askYesNo(rl, prompt) {
50
+ return new Promise(res => {
51
+ rl.question(prompt, a => res(/^y(es)?$/i.test(a.trim())));
52
+ });
53
+ }
54
+
55
+ async function ensureInitializedOrExit(rl) {
56
+ const path = require('path');
57
+ const fs = require('fs');
58
+ const { ensureDirectory } = require('../utils/config-helper');
59
+ const settingsManager = require('../settings/settings-manager');
60
+
61
+ // Get configuration from settings manager
62
+ const settings = settingsManager.getAllSettings();
63
+
64
+ const cfg = {
65
+ sourceDir: path.resolve(settings.sourceDir || './locales'),
66
+ sourceLanguage: settings.sourceLanguage || 'en',
67
+ projectRoot: path.resolve('.'),
68
+ framework: settings.framework || { detected: false, prompt: 'always' }
69
+ };
70
+
71
+ // Check if already initialized using new tracking system
72
+ const initFilePath = path.join(settingsManager.configDir, 'initialization.json');
73
+
74
+ let isInitialized = false;
75
+ if (fs.existsSync(initFilePath)) {
76
+ try {
77
+ const initStatus = JSON.parse(fs.readFileSync(initFilePath, 'utf8'));
78
+ isInitialized = initStatus.initialized && initStatus.version === '1.7.2';
79
+ } catch (e) {
80
+ // Invalid init file, proceed with check
81
+ }
82
+ }
83
+
84
+ if (isInitialized) {
85
+ return cfg;
86
+ }
87
+
88
+ // Check if source language files exist
89
+ const langDir = path.join(cfg.sourceDir, cfg.sourceLanguage);
90
+
91
+ const hasLanguageFiles = fs.existsSync(langDir) &&
92
+ fs.readdirSync(langDir).some(f => f.endsWith('.json'));
93
+
94
+ // If language files exist, mark as initialized
95
+ if (hasLanguageFiles) {
96
+ const initDir = path.dirname(initFilePath);
97
+ ensureDirectory(initDir);
98
+ fs.writeFileSync(initFilePath, JSON.stringify({
99
+ initialized: true,
100
+ version: '1.7.2',
101
+ timestamp: new Date().toISOString(),
102
+ sourceDir: cfg.sourceDir,
103
+ sourceLanguage: cfg.sourceLanguage
104
+ }, null, 2));
105
+ return cfg;
106
+ }
107
+
108
+ const answer = await askYesNo(rl, 'Initialization Required\nThis project must be initialized before running this command.\nWould you like to run initialization now? (y/N): ');
109
+ if (!answer) {
110
+ console.log('Operation cancelled.');
111
+ process.exit(0);
112
+ }
113
+
114
+ const result = await runInitFlow(rl);
115
+
116
+ // Mark as initialized after successful init
117
+ const initDir = path.dirname(initFilePath);
118
+ ensureDirectory(initDir);
119
+ fs.writeFileSync(initFilePath, JSON.stringify({
120
+ initialized: true,
121
+ version: '1.7.2',
122
+ timestamp: new Date().toISOString(),
123
+ sourceDir: result.sourceDir || cfg.sourceDir,
124
+ sourceLanguage: cfg.sourceLanguage
125
+ }, null, 2));
126
+
127
+ return {
128
+ ...cfg,
129
+ sourceDir: result.sourceDir || cfg.sourceDir,
130
+ i18nDir: result.i18nDir || cfg.i18nDir
131
+ };
132
+ }
133
+
134
+ async function maybePromptFramework(rl, cfg, currentVersion) {
135
+ // Load current settings to check framework configuration
136
+ const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
137
+
138
+ // Ensure framework configuration exists
139
+ if (!settings.framework) {
140
+ settings.framework = {
141
+ detected: false,
142
+ preference: null,
143
+ prompt: 'always',
144
+ lastPromptedVersion: null
145
+ };
146
+ }
147
+
148
+ // Reload settings to ensure we have latest framework detection results
149
+ const freshSettings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
150
+ if (freshSettings.framework) {
151
+ settings.framework = freshSettings.framework;
152
+ }
153
+
154
+ // Check if framework is already detected or preference is set to none
155
+ if (settings.framework.detected || settings.framework.preference === 'none') {
156
+ return cfg;
157
+ }
158
+
159
+ // Check if dnr (do not remind) is active for this version
160
+ if (settings.framework.prompt === 'suppress' && settings.framework.lastPromptedVersion === currentVersion) {
161
+ return cfg;
162
+ }
163
+
164
+ // Reset suppress if version changed
165
+ if (settings.framework.prompt === 'suppress' && settings.framework.lastPromptedVersion !== currentVersion) {
166
+ settings.framework.prompt = 'always';
167
+ }
168
+
169
+ if (settings.framework.prompt === 'always') {
170
+ const ans = await new Promise(res =>
171
+ rl.question([
172
+ t('init.suggestions.noFramework'),
173
+ t('init.frameworks.react'),
174
+ t('init.frameworks.vue'),
175
+ t('init.frameworks.i18next'),
176
+ t('init.frameworks.nuxt'),
177
+ t('init.frameworks.svelte'),
178
+ '',
179
+ t('init.continueWithoutI18nPrompt') + ' (y/N/dnr = do not remind until next update): '
180
+ ].join('\n'), a => res(a.trim().toLowerCase()))
181
+ );
182
+
183
+ if (ans === 'y' || ans === 'yes') {
184
+ settings.framework.preference = 'none';
185
+ settings.framework.prompt = 'always'; // Keep asking until explicitly suppressed
186
+ } else if (ans === 'dnr') {
187
+ settings.framework.preference = 'none';
188
+ settings.framework.prompt = 'suppress';
189
+ settings.framework.lastPromptedVersion = currentVersion;
190
+ } else {
191
+ console.log('Operation cancelled.');
192
+ process.exit(0);
193
+ }
194
+
195
+ // Save configuration using settings manager
196
+ if (configManager.saveSettings) {
197
+ configManager.saveSettings(settings);
198
+ } else if (configManager.saveConfig) {
199
+ configManager.saveConfig(settings);
200
+ }
201
+ }
202
+
203
+ return cfg;
204
+ }
40
205
 
41
206
  // Use unified configuration system
42
207
  const { getUnifiedConfig, ensureInitialized, validateSourceDir } = require('../utils/config-helper');
@@ -48,7 +213,7 @@ class I18nManager {
48
213
  this.isReadlineClosed = false;
49
214
  this.isAuthenticated = false;
50
215
  this.ui = null;
51
- this.adminAuth = null;
216
+ this.adminAuth = new AdminAuth();
52
217
 
53
218
  // No longer create readline interface here - use CLI helpers
54
219
  }
@@ -62,34 +227,29 @@ class I18nManager {
62
227
  // Initialize configuration using unified system
63
228
  async initialize() {
64
229
  try {
230
+ // Parse args here for other initialization needs (but language is already loaded)
65
231
  const args = this.parseArgs();
66
232
  if (args.help) {
67
233
  this.showHelp();
68
234
  process.exit(0);
69
235
  }
70
236
 
71
- const baseConfig = await getUnifiedConfig('manage', args);
72
- this.config = { ...baseConfig, ...this.config };
73
-
74
- // Initialize UI localization system after configuration is loaded
75
- this.ui = new UIi18n();
76
-
77
- // Initialize admin authentication
78
- this.adminAuth = new AdminAuth();
79
-
80
- // Load language from saved configuration, not just CLI args
81
- const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
82
- const uiLanguage = settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
83
- this.ui.loadLanguage(uiLanguage);
237
+ // Ensure UI is initialized (it should already be loaded in run())
238
+ if (!this.ui) {
239
+ const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
240
+ const uiLanguage = args.uiLanguage || settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
241
+ this.ui.loadLanguage(uiLanguage);
242
+ loadTranslations(uiLanguage);
243
+ }
84
244
 
85
245
  // Validate source directory exists
86
246
  const {validateSourceDir, displayPaths} = require('../utils/config-helper');
87
247
  try {
88
248
  validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
89
249
  } catch (err) {
90
- console.log(t('init.requiredTitle'));
91
- console.log(t('init.requiredBody'));
92
- const answer = await cliHelper.prompt(t('init.promptRunNow'));
250
+ console.log(this.ui.t('init.requiredTitle'));
251
+ console.log(this.ui.t('init.requiredBody'));
252
+ const answer = await cliHelper.prompt(this.ui.t('init.promptRunNow'));
93
253
  if (answer.trim().toLowerCase() === 'y') {
94
254
  const initializer = new I18nInitializer(this.config);
95
255
  await initializer.run({ fromMenu: true });
@@ -99,13 +259,14 @@ class I18nManager {
99
259
  }
100
260
 
101
261
  } catch (error) {
102
- console.error(`Error initializing i18n manager: ${error.message}`);
262
+
103
263
  throw error;
104
264
  }
105
265
  }
106
266
 
107
267
  // Auto-detect i18n directory from common locations only if not configured in settings
108
268
  detectI18nDirectory() {
269
+
109
270
  const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
110
271
  const projectRoot = path.resolve(settings.projectRoot || this.config.projectRoot || '.');
111
272
 
@@ -156,13 +317,13 @@ class I18nManager {
156
317
  }
157
318
  }
158
319
 
159
- // Check if i18n framework is installed
320
+ // Check if i18n framework is installed - configuration-based check without prompts
160
321
  async checkI18nDependencies() {
161
322
  const packageJsonPath = path.resolve('./package.json');
162
323
 
163
324
  if (!fs.existsSync(packageJsonPath)) {
164
- console.log(t('init.noPackageJson'));
165
- return await this.promptContinueWithoutI18n();
325
+ console.log(this.ui ? this.ui.t('init.noPackageJson') : 'No package.json found');
326
+ return true; // Allow to continue without framework
166
327
  }
167
328
 
168
329
  try {
@@ -185,22 +346,44 @@ class I18nManager {
185
346
  ];
186
347
 
187
348
  const installedFrameworks = i18nFrameworks.filter(framework => dependencies[framework]);
188
-
349
+
189
350
  if (installedFrameworks.length > 0) {
190
- t('init.detectedFrameworks', { frameworks: installedFrameworks.join(', ') });
351
+ if (this.ui && this.ui.t) {
352
+ console.log(this.ui.t('init.detectedFrameworks', { frameworks: installedFrameworks.join(', ') }));
353
+ } else {
354
+ console.log(`Detected frameworks: ${installedFrameworks.join(', ')}`);
355
+ }
356
+ const cfg = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
357
+ cfg.framework = cfg.framework || {};
358
+ cfg.framework.detected = true;
359
+ cfg.framework.installed = installedFrameworks;
360
+ if (configManager.saveSettings) {
361
+ configManager.saveSettings(cfg);
362
+ } else if (configManager.saveConfig) {
363
+ configManager.saveConfig(cfg);
364
+ }
191
365
  return true;
192
366
  } else {
193
367
  const cfg = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
194
- if (cfg.framework === 'none') {
368
+ if (cfg.framework) {
369
+ cfg.framework.detected = false;
370
+ if (configManager.saveSettings) {
371
+ configManager.saveSettings(cfg);
372
+ } else if (configManager.saveConfig) {
373
+ configManager.saveConfig(cfg);
374
+ }
375
+ }
376
+ // If framework preference is already set to 'none', skip warning
377
+ if (cfg.framework === 'none' || (cfg.framework && cfg.framework.preference === 'none')) {
195
378
  return true;
196
379
  }
197
- showFrameworkWarningOnce(this.ui);
198
380
 
199
- return await this.promptContinueWithoutI18n();
381
+ // Framework detection is handled by maybePromptFramework, so just return true here
382
+ return true;
200
383
  }
201
384
  } catch (error) {
202
385
  console.log(t('init.errors.packageJsonRead'));
203
- return await this.promptContinueWithoutI18n();
386
+ return true; // Allow to continue on error
204
387
  }
205
388
  }
206
389
 
@@ -208,7 +391,8 @@ class I18nManager {
208
391
  * Prompt user to continue without i18n framework
209
392
  */
210
393
  async promptContinueWithoutI18n() {
211
- const answer = await this.prompt('\n🤔 ' + t('init.continueWithoutI18nPrompt'));
394
+ const promptText = this.ui && this.ui.t ? this.ui.t('init.continueWithoutI18nPrompt') : 'Do you want to continue without one? (y/N)';
395
+ const answer = await this.prompt('\n' + promptText);
212
396
  return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
213
397
  }
214
398
 
@@ -259,11 +443,28 @@ class I18nManager {
259
443
  // Add this run method after the checkI18nDependencies method
260
444
  async run() {
261
445
  try {
446
+ // Parse command line arguments first
447
+ const args = this.parseArgs();
448
+
449
+ // Load settings to get language preference BEFORE any messages
450
+ const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
451
+
452
+ // Initialize UI localization system with language from settings
453
+ this.ui = new UIi18n();
454
+ const uiLanguage = args.uiLanguage || settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
455
+ this.ui.loadLanguage(uiLanguage);
456
+
457
+ const rl = cliHelper.getInterface();
458
+ const cfgAfterInitCheck = await ensureInitializedOrExit(rl);
459
+ await this.checkI18nDependencies();
460
+ await maybePromptFramework(rl, cfgAfterInitCheck, pkg.version);
461
+
462
+ // Update this.config with the configuration from ensureInitializedOrExit
463
+ this.config = { ...this.config, ...cfgAfterInitCheck };
464
+
262
465
  // Initialize configuration using unified system
263
466
  await this.initialize();
264
467
 
265
- // Parse command line arguments
266
- const args = this.parseArgs();
267
468
  const rawArgs = process.argv.slice(2); // Preserve original CLI args array for positional checks
268
469
  let commandToExecute = null;
269
470
 
@@ -301,13 +502,8 @@ class I18nManager {
301
502
  return;
302
503
  }
303
504
 
304
- // Check dependencies and exit if user chooses not to continue
305
- const shouldContinue = await this.checkI18nDependencies();
306
- if (!shouldContinue) {
307
- console.log(t('init.errorsNoFramework'));
308
- console.log(t('init.suggestions.installFramework'));
309
- process.exit(0);
310
- }
505
+ // Framework detection is now handled by maybePromptFramework above
506
+ // Skip the redundant checkI18nDependencies prompt
311
507
 
312
508
  // Interactive mode - showInteractiveMenu will handle the title
313
509
  await this.showInteractiveMenu();
@@ -891,7 +1087,7 @@ class I18nManager {
891
1087
  { path: path.join(process.cwd(), 'reports', 'backups'), name: 'Reports Backups', type: 'backups' },
892
1088
  { path: path.join(process.cwd(), 'scripts', 'debug', 'logs'), name: 'Debug Logs', type: 'logs' },
893
1089
  { path: path.join(process.cwd(), 'scripts', 'debug', 'reports'), name: 'Debug Reports', type: 'reports' },
894
- { path: path.join(process.cwd(), 'settings', 'backups'), name: 'Settings Backups', type: 'backups' },
1090
+ { path: path.join(configManager.configDir, 'backups'), name: 'Settings Backups', type: 'backups' },
895
1091
  { path: path.join(process.cwd(), 'utils', 'i18ntk-reports'), name: 'Utils Reports', type: 'reports' }
896
1092
  ];
897
1093
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * I18n Sizing Analyzer
@@ -35,7 +35,7 @@ const fs = require('fs');
35
35
  const path = require('path');
36
36
  const { performance } = require('perf_hooks');
37
37
  const { loadTranslations, t } = require('../utils/i18n-helper');
38
- loadTranslations(process.env.I18NTK_LANG || 'en');
38
+ loadTranslations(process.env.I18NTK_LANG);
39
39
  const configManager = require('../settings/settings-manager');
40
40
  const SecurityUtils = require('../utils/security');
41
41
  const { getUnifiedConfig } = require('../utils/config-helper');
@@ -1,9 +1,9 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { loadTranslations, t } = require('../utils/i18n-helper');
6
- loadTranslations(process.env.I18NTK_LANG || 'en');
6
+ loadTranslations(process.env.I18NTK_LANG);
7
7
  const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
8
8
  const SecurityUtils = require('../utils/security');
9
9
  const AdminCLI = require('../utils/admin-cli');