@kikkimo/claude-launcher 1.0.0 → 2.0.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/CHANGELOG.md +55 -0
- package/README.md +100 -41
- package/claude-launcher +1017 -576
- package/docs/README-zh.md +104 -45
- package/lib/api-manager.js +449 -0
- package/lib/auth/password-input.js +144 -0
- package/lib/auth/password-strength.js +154 -0
- package/lib/auth/password-validator.js +255 -0
- package/lib/crypto.js +85 -0
- package/lib/i18n/formatter.js +62 -0
- package/lib/i18n/index.js +218 -0
- package/lib/i18n/language-manager.js +160 -0
- package/lib/i18n/locales/de.js +523 -0
- package/lib/i18n/locales/en.js +524 -0
- package/lib/i18n/locales/es.js +523 -0
- package/lib/i18n/locales/fr.js +523 -0
- package/lib/i18n/locales/it.js +523 -0
- package/lib/i18n/locales/ja.js +523 -0
- package/lib/i18n/locales/ko.js +523 -0
- package/lib/i18n/locales/pt.js +523 -0
- package/lib/i18n/locales/ru.js +523 -0
- package/lib/i18n/locales/zh-TW.js +523 -0
- package/lib/i18n/locales/zh.js +523 -0
- package/lib/launcher.js +253 -0
- package/lib/presets/providers.js +104 -0
- package/lib/ui/colors.js +32 -0
- package/lib/ui/interactive-table.js +260 -0
- package/lib/ui/menu.js +314 -0
- package/lib/ui/prompts.js +540 -0
- package/lib/utils/string-width.js +180 -0
- package/lib/utils/version-checker.js +240 -0
- package/lib/validators.js +130 -0
- package/package.json +2 -2
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internationalization (i18n) Main Module
|
|
3
|
+
* Provides translation and localization services
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const LanguageManager = require('./language-manager');
|
|
7
|
+
const MessageFormatter = require('./formatter');
|
|
8
|
+
|
|
9
|
+
class I18n {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.languageManager = new LanguageManager();
|
|
12
|
+
this.formatter = MessageFormatter;
|
|
13
|
+
this._cache = new Map(); // Cache for translated strings
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Translate a message key to localized text
|
|
18
|
+
* @param {string} key - Translation key (dot notation: 'menu.main.title')
|
|
19
|
+
* @param {...any} args - Arguments for placeholder formatting
|
|
20
|
+
* @returns {Promise<string>} - Translated and formatted text
|
|
21
|
+
*/
|
|
22
|
+
async t(key, ...args) {
|
|
23
|
+
try {
|
|
24
|
+
// Check cache first for static translations (no args)
|
|
25
|
+
const cacheKey = args.length === 0 ? `${this.languageManager.getCurrentLanguage()}.${key}` : null;
|
|
26
|
+
if (cacheKey && this._cache.has(cacheKey)) {
|
|
27
|
+
return this._cache.get(cacheKey);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const keys = key.split('.');
|
|
31
|
+
const languagePack = await this.languageManager.getLanguagePack();
|
|
32
|
+
|
|
33
|
+
let value = languagePack;
|
|
34
|
+
for (const k of keys) {
|
|
35
|
+
value = value?.[k];
|
|
36
|
+
if (value === undefined) {
|
|
37
|
+
console.warn(`Translation key not found: ${key}`);
|
|
38
|
+
return key; // Return key as fallback
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// If value is not a string, return the key as fallback
|
|
43
|
+
if (typeof value !== 'string') {
|
|
44
|
+
console.warn(`Translation value is not a string for key: ${key}`);
|
|
45
|
+
return key;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Format with arguments if provided
|
|
49
|
+
let result = value;
|
|
50
|
+
if (args.length > 0) {
|
|
51
|
+
result = this.formatter.format(value, ...args);
|
|
52
|
+
} else if (cacheKey) {
|
|
53
|
+
// Cache static translations
|
|
54
|
+
this._cache.set(cacheKey, result);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.warn(`Translation error for key ${key}:`, error.message);
|
|
60
|
+
return key; // Return key as fallback
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get current language code
|
|
66
|
+
* @returns {string} - Current language code
|
|
67
|
+
*/
|
|
68
|
+
getCurrentLanguage() {
|
|
69
|
+
return this.languageManager.getCurrentLanguage();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get current language display name
|
|
74
|
+
* @returns {string} - Current language display name
|
|
75
|
+
*/
|
|
76
|
+
getCurrentLanguageName() {
|
|
77
|
+
return this.languageManager.getCurrentLanguageName();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get supported languages
|
|
82
|
+
* @returns {Object} - Object with language codes and display names
|
|
83
|
+
*/
|
|
84
|
+
getSupportedLanguages() {
|
|
85
|
+
return this.languageManager.getSupportedLanguages();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Set current language
|
|
90
|
+
* @param {string} langCode - Language code to set
|
|
91
|
+
* @returns {Promise<void>}
|
|
92
|
+
*/
|
|
93
|
+
async setLanguage(langCode) {
|
|
94
|
+
await this.languageManager.setLanguage(langCode);
|
|
95
|
+
this._cache.clear(); // Clear cache when language changes
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if a language is supported
|
|
100
|
+
* @param {string} langCode - Language code to check
|
|
101
|
+
* @returns {boolean} - True if supported
|
|
102
|
+
*/
|
|
103
|
+
isLanguageSupported(langCode) {
|
|
104
|
+
return this.languageManager.isLanguageSupported(langCode);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Clear translation cache (useful for development/testing)
|
|
109
|
+
*/
|
|
110
|
+
clearCache() {
|
|
111
|
+
this._cache.clear();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get cache statistics (for debugging)
|
|
116
|
+
* @returns {Object} - Cache statistics
|
|
117
|
+
*/
|
|
118
|
+
getCacheStats() {
|
|
119
|
+
return {
|
|
120
|
+
size: this._cache.size,
|
|
121
|
+
language: this.getCurrentLanguage(),
|
|
122
|
+
keys: Array.from(this._cache.keys())
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Batch translate multiple keys (optimization for menu loading)
|
|
128
|
+
* @param {Array<string>} keys - Array of translation keys
|
|
129
|
+
* @returns {Promise<Array<string>>} - Array of translated strings
|
|
130
|
+
*/
|
|
131
|
+
async translateBatch(keys) {
|
|
132
|
+
const promises = keys.map(key => this.t(key));
|
|
133
|
+
return Promise.all(promises);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Format a number according to current locale
|
|
138
|
+
* @param {number} number - Number to format
|
|
139
|
+
* @returns {string} - Formatted number
|
|
140
|
+
*/
|
|
141
|
+
formatNumber(number) {
|
|
142
|
+
return this.formatter.formatNumber(number, this.getCurrentLanguage());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Format a date according to current locale
|
|
147
|
+
* @param {string|Date} date - Date to format
|
|
148
|
+
* @returns {string} - Formatted date
|
|
149
|
+
*/
|
|
150
|
+
formatDate(date) {
|
|
151
|
+
return this.formatter.formatDate(date, this.getCurrentLanguage());
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get translation synchronously from cache (for performance in sync contexts)
|
|
156
|
+
* @param {string} key - Translation key
|
|
157
|
+
* @param {...any} args - Arguments for placeholder formatting
|
|
158
|
+
* @returns {string} - Translated text or fallback key
|
|
159
|
+
*/
|
|
160
|
+
tSync(key, ...args) {
|
|
161
|
+
try {
|
|
162
|
+
const cacheKey = args.length === 0 ? `${this.languageManager.getCurrentLanguage()}.${key}` : null;
|
|
163
|
+
if (cacheKey && this._cache.has(cacheKey)) {
|
|
164
|
+
return this._cache.get(cacheKey);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Try to get from current language pack synchronously
|
|
168
|
+
let languagePack = this.languageManager.languageCache.get(this.languageManager.getCurrentLanguage());
|
|
169
|
+
|
|
170
|
+
// If not in cache, try to load it synchronously
|
|
171
|
+
if (!languagePack) {
|
|
172
|
+
try {
|
|
173
|
+
const path = require('path');
|
|
174
|
+
languagePack = require(path.join(__dirname, 'locales', `${this.languageManager.getCurrentLanguage()}.js`));
|
|
175
|
+
this.languageManager.languageCache.set(this.languageManager.getCurrentLanguage(), languagePack);
|
|
176
|
+
} catch (loadError) {
|
|
177
|
+
// Fallback to English
|
|
178
|
+
try {
|
|
179
|
+
languagePack = require(path.join(__dirname, 'locales', 'en.js'));
|
|
180
|
+
} catch (fallbackError) {
|
|
181
|
+
return key;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const keys = key.split('.');
|
|
187
|
+
let value = languagePack;
|
|
188
|
+
for (const k of keys) {
|
|
189
|
+
value = value?.[k];
|
|
190
|
+
if (value === undefined) {
|
|
191
|
+
return key; // Return key as fallback
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (typeof value !== 'string' && !Array.isArray(value)) {
|
|
196
|
+
return key;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Format with arguments if provided (only for strings)
|
|
200
|
+
let result = value;
|
|
201
|
+
if (args.length > 0 && typeof value === 'string') {
|
|
202
|
+
result = this.formatter.format(value, ...args);
|
|
203
|
+
} else if (cacheKey && typeof value === 'string') {
|
|
204
|
+
// Cache static translations (only for strings)
|
|
205
|
+
this._cache.set(cacheKey, result);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return result;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return key; // Return key as fallback
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Create global singleton instance
|
|
216
|
+
const i18n = new I18n();
|
|
217
|
+
|
|
218
|
+
module.exports = i18n;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language Manager Module
|
|
3
|
+
* Manages language preferences and loading
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
class LanguageManager {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.currentLanguage = 'en';
|
|
13
|
+
this.supportedLanguages = {
|
|
14
|
+
'zh': '简体中文',
|
|
15
|
+
'zh-TW': '繁體中文',
|
|
16
|
+
'en': 'English',
|
|
17
|
+
'ja': '日本語',
|
|
18
|
+
'ko': '한국어',
|
|
19
|
+
'de': 'Deutsch',
|
|
20
|
+
'fr': 'Français',
|
|
21
|
+
'es': 'Español',
|
|
22
|
+
'ru': 'Русский',
|
|
23
|
+
'it': 'Italiano',
|
|
24
|
+
'pt': 'Português'
|
|
25
|
+
};
|
|
26
|
+
this.languageCache = new Map();
|
|
27
|
+
this.configFile = path.join(os.homedir(), '.claude-launcher-config.json');
|
|
28
|
+
this.loadLanguagePreference();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get list of supported languages
|
|
33
|
+
* @returns {Object} - Object with language codes as keys and names as values
|
|
34
|
+
*/
|
|
35
|
+
getSupportedLanguages() {
|
|
36
|
+
return { ...this.supportedLanguages };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get current language code
|
|
41
|
+
* @returns {string} - Current language code
|
|
42
|
+
*/
|
|
43
|
+
getCurrentLanguage() {
|
|
44
|
+
return this.currentLanguage;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get current language display name
|
|
49
|
+
* @returns {string} - Current language display name
|
|
50
|
+
*/
|
|
51
|
+
getCurrentLanguageName() {
|
|
52
|
+
return this.supportedLanguages[this.currentLanguage] || 'Unknown';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Set current language
|
|
57
|
+
* @param {string} langCode - Language code to set
|
|
58
|
+
* @throws {Error} - If language is not supported
|
|
59
|
+
*/
|
|
60
|
+
async setLanguage(langCode) {
|
|
61
|
+
if (!this.supportedLanguages[langCode]) {
|
|
62
|
+
throw new Error(`Unsupported language: ${langCode}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.currentLanguage = langCode;
|
|
66
|
+
await this.saveLanguagePreference();
|
|
67
|
+
|
|
68
|
+
// Clear cache to force reload
|
|
69
|
+
this.languageCache.clear();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get language pack for current language
|
|
74
|
+
* @returns {Object} - Language pack object
|
|
75
|
+
*/
|
|
76
|
+
async getLanguagePack() {
|
|
77
|
+
if (this.languageCache.has(this.currentLanguage)) {
|
|
78
|
+
return this.languageCache.get(this.currentLanguage);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const languagePack = require(path.join(__dirname, 'locales', `${this.currentLanguage}.js`));
|
|
83
|
+
this.languageCache.set(this.currentLanguage, languagePack);
|
|
84
|
+
return languagePack;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.warn(`Failed to load language pack for ${this.currentLanguage}, falling back to English. Error: ${error.message}`);
|
|
87
|
+
|
|
88
|
+
// Fallback to English
|
|
89
|
+
if (this.currentLanguage !== 'en') {
|
|
90
|
+
const englishPack = require(path.join(__dirname, 'locales', 'en.js'));
|
|
91
|
+
return englishPack;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Save language preference to config file
|
|
100
|
+
*/
|
|
101
|
+
async saveLanguagePreference() {
|
|
102
|
+
try {
|
|
103
|
+
let config = {};
|
|
104
|
+
|
|
105
|
+
// Load existing config to preserve other settings
|
|
106
|
+
if (fs.existsSync(this.configFile)) {
|
|
107
|
+
try {
|
|
108
|
+
config = JSON.parse(fs.readFileSync(this.configFile, 'utf8'));
|
|
109
|
+
} catch (e) {
|
|
110
|
+
// If parse fails, start fresh
|
|
111
|
+
config = {};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Update language setting
|
|
116
|
+
config.language = this.currentLanguage;
|
|
117
|
+
config.lastUpdated = new Date().toISOString();
|
|
118
|
+
|
|
119
|
+
fs.writeFileSync(this.configFile, JSON.stringify(config, null, 2));
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.warn('Failed to save language preference:', error.message);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Load language preference from config file
|
|
127
|
+
*/
|
|
128
|
+
loadLanguagePreference() {
|
|
129
|
+
try {
|
|
130
|
+
if (fs.existsSync(this.configFile)) {
|
|
131
|
+
const config = JSON.parse(fs.readFileSync(this.configFile, 'utf8'));
|
|
132
|
+
if (config.language && this.supportedLanguages[config.language]) {
|
|
133
|
+
this.currentLanguage = config.language;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.warn('Failed to load language preference, using default (English)');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if a language is supported
|
|
143
|
+
* @param {string} langCode - Language code to check
|
|
144
|
+
* @returns {boolean} - True if supported
|
|
145
|
+
*/
|
|
146
|
+
isLanguageSupported(langCode) {
|
|
147
|
+
return !!this.supportedLanguages[langCode];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Add support for a new language (for future extension)
|
|
152
|
+
* @param {string} langCode - Language code
|
|
153
|
+
* @param {string} displayName - Display name for the language
|
|
154
|
+
*/
|
|
155
|
+
addLanguageSupport(langCode, displayName) {
|
|
156
|
+
this.supportedLanguages[langCode] = displayName;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = LanguageManager;
|