@cloudbase/cli 2.9.9 → 2.9.10-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/bin/cloudbase.js +2 -1
  2. package/bin/tcb.js +35 -28
  3. package/lib/auth/login.js +21 -13
  4. package/lib/commands/account/login.js +43 -42
  5. package/lib/commands/account/logout.js +3 -2
  6. package/lib/commands/ai/index.js +13 -12
  7. package/lib/commands/cloudfunction/base.js +24 -26
  8. package/lib/commands/cloudrun/base.js +87 -96
  9. package/lib/commands/common.js +11 -10
  10. package/lib/commands/config/delete.js +72 -0
  11. package/lib/commands/config/get.js +69 -0
  12. package/lib/commands/config/index.js +21 -0
  13. package/lib/commands/config/interface.js +24 -0
  14. package/lib/commands/config/list.js +72 -0
  15. package/lib/commands/config/set.js +89 -0
  16. package/lib/commands/db/base.js +33 -32
  17. package/lib/commands/env/base.js +12 -11
  18. package/lib/commands/env/domain.js +24 -23
  19. package/lib/commands/env/login.js +31 -30
  20. package/lib/commands/fun/base.js +37 -36
  21. package/lib/commands/functions/alias/getRoute.js +5 -4
  22. package/lib/commands/functions/alias/setRoute.js +7 -6
  23. package/lib/commands/functions/code-download.js +15 -11
  24. package/lib/commands/functions/code-update.js +8 -7
  25. package/lib/commands/functions/concurrency/delete.js +5 -4
  26. package/lib/commands/functions/concurrency/list.js +6 -5
  27. package/lib/commands/functions/concurrency/set.js +5 -4
  28. package/lib/commands/functions/config-update.js +8 -7
  29. package/lib/commands/functions/copy.js +7 -6
  30. package/lib/commands/functions/delete.js +8 -7
  31. package/lib/commands/functions/deploy.js +35 -34
  32. package/lib/commands/functions/detail.js +32 -31
  33. package/lib/commands/functions/invoke.js +16 -15
  34. package/lib/commands/functions/layer/bind.js +26 -25
  35. package/lib/commands/functions/layer/common.js +2 -1
  36. package/lib/commands/functions/layer/create.js +7 -6
  37. package/lib/commands/functions/layer/delete.js +9 -8
  38. package/lib/commands/functions/layer/download.js +10 -9
  39. package/lib/commands/functions/layer/list.js +10 -9
  40. package/lib/commands/functions/layer/sort.js +9 -8
  41. package/lib/commands/functions/list.js +9 -8
  42. package/lib/commands/functions/log.js +28 -27
  43. package/lib/commands/functions/run.js +21 -20
  44. package/lib/commands/functions/trigger-create.js +9 -8
  45. package/lib/commands/functions/trigger-delete.js +10 -9
  46. package/lib/commands/functions/version/list.js +9 -8
  47. package/lib/commands/functions/version/publish.js +5 -4
  48. package/lib/commands/gateway/create.js +14 -13
  49. package/lib/commands/gateway/delete.js +10 -9
  50. package/lib/commands/gateway/domain.js +25 -24
  51. package/lib/commands/gateway/list.js +11 -10
  52. package/lib/commands/gateway/switch.js +22 -19
  53. package/lib/commands/helpers/init.js +38 -37
  54. package/lib/commands/helpers/new.js +7 -6
  55. package/lib/commands/helpers/open.js +7 -6
  56. package/lib/commands/hosting/hosting.js +45 -44
  57. package/lib/commands/index.js +1 -0
  58. package/lib/commands/pull/pull.js +12 -11
  59. package/lib/commands/run/delete.js +16 -15
  60. package/lib/commands/run/image/common.js +2 -1
  61. package/lib/commands/run/image/delete.js +10 -9
  62. package/lib/commands/run/image/download.js +8 -7
  63. package/lib/commands/run/image/list.js +11 -10
  64. package/lib/commands/run/image/upload.js +9 -8
  65. package/lib/commands/run/list.js +10 -9
  66. package/lib/commands/run/service/config.js +13 -12
  67. package/lib/commands/run/service/deploy.js +24 -23
  68. package/lib/commands/run/service/list.js +10 -9
  69. package/lib/commands/run/service/update.js +22 -21
  70. package/lib/commands/run/standalonegateway/common.js +2 -1
  71. package/lib/commands/run/standalonegateway/create.js +14 -13
  72. package/lib/commands/run/standalonegateway/destroy.js +9 -8
  73. package/lib/commands/run/standalonegateway/list.js +7 -6
  74. package/lib/commands/run/standalonegateway/package.js +7 -6
  75. package/lib/commands/run/standalonegateway/turn.js +10 -9
  76. package/lib/commands/run/version/common.js +2 -1
  77. package/lib/commands/run/version/create.js +41 -40
  78. package/lib/commands/run/version/delete.js +10 -9
  79. package/lib/commands/run/version/list.js +13 -12
  80. package/lib/commands/run/version/modify.js +13 -12
  81. package/lib/commands/run/version/update.js +57 -54
  82. package/lib/commands/runf/base.js +42 -40
  83. package/lib/commands/self-update.js +11 -10
  84. package/lib/commands/smart.js +5 -4
  85. package/lib/commands/storage/storage.js +61 -56
  86. package/lib/commands/third/thirdAttach.js +6 -5
  87. package/lib/commands/utils.js +8 -5
  88. package/lib/constant.js +18 -17
  89. package/lib/decorators/deprecate.js +2 -1
  90. package/lib/function/alias.js +3 -2
  91. package/lib/function/base.js +9 -8
  92. package/lib/function/concurrency.js +4 -3
  93. package/lib/function/create.js +5 -4
  94. package/lib/function/delete.js +2 -1
  95. package/lib/function/layer/attach.js +2 -1
  96. package/lib/function/layer/create.js +2 -1
  97. package/lib/function/layer/download.js +2 -1
  98. package/lib/function/trigger.js +6 -5
  99. package/lib/function/update.js +3 -2
  100. package/lib/function/version.js +3 -2
  101. package/lib/help.js +22 -20
  102. package/lib/hosting.js +18 -17
  103. package/lib/i18n/index.js +81 -0
  104. package/lib/run/service/common.js +14 -13
  105. package/lib/run/service/config.js +4 -3
  106. package/lib/run/service/deployPackage.js +6 -5
  107. package/lib/run/service/showLogs.js +3 -2
  108. package/lib/run/service/update.js +7 -6
  109. package/lib/run/standalonegateway/list.js +5 -1
  110. package/lib/storage.js +2 -1
  111. package/lib/utils/ai/banner.js +12 -11
  112. package/lib/utils/ai/claudeWindows.js +5 -3
  113. package/lib/utils/ai/config.js +3 -2
  114. package/lib/utils/ai/const.js +4 -3
  115. package/lib/utils/ai/env.js +3 -2
  116. package/lib/utils/ai/envLocalManager.js +6 -5
  117. package/lib/utils/ai/nodeVersion.js +5 -1
  118. package/lib/utils/ai/router.js +129 -106
  119. package/lib/utils/ai/setup.js +95 -91
  120. package/lib/utils/checkTcbrEnv.js +22 -21
  121. package/lib/utils/cli-table.js +2 -1
  122. package/lib/utils/commonParamsCheck.js +4 -3
  123. package/lib/utils/debug-logger.js +2 -1
  124. package/lib/utils/dts.js +4 -3
  125. package/lib/utils/env.js +23 -21
  126. package/lib/utils/fs/index.js +5 -4
  127. package/lib/utils/function-packer.js +3 -2
  128. package/lib/utils/index.js +1 -0
  129. package/lib/utils/mcp-config-modifier.js +18 -17
  130. package/lib/utils/net/cloud-api-request.js +2 -1
  131. package/lib/utils/net/credential.js +2 -1
  132. package/lib/utils/net/http-request.js +2 -1
  133. package/lib/utils/progress-bar.js +2 -1
  134. package/lib/utils/prompt/select.js +8 -6
  135. package/lib/utils/store/auth.js +3 -2
  136. package/lib/utils/store/config.js +49 -0
  137. package/lib/utils/store/db.js +5 -0
  138. package/lib/utils/store/index.js +1 -1
  139. package/lib/utils/tcbrApi/callTcbrApi.js +2 -1
  140. package/lib/utils/template-manager.js +37 -36
  141. package/lib/utils/template.js +10 -9
  142. package/lib/utils/tools/common.js +3 -2
  143. package/lib/utils/url.js +19 -0
  144. package/lib/utils/validator.js +12 -4
  145. package/locales/README.md +160 -0
  146. package/locales/i18next-scanner.config.js +206 -0
  147. package/locales/langs/en.json +1235 -0
  148. package/locales/langs/zh.json +1235 -0
  149. package/locales/mapping.json +1235 -0
  150. package/package.json +9 -2
  151. package/types/commands/config/delete.d.ts +13 -0
  152. package/types/commands/config/get.d.ts +13 -0
  153. package/types/commands/config/index.d.ts +5 -0
  154. package/types/commands/config/interface.d.ts +17 -0
  155. package/types/commands/config/list.d.ts +13 -0
  156. package/types/commands/config/set.d.ts +13 -0
  157. package/types/commands/index.d.ts +1 -0
  158. package/types/i18n/index.d.ts +3 -0
  159. package/types/utils/ai/const.d.ts +1 -1
  160. package/types/utils/index.d.ts +1 -0
  161. package/types/utils/store/config.d.ts +8 -0
  162. package/types/utils/store/db.d.ts +2 -0
  163. package/types/utils/store/index.d.ts +1 -1
  164. package/types/utils/url.d.ts +18 -0
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.callTcbrApi = void 0;
13
13
  const toolbox_1 = require("@cloudbase/toolbox");
14
14
  const net_1 = require("../net");
15
+ const i18n_1 = require("../../i18n");
15
16
  const tcbrService = net_1.CloudApiService.getInstance('tcbr');
16
17
  function callTcbrApi(action, data) {
17
18
  return __awaiter(this, void 0, void 0, function* () {
@@ -26,7 +27,7 @@ function callTcbrApi(action, data) {
26
27
  catch (e) {
27
28
  if (e.code === 'AuthFailure.UnauthorizedOperation') {
28
29
  console.log('\n', `requestId: ${e.requestId}`);
29
- throw new toolbox_1.CloudBaseError('您没有权限执行此操作,请检查 CAM 策略\n');
30
+ throw new toolbox_1.CloudBaseError((0, i18n_1.t)('您没有权限执行此操作,请检查 CAM 策略\n'));
30
31
  }
31
32
  else if (e.code === 'LimitExceeded') {
32
33
  throw new toolbox_1.CloudBaseError(`${e.original.Message}\n`);
@@ -41,26 +41,27 @@ const path_1 = __importDefault(require("path"));
41
41
  const simple_git_1 = require("simple-git");
42
42
  const error_1 = require("../error");
43
43
  const mcp_config_modifier_1 = require("./mcp-config-modifier");
44
+ const i18n_1 = require("../i18n");
44
45
  const BUILTIN_TEMPLATES = {
45
46
  miniprogram: {
46
47
  url: 'https://static.cloudbase.net/cloudbase-examples/miniprogram-cloudbase-miniprogram-template.zip',
47
- name: '微信小程序 + CloudBase'
48
+ name: `${(0, i18n_1.t)('微信小程序')} + CloudBase`
48
49
  },
49
50
  react: {
50
51
  url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-react-template.zip',
51
- name: 'Web 应用 - React + CloudBase'
52
+ name: `${(0, i18n_1.t)('Web 应用')} - React + CloudBase`
52
53
  },
53
54
  vue: {
54
55
  url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-vue-template.zip',
55
- name: 'Web 应用 - Vue + CloudBase'
56
+ name: `${(0, i18n_1.t)('Web 应用')} - Vue + CloudBase`
56
57
  },
57
58
  uniapp: {
58
59
  url: 'https://static.cloudbase.net/cloudbase-examples/universal-cloudbase-uniapp-template.zip',
59
- name: '跨端应用 - UniApp + CloudBase'
60
+ name: `${(0, i18n_1.t)('跨端应用')} - UniApp + CloudBase`
60
61
  },
61
62
  rules: {
62
63
  url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-project.zip',
63
- name: 'AI 规则和配置'
64
+ name: `${(0, i18n_1.t)('AI 规则和配置')}`
64
65
  }
65
66
  };
66
67
  class TemplateManager {
@@ -86,10 +87,10 @@ class TemplateManager {
86
87
  yield this.downloadGitTemplateToTemp(gitInfo, tempDir, log);
87
88
  }
88
89
  else {
89
- throw new error_1.CloudBaseError(`不支持的模板来源: ${typeof source === 'string' ? source : JSON.stringify(source)}`);
90
+ throw new error_1.CloudBaseError((0, i18n_1.t)('不支持的模板来源: {{source}}', { source: typeof source === 'string' ? source : JSON.stringify(source) }));
90
91
  }
91
92
  yield this.copyFromTempToTarget(tempDir, targetPath, force, log);
92
- log.info(`✅ 模板拉取完成: ${targetPath}`);
93
+ log.info((0, i18n_1.t)('✅ 模板拉取完成: {{targetPath}}', { targetPath }));
93
94
  }
94
95
  finally {
95
96
  yield fs_extra_1.default.remove(tempDir);
@@ -100,7 +101,7 @@ class TemplateManager {
100
101
  return Object.keys(BUILTIN_TEMPLATES).includes(source);
101
102
  }
102
103
  isZipUrl(source) {
103
- console.log("zip source", source);
104
+ console.log('zip source', source);
104
105
  if (!source || typeof source !== 'string') {
105
106
  return false;
106
107
  }
@@ -179,7 +180,7 @@ class TemplateManager {
179
180
  branch: 'main'
180
181
  };
181
182
  }
182
- throw new error_1.CloudBaseError(`无法解析 Git URL: ${url}`);
183
+ throw new error_1.CloudBaseError((0, i18n_1.t)('无法解析 Git URL: {{url}}', { url }));
183
184
  }
184
185
  buildGitUrl(gitInfo) {
185
186
  if (gitInfo.platform === 'github') {
@@ -191,7 +192,7 @@ class TemplateManager {
191
192
  else if (gitInfo.platform === 'cnb') {
192
193
  return `https://cnb.cool/${gitInfo.owner}/${gitInfo.repo}.git`;
193
194
  }
194
- throw new error_1.CloudBaseError(`不支持的 Git 平台: ${gitInfo.platform}`);
195
+ throw new error_1.CloudBaseError((0, i18n_1.t)('不支持的 Git 平台: {{platform}}', { platform: gitInfo.platform }));
195
196
  }
196
197
  cloneWithSubpathOptimized(gitUrl, tempDir, gitInfo, log) {
197
198
  return __awaiter(this, void 0, void 0, function* () {
@@ -204,7 +205,7 @@ class TemplateManager {
204
205
  ]);
205
206
  const subpathFull = path_1.default.join(tempDir, gitInfo.subpath);
206
207
  if (!(yield fs_extra_1.default.pathExists(subpathFull))) {
207
- throw new error_1.CloudBaseError(`子目录不存在: ${gitInfo.subpath}`);
208
+ throw new error_1.CloudBaseError((0, i18n_1.t)('子目录不存在: {{subpath}}', { subpath: gitInfo.subpath }));
208
209
  }
209
210
  const tempContentDir = path_1.default.join(tempDir, '.temp-content-' + Date.now());
210
211
  yield fs_extra_1.default.move(subpathFull, tempContentDir);
@@ -221,7 +222,7 @@ class TemplateManager {
221
222
  yield fs_extra_1.default.remove(tempContentDir);
222
223
  }
223
224
  catch (error) {
224
- throw new error_1.CloudBaseError(`克隆子目录失败: ${error.message}`, { original: error });
225
+ throw new error_1.CloudBaseError((0, i18n_1.t)('克隆子目录失败: {{error}}', { error: error.message }), { original: error });
225
226
  }
226
227
  });
227
228
  }
@@ -233,7 +234,7 @@ class TemplateManager {
233
234
  yield tempGit.checkout(gitInfo.branch);
234
235
  const subpathFull = path_1.default.join(tempDir, gitInfo.subpath);
235
236
  if (!(yield fs_extra_1.default.pathExists(subpathFull))) {
236
- throw new error_1.CloudBaseError(`子目录不存在: ${gitInfo.subpath}`);
237
+ throw new error_1.CloudBaseError((0, i18n_1.t)('子目录不存在: {{subpath}}', { subpath: gitInfo.subpath }));
237
238
  }
238
239
  const tempContentDir = path_1.default.join(tempDir, '.temp-content-' + Date.now());
239
240
  yield fs_extra_1.default.move(subpathFull, tempContentDir);
@@ -250,7 +251,7 @@ class TemplateManager {
250
251
  yield fs_extra_1.default.remove(tempContentDir);
251
252
  }
252
253
  catch (error) {
253
- throw new error_1.CloudBaseError(`克隆子目录失败: ${error.message}`, { original: error });
254
+ throw new error_1.CloudBaseError((0, i18n_1.t)('克隆子目录失败: {{error}}', { error: error.message }), { original: error });
254
255
  }
255
256
  });
256
257
  }
@@ -258,36 +259,36 @@ class TemplateManager {
258
259
  return __awaiter(this, void 0, void 0, function* () {
259
260
  const template = BUILTIN_TEMPLATES[templateId];
260
261
  if (!template) {
261
- throw new error_1.CloudBaseError(`未知的内置模板: ${templateId}`);
262
+ throw new error_1.CloudBaseError((0, i18n_1.t)('未知的内置模板: {{templateId}}', { templateId }));
262
263
  }
263
- log.info(`📦 正在下载 ${template.name} 模板到临时目录...`);
264
+ log.info((0, i18n_1.t)('📦 正在下载 {{templateName}} 模板到临时目录...', { templateName: template.name }));
264
265
  try {
265
266
  const { downloadAndExtractRemoteZip } = yield Promise.resolve().then(() => __importStar(require('./tools/common')));
266
267
  yield downloadAndExtractRemoteZip(template.url, tempDir);
267
- log.info(`✅ ${template.name} 模板下载完成到临时目录`);
268
+ log.info((0, i18n_1.t)('✅ {{templateName}} 模板下载完成到临时目录', { templateName: template.name }));
268
269
  }
269
270
  catch (error) {
270
- throw new error_1.CloudBaseError(`下载内置模板失败: ${error.message}`, { original: error });
271
+ throw new error_1.CloudBaseError((0, i18n_1.t)('下载内置模板失败: {{error}}', { error: error.message }), { original: error });
271
272
  }
272
273
  });
273
274
  }
274
275
  downloadZipTemplateToTemp(zipUrl, tempDir, log) {
275
276
  return __awaiter(this, void 0, void 0, function* () {
276
- log.info(`📦 正在下载 zip 文件模板到临时目录: ${zipUrl}`);
277
+ log.info((0, i18n_1.t)('📦 正在下载 zip 文件模板到临时目录: {{zipUrl}}', { zipUrl }));
277
278
  try {
278
279
  const { downloadAndExtractRemoteZip } = yield Promise.resolve().then(() => __importStar(require('./tools/common')));
279
280
  yield downloadAndExtractRemoteZip(zipUrl, tempDir);
280
- log.info(`✅ zip 文件模板下载完成到临时目录`);
281
+ log.info((0, i18n_1.t)('✅ zip 文件模板下载完成到临时目录'));
281
282
  }
282
283
  catch (error) {
283
- throw new error_1.CloudBaseError(`下载 zip 文件模板失败: ${error.message}`, { original: error });
284
+ throw new error_1.CloudBaseError((0, i18n_1.t)('下载 zip 文件模板失败: {{error}}', { error: error.message }), { original: error });
284
285
  }
285
286
  });
286
287
  }
287
288
  downloadGitTemplateToTemp(gitInfo, tempDir, log) {
288
289
  return __awaiter(this, void 0, void 0, function* () {
289
290
  const gitUrl = this.buildGitUrl(gitInfo);
290
- log.info(`📦 正在从 ${gitInfo.platform} 下载模板到临时目录...`);
291
+ log.info((0, i18n_1.t)('📦 正在从 {{platform}} 下载模板到临时目录...', { platform: gitInfo.platform }));
291
292
  try {
292
293
  if (gitInfo.subpath) {
293
294
  yield this.cloneWithSubpathOptimized(gitUrl, tempDir, gitInfo, log);
@@ -295,22 +296,22 @@ class TemplateManager {
295
296
  else {
296
297
  yield this.git.clone(gitUrl, tempDir, ['--depth', '1', '--single-branch', '--branch', gitInfo.branch]);
297
298
  }
298
- log.info(`✅ Git 模板下载完成到临时目录`);
299
+ log.info((0, i18n_1.t)('✅ Git 模板下载完成到临时目录'));
299
300
  }
300
301
  catch (error) {
301
- throw new error_1.CloudBaseError(`Git 模板下载失败: ${error.message}`, { original: error });
302
+ throw new error_1.CloudBaseError((0, i18n_1.t)('Git 模板下载失败: {{error}}', { error: error.message }), { original: error });
302
303
  }
303
304
  });
304
305
  }
305
306
  copyFromTempToTarget(tempDir, targetPath, force, log) {
306
307
  return __awaiter(this, void 0, void 0, function* () {
307
- log.info(`📦 正在从临时目录复制到目标目录 ${targetPath}...`);
308
+ log.info((0, i18n_1.t)('📦 正在从临时目录复制到目标目录 {{targetPath}}...', { targetPath }));
308
309
  try {
309
310
  if (force) {
310
311
  if (yield fs_extra_1.default.pathExists(targetPath)) {
311
312
  const files = yield fs_extra_1.default.readdir(targetPath);
312
313
  if (files.length > 0) {
313
- log.info(`⚠️ 使用 --force 参数,将覆盖目标目录中的现有文件`);
314
+ log.info((0, i18n_1.t)('⚠️ 使用 --force 参数,将覆盖目标目录中的现有文件'));
314
315
  }
315
316
  }
316
317
  yield this.copyFilesWithOverwrite(tempDir, targetPath, log);
@@ -319,23 +320,23 @@ class TemplateManager {
319
320
  if (yield fs_extra_1.default.pathExists(targetPath)) {
320
321
  const files = yield fs_extra_1.default.readdir(targetPath);
321
322
  if (files.length > 0) {
322
- log.info(`ℹ️ 目标目录不为空,将跳过已存在的文件,只复制新文件`);
323
+ log.info((0, i18n_1.t)('ℹ️ 目标目录不为空,将跳过已存在的文件,只复制新文件'));
323
324
  }
324
325
  }
325
326
  yield this.copyFilesSkipExisting(tempDir, targetPath, log);
326
327
  }
327
- log.info(`✅ 模板复制完成`);
328
+ log.info((0, i18n_1.t)('✅ 模板复制完成'));
328
329
  try {
329
- log.debug('🔧 开始调用 MCP 配置修改器...');
330
+ log.debug((0, i18n_1.t)('🔧 开始调用 MCP 配置修改器...'));
330
331
  yield this.mcpConfigModifier.modifyMCPConfigs(targetPath, log);
331
- log.debug('✅ MCP 配置修改器调用完成');
332
+ log.debug((0, i18n_1.t)('✅ MCP 配置修改器调用完成'));
332
333
  }
333
334
  catch (error) {
334
- log.warn(`⚠️ MCP 配置修改失败: ${error.message}`);
335
+ log.warn((0, i18n_1.t)('⚠️ MCP 配置修改失败: {{error}}', { error: error.message }));
335
336
  }
336
337
  }
337
338
  catch (error) {
338
- throw new error_1.CloudBaseError(`复制模板失败: ${error.message}`, { original: error });
339
+ throw new error_1.CloudBaseError((0, i18n_1.t)('复制模板失败: {{error}}', { error: error.message }), { original: error });
339
340
  }
340
341
  });
341
342
  }
@@ -373,7 +374,7 @@ class TemplateManager {
373
374
  }
374
375
  }
375
376
  catch (error) {
376
- log.warn(`⚠️ 复制失败: ${path_1.default.relative(sourceDir, srcPath)} - ${error.message}`);
377
+ log.warn((0, i18n_1.t)('⚠️ 复制失败: {{path}} - {{error}}', { path: path_1.default.relative(sourceDir, srcPath), error: error.message }));
377
378
  }
378
379
  });
379
380
  const entries = yield fs_extra_1.default.readdir(sourceDir, { withFileTypes: true });
@@ -386,7 +387,7 @@ class TemplateManager {
386
387
  for (const chunk of chunks) {
387
388
  yield Promise.all(chunk);
388
389
  }
389
- log.info(`📊 复制统计: ${copiedCount} 个新文件复制, ${overwrittenCount} 个文件覆盖`);
390
+ log.info((0, i18n_1.t)('📊 复制统计: {{copiedCount}} 个新文件复制, {{overwrittenCount}} 个文件覆盖', { copiedCount, overwrittenCount }));
390
391
  });
391
392
  }
392
393
  copyFilesSkipExisting(sourceDir, targetDir, log) {
@@ -423,7 +424,7 @@ class TemplateManager {
423
424
  }
424
425
  }
425
426
  catch (error) {
426
- log.warn(`⚠️ 复制失败: ${path_1.default.relative(sourceDir, srcPath)} - ${error.message}`);
427
+ log.warn((0, i18n_1.t)('⚠️ 复制失败: {{path}} - {{error}}', { path: path_1.default.relative(sourceDir, srcPath), error: error.message }));
427
428
  }
428
429
  });
429
430
  const entries = yield fs_extra_1.default.readdir(sourceDir, { withFileTypes: true });
@@ -436,7 +437,7 @@ class TemplateManager {
436
437
  for (const chunk of chunks) {
437
438
  yield Promise.all(chunk);
438
439
  }
439
- log.info(`📊 复制统计: ${copiedCount} 个文件复制完成, ${skippedCount} 个文件跳过`);
440
+ log.info((0, i18n_1.t)('📊 复制统计: {{copiedCount}} 个文件复制完成, {{skippedCount}} 个文件跳过', { copiedCount, skippedCount }));
440
441
  });
441
442
  }
442
443
  chunkArray(array, chunkSize) {
@@ -24,13 +24,14 @@ const log_1 = require("./log");
24
24
  const fs_2 = require("./fs");
25
25
  const output_1 = require("./output");
26
26
  const reporter_1 = require("./reporter");
27
+ const i18n_1 = require("../i18n");
27
28
  const listUrl = 'https://tcli.service.tcloudbase.com/templates';
28
29
  const getTemplateAddress = (templatePath) => `https://7463-tcli-1258016615.tcb.qcloud.la/cloudbase-templates/${templatePath}.zip`;
29
30
  function downloadTemplate(options = {}) {
30
31
  return __awaiter(this, void 0, void 0, function* () {
31
32
  let { templateUri, appName, newProject, projectPath = process.cwd() } = options;
32
33
  const templates = yield (0, output_1.execWithLoading)(() => (0, net_1.fetch)(listUrl), {
33
- startTip: '获取应用模板列表中'
34
+ startTip: (0, i18n_1.t)('获取应用模板列表中')
34
35
  });
35
36
  let templateName;
36
37
  let tempateId;
@@ -41,7 +42,7 @@ function downloadTemplate(options = {}) {
41
42
  const { selectTemplateName } = yield (0, enquirer_1.prompt)({
42
43
  type: 'select',
43
44
  name: 'selectTemplateName',
44
- message: '请选择应用模板',
45
+ message: (0, i18n_1.t)('请选择应用模板'),
45
46
  choices: templates.map((item) => item.name)
46
47
  });
47
48
  templateName = selectTemplateName;
@@ -50,7 +51,7 @@ function downloadTemplate(options = {}) {
50
51
  ? templates.find((item) => item.name === templateName)
51
52
  : templates.find((item) => item.path === tempateId);
52
53
  if (!selectedTemplate) {
53
- log_1.logger.info(`模板 \`${templateName || tempateId}\` 不存在`);
54
+ log_1.logger.info((0, i18n_1.t)('模板 `{{template}}` 不存在', { template: templateName || tempateId }));
54
55
  return;
55
56
  }
56
57
  if (newProject) {
@@ -58,7 +59,7 @@ function downloadTemplate(options = {}) {
58
59
  const { projectName } = yield (0, enquirer_1.prompt)({
59
60
  type: 'input',
60
61
  name: 'projectName',
61
- message: '请输入项目名称',
62
+ message: (0, i18n_1.t)('请输入项目名称'),
62
63
  initial: selectedTemplate.path
63
64
  });
64
65
  appName = projectName;
@@ -68,11 +69,11 @@ function downloadTemplate(options = {}) {
68
69
  const { cover } = yield (0, enquirer_1.prompt)({
69
70
  type: 'confirm',
70
71
  name: 'cover',
71
- message: `已存在同名文件夹:${appName},是否覆盖?`,
72
+ message: (0, i18n_1.t)('已存在同名文件夹:{{name}},是否覆盖?', { name: appName }),
72
73
  initial: false
73
74
  });
74
75
  if (!cover) {
75
- throw new error_1.CloudBaseError('操作终止!');
76
+ throw new error_1.CloudBaseError((0, i18n_1.t)('操作终止!'));
76
77
  }
77
78
  else {
78
79
  fs_extra_1.default.removeSync(projectPath);
@@ -83,7 +84,7 @@ function downloadTemplate(options = {}) {
83
84
  yield (0, reporter_1.templateDownloadReport)(selectedTemplate.path, selectedTemplate.name);
84
85
  yield extractTemplate(projectPath, selectedTemplate.path, selectedTemplate.url);
85
86
  }), {
86
- startTip: '下载文件中'
87
+ startTip: (0, i18n_1.t)('下载文件中')
87
88
  });
88
89
  return projectPath;
89
90
  });
@@ -94,10 +95,10 @@ function extractTemplate(projectPath, templatePath, remoteUrl) {
94
95
  const url = remoteUrl || getTemplateAddress(templatePath);
95
96
  return (0, net_1.fetchStream)(url).then((res) => __awaiter(this, void 0, void 0, function* () {
96
97
  if (!res) {
97
- throw new error_1.CloudBaseError('请求异常');
98
+ throw new error_1.CloudBaseError((0, i18n_1.t)('请求异常'));
98
99
  }
99
100
  if (res.status !== 200) {
100
- throw new error_1.CloudBaseError('未找到文件');
101
+ throw new error_1.CloudBaseError((0, i18n_1.t)('未找到文件'));
101
102
  }
102
103
  yield (0, toolbox_1.unzipStream)(res.body, projectPath);
103
104
  }));
@@ -17,14 +17,15 @@ const toolbox_1 = require("@cloudbase/toolbox");
17
17
  const path_1 = __importDefault(require("path"));
18
18
  const fs_extra_1 = __importDefault(require("fs-extra"));
19
19
  const unzipper_1 = __importDefault(require("unzipper"));
20
+ const i18n_1 = require("../../i18n");
20
21
  function downloadAndExtractRemoteZip(downloadUrl, targetPath) {
21
22
  return __awaiter(this, void 0, void 0, function* () {
22
23
  const downloadResponse = yield (0, toolbox_1.fetchStream)(downloadUrl);
23
24
  if (!downloadResponse.ok) {
24
- throw new Error(`下载失败,状态码: ${downloadResponse.status}`);
25
+ throw new Error((0, i18n_1.t)('下载失败,状态码: {{status}}', { status: downloadResponse.status }));
25
26
  }
26
27
  if (!downloadResponse.body) {
27
- throw new Error('下载响应中没有数据流');
28
+ throw new Error((0, i18n_1.t)('下载响应中没有数据流'));
28
29
  }
29
30
  const dirPath = path_1.default.resolve(targetPath);
30
31
  yield fs_extra_1.default.ensureDir(dirPath);
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getGatewayUrl = exports.EUrl = exports.EDomain = void 0;
4
+ const i18n_1 = require("../i18n");
5
+ exports.EDomain = {
6
+ Console: (0, i18n_1.isIntl)() ? 'console.tencentcloud.com' : 'console.cloud.tencent.com',
7
+ Official: (0, i18n_1.isIntl)() ? 'www.tencentcloud.com' : 'cloud.tencent.com',
8
+ Gateway: (0, i18n_1.isIntl)() ? 'api.intl.tcloudbasegateway.com' : 'api.tcloudbasegateway.com',
9
+ Tcb: (0, i18n_1.isIntl)() ? 'tcb.tencentcloud.com' : 'tcb.cloud.tencent.com',
10
+ Docs: 'docs.cloudbase.net'
11
+ };
12
+ exports.EUrl = Object.assign(Object.assign({}, ((domains) => Object.keys(domains).reduce((acc, key) => {
13
+ acc[key] = `https://${domains[key]}`;
14
+ return acc;
15
+ }, {}))(exports.EDomain)), { Docs: `https://${exports.EDomain.Docs}` + ((0, i18n_1.isIntl)() ? '/en' : ''), DevPlatform: `https://${exports.EDomain.Tcb}/dev`, TcbConsole: `https://${exports.EDomain.Console}/tcb`, CloudAdmin: `https://${exports.EDomain.Tcb}/cloud-admin` });
16
+ function getGatewayUrl(envId) {
17
+ return `https://${envId}.${exports.EDomain.Gateway}`;
18
+ }
19
+ exports.getGatewayUrl = getGatewayUrl;
@@ -4,6 +4,7 @@ exports.validateCpuMem = exports.validateIp = exports.assertHas = exports.assert
4
4
  const error_1 = require("../error");
5
5
  const constant_1 = require("../constant");
6
6
  const run_1 = require("../run");
7
+ const i18n_1 = require("../i18n");
7
8
  function assertTruthy(val, errMsg) {
8
9
  let ok;
9
10
  if (Array.isArray(val)) {
@@ -39,8 +40,11 @@ const validateCpuMem = (cpuInput, memInput) => {
39
40
  let memSet = (0, run_1.convertNumber)(memInput);
40
41
  let validMemSet = constant_1.CPU_MEM_OPTS.find(({ cpu }) => cpu === cpuSet);
41
42
  if (!validMemSet || !validMemSet.mems.length || !validMemSet.mems.includes(memSet)) {
42
- throw new error_1.CloudBaseError(`cpu 与 mem 规格不匹配,当前规格:cpu: ${cpuInput}, mem: ${memInput}
43
- 请使用下列规格组合之一:${constant_1.CPU_MEM_OPTS.map(({ cpu, mems }) => `${cpu}-${mems.join('/')}`).join(',')}`);
43
+ throw new error_1.CloudBaseError((0, i18n_1.t)('cpu 与 mem 规格不匹配,当前规格:cpu: {{cpu}}, mem: {{mem}}\n请使用下列规格组合之一:{{options}}', {
44
+ cpu: cpuInput,
45
+ mem: memInput,
46
+ options: constant_1.CPU_MEM_OPTS.map(({ cpu, mems }) => `${cpu}-${mems.join('/')}`).join(',')
47
+ }));
44
48
  }
45
49
  return { cpuOutput: cpuSet, memOutput: memSet };
46
50
  }
@@ -48,7 +52,9 @@ const validateCpuMem = (cpuInput, memInput) => {
48
52
  let cpuSet = (0, run_1.convertNumber)(cpuInput);
49
53
  let validSet = constant_1.CPU_MEM_OPTS.find(({ cpu }) => cpu === cpuSet);
50
54
  if (!validSet) {
51
- throw new error_1.CloudBaseError(`不支持当前 cpu 规格,请使用下列 cpu 规格之一:${constant_1.CPU_MEM_OPTS.map(({ cpu }) => cpu).join(',')}`);
55
+ throw new error_1.CloudBaseError((0, i18n_1.t)('不支持当前 cpu 规格,请使用下列 cpu 规格之一:{{options}}', {
56
+ options: constant_1.CPU_MEM_OPTS.map(({ cpu }) => cpu).join(',')
57
+ }));
52
58
  }
53
59
  return { cpuOutput: cpuSet, memOutput: constant_1.CPU_MEM_OPTS.find(({ cpu }) => cpu === cpuSet).mems[0] };
54
60
  }
@@ -56,7 +62,9 @@ const validateCpuMem = (cpuInput, memInput) => {
56
62
  let memSet = (0, run_1.convertNumber)(memInput);
57
63
  let validSet = constant_1.CPU_MEM_OPTS.find(({ mems }) => mems.includes(memSet));
58
64
  if (!validSet) {
59
- throw new error_1.CloudBaseError(`不支持当前 mem 规格,请使用下列 mem 规格之一:${constant_1.CPU_MEM_OPTS.map(({ mems }) => mems.join('/')).join(',')}`);
65
+ throw new error_1.CloudBaseError((0, i18n_1.t)('不支持当前 mem 规格,请使用下列 mem 规格之一:{{options}}', {
66
+ options: constant_1.CPU_MEM_OPTS.map(({ mems }) => mems.join('/')).join(',')
67
+ }));
60
68
  }
61
69
  return { cpuOutput: validSet.cpu, memOutput: memSet };
62
70
  }
@@ -0,0 +1,160 @@
1
+ # 国际化功能使用说明
2
+
3
+ ## 概述
4
+
5
+ 本项目使用 i18next 实现国际化功能,支持中文和英文两种语言。
6
+
7
+ ## 使用方法
8
+
9
+ ### 1. 在代码中使用
10
+
11
+ ```typescript
12
+ import { t } from 'src/i18n'
13
+
14
+ // 使用 t() 函数包装需要国际化的文本
15
+ console.log(t('你好')) // 你好
16
+ // 插值
17
+ console.log(t('你好, {{name}}', { name: 'TCB' })) // 你好, TCB
18
+ ```
19
+
20
+ ### 2. 扫描和生成标识
21
+
22
+ ```bash
23
+ npm run i18n:scan
24
+ ```
25
+
26
+ ## 工作流程
27
+
28
+ 1. **开发阶段**:在代码中使用 `t('文本内容')` 包装需要国际化的文本
29
+ 2. **扫描阶段**:运行 `npm run i18n:scan` 扫描代码并提取文本
30
+ 3. **生成阶段**:自动为每个文本生成唯一的标识(格式:`k_${hash}`)
31
+ 4. **增量管理**:在 `locales/inc/` 目录下生成增量词条文件
32
+ 5. **翻译阶段**:开发者在 `inc/en.json` 中填写英文翻译
33
+ 6. **同步阶段**:运行 `npm run i18n:sync` 将翻译同步到正式语言文件,并清理增量目录(可选)
34
+
35
+ ## 文件结构
36
+
37
+ ```
38
+ locales/
39
+ ├── scripts/ # 脚本目录
40
+ │ ├── generate.js # 标识生成脚本
41
+ │ └── sync.js # 翻译同步脚本
42
+ ├── inc/ # 增量词条目录(临时)
43
+ │ ├── zh.json # 增量中文词条
44
+ │ └── en.json # 增量英文翻译
45
+ ├── langs/ # 正式语言文件
46
+ │ ├── zh.json # 中文语言文件
47
+ │ └── en.json # 英文语言文件
48
+ ├── i18next-scanner.config.js # 扫描配置
49
+ ├── mapping.json # 文本到标识的映射文件
50
+ └── README.md # 本说明文档
51
+ ```
52
+
53
+ ## 标识生成规则
54
+
55
+ - 标识格式:`k_${hash}`
56
+ - 相同文本生成相同标识
57
+ - 使用简单的字符串哈希算法
58
+ - 支持增量更新,只处理新增文本
59
+
60
+ ## 核心特性
61
+
62
+ - 已有英文翻译不会被覆盖
63
+ - 自动识别和保留现有翻译
64
+ - 增量词条单独放在 `inc` 目录下
65
+ - 与正式语言文件分离,便于管理
66
+ - 自动化同步增量词条
67
+ - 只处理新增或修改的文本
68
+ - 自动移除不再使用的词条
69
+
70
+ ## 命令说明
71
+
72
+ - **`npm run i18n:scan`**:完整的国际化处理流程
73
+ - 扫描代码中的 `t()` 函数调用
74
+ - 提取需要国际化的文本
75
+ - 生成唯一标识
76
+ - 更新语言文件
77
+ - 生成增量词条文件
78
+
79
+ - **`npm run i18n:sync`**:同步增量翻译
80
+ - 将 `inc/en.json` 中的翻译同步到正式语言文件
81
+ - 只同步有效的翻译(跳过 `__STRING_NOT_TRANSLATED__`)
82
+ - 自动更新 `langs/en.json`
83
+
84
+ ## 增量词条翻译流程
85
+
86
+ ### 步骤1:生成增量文件
87
+ ```bash
88
+ npm run i18n
89
+ ```
90
+ - 扫描代码,生成增量词条
91
+ - 在 `locales/inc/` 目录下创建文件
92
+
93
+ ### 步骤2:编辑翻译
94
+ 编辑 `locales/inc/en.json` 文件:
95
+ ```json
96
+ {
97
+ "k_4293de80": "Test Text 001",
98
+ "k_62b2fe13": "Welcome to CloudBase CLI",
99
+ "k_28810860": "Hehe Haha",
100
+ "k_29c6c120": "Available configurations:"
101
+ }
102
+ ```
103
+
104
+ ### 步骤3:同步翻译
105
+ ```bash
106
+ npm run i18n:sync
107
+ ```
108
+ - 将翻译同步到正式语言文件
109
+ - 显示同步进度和结果
110
+
111
+ ## 注意事项
112
+
113
+ 1. 确保在代码中使用 `t()` 函数包装需要国际化的文本
114
+ 2. 运行 `npm run i18n:scan` 后会自动生成增量词条文件
115
+ 3. 在 `inc/en.json` 中填写英文翻译
116
+ 4. 翻译完成后运行 `npm run i18n:sync` 同步到正式文件
117
+ 5. 同步完成后 `inc` 目录会自动清理,无需手动删除
118
+ 6. 语言文件中的 key 会自动更新为生成的标识
119
+
120
+ ## 示例
121
+
122
+ ### 源码
123
+ ```typescript
124
+ // 代码中
125
+ console.log(t('欢迎使用云开发 CLI'))
126
+ ```
127
+
128
+ ### 扫描后
129
+ ```json
130
+ // mapping.json
131
+ {
132
+ "欢迎使用云开发 CLI": "k_62b2fe13"
133
+ }
134
+
135
+ // inc/zh.json
136
+ {
137
+ "k_62b2fe13": "欢迎使用云开发 CLI"
138
+ }
139
+
140
+ // inc/en.json
141
+ {
142
+ "k_62b2fe13": "__STRING_NOT_TRANSLATED__"
143
+ }
144
+ ```
145
+
146
+ ### 翻译后
147
+ ```json
148
+ // inc/en.json
149
+ {
150
+ "k_62b2fe13": "Welcome to CloudBase CLI"
151
+ }
152
+ ```
153
+
154
+ ### 同步后
155
+ ```json
156
+ // langs/en.json
157
+ {
158
+ "k_62b2fe13": "Welcome to CloudBase CLI"
159
+ }
160
+ ```