@nbtca/prompt 1.0.5 → 1.0.7

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 (48) hide show
  1. package/dist/config/data.d.ts +1 -1
  2. package/dist/config/data.js +1 -1
  3. package/dist/core/logo.d.ts +3 -3
  4. package/dist/core/logo.js +40 -40
  5. package/dist/core/logo.js.map +1 -1
  6. package/dist/core/menu.d.ts +3 -3
  7. package/dist/core/menu.d.ts.map +1 -1
  8. package/dist/core/menu.js +112 -67
  9. package/dist/core/menu.js.map +1 -1
  10. package/dist/core/ui.d.ts +11 -11
  11. package/dist/core/ui.js +15 -15
  12. package/dist/core/ui.js.map +1 -1
  13. package/dist/features/calendar.d.ts.map +1 -1
  14. package/dist/features/calendar.js +21 -15
  15. package/dist/features/calendar.js.map +1 -1
  16. package/dist/features/docs.d.ts.map +1 -1
  17. package/dist/features/docs.js +35 -29
  18. package/dist/features/docs.js.map +1 -1
  19. package/dist/features/repair.d.ts.map +1 -1
  20. package/dist/features/repair.js +6 -10
  21. package/dist/features/repair.js.map +1 -1
  22. package/dist/features/website.d.ts +3 -3
  23. package/dist/features/website.d.ts.map +1 -1
  24. package/dist/features/website.js +11 -9
  25. package/dist/features/website.js.map +1 -1
  26. package/dist/i18n/index.d.ts +140 -0
  27. package/dist/i18n/index.d.ts.map +1 -0
  28. package/dist/i18n/index.js +113 -0
  29. package/dist/i18n/index.js.map +1 -0
  30. package/dist/i18n/locales/en.json +107 -0
  31. package/dist/i18n/locales/zh.json +107 -0
  32. package/dist/main.d.ts +2 -2
  33. package/dist/main.d.ts.map +1 -1
  34. package/dist/main.js +11 -10
  35. package/dist/main.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/config/data.ts +1 -1
  38. package/src/core/logo.ts +40 -40
  39. package/src/core/menu.ts +119 -67
  40. package/src/core/ui.ts +15 -15
  41. package/src/features/calendar.ts +23 -15
  42. package/src/features/docs.ts +35 -29
  43. package/src/features/repair.ts +6 -10
  44. package/src/features/website.ts +11 -9
  45. package/src/i18n/index.ts +236 -0
  46. package/src/i18n/locales/en.json +107 -0
  47. package/src/i18n/locales/zh.json +107 -0
  48. package/src/main.ts +11 -10
package/src/core/logo.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * 智能Logo显示模块
3
- * 尝试显示iTerm2图片格式logo,失败则降级到ASCII艺术字
2
+ * Smart logo display module
3
+ * Attempts to display iTerm2 image format logo, falls back to ASCII art
4
4
  */
5
5
 
6
6
  import { readFileSync } from 'fs';
@@ -12,20 +12,20 @@ const __filename = fileURLToPath(import.meta.url);
12
12
  const __dirname = dirname(__filename);
13
13
 
14
14
  /**
15
- * 创建蓝色主调的渐变效果
15
+ * Create blue-toned gradient effect
16
16
  */
17
17
  function createBlueGradient(text: string): string {
18
- // 使用蓝色系渐变:深蓝 -> 青色 -> 天蓝
18
+ // Blue gradient: deep blue -> sky blue -> cyan
19
19
  const blueGradient = gradient([
20
- { color: '#1e3a8a', pos: 0 }, // 深蓝
21
- { color: '#0ea5e9', pos: 0.5 }, // 天蓝
22
- { color: '#06b6d4', pos: 1 } // 青色
20
+ { color: '#1e3a8a', pos: 0 }, // Deep blue
21
+ { color: '#0ea5e9', pos: 0.5 }, // Sky blue
22
+ { color: '#06b6d4', pos: 1 } // Cyan
23
23
  ]);
24
24
  return blueGradient(text);
25
25
  }
26
26
 
27
27
  /**
28
- * 将十六进制颜色转换为RGB
28
+ * Convert hex color to RGB
29
29
  */
30
30
  function hexToRgb(hex: string): [number, number, number] {
31
31
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
@@ -35,7 +35,7 @@ function hexToRgb(hex: string): [number, number, number] {
35
35
  }
36
36
 
37
37
  /**
38
- * RGB转换为十六进制颜色
38
+ * Convert RGB to hex color
39
39
  */
40
40
  function rgbToHex(r: number, g: number, b: number): string {
41
41
  return '#' + [r, g, b].map(x => {
@@ -45,7 +45,7 @@ function rgbToHex(r: number, g: number, b: number): string {
45
45
  }
46
46
 
47
47
  /**
48
- * 在两个颜色之间进行线性插值
48
+ * Linear interpolation between two colors
49
49
  */
50
50
  function interpolateColor(color1: string, color2: string, factor: number): string {
51
51
  const [r1, g1, b1] = hexToRgb(color1);
@@ -59,45 +59,45 @@ function interpolateColor(color1: string, color2: string, factor: number): strin
59
59
  }
60
60
 
61
61
  /**
62
- * 缓动函数 - 平滑的进出效果
62
+ * Easing function - smooth in-out effect
63
63
  */
64
64
  function easeInOutSine(t: number): number {
65
65
  return -(Math.cos(Math.PI * t) - 1) / 2;
66
66
  }
67
67
 
68
68
  /**
69
- * 显示渐变动画效果(优化版 - 真正丝滑的动画)
69
+ * Display gradient animation effect (optimized - truly smooth animation)
70
70
  */
71
71
  async function animateGradient(text: string, duration: number = 1200): Promise<void> {
72
- const frames = 60; // 60帧实现真正的流畅动画
72
+ const frames = 60; // 60 frames for truly smooth animation
73
73
  const frameDelay = duration / frames;
74
74
 
75
- // 定义颜色序列 - 形成完整的蓝色系循环
75
+ // Define color sequence - forms complete blue spectrum cycle
76
76
  const colorSequence = [
77
- '#1e3a8a', // 深蓝
78
- '#2563eb', //
79
- '#3b82f6', // 亮蓝
80
- '#0ea5e9', // 天蓝
81
- '#06b6d4', // 青色
82
- '#14b8a6', // 青绿
83
- '#06b6d4', // 青色
84
- '#0ea5e9', // 天蓝
85
- '#3b82f6', // 亮蓝
86
- '#2563eb', //
87
- '#1e3a8a', // 深蓝
77
+ '#1e3a8a', // Deep blue
78
+ '#2563eb', // Blue
79
+ '#3b82f6', // Bright blue
80
+ '#0ea5e9', // Sky blue
81
+ '#06b6d4', // Cyan
82
+ '#14b8a6', // Teal
83
+ '#06b6d4', // Cyan
84
+ '#0ea5e9', // Sky blue
85
+ '#3b82f6', // Bright blue
86
+ '#2563eb', // Blue
87
+ '#1e3a8a', // Deep blue
88
88
  ];
89
89
 
90
90
  for (let i = 0; i < frames; i++) {
91
- // 使用平滑的正弦缓动
91
+ // Use smooth sine easing
92
92
  const progress = easeInOutSine(i / frames);
93
93
 
94
- // 计算在颜色序列中的位置
94
+ // Calculate position in color sequence
95
95
  const position = progress * (colorSequence.length - 1);
96
96
  const index1 = Math.floor(position);
97
97
  const index2 = Math.min(index1 + 1, colorSequence.length - 1);
98
98
  const localProgress = position - index1;
99
99
 
100
- // 在相邻颜色间插值,生成三个平滑过渡的颜色
100
+ // Interpolate between adjacent colors, generating three smoothly transitioning colors
101
101
  const color1 = interpolateColor(
102
102
  colorSequence[index1]!,
103
103
  colorSequence[index2]!,
@@ -120,44 +120,44 @@ async function animateGradient(text: string, duration: number = 1200): Promise<v
120
120
  localProgress
121
121
  );
122
122
 
123
- // 为当前帧创建渐变
123
+ // Create gradient for current frame
124
124
  const frameGradient = gradient(color1, color2, color3);
125
125
 
126
- // 清除当前行并显示新帧
126
+ // Clear current line and display new frame
127
127
  process.stdout.write('\r' + frameGradient(text));
128
128
 
129
129
  await new Promise(resolve => setTimeout(resolve, frameDelay));
130
130
  }
131
131
 
132
- // 最后显示静态的蓝色渐变
132
+ // Finally display static blue gradient
133
133
  process.stdout.write('\r' + createBlueGradient(text) + '\n');
134
134
  }
135
135
 
136
136
  /**
137
- * 尝试读取并显示logo文件
137
+ * Attempt to read and display logo file
138
138
  */
139
139
  export async function printLogo(): Promise<void> {
140
140
  try {
141
- // 尝试读取iTerm2图片格式logo
141
+ // Try to read iTerm2 image format logo
142
142
  const logoPath = join(__dirname, '../logo/logo.txt');
143
143
  const logoContent = readFileSync(logoPath, 'utf-8');
144
144
 
145
- // 如果成功读取且内容有效,直接显示
145
+ // If successfully read and content is valid, display directly
146
146
  if (logoContent && logoContent.length > 100) {
147
147
  console.log(logoContent);
148
148
  await printDescription();
149
149
  return;
150
150
  }
151
151
  } catch (error) {
152
- // iTerm2 logo读取失败,继续尝试ASCII logo
152
+ // iTerm2 logo read failed, continue trying ASCII logo
153
153
  }
154
154
 
155
- // 降级:显示ASCII艺术字logo
155
+ // Fallback: display ASCII art logo
156
156
  try {
157
157
  const asciiLogoPath = join(__dirname, '../logo/ascii-logo.txt');
158
158
  const asciiContent = readFileSync(asciiLogoPath, 'utf-8');
159
159
 
160
- // 使用渐变彩色显示ASCII logo
160
+ // Display ASCII logo with gradient colors
161
161
  console.log();
162
162
  const lines = asciiContent.split('\n').filter(line => line.trim());
163
163
  lines.forEach(line => {
@@ -166,7 +166,7 @@ export async function printLogo(): Promise<void> {
166
166
 
167
167
  await printDescription();
168
168
  } catch (error) {
169
- // 如果ASCII logo也失败,显示简单的文本logo
169
+ // If ASCII logo also fails, display simple text logo
170
170
  console.log();
171
171
  console.log(createBlueGradient(' NBTCA'));
172
172
  await printDescription();
@@ -174,14 +174,14 @@ export async function printLogo(): Promise<void> {
174
174
  }
175
175
 
176
176
  /**
177
- * 显示描述文字(带渐变动画)
177
+ * Display description text (with gradient animation)
178
178
  */
179
179
  async function printDescription(): Promise<void> {
180
180
  const tagline = 'To be at the intersection of technology and liberal arts.';
181
181
 
182
182
  console.log();
183
183
 
184
- // 显示渐变动画
184
+ // Display gradient animation
185
185
  await animateGradient(tagline, 1500);
186
186
 
187
187
  console.log();
package/src/core/menu.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * 极简菜单系统
3
- * 6大核心功能菜单
2
+ * Minimalist menu system
3
+ * Six core feature menus
4
4
  */
5
5
 
6
6
  import inquirer from 'inquirer';
@@ -11,88 +11,99 @@ import { showDocsMenu } from '../features/docs.js';
11
11
  import { openHomepage, openGithub } from '../features/website.js';
12
12
  import { printDivider, printNewLine } from './ui.js';
13
13
  import { APP_INFO, URLS } from '../config/data.js';
14
+ import { t, getCurrentLanguage, setLanguage, clearTranslationCache, type Language } from '../i18n/index.js';
14
15
 
15
16
  /**
16
- * 主菜单选项
17
+ * Get main menu options
17
18
  */
18
- const MAIN_MENU = [
19
- {
20
- name: '[*] Recent Events ' + chalk.gray('查看最近30天的社团活动'),
21
- value: 'events',
22
- short: 'Recent Events'
23
- },
24
- {
25
- name: '[*] Repair Service ' + chalk.gray('电脑维修、软件安装'),
26
- value: 'repair',
27
- short: 'Repair Service'
28
- },
29
- {
30
- name: '[*] Knowledge Base ' + chalk.gray('技术文档、教程资源'),
31
- value: 'docs',
32
- short: 'Knowledge Base'
33
- },
34
- {
35
- name: '[*] Official Website ' + chalk.gray('访问NBTCA主页'),
36
- value: 'website',
37
- short: 'Official Website'
38
- },
39
- {
40
- name: '[*] GitHub ' + chalk.gray('开源项目与代码'),
41
- value: 'github',
42
- short: 'GitHub'
43
- },
44
- {
45
- name: '[?] About ' + chalk.gray('项目信息与帮助'),
46
- value: 'about',
47
- short: 'About'
48
- },
49
- new inquirer.Separator(' '),
50
- {
51
- name: chalk.dim('[x] Exit'),
52
- value: 'exit',
53
- short: 'Exit'
54
- }
55
- ];
19
+ function getMainMenuOptions() {
20
+ const trans = t();
21
+ return [
22
+ {
23
+ name: '[*] ' + trans.menu.events.padEnd(16) + ' ' + chalk.gray(trans.menu.eventsDesc),
24
+ value: 'events',
25
+ short: trans.menu.events
26
+ },
27
+ {
28
+ name: '[*] ' + trans.menu.repair.padEnd(16) + ' ' + chalk.gray(trans.menu.repairDesc),
29
+ value: 'repair',
30
+ short: trans.menu.repair
31
+ },
32
+ {
33
+ name: '[*] ' + trans.menu.docs.padEnd(16) + ' ' + chalk.gray(trans.menu.docsDesc),
34
+ value: 'docs',
35
+ short: trans.menu.docs
36
+ },
37
+ {
38
+ name: '[*] ' + trans.menu.website.padEnd(16) + ' ' + chalk.gray(trans.menu.websiteDesc),
39
+ value: 'website',
40
+ short: trans.menu.website
41
+ },
42
+ {
43
+ name: '[*] ' + trans.menu.github.padEnd(16) + ' ' + chalk.gray(trans.menu.githubDesc),
44
+ value: 'github',
45
+ short: trans.menu.github
46
+ },
47
+ {
48
+ name: '[?] ' + trans.menu.about.padEnd(16) + ' ' + chalk.gray(trans.menu.aboutDesc),
49
+ value: 'about',
50
+ short: trans.menu.about
51
+ },
52
+ {
53
+ name: '[⚙] ' + trans.menu.language.padEnd(16) + ' ' + chalk.gray(trans.menu.languageDesc),
54
+ value: 'language',
55
+ short: trans.menu.language
56
+ },
57
+ new inquirer.Separator(' '),
58
+ {
59
+ name: chalk.dim('[x] ' + trans.common.exit),
60
+ value: 'exit',
61
+ short: trans.common.exit
62
+ }
63
+ ];
64
+ }
56
65
 
57
66
  /**
58
- * 显示主菜单
67
+ * Display main menu
59
68
  */
60
69
  export async function showMainMenu(): Promise<void> {
61
70
  while (true) {
62
71
  try {
72
+ const trans = t();
73
+
63
74
  // Show keybinding hints
64
- console.log(chalk.dim(' Navigation: j/k or ↑/↓ | Jump: g/G | Quit: q or Ctrl+C'));
75
+ console.log(chalk.dim(' ' + trans.menu.navigationHint));
65
76
  console.log();
66
77
 
67
78
  const { action } = await inquirer.prompt<{ action: string }>([
68
79
  {
69
80
  type: 'list',
70
81
  name: 'action',
71
- message: '选择功能',
72
- choices: MAIN_MENU,
82
+ message: trans.menu.chooseAction,
83
+ choices: getMainMenuOptions(),
73
84
  pageSize: 15,
74
85
  loop: false
75
86
  } as any
76
87
  ]);
77
88
 
78
- // 处理用户选择
89
+ // Handle user selection
79
90
  if (action === 'exit') {
80
91
  console.log();
81
- console.log(chalk.dim('再见!'));
92
+ console.log(chalk.dim(trans.common.goodbye));
82
93
  process.exit(0);
83
94
  }
84
95
 
85
96
  await handleAction(action);
86
97
 
87
- // 操作完成后显示分隔线
98
+ // Show divider after operation
88
99
  printNewLine();
89
100
  printDivider();
90
101
  printNewLine();
91
102
  } catch (err: any) {
92
- // 处理Ctrl+C退出
103
+ // Handle Ctrl+C exit
93
104
  if (err.message?.includes('User force closed')) {
94
105
  console.log();
95
- console.log(chalk.dim('再见!'));
106
+ console.log(chalk.dim(t().common.goodbye));
96
107
  process.exit(0);
97
108
  }
98
109
  throw err;
@@ -101,7 +112,7 @@ export async function showMainMenu(): Promise<void> {
101
112
  }
102
113
 
103
114
  /**
104
- * 处理用户操作
115
+ * Handle user action
105
116
  */
106
117
  async function handleAction(action: string): Promise<void> {
107
118
  switch (action) {
@@ -129,33 +140,74 @@ async function handleAction(action: string): Promise<void> {
129
140
  showAbout();
130
141
  break;
131
142
 
143
+ case 'language':
144
+ await showLanguageMenu();
145
+ break;
146
+
132
147
  default:
133
- console.log(chalk.gray('未知操作'));
148
+ console.log(chalk.gray('Unknown action'));
134
149
  }
135
150
  }
136
151
 
137
152
  /**
138
- * 显示关于信息
153
+ * Display about information
139
154
  */
140
155
  function showAbout(): void {
156
+ const trans = t();
141
157
  console.log();
142
- console.log(chalk.bold('>> About NBTCA'));
158
+ console.log(chalk.bold('>> ' + trans.about.title));
143
159
  console.log();
144
- console.log(chalk.dim('Project ') + APP_INFO.name);
145
- console.log(chalk.dim('Version ') + `v${APP_INFO.version}`);
146
- console.log(chalk.dim('Description ') + APP_INFO.fullDescription);
160
+ console.log(chalk.dim(trans.about.project.padEnd(12)) + APP_INFO.name);
161
+ console.log(chalk.dim(trans.about.version.padEnd(12)) + `v${APP_INFO.version}`);
162
+ console.log(chalk.dim(trans.about.description.padEnd(12)) + APP_INFO.fullDescription);
147
163
  console.log();
148
- console.log(chalk.dim('GitHub ') + chalk.cyan(APP_INFO.repository));
149
- console.log(chalk.dim('Website ') + chalk.cyan(URLS.homepage));
150
- console.log(chalk.dim('Email ') + chalk.cyan(URLS.email));
164
+ console.log(chalk.dim(trans.about.github.padEnd(12)) + chalk.cyan(APP_INFO.repository));
165
+ console.log(chalk.dim(trans.about.website.padEnd(12)) + chalk.cyan(URLS.homepage));
166
+ console.log(chalk.dim(trans.about.email.padEnd(12)) + chalk.cyan(URLS.email));
151
167
  console.log();
152
- console.log(chalk.dim('Features:'));
153
- console.log(' - View recent association events');
154
- console.log(' - Online repair service');
155
- console.log(' - Technical knowledge base access');
156
- console.log(' - Quick access to website and GitHub');
168
+ console.log(chalk.dim(trans.about.features));
169
+ console.log(' ' + trans.about.feature1);
170
+ console.log(' ' + trans.about.feature2);
171
+ console.log(' ' + trans.about.feature3);
172
+ console.log(' ' + trans.about.feature4);
157
173
  console.log();
158
- console.log(chalk.dim('License ') + 'MIT License');
159
- console.log(chalk.dim('Author ') + 'm1ngsama');
174
+ console.log(chalk.dim(trans.about.license.padEnd(12)) + 'MIT License');
175
+ console.log(chalk.dim(trans.about.author.padEnd(12)) + 'm1ngsama');
160
176
  console.log();
161
177
  }
178
+
179
+ /**
180
+ * Display language selection menu
181
+ */
182
+ async function showLanguageMenu(): Promise<void> {
183
+ const trans = t();
184
+ const currentLang = getCurrentLanguage();
185
+
186
+ console.log();
187
+ console.log(chalk.bold('>> ' + trans.language.title));
188
+ console.log();
189
+ console.log(chalk.dim(trans.language.currentLanguage + ': ') + chalk.cyan(trans.language[currentLang]));
190
+ console.log();
191
+
192
+ const { language } = await inquirer.prompt<{ language: Language }>([
193
+ {
194
+ type: 'list',
195
+ name: 'language',
196
+ message: trans.language.selectLanguage,
197
+ choices: [
198
+ { name: trans.language.zh, value: 'zh' as Language },
199
+ { name: trans.language.en, value: 'en' as Language }
200
+ ],
201
+ default: currentLang
202
+ }
203
+ ]);
204
+
205
+ if (language !== currentLang) {
206
+ setLanguage(language);
207
+ clearTranslationCache();
208
+ console.log();
209
+ console.log(chalk.green('✓ ' + t().language.changed));
210
+ console.log();
211
+ }
212
+ }
213
+
package/src/core/ui.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
- * 极简UI组件库
3
- * 提供基础的终端UI组件
2
+ * Minimalist UI component library
3
+ * Provides basic terminal UI components
4
4
  */
5
5
 
6
6
  import chalk from 'chalk';
7
7
 
8
8
  /**
9
- * 显示顶部边框标题
9
+ * Display header title
10
10
  */
11
11
  export function printHeader(title: string): void {
12
12
  console.log(chalk.dim(title));
@@ -14,7 +14,7 @@ export function printHeader(title: string): void {
14
14
  }
15
15
 
16
16
  /**
17
- * 显示分隔线
17
+ * Display divider line
18
18
  */
19
19
  export function printDivider(): void {
20
20
  const terminalWidth = process.stdout.columns || 80;
@@ -22,7 +22,7 @@ export function printDivider(): void {
22
22
  }
23
23
 
24
24
  /**
25
- * 显示加载spinner
25
+ * Display loading spinner
26
26
  */
27
27
  export async function showSpinner(text: string, duration: number): Promise<void> {
28
28
  const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
@@ -44,42 +44,42 @@ export async function showSpinner(text: string, duration: number): Promise<void>
44
44
  }
45
45
 
46
46
  /**
47
- * 显示成功消息
47
+ * Display success message
48
48
  */
49
49
  export function success(msg: string): void {
50
- console.log(chalk.green('✓') + ' ' + msg);
50
+ console.log(chalk.green('[]') + ' ' + msg);
51
51
  }
52
52
 
53
53
  /**
54
- * 显示错误消息
54
+ * Display error message
55
55
  */
56
56
  export function error(msg: string): void {
57
- console.log(chalk.red('✗') + ' ' + msg);
57
+ console.log(chalk.red('[]') + ' ' + msg);
58
58
  }
59
59
 
60
60
  /**
61
- * 显示信息消息
61
+ * Display info message
62
62
  */
63
63
  export function info(msg: string): void {
64
- console.log(chalk.blue('ℹ') + ' ' + msg);
64
+ console.log(chalk.blue('[]') + ' ' + msg);
65
65
  }
66
66
 
67
67
  /**
68
- * 显示警告消息
68
+ * Display warning message
69
69
  */
70
70
  export function warning(msg: string): void {
71
- console.log(chalk.yellow('⚠') + ' ' + msg);
71
+ console.log(chalk.yellow('[]') + ' ' + msg);
72
72
  }
73
73
 
74
74
  /**
75
- * 清屏
75
+ * Clear screen
76
76
  */
77
77
  export function clearScreen(): void {
78
78
  console.clear();
79
79
  }
80
80
 
81
81
  /**
82
- * 打印空行
82
+ * Print empty lines
83
83
  */
84
84
  export function printNewLine(count: number = 1): void {
85
85
  for (let i = 0; i < count; i++) {
@@ -7,6 +7,7 @@ import axios from 'axios';
7
7
  import ICAL from 'ical.js';
8
8
  import chalk from 'chalk';
9
9
  import { error, info, printDivider } from '../core/ui.js';
10
+ import { t } from '../i18n/index.js';
10
11
 
11
12
  /**
12
13
  * 活动接口
@@ -43,13 +44,17 @@ export async function fetchEvents(): Promise<Event[]> {
43
44
  const event = new ICAL.Event(vevent);
44
45
  const startDate = event.startDate.toJSDate();
45
46
 
46
- // 只显示未来30天内的活动
47
+ // Only show events within next 30 days
47
48
  if (startDate >= now && startDate <= thirtyDaysLater) {
49
+ const trans = t();
50
+ const untitledEvent = trans.calendar.eventName === 'Event Name' ? 'Untitled Event' : '未命名活动';
51
+ const tbdLocation = trans.calendar.location === 'Location' ? 'TBD' : '待定';
52
+
48
53
  events.push({
49
54
  date: formatDate(startDate),
50
55
  time: formatTime(startDate),
51
- title: event.summary || '未命名活动',
52
- location: event.location || '待定',
56
+ title: event.summary || untitledEvent,
57
+ location: event.location || tbdLocation,
53
58
  startDate: startDate
54
59
  });
55
60
  }
@@ -60,7 +65,7 @@ export async function fetchEvents(): Promise<Event[]> {
60
65
 
61
66
  return events;
62
67
  } catch (err) {
63
- throw new Error('获取活动日历失败');
68
+ throw new Error(t().calendar.error);
64
69
  }
65
70
  }
66
71
 
@@ -68,30 +73,32 @@ export async function fetchEvents(): Promise<Event[]> {
68
73
  * 以表格形式显示活动
69
74
  */
70
75
  export function displayEvents(events: Event[]): void {
76
+ const trans = t();
77
+
71
78
  if (events.length === 0) {
72
- info('近期暂无活动安排');
79
+ info(trans.calendar.noEvents);
73
80
  return;
74
81
  }
75
82
 
76
83
  console.log();
77
- console.log(chalk.cyan.bold(' 近期活动') + chalk.gray(` (最近30天)`));
84
+ console.log(chalk.cyan.bold(' ' + trans.calendar.title) + chalk.gray(` ${trans.calendar.subtitle}`));
78
85
  console.log();
79
86
 
80
- // 表头
87
+ // Table header
81
88
  const dateWidth = 14;
82
89
  const titleWidth = 25;
83
90
  const locationWidth = 15;
84
91
 
85
92
  console.log(
86
93
  ' ' +
87
- chalk.bold('日期时间'.padEnd(dateWidth)) +
88
- chalk.bold('活动名称'.padEnd(titleWidth)) +
89
- chalk.bold('地点')
94
+ chalk.bold(trans.calendar.dateTime.padEnd(dateWidth)) +
95
+ chalk.bold(trans.calendar.eventName.padEnd(titleWidth)) +
96
+ chalk.bold(trans.calendar.location)
90
97
  );
91
98
 
92
99
  printDivider();
93
100
 
94
- // 活动列表
101
+ // Event list
95
102
  events.forEach(event => {
96
103
  const dateTime = `${event.date} ${event.time}`.padEnd(dateWidth);
97
104
  const title = truncate(event.title, titleWidth - 2).padEnd(titleWidth);
@@ -140,14 +147,15 @@ function truncate(str: string, maxLength: number): string {
140
147
  * 主函数:获取并显示活动
141
148
  */
142
149
  export async function showCalendar(): Promise<void> {
150
+ const trans = t();
143
151
  try {
144
- info('正在获取活动日历...');
152
+ info(trans.calendar.loading);
145
153
  const events = await fetchEvents();
146
- console.log('\r' + ' '.repeat(50) + '\r'); // 清除加载提示
154
+ console.log('\r' + ' '.repeat(50) + '\r'); // Clear loading message
147
155
  displayEvents(events);
148
156
  } catch (err) {
149
- error('无法获取活动日历');
150
- console.log(chalk.gray(' 请稍后重试或访问 https://nbtca.space'));
157
+ error(trans.calendar.error);
158
+ console.log(chalk.gray(' ' + trans.calendar.errorHint));
151
159
  console.log();
152
160
  }
153
161
  }