i18ntk 1.7.0 → 1.7.1

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.1
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.1)
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.1** Enhanced security logging, flexible 4-6 digit PIN authentication, configuration stability improvements, 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
 
@@ -88,7 +87,7 @@ Configuration is managed through the `settings/i18ntk-config.json` file:
88
87
 
89
88
  ```json
90
89
  {
91
- "version": "1.7.0",
90
+ "version": "1.7.1",
92
91
  "sourceDir": "./locales",
93
92
  "outputDir": "./i18ntk-reports",
94
93
  "defaultLanguage": "en",
@@ -176,6 +175,7 @@ const i18n = createI18n({ locale: 'en', messages: translations });
176
175
  - **Encrypted Backups**: AES-256 encrypted backup storage
177
176
 
178
177
  ### 🎯 **NEW INTERACTIVE LOCALE OPTIMIZER** - up to 86% Package Size Reduction
178
+
179
179
  - **Package Size**: 830.4KB → 115.3KB (86% reduction for English only)
180
180
  - **Smart Management**: Interactive selection with automatic backups
181
181
  - **Zero Breaking Changes**: Safe restoration from backups
@@ -199,51 +199,10 @@ your-project/
199
199
 
200
200
  - **Locale files are backed up automatically** before optimization
201
201
  - **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**
202
+ - **Zero breaking changes** from v1.6.x to v1.7.1
203
+ - **All versions prior to 1.7.1 are deprecated**
204
204
  - **All improvements applied automatically** on update
205
205
 
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
206
 
248
207
  #### Preserved Features from 1.6.3
249
208
  - ✅ Ultra-extreme performance improvements
@@ -256,9 +215,12 @@ your-project/
256
215
  #### Breaking Changes
257
216
  - **None** - 1.6.3 is fully backward compatible
258
217
 
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)
218
+ ## 📞 Support
219
+
220
+ - **Issues**: [GitHub Issues](https://github.com/vladnoskv/i18ntk/issues)
221
+ - **Documentation**: [Complete docs](./docs)
222
+ - **Performance**: [Benchmark results](./benchmarks/results)
223
+ - **Version**: `i18ntk --version`
224
+ ---
264
225
 
226
+ **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
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * I18N INITIALIZATION SCRIPT
4
4
  *
@@ -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 {
@@ -102,12 +103,13 @@ class I18nInitializer {
102
103
  console.log(t('init.detectedI18nFrameworks', { frameworks: installedFrameworks.join(', ') }));
103
104
  return true;
104
105
  } else {
105
- showFrameworkWarningOnce(this.ui);
106
- return await this.promptContinueWithoutI18n(noPrompt);
106
+ // Framework detection is now handled by maybePromptFramework in i18ntk-manage.js
107
+ // Skip prompting here to avoid double prompts
108
+ return true;
107
109
  }
108
110
  } catch (error) {
109
111
  console.log(t('init.errors.packageJsonRead'));
110
- return await this.promptContinueWithoutI18n(noPrompt);
112
+ return true; // Allow to continue on error
111
113
  }
112
114
  }
113
115
 
@@ -239,7 +241,10 @@ class I18nInitializer {
239
241
 
240
242
  if (selectedIndex >= 0 && selectedIndex < existingLocations.length) {
241
243
  const selectedDir = existingLocations[selectedIndex];
242
- console.log(t('init.usingExistingDirectory', { dir: selectedDir }));
244
+ if (!this.announcedExistingDir) {
245
+ console.log(t('init.usingExistingDirectory', { dir: selectedDir }));
246
+ this.announcedExistingDir = true;
247
+ }
243
248
 
244
249
  this.config.sourceDir = selectedDir;
245
250
  this.sourceDir = path.resolve(selectedDir);
@@ -733,7 +738,10 @@ class I18nInitializer {
733
738
  this.config.sourceDir = selectedDir;
734
739
  this.sourceDir = path.resolve(selectedDir);
735
740
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
736
- console.log(t('init.usingExistingDirectory', { dir: selectedDir }));
741
+ if (!this.announcedExistingDir) {
742
+ console.log(t('init.usingExistingDirectory', { dir: selectedDir }));
743
+ this.announcedExistingDir = true;
744
+ }
737
745
  } else {
738
746
  await this.setupInitialStructure(args.noPrompt);
739
747
  }
@@ -907,7 +915,14 @@ class I18nInitializer {
907
915
  try {
908
916
  // Parse command line arguments
909
917
  const args = this.parseArgs();
910
-
918
+
919
+ // On first run, prompt user for preferred UI language
920
+ if (!fs.existsSync(configManager.CONFIG_PATH)) {
921
+ const { getGlobalReadline } = require('../utils/cli');
922
+ getGlobalReadline();
923
+ const selectedLang = await this.ui.selectLanguage();
924
+ loadTranslations(selectedLang);
925
+ }
911
926
  // Initialize configuration properly when called from menu
912
927
  if (fromMenu && !this.sourceDir) {
913
928
  const baseConfig = await getUnifiedConfig('init', args);
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * I18N MANAGEMENT TOOLKIT - MAIN MANAGER
4
4
  *
@@ -34,9 +34,168 @@ 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(process.cwd(), 'settings', '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.1';
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.1',
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.1',
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
+ // Check if framework is already detected or preference is set to none
149
+ if (settings.framework.detected || settings.framework.preference === 'none') {
150
+ return cfg;
151
+ }
152
+
153
+ // Check if dnr (do not remind) is active for this version
154
+ if (settings.framework.prompt === 'suppress' && settings.framework.lastPromptedVersion === currentVersion) {
155
+ return cfg;
156
+ }
157
+
158
+ // Reset suppress if version changed
159
+ if (settings.framework.prompt === 'suppress' && settings.framework.lastPromptedVersion !== currentVersion) {
160
+ settings.framework.prompt = 'always';
161
+ }
162
+
163
+ if (settings.framework.prompt === 'always') {
164
+ const ans = await new Promise(res =>
165
+ rl.question([
166
+ t('init.suggestions.noFramework'),
167
+ t('init.frameworks.react'),
168
+ t('init.frameworks.vue'),
169
+ t('init.frameworks.i18next'),
170
+ t('init.frameworks.nuxt'),
171
+ t('init.frameworks.svelte'),
172
+ '',
173
+ t('init.continueWithoutI18nPrompt') + ' (y/N/dnr = do not remind until next update): '
174
+ ].join('\n'), a => res(a.trim().toLowerCase()))
175
+ );
176
+
177
+ if (ans === 'y' || ans === 'yes') {
178
+ settings.framework.preference = 'none';
179
+ settings.framework.prompt = 'always'; // Keep asking until explicitly suppressed
180
+ } else if (ans === 'dnr') {
181
+ settings.framework.preference = 'none';
182
+ settings.framework.prompt = 'suppress';
183
+ settings.framework.lastPromptedVersion = currentVersion;
184
+ } else {
185
+ console.log('Operation cancelled.');
186
+ process.exit(0);
187
+ }
188
+
189
+ // Save configuration using settings manager
190
+ if (configManager.saveSettings) {
191
+ configManager.saveSettings(settings);
192
+ } else if (configManager.saveConfig) {
193
+ configManager.saveConfig(settings);
194
+ }
195
+ }
196
+
197
+ return cfg;
198
+ }
40
199
 
41
200
  // Use unified configuration system
42
201
  const { getUnifiedConfig, ensureInitialized, validateSourceDir } = require('../utils/config-helper');
@@ -48,7 +207,7 @@ class I18nManager {
48
207
  this.isReadlineClosed = false;
49
208
  this.isAuthenticated = false;
50
209
  this.ui = null;
51
- this.adminAuth = null;
210
+ this.adminAuth = new AdminAuth();
52
211
 
53
212
  // No longer create readline interface here - use CLI helpers
54
213
  }
@@ -62,34 +221,29 @@ class I18nManager {
62
221
  // Initialize configuration using unified system
63
222
  async initialize() {
64
223
  try {
224
+ // Parse args here for other initialization needs (but language is already loaded)
65
225
  const args = this.parseArgs();
66
226
  if (args.help) {
67
227
  this.showHelp();
68
228
  process.exit(0);
69
229
  }
70
230
 
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);
231
+ // Ensure UI is initialized (it should already be loaded in run())
232
+ if (!this.ui) {
233
+ const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
234
+ const uiLanguage = args.uiLanguage || settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
235
+ this.ui.loadLanguage(uiLanguage);
236
+ loadTranslations(uiLanguage);
237
+ }
84
238
 
85
239
  // Validate source directory exists
86
240
  const {validateSourceDir, displayPaths} = require('../utils/config-helper');
87
241
  try {
88
242
  validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
89
243
  } catch (err) {
90
- console.log(t('init.requiredTitle'));
91
- console.log(t('init.requiredBody'));
92
- const answer = await cliHelper.prompt(t('init.promptRunNow'));
244
+ console.log(this.ui.t('init.requiredTitle'));
245
+ console.log(this.ui.t('init.requiredBody'));
246
+ const answer = await cliHelper.prompt(this.ui.t('init.promptRunNow'));
93
247
  if (answer.trim().toLowerCase() === 'y') {
94
248
  const initializer = new I18nInitializer(this.config);
95
249
  await initializer.run({ fromMenu: true });
@@ -99,13 +253,14 @@ class I18nManager {
99
253
  }
100
254
 
101
255
  } catch (error) {
102
- console.error(`Error initializing i18n manager: ${error.message}`);
256
+
103
257
  throw error;
104
258
  }
105
259
  }
106
260
 
107
261
  // Auto-detect i18n directory from common locations only if not configured in settings
108
262
  detectI18nDirectory() {
263
+
109
264
  const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
110
265
  const projectRoot = path.resolve(settings.projectRoot || this.config.projectRoot || '.');
111
266
 
@@ -156,13 +311,13 @@ class I18nManager {
156
311
  }
157
312
  }
158
313
 
159
- // Check if i18n framework is installed
314
+ // Check if i18n framework is installed - configuration-based check without prompts
160
315
  async checkI18nDependencies() {
161
316
  const packageJsonPath = path.resolve('./package.json');
162
317
 
163
318
  if (!fs.existsSync(packageJsonPath)) {
164
- console.log(t('init.noPackageJson'));
165
- return await this.promptContinueWithoutI18n();
319
+ console.log(this.ui ? this.ui.t('init.noPackageJson') : 'No package.json found');
320
+ return true; // Allow to continue without framework
166
321
  }
167
322
 
168
323
  try {
@@ -187,20 +342,27 @@ class I18nManager {
187
342
  const installedFrameworks = i18nFrameworks.filter(framework => dependencies[framework]);
188
343
 
189
344
  if (installedFrameworks.length > 0) {
190
- t('init.detectedFrameworks', { frameworks: installedFrameworks.join(', ') });
345
+ if (this.ui && this.ui.t) {
346
+ console.log(this.ui.t('init.detectedFrameworks', { frameworks: installedFrameworks.join(', ') }));
347
+ } else {
348
+ console.log(`Detected frameworks: ${installedFrameworks.join(', ')}`);
349
+ }
191
350
  return true;
192
351
  } else {
352
+ // Check configuration for framework preference
193
353
  const cfg = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
194
- if (cfg.framework === 'none') {
354
+
355
+ // If framework preference is already set to 'none', skip warning
356
+ if (cfg.framework === 'none' || (cfg.framework && cfg.framework.preference === 'none')) {
195
357
  return true;
196
358
  }
197
- showFrameworkWarningOnce(this.ui);
198
-
199
- return await this.promptContinueWithoutI18n();
359
+
360
+ // Framework detection is handled by maybePromptFramework, so just return true here
361
+ return true;
200
362
  }
201
363
  } catch (error) {
202
364
  console.log(t('init.errors.packageJsonRead'));
203
- return await this.promptContinueWithoutI18n();
365
+ return true; // Allow to continue on error
204
366
  }
205
367
  }
206
368
 
@@ -208,7 +370,8 @@ class I18nManager {
208
370
  * Prompt user to continue without i18n framework
209
371
  */
210
372
  async promptContinueWithoutI18n() {
211
- const answer = await this.prompt('\n🤔 ' + t('init.continueWithoutI18nPrompt'));
373
+ const promptText = this.ui && this.ui.t ? this.ui.t('init.continueWithoutI18nPrompt') : 'Do you want to continue without one? (y/N)';
374
+ const answer = await this.prompt('\n' + promptText);
212
375
  return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
213
376
  }
214
377
 
@@ -259,11 +422,27 @@ class I18nManager {
259
422
  // Add this run method after the checkI18nDependencies method
260
423
  async run() {
261
424
  try {
425
+ // Parse command line arguments first
426
+ const args = this.parseArgs();
427
+
428
+ // Load settings to get language preference BEFORE any messages
429
+ const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
430
+
431
+ // Initialize UI localization system with language from settings
432
+ this.ui = new UIi18n();
433
+ const uiLanguage = args.uiLanguage || settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
434
+ this.ui.loadLanguage(uiLanguage);
435
+
436
+ const rl = cliHelper.getInterface();
437
+ const cfgAfterInitCheck = await ensureInitializedOrExit(rl);
438
+ await maybePromptFramework(rl, cfgAfterInitCheck, pkg.version);
439
+
440
+ // Update this.config with the configuration from ensureInitializedOrExit
441
+ this.config = { ...this.config, ...cfgAfterInitCheck };
442
+
262
443
  // Initialize configuration using unified system
263
444
  await this.initialize();
264
445
 
265
- // Parse command line arguments
266
- const args = this.parseArgs();
267
446
  const rawArgs = process.argv.slice(2); // Preserve original CLI args array for positional checks
268
447
  let commandToExecute = null;
269
448
 
@@ -301,13 +480,8 @@ class I18nManager {
301
480
  return;
302
481
  }
303
482
 
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
- }
483
+ // Framework detection is now handled by maybePromptFramework above
484
+ // Skip the redundant checkI18nDependencies prompt
311
485
 
312
486
  // Interactive mode - showInteractiveMenu will handle the title
313
487
  await this.showInteractiveMenu();
@@ -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');
package/main/i18ntk-ui.js CHANGED
@@ -6,6 +6,7 @@
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
  const SettingsManager = require('../settings/settings-manager');
9
+ const legacyConfigManager = require('../utils/config-manager');
9
10
  const configManager = SettingsManager;
10
11
 
11
12
  class UIi18n {
@@ -223,6 +224,11 @@ this.translations = {};
223
224
  await configManager.updateConfig(settings);
224
225
  }
225
226
  }
227
+
228
+ // Keep legacy config-manager in sync for modules using it directly
229
+ if (legacyConfigManager && legacyConfigManager.updateConfig) {
230
+ await legacyConfigManager.updateConfig({ language, uiLanguage: language });
231
+ }
226
232
  } catch (error) {
227
233
  console.error(`Error saving language preference: ${error.message}`);
228
234
  }
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * I18N USAGE ANALYSIS TOOLKIT - Version 1.6.3
4
4
  *
@@ -25,7 +25,7 @@ const fs = require('fs');
25
25
  const path = require('path');
26
26
  const { loadTranslations, t } = require('../utils/i18n-helper');
27
27
  const { getGlobalReadline, closeGlobalReadline, askHidden } = require('../utils/cli');
28
- loadTranslations(process.env.I18NTK_LANG || 'en');
28
+ loadTranslations(process.env.I18NTK_LANG);
29
29
  const configManager = require('../utils/config-manager');
30
30
  const SecurityUtils = require('../utils/security');
31
31
  const AdminCLI = require('../utils/admin-cli');