@4399ywkf/cli 1.0.7 → 1.0.9

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 (254) hide show
  1. package/dist/templates/AntdStaticMethods/index.tsx +20 -0
  2. package/dist/templates/AppTheme.tsx +136 -0
  3. package/dist/templates/DIRECTORY_STRUCTURE.md +141 -0
  4. package/dist/templates/GlobalProvider/AppTheme.tsx +136 -0
  5. package/dist/templates/GlobalProvider/Locale.tsx +84 -0
  6. package/dist/templates/GlobalProvider/Query.tsx +12 -0
  7. package/dist/templates/GlobalProvider/StyleRegistry.tsx +9 -0
  8. package/dist/templates/GlobalProvider/index.tsx +23 -0
  9. package/dist/templates/HarmonyOS_Sans_Bold.woff2 +0 -0
  10. package/dist/templates/HarmonyOS_Sans_Medium.woff2 +0 -0
  11. package/dist/templates/HarmonyOS_Sans_Regular.woff2 +0 -0
  12. package/dist/templates/Locale.tsx +49 -54
  13. package/dist/templates/MainContentWrap.tsx +11 -15
  14. package/dist/templates/Query.tsx +12 -0
  15. package/dist/templates/StyleRegistry.tsx +9 -0
  16. package/dist/templates/ThemeContext.tsx +27 -24
  17. package/dist/templates/analyzeUnusedKeys.ts +506 -0
  18. package/dist/templates/app/.i18nrc.js +57 -0
  19. package/dist/templates/app/config/env/.env.public.tpl +2 -19
  20. package/dist/templates/app/config/jwt/index.ts +4 -4
  21. package/dist/templates/app/config/request/error-handler.ts +67 -0
  22. package/dist/templates/app/config/request/index.ts +127 -129
  23. package/dist/templates/app/config/request/interceptors.ts +118 -0
  24. package/dist/templates/app/config/request/token-manager.ts +23 -0
  25. package/dist/templates/app/config/request/types.ts +63 -0
  26. package/dist/templates/app/config/rspack/rspack.config.mjs +62 -61
  27. package/dist/templates/app/config/rspack/rspack.prod.mjs +41 -62
  28. package/dist/templates/app/docs/DIRECTORY_STRUCTURE.md +141 -0
  29. package/dist/templates/app/docs/glossary.md +11 -0
  30. package/dist/templates/app/locales/zh-CN/common.json +3 -0
  31. package/dist/templates/app/package.json.tpl +8 -25
  32. package/dist/templates/app/public/fonts/HarmonyOS_Sans_Bold.woff2 +0 -0
  33. package/dist/templates/app/public/fonts/HarmonyOS_Sans_Medium.woff2 +0 -0
  34. package/dist/templates/app/public/fonts/HarmonyOS_Sans_Regular.woff2 +0 -0
  35. package/dist/templates/app/react-app-env.d.ts +13 -8
  36. package/dist/templates/app/scripts/i18nWorkflow/analyzeUnusedKeys.ts +506 -0
  37. package/dist/templates/app/scripts/i18nWorkflow/cleanUnusedKeys.ts +344 -0
  38. package/dist/templates/app/scripts/i18nWorkflow/const.ts +18 -0
  39. package/dist/templates/app/scripts/i18nWorkflow/flattenLocaleKeys.ts +139 -0
  40. package/dist/templates/app/scripts/i18nWorkflow/genDefaultLocale.ts +19 -0
  41. package/dist/templates/app/scripts/i18nWorkflow/genDiff.ts +49 -0
  42. package/dist/templates/app/scripts/i18nWorkflow/i18nConfig.ts +7 -0
  43. package/dist/templates/app/scripts/i18nWorkflow/index.ts +11 -0
  44. package/dist/templates/app/scripts/i18nWorkflow/protectedPatterns.ts +91 -0
  45. package/dist/templates/app/scripts/i18nWorkflow/utils.ts +76 -0
  46. package/dist/templates/app/src/bootstrap/index.ts +34 -0
  47. package/dist/templates/app/src/components/AntdStaticMethods/index.tsx +20 -0
  48. package/dist/templates/app/src/config/env.ts +84 -0
  49. package/dist/templates/app/src/index.tsx +13 -51
  50. package/dist/templates/app/src/layout/GlobalProvider/AppTheme.tsx +136 -0
  51. package/dist/templates/app/src/layout/GlobalProvider/Locale.tsx +84 -0
  52. package/dist/templates/app/src/layout/GlobalProvider/Query.tsx +12 -0
  53. package/dist/templates/app/src/layout/GlobalProvider/StyleRegistry.tsx +9 -0
  54. package/dist/templates/app/src/layout/GlobalProvider/index.tsx +23 -0
  55. package/dist/templates/app/src/layout/Locale.tsx +14 -18
  56. package/dist/templates/app/src/layout/MainContentWrap.tsx +11 -15
  57. package/dist/templates/app/src/layout/ThemeContext.tsx +27 -24
  58. package/dist/templates/app/src/locales/default/common.ts +3 -1
  59. package/dist/templates/app/src/locales/utils.ts +23 -0
  60. package/dist/templates/app/src/micro/garfish.ts +53 -0
  61. package/dist/templates/app/src/pages/base/index.tsx +280 -25
  62. package/dist/templates/app/src/routes.tsx +21 -12
  63. package/dist/templates/app/src/types/global.d.ts +19 -0
  64. package/dist/templates/app/src/utils/index.ts +3 -1
  65. package/dist/templates/app/store/middleware/createDevtools.ts +7 -7
  66. package/dist/templates/app/tsconfig.json +19 -3
  67. package/dist/templates/base/index.tsx +280 -25
  68. package/dist/templates/bootstrap/index.ts +34 -0
  69. package/dist/templates/cleanUnusedKeys.ts +344 -0
  70. package/dist/templates/common.json +3 -0
  71. package/dist/templates/common.ts +3 -1
  72. package/dist/templates/components/AntdStaticMethods/index.tsx +20 -0
  73. package/dist/templates/config/env/.env.public.tpl +2 -19
  74. package/dist/templates/config/env.ts +84 -0
  75. package/dist/templates/config/jwt/index.ts +4 -4
  76. package/dist/templates/config/request/error-handler.ts +67 -0
  77. package/dist/templates/config/request/index.ts +127 -129
  78. package/dist/templates/config/request/interceptors.ts +118 -0
  79. package/dist/templates/config/request/token-manager.ts +23 -0
  80. package/dist/templates/config/request/types.ts +63 -0
  81. package/dist/templates/config/rspack/rspack.config.mjs +62 -61
  82. package/dist/templates/config/rspack/rspack.prod.mjs +41 -62
  83. package/dist/templates/const.ts +18 -0
  84. package/dist/templates/createDevtools.ts +7 -7
  85. package/dist/templates/default/common.ts +3 -1
  86. package/dist/templates/docs/DIRECTORY_STRUCTURE.md +141 -0
  87. package/dist/templates/docs/glossary.md +11 -0
  88. package/dist/templates/env/.env.public.tpl +2 -19
  89. package/dist/templates/env.ts +83 -2
  90. package/dist/templates/error-handler.ts +67 -0
  91. package/dist/templates/flattenLocaleKeys.ts +139 -0
  92. package/dist/templates/fonts/HarmonyOS_Sans_Bold.woff2 +0 -0
  93. package/dist/templates/fonts/HarmonyOS_Sans_Medium.woff2 +0 -0
  94. package/dist/templates/fonts/HarmonyOS_Sans_Regular.woff2 +0 -0
  95. package/dist/templates/garfish.ts +53 -0
  96. package/dist/templates/genDefaultLocale.ts +19 -0
  97. package/dist/templates/genDiff.ts +49 -0
  98. package/dist/templates/global.d.ts +19 -0
  99. package/dist/templates/glossary.md +11 -0
  100. package/dist/templates/i18nConfig.ts +7 -0
  101. package/dist/templates/i18nWorkflow/analyzeUnusedKeys.ts +506 -0
  102. package/dist/templates/i18nWorkflow/cleanUnusedKeys.ts +344 -0
  103. package/dist/templates/i18nWorkflow/const.ts +18 -0
  104. package/dist/templates/i18nWorkflow/flattenLocaleKeys.ts +139 -0
  105. package/dist/templates/i18nWorkflow/genDefaultLocale.ts +19 -0
  106. package/dist/templates/i18nWorkflow/genDiff.ts +49 -0
  107. package/dist/templates/i18nWorkflow/i18nConfig.ts +7 -0
  108. package/dist/templates/i18nWorkflow/index.ts +11 -0
  109. package/dist/templates/i18nWorkflow/protectedPatterns.ts +91 -0
  110. package/dist/templates/i18nWorkflow/utils.ts +76 -0
  111. package/dist/templates/index.tsx +280 -25
  112. package/dist/templates/interceptors.ts +118 -0
  113. package/dist/templates/jwt/index.ts +4 -4
  114. package/dist/templates/layout/GlobalProvider/AppTheme.tsx +136 -0
  115. package/dist/templates/layout/GlobalProvider/Locale.tsx +84 -0
  116. package/dist/templates/layout/GlobalProvider/Query.tsx +12 -0
  117. package/dist/templates/layout/GlobalProvider/StyleRegistry.tsx +9 -0
  118. package/dist/templates/layout/GlobalProvider/index.tsx +23 -0
  119. package/dist/templates/layout/Locale.tsx +14 -18
  120. package/dist/templates/layout/MainContentWrap.tsx +11 -15
  121. package/dist/templates/layout/ThemeContext.tsx +27 -24
  122. package/dist/templates/locales/default/common.ts +3 -1
  123. package/dist/templates/locales/utils.ts +23 -0
  124. package/dist/templates/locales/zh-CN/common.json +3 -0
  125. package/dist/templates/micro/garfish.ts +53 -0
  126. package/dist/templates/middleware/createDevtools.ts +7 -7
  127. package/dist/templates/package.json.tpl +8 -25
  128. package/dist/templates/page.tsx +21 -19
  129. package/dist/templates/pages/base/index.tsx +280 -25
  130. package/dist/templates/protectedPatterns.ts +91 -0
  131. package/dist/templates/public/fonts/HarmonyOS_Sans_Bold.woff2 +0 -0
  132. package/dist/templates/public/fonts/HarmonyOS_Sans_Medium.woff2 +0 -0
  133. package/dist/templates/public/fonts/HarmonyOS_Sans_Regular.woff2 +0 -0
  134. package/dist/templates/react-app-env.d.ts +13 -8
  135. package/dist/templates/request/error-handler.ts +67 -0
  136. package/dist/templates/request/index.ts +127 -129
  137. package/dist/templates/request/interceptors.ts +118 -0
  138. package/dist/templates/request/token-manager.ts +23 -0
  139. package/dist/templates/request/types.ts +63 -0
  140. package/dist/templates/routes.tsx +21 -12
  141. package/dist/templates/rspack/rspack.config.mjs +62 -61
  142. package/dist/templates/rspack/rspack.prod.mjs +41 -62
  143. package/dist/templates/rspack.config.mjs +62 -61
  144. package/dist/templates/rspack.prod.mjs +41 -62
  145. package/dist/templates/scripts/i18nWorkflow/analyzeUnusedKeys.ts +506 -0
  146. package/dist/templates/scripts/i18nWorkflow/cleanUnusedKeys.ts +344 -0
  147. package/dist/templates/scripts/i18nWorkflow/const.ts +18 -0
  148. package/dist/templates/scripts/i18nWorkflow/flattenLocaleKeys.ts +139 -0
  149. package/dist/templates/scripts/i18nWorkflow/genDefaultLocale.ts +19 -0
  150. package/dist/templates/scripts/i18nWorkflow/genDiff.ts +49 -0
  151. package/dist/templates/scripts/i18nWorkflow/i18nConfig.ts +7 -0
  152. package/dist/templates/scripts/i18nWorkflow/index.ts +11 -0
  153. package/dist/templates/scripts/i18nWorkflow/protectedPatterns.ts +91 -0
  154. package/dist/templates/scripts/i18nWorkflow/utils.ts +76 -0
  155. package/dist/templates/src/bootstrap/index.ts +34 -0
  156. package/dist/templates/src/components/AntdStaticMethods/index.tsx +20 -0
  157. package/dist/templates/src/config/env.ts +84 -0
  158. package/dist/templates/src/index.tsx +13 -51
  159. package/dist/templates/src/layout/GlobalProvider/AppTheme.tsx +136 -0
  160. package/dist/templates/src/layout/GlobalProvider/Locale.tsx +84 -0
  161. package/dist/templates/src/layout/GlobalProvider/Query.tsx +12 -0
  162. package/dist/templates/src/layout/GlobalProvider/StyleRegistry.tsx +9 -0
  163. package/dist/templates/src/layout/GlobalProvider/index.tsx +23 -0
  164. package/dist/templates/src/layout/Locale.tsx +14 -18
  165. package/dist/templates/src/layout/MainContentWrap.tsx +11 -15
  166. package/dist/templates/src/layout/ThemeContext.tsx +27 -24
  167. package/dist/templates/src/locales/default/common.ts +3 -1
  168. package/dist/templates/src/locales/utils.ts +23 -0
  169. package/dist/templates/src/micro/garfish.ts +53 -0
  170. package/dist/templates/src/pages/base/index.tsx +280 -25
  171. package/dist/templates/src/routes.tsx +21 -12
  172. package/dist/templates/src/types/global.d.ts +19 -0
  173. package/dist/templates/src/utils/index.ts +3 -1
  174. package/dist/templates/store/middleware/createDevtools.ts +7 -7
  175. package/dist/templates/token-manager.ts +23 -0
  176. package/dist/templates/tsconfig.json +19 -3
  177. package/dist/templates/type.ts +23 -24
  178. package/dist/templates/types/global.d.ts +19 -0
  179. package/dist/templates/utils/index.ts +3 -1
  180. package/dist/templates/utils.ts +23 -0
  181. package/dist/templates/zh-CN/common.json +3 -0
  182. package/package.json +19 -21
  183. package/dist/templates/app/config/sentry/sentry.config.ts +0 -188
  184. package/dist/templates/app/src/hooks/useRouteTitle.tsx +0 -36
  185. package/dist/templates/app/src/hooks/useSentry.ts +0 -92
  186. package/dist/templates/app/src/pages/base/layout.tsx +0 -6
  187. package/dist/templates/app/src/pages/base/page.tsx +0 -25
  188. package/dist/templates/app/src/utils/env.ts +0 -3
  189. package/dist/templates/app/src/utils/format.ts +0 -21
  190. package/dist/templates/app/src/utils/getMicroApp.ts +0 -39
  191. package/dist/templates/app/src/utils/sentry.ts +0 -187
  192. package/dist/templates/app/src/utils/sentryDecorators.ts +0 -34
  193. package/dist/templates/app/src/utils/updateVersion.ts +0 -186
  194. package/dist/templates/base/layout.tsx +0 -6
  195. package/dist/templates/base/page.tsx +0 -25
  196. package/dist/templates/config/public/404.png +0 -0
  197. package/dist/templates/config/public/favicon.ico +0 -0
  198. package/dist/templates/config/public/images/banner_market_modal.webp +0 -0
  199. package/dist/templates/config/public/images/chatmode_chat_dark.webp +0 -0
  200. package/dist/templates/config/public/images/chatmode_chat_light.webp +0 -0
  201. package/dist/templates/config/public/images/chatmode_docs_dark.webp +0 -0
  202. package/dist/templates/config/public/images/chatmode_docs_light.webp +0 -0
  203. package/dist/templates/config/public/images/empty_topic_dark.webp +0 -0
  204. package/dist/templates/config/public/images/empty_topic_light.webp +0 -0
  205. package/dist/templates/config/public/images/screenshot_background.webp +0 -0
  206. package/dist/templates/config/public/images/theme_auto.webp +0 -0
  207. package/dist/templates/config/public/images/theme_dark.webp +0 -0
  208. package/dist/templates/config/public/images/theme_light.webp +0 -0
  209. package/dist/templates/config/public/index.html +0 -29
  210. package/dist/templates/config/sentry/sentry.config.ts +0 -188
  211. package/dist/templates/format.ts +0 -21
  212. package/dist/templates/getMicroApp.ts +0 -39
  213. package/dist/templates/hooks/useRouteTitle.tsx +0 -36
  214. package/dist/templates/hooks/useSentry.ts +0 -92
  215. package/dist/templates/layout.tsx +0 -6
  216. package/dist/templates/pages/base/layout.tsx +0 -6
  217. package/dist/templates/pages/base/page.tsx +0 -25
  218. package/dist/templates/sentry/sentry.config.ts +0 -188
  219. package/dist/templates/sentry.config.ts +0 -188
  220. package/dist/templates/sentry.ts +0 -187
  221. package/dist/templates/sentryDecorators.ts +0 -34
  222. package/dist/templates/src/hooks/useRouteTitle.tsx +0 -36
  223. package/dist/templates/src/hooks/useSentry.ts +0 -92
  224. package/dist/templates/src/pages/base/layout.tsx +0 -6
  225. package/dist/templates/src/pages/base/page.tsx +0 -25
  226. package/dist/templates/src/utils/env.ts +0 -3
  227. package/dist/templates/src/utils/format.ts +0 -21
  228. package/dist/templates/src/utils/getMicroApp.ts +0 -39
  229. package/dist/templates/src/utils/sentry.ts +0 -187
  230. package/dist/templates/src/utils/sentryDecorators.ts +0 -34
  231. package/dist/templates/src/utils/updateVersion.ts +0 -186
  232. package/dist/templates/updateVersion.ts +0 -186
  233. package/dist/templates/useRouteTitle.tsx +0 -36
  234. package/dist/templates/useSentry.ts +0 -92
  235. package/dist/templates/utils/env.ts +0 -3
  236. package/dist/templates/utils/format.ts +0 -21
  237. package/dist/templates/utils/getMicroApp.ts +0 -39
  238. package/dist/templates/utils/sentry.ts +0 -187
  239. package/dist/templates/utils/sentryDecorators.ts +0 -34
  240. package/dist/templates/utils/updateVersion.ts +0 -186
  241. /package/dist/templates/app/{config/public → public}/404.png +0 -0
  242. /package/dist/templates/app/{config/public → public}/favicon.ico +0 -0
  243. /package/dist/templates/app/{config/public → public}/images/banner_market_modal.webp +0 -0
  244. /package/dist/templates/app/{config/public → public}/images/chatmode_chat_dark.webp +0 -0
  245. /package/dist/templates/app/{config/public → public}/images/chatmode_chat_light.webp +0 -0
  246. /package/dist/templates/app/{config/public → public}/images/chatmode_docs_dark.webp +0 -0
  247. /package/dist/templates/app/{config/public → public}/images/chatmode_docs_light.webp +0 -0
  248. /package/dist/templates/app/{config/public → public}/images/empty_topic_dark.webp +0 -0
  249. /package/dist/templates/app/{config/public → public}/images/empty_topic_light.webp +0 -0
  250. /package/dist/templates/app/{config/public → public}/images/screenshot_background.webp +0 -0
  251. /package/dist/templates/app/{config/public → public}/images/theme_auto.webp +0 -0
  252. /package/dist/templates/app/{config/public → public}/images/theme_dark.webp +0 -0
  253. /package/dist/templates/app/{config/public → public}/images/theme_light.webp +0 -0
  254. /package/dist/templates/app/{config/public → public}/index.html +0 -0
@@ -0,0 +1,506 @@
1
+ /* eslint-disable unicorn/prefer-top-level-await */
2
+ import { consola } from 'consola';
3
+ import { colors } from 'consola/utils';
4
+ import { glob } from 'glob';
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+
8
+ import { IGNORED_FILES, PROTECTED_KEY_PATTERNS } from './protectedPatterns';
9
+
10
+ interface I18nKey {
11
+ fullKey: string;
12
+ key: string;
13
+ namespace: string;
14
+ }
15
+
16
+ interface UnusedKey extends I18nKey {
17
+ filePath: string;
18
+ }
19
+
20
+ /**
21
+ * Check if a key should be protected (considered as "used")
22
+ */
23
+ function isProtectedKey(namespace: string, key: string): boolean {
24
+ // Check if namespace is in protected list
25
+ if (PROTECTED_KEY_PATTERNS.includes(namespace)) {
26
+ return true;
27
+ }
28
+
29
+ // Check if key matches any protected pattern
30
+ const fullKey = `${namespace}.${key}`;
31
+ return PROTECTED_KEY_PATTERNS.some((pattern) => {
32
+ // Exact namespace match
33
+ if (pattern === namespace) return true;
34
+ // Partial key match (e.g., "error.code" matches "error.code.NOT_FOUND")
35
+ if (fullKey.startsWith(pattern + '.')) return true;
36
+ return false;
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Recursively extract all keys from a nested object
42
+ */
43
+ function extractKeysFromObject(obj: any, namespace: string, prefix: string = ''): I18nKey[] {
44
+ const keys: I18nKey[] = [];
45
+
46
+ for (const [key, value] of Object.entries(obj)) {
47
+ const fullKey = prefix ? `${prefix}.${key}` : key;
48
+
49
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
50
+ // Recursively extract keys from nested objects
51
+ keys.push(...extractKeysFromObject(value, namespace, fullKey));
52
+ } else {
53
+ // This is a leaf node (actual translation)
54
+ keys.push({
55
+ fullKey: `${namespace}:${fullKey}`,
56
+ key: fullKey,
57
+ namespace,
58
+ });
59
+ }
60
+ }
61
+
62
+ return keys;
63
+ }
64
+
65
+ /**
66
+ * Load all i18n keys from src/locales/default
67
+ */
68
+ function loadAllI18nKeys(): I18nKey[] {
69
+ const defaultLocalesPath = path.join(process.cwd(), 'src/locales/default');
70
+ const allKeys: I18nKey[] = [];
71
+
72
+ // Get all TypeScript files except index.ts and ignored files
73
+ const ignoredFiles: string[] = [...IGNORED_FILES];
74
+ const files = fs
75
+ .readdirSync(defaultLocalesPath)
76
+ .filter((f) => f.endsWith('.ts') && f !== 'index.ts' && !ignoredFiles.includes(f));
77
+
78
+ consola.info(`Found ${files.length} namespace files (ignored: ${ignoredFiles.join(', ')})`);
79
+
80
+ for (const file of files) {
81
+ const namespace = path.basename(file, '.ts');
82
+ const filePath = path.join(defaultLocalesPath, file);
83
+
84
+ try {
85
+ // Use require to load the TypeScript file (after it's compiled)
86
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
87
+ const loadedModule = require(filePath);
88
+ const translations = loadedModule.default || loadedModule;
89
+
90
+ const keys = extractKeysFromObject(translations, namespace);
91
+ allKeys.push(...keys);
92
+
93
+ consola.success(colors.cyan(namespace.padEnd(20)), colors.gray(`${keys.length} keys`));
94
+ } catch (error) {
95
+ consola.error(`Failed to load ${file}:`, error);
96
+ }
97
+ }
98
+
99
+ return allKeys;
100
+ }
101
+
102
+ /**
103
+ * Find all t() function calls in the codebase
104
+ */
105
+ async function findAllTranslationCalls(): Promise<Set<string>> {
106
+ const usedKeys = new Set<string>();
107
+
108
+ // Patterns to search for translation calls
109
+ const patterns = [
110
+ 'src/**/*.{ts,tsx,js,jsx}',
111
+ 'apps/desktop/src/**/*.{ts,tsx,js,jsx}',
112
+ 'packages/**/src/**/*.{ts,tsx,js,jsx}', // Include packages directory
113
+ '!**/*.test.{ts,tsx}',
114
+ '!**/*.spec.{ts,tsx}',
115
+ '!**/node_modules/**',
116
+ '!**/.next/**',
117
+ ];
118
+
119
+ consola.start('Scanning codebase for translation calls...');
120
+
121
+ const files = await glob(patterns);
122
+ consola.info(`Found ${files.length} files to scan`);
123
+
124
+ // Regular expressions to match translation calls
125
+ // Mark dynamic patterns with special flag to handle them differently
126
+ const regexPatterns: Array<{
127
+ // Whether this pattern captures dynamic keys
128
+ captureNs?: boolean;
129
+ isDynamic?: boolean;
130
+ pattern: RegExp; // Whether pattern can capture namespace
131
+ }> = [
132
+ // Static patterns
133
+ { pattern: /\bt[A-Z]?\w*\(\s*["'`]([^"'`]+)["'`]/g },
134
+ {
135
+ captureNs: true,
136
+ pattern: /\bt[A-Z]?\w*\(\s*["'`]([^"'`]+)["'`]\s*,\s*{[^}]*ns:\s*["'`]([^"'`]+)["'`]/g,
137
+ },
138
+ { pattern: /i18n\.t\(\s*["'`]([^"'`]+)["'`]/g },
139
+ { pattern: /\bt[A-Z]?\w*\([^)]*\?\s*["'`]([^"'`]+)["'`]/g },
140
+ { pattern: /\bt[A-Z]?\w*\([^)]*:\s*["'`]([^"'`]+)["'`]/g },
141
+ { pattern: /<Trans[^>]+i18nKey=["']([^"']+)["']/g },
142
+ { pattern: /<Trans[^>]+i18nKey={["']([^"']+)["']}/g },
143
+ { captureNs: true, pattern: /<Trans[^>]+i18nKey=["']([^"']+)["'][\S\s]*?ns=["']([^"']+)["']/g },
144
+ {
145
+ captureNs: true,
146
+ pattern: /<Trans[^>]+i18nKey={["']([^"']+)["']}[\S\s]*?ns={["']([^"']+)["']}/g,
147
+ },
148
+
149
+ // Dynamic patterns (template strings, concatenations, etc.)
150
+ // Pattern 1: t(`prefix.${var}.suffix`) - variable in the middle
151
+ { captureNs: false, isDynamic: true, pattern: /\bt[A-Z]?\w*\(\s*`([^$`]+)\${[^}]+}([^`]*)`/g },
152
+ // Pattern 2: t(`${var}.suffix`) - variable at the start
153
+ { captureNs: false, isDynamic: true, pattern: /\bt[A-Z]?\w*\(\s*`\${[^}]+}([^`]+)`/g },
154
+ // Pattern 3: t(`prefix.${var}.suffix`, { ns: 'namespace' }) - with explicit ns
155
+ {
156
+ captureNs: true,
157
+ isDynamic: true,
158
+ pattern: /\bt[A-Z]?\w*\(\s*`([^$`]*)\${[^}]+}([^`]*)`\s*,\s*{[^}]*ns:\s*["'`]([^"'`]+)["'`]/g,
159
+ },
160
+ // Pattern 4: t(`${var}.suffix`, { ns: 'namespace' }) - variable at start with ns
161
+ {
162
+ captureNs: true,
163
+ isDynamic: true,
164
+ pattern: /\bt[A-Z]?\w*\(\s*`\${[^}]+}([^`]+)`\s*,\s*{[^}]*ns:\s*["'`]([^"'`]+)["'`]/g,
165
+ },
166
+ // Pattern 5: String concatenation
167
+ { isDynamic: true, pattern: /\bt[A-Z]?\w*\(\s*["'`]([^"'`]+)["'`]\s*\+/g },
168
+ // Pattern 6: <Trans> with dynamic keys
169
+ { isDynamic: true, pattern: /<Trans[^>]+i18nKey={`([^$`]+)\${[^}]+}([^`]*)`}/g },
170
+ ];
171
+
172
+ let totalMatches = 0;
173
+
174
+ for (const file of files) {
175
+ const content = fs.readFileSync(file, 'utf8');
176
+
177
+ // Extract namespace from useTranslation hook
178
+ const useTranslationMatch = content.match(/useTranslation\(\s*["'`]([^"'`]+)["'`]\s*\)/g);
179
+ const useTranslationMultiMatch = content.match(/useTranslation\(\s*\[([^\]]+)]\s*\)/g);
180
+
181
+ // Extract aliases: const { t: tAuth } = useTranslation('auth')
182
+ const aliasPattern =
183
+ /const\s*{\s*t\s*:\s*(\w+)\s*}\s*=\s*useTranslation\(\s*["'`]([^"'`]+)["'`]\s*\)/g;
184
+ const aliasMatches = content.matchAll(aliasPattern);
185
+
186
+ const namespacesInFile = new Set<string>();
187
+ const aliasToNamespace = new Map<string, string>();
188
+
189
+ // Extract namespaces from useTranslation('namespace')
190
+ if (useTranslationMatch) {
191
+ for (const match of useTranslationMatch) {
192
+ const ns = match.match(/["'`]([^"'`]+)["'`]/)?.[1];
193
+ if (ns) namespacesInFile.add(ns);
194
+ }
195
+ }
196
+
197
+ // Extract namespaces from useTranslation(['ns1', 'ns2'])
198
+ if (useTranslationMultiMatch) {
199
+ for (const match of useTranslationMultiMatch) {
200
+ const nsArray = match.match(/\[([^\]]+)]/)?.[1];
201
+ if (nsArray) {
202
+ const namespaces = nsArray.match(/["'`]([^"'`]+)["'`]/g);
203
+ if (namespaces) {
204
+ for (const ns of namespaces) {
205
+ const cleanNs = ns.replaceAll(/["'`]/g, '');
206
+ namespacesInFile.add(cleanNs);
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ // Extract alias mappings (e.g., tAuth -> 'auth')
214
+ for (const match of aliasMatches) {
215
+ const alias = match[1];
216
+ const namespace = match[2];
217
+ aliasToNamespace.set(alias, namespace);
218
+ namespacesInFile.add(namespace);
219
+ }
220
+
221
+ // Find all t() calls
222
+ for (const { pattern: regex, captureNs, isDynamic } of regexPatterns) {
223
+ const matches = content.matchAll(regex);
224
+
225
+ for (const match of matches) {
226
+ totalMatches++;
227
+ const fullMatch = match[0];
228
+ const key = match[1];
229
+ let explicitNs: string | undefined;
230
+
231
+ // For patterns with captureNs, namespace is in a different position
232
+ if (captureNs && isDynamic) {
233
+ // Dynamic patterns with ns: match[1] + match[2] = key parts, match[3] = ns
234
+ explicitNs = match[3] || match[2]; // Try match[3] first, fall back to match[2]
235
+ } else if (captureNs) {
236
+ // Static patterns with ns: match[1] = key, match[2] = ns
237
+ explicitNs = match[2];
238
+ }
239
+
240
+ if (!key) continue;
241
+
242
+ // Extract function name (t, tAuth, tCommon, etc.)
243
+ const funcNameMatch = fullMatch.match(/\b(t[A-Z]?\w*)\(/);
244
+ const funcName = funcNameMatch?.[1] || 't';
245
+
246
+ // Check if it's an alias with known namespace
247
+ let aliasNamespace: string | undefined;
248
+ if (funcName !== 't' && aliasToNamespace.has(funcName)) {
249
+ aliasNamespace = aliasToNamespace.get(funcName);
250
+ }
251
+
252
+ // Handle dynamic keys differently
253
+ if (isDynamic) {
254
+ // For dynamic patterns, extract the static prefix/suffix
255
+ // e.g., t(`mcp.details.${var}.title`) -> "mcp.details." and ".title"
256
+ // e.g., t(`${var}.title`) -> ".title"
257
+ let prefix = '';
258
+ let suffix = '';
259
+
260
+ if (match[2] !== undefined) {
261
+ // Pattern has both prefix and suffix: match[1] = prefix, match[2] = suffix
262
+ prefix = match[1] || '';
263
+ suffix = match[2] || '';
264
+ } else {
265
+ // Pattern has only suffix (var at start): match[1] = suffix
266
+ suffix = match[1] || '';
267
+ }
268
+
269
+ // Combine prefix and suffix for the pattern
270
+ const pattern = (prefix + suffix).trim();
271
+ if (!pattern) continue; // Skip if nothing to protect
272
+
273
+ // Determine the namespace
274
+ let targetNs: string | undefined;
275
+ if (aliasNamespace) {
276
+ targetNs = aliasNamespace;
277
+ } else if (explicitNs) {
278
+ targetNs = explicitNs;
279
+ } else if (namespacesInFile.size === 1) {
280
+ targetNs = [...namespacesInFile][0];
281
+ } else if (namespacesInFile.size > 0) {
282
+ // Multiple namespaces, add prefix pattern for each
283
+ for (const ns of namespacesInFile) {
284
+ usedKeys.add(`${ns}:${pattern}*`);
285
+ }
286
+ continue;
287
+ }
288
+
289
+ if (targetNs) {
290
+ usedKeys.add(`${targetNs}:${pattern}*`);
291
+ }
292
+ continue;
293
+ }
294
+
295
+ // Handle static keys
296
+ if (explicitNs) {
297
+ // Has explicit namespace
298
+ usedKeys.add(`${explicitNs}:${key}`);
299
+ } else if (aliasNamespace) {
300
+ // Using alias (e.g., tAuth('key'))
301
+ usedKeys.add(`${aliasNamespace}:${key}`);
302
+ } else if (key.includes(':')) {
303
+ // Key already includes namespace (e.g., t('common:key'))
304
+ usedKeys.add(key);
305
+ } else {
306
+ // Use namespaces from useTranslation hook
307
+ if (namespacesInFile.size > 0) {
308
+ for (const ns of namespacesInFile) {
309
+ usedKeys.add(`${ns}:${key}`);
310
+ }
311
+ } else {
312
+ // Default to 'common' if no namespace found
313
+ usedKeys.add(`common:${key}`);
314
+ }
315
+ }
316
+ }
317
+ }
318
+ }
319
+
320
+ consola.success(`Found ${totalMatches} translation calls`);
321
+ consola.info(`Extracted ${usedKeys.size} unique keys`);
322
+
323
+ return usedKeys;
324
+ }
325
+
326
+ /**
327
+ * Find unused i18n keys
328
+ */
329
+ function findUnusedKeys(allKeys: I18nKey[], usedKeys: Set<string>): UnusedKey[] {
330
+ const unused: UnusedKey[] = [];
331
+ const protectedKeys: UnusedKey[] = [];
332
+
333
+ // Extract prefix patterns from usedKeys
334
+ // e.g., "discover:mcp.details.*" means any key starting with "mcp.details." in discover namespace
335
+ const prefixPatterns: Array<{ namespace: string; prefix: string }> = [];
336
+ for (const key of usedKeys) {
337
+ if (key.includes('*')) {
338
+ const [namespace, pattern] = key.split(':');
339
+ const prefix = pattern.replace(/\*$/, ''); // Remove trailing *
340
+ prefixPatterns.push({ namespace, prefix });
341
+ }
342
+ }
343
+
344
+ for (const keyInfo of allKeys) {
345
+ // Check if key is protected by configuration
346
+ if (isProtectedKey(keyInfo.namespace, keyInfo.key)) {
347
+ protectedKeys.push({
348
+ ...keyInfo,
349
+ filePath: `src/locales/default/${keyInfo.namespace}.ts`,
350
+ });
351
+ continue;
352
+ }
353
+
354
+ // Check if key matches any prefix pattern (from dynamic usage)
355
+ let matchesPrefix = false;
356
+ for (const { namespace, prefix } of prefixPatterns) {
357
+ if (keyInfo.namespace === namespace && keyInfo.key.startsWith(prefix)) {
358
+ matchesPrefix = true;
359
+ break;
360
+ }
361
+ }
362
+
363
+ if (matchesPrefix) {
364
+ protectedKeys.push({
365
+ ...keyInfo,
366
+ filePath: `src/locales/default/${keyInfo.namespace}.ts`,
367
+ });
368
+ continue;
369
+ }
370
+
371
+ // Check if key is actually used
372
+ if (!usedKeys.has(keyInfo.fullKey)) {
373
+ unused.push({
374
+ ...keyInfo,
375
+ filePath: `src/locales/default/${keyInfo.namespace}.ts`,
376
+ });
377
+ }
378
+ }
379
+
380
+ if (protectedKeys.length > 0) {
381
+ consola.info('');
382
+ consola.info(colors.cyan('Protected keys (considered as used):'));
383
+ consola.info(
384
+ ` ${colors.green(protectedKeys.length.toString())} keys protected by patterns or dynamic usage`,
385
+ );
386
+ }
387
+
388
+ return unused;
389
+ }
390
+
391
+ /**
392
+ * Generate report
393
+ */
394
+ function generateReport(unusedKeys: UnusedKey[], allKeysCount: number, usedKeysCount: number) {
395
+ consola.box('📊 Unused i18n Keys Analysis Report');
396
+
397
+ const actualUsedCount = allKeysCount - unusedKeys.length;
398
+
399
+ consola.info('');
400
+ consola.info(colors.cyan('Statistics:'));
401
+ consola.info(` Total defined keys: ${colors.yellow(allKeysCount.toString())}`);
402
+ consola.info(` Used keys: ${colors.green(actualUsedCount.toString())}`);
403
+ consola.info(` Unused keys: ${colors.red(unusedKeys.length.toString())}`);
404
+ consola.info(
405
+ ` Usage rate: ${colors.cyan(((actualUsedCount / allKeysCount) * 100).toFixed(2) + '%')}`,
406
+ );
407
+ consola.info('');
408
+ consola.info(colors.gray('Protected patterns:'));
409
+ consola.info(` ${colors.gray(PROTECTED_KEY_PATTERNS.map((p) => `"${p}"`).join(', '))}`);
410
+ consola.info('');
411
+
412
+ if (unusedKeys.length === 0) {
413
+ consola.success('🎉 All i18n keys are being used!');
414
+ return;
415
+ }
416
+
417
+ // Group by namespace
418
+ const byNamespace = new Map<string, UnusedKey[]>();
419
+ for (const key of unusedKeys) {
420
+ if (!byNamespace.has(key.namespace)) {
421
+ byNamespace.set(key.namespace, []);
422
+ }
423
+ byNamespace.get(key.namespace)!.push(key);
424
+ }
425
+
426
+ consola.info(colors.yellow('Unused keys by namespace:'));
427
+ consola.info('');
428
+
429
+ for (const [namespace, keys] of byNamespace.entries()) {
430
+ consola.warn(
431
+ `${colors.cyan(namespace.padEnd(20))} ${colors.gray('→')} ${colors.red(keys.length + ' unused keys')}`,
432
+ );
433
+
434
+ // Show first 10 keys
435
+ const displayKeys = keys.slice(0, 10);
436
+ for (const key of displayKeys) {
437
+ consola.log(` ${colors.gray('•')} ${key.key}`);
438
+ }
439
+
440
+ if (keys.length > 10) {
441
+ consola.log(` ${colors.gray(`... and ${keys.length - 10} more`)}`);
442
+ }
443
+ consola.info('');
444
+ }
445
+
446
+ // Save detailed report to file
447
+ const reportPath = path.join(process.cwd(), 'i18n-unused-keys-report.json');
448
+ fs.writeFileSync(
449
+ reportPath,
450
+ JSON.stringify(
451
+ {
452
+ generatedAt: new Date().toISOString(),
453
+ statistics: {
454
+ totalKeys: allKeysCount,
455
+ unusedKeys: unusedKeys.length,
456
+ usageRate: ((usedKeysCount / allKeysCount) * 100).toFixed(2) + '%',
457
+ usedKeys: usedKeysCount,
458
+ },
459
+ unusedKeys: unusedKeys.map((k) => ({
460
+ filePath: k.filePath,
461
+ fullKey: k.fullKey,
462
+ key: k.key,
463
+ namespace: k.namespace,
464
+ })),
465
+ unusedKeysByNamespace: Array.from(byNamespace.entries()).map(([ns, keys]) => ({
466
+ count: keys.length,
467
+ keys: keys.map((k) => k.key),
468
+ namespace: ns,
469
+ })),
470
+ },
471
+ null,
472
+ 2,
473
+ ),
474
+ );
475
+
476
+ consola.success(`Detailed report saved to: ${colors.cyan(reportPath)}`);
477
+ }
478
+
479
+ /**
480
+ * Main function
481
+ */
482
+ async function main() {
483
+ consola.start('Starting i18n unused keys analysis...');
484
+ consola.info('');
485
+
486
+ // Step 1: Load all defined keys
487
+ consola.box('Step 1: Loading all i18n keys');
488
+ const allKeys = loadAllI18nKeys();
489
+ consola.success(`Total keys loaded: ${allKeys.length}`);
490
+ consola.info('');
491
+
492
+ // Step 2: Find all translation calls
493
+ consola.box('Step 2: Finding translation calls in codebase');
494
+ const usedKeys = await findAllTranslationCalls();
495
+ consola.info('');
496
+
497
+ // Step 3: Find unused keys
498
+ consola.box('Step 3: Analyzing unused keys');
499
+ const unusedKeys = findUnusedKeys(allKeys, usedKeys);
500
+ consola.info('');
501
+
502
+ // Step 4: Generate report
503
+ generateReport(unusedKeys, allKeys.length, usedKeys.size);
504
+ }
505
+
506
+ main();
@@ -0,0 +1,57 @@
1
+ const { defineConfig } = require("@lobehub/i18n-cli");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+
5
+ module.exports = defineConfig({
6
+ entry: "locales/en-US",
7
+ entryLocale: "en-US",
8
+ output: "locales",
9
+ outputLocales: [
10
+ "ar",
11
+ "bg-BG",
12
+ "zh-CN",
13
+ "zh-TW",
14
+ "ru-RU",
15
+ "ja-JP",
16
+ "ko-KR",
17
+ "fr-FR",
18
+ "tr-TR",
19
+ "es-ES",
20
+ "pt-BR",
21
+ "de-DE",
22
+ "it-IT",
23
+ "nl-NL",
24
+ "pl-PL",
25
+ "vi-VN",
26
+ "fa-IR",
27
+ ],
28
+ temperature: 0,
29
+ saveImmediately: true,
30
+ modelName: "deepseek-chat",
31
+ experimental: {
32
+ jsonMode: true,
33
+ },
34
+ markdown: {
35
+ reference:
36
+ "You need to maintain the component format of the mdx file; the output text does not need to be wrapped in any code block syntax on the outermost layer.\n" +
37
+ fs.readFileSync(path.join(__dirname, "docs/glossary.md"), "utf-8"),
38
+ entry: ["./README.md", "./docs/**/*.md", "./docs/**/*.mdx"],
39
+ entryLocale: "en-US",
40
+ outputLocales: ["zh-CN"],
41
+ includeMatter: true,
42
+ exclude: [
43
+ "./README.zh-CN.md",
44
+ "./docs/**/*.zh-CN.md",
45
+ "./docs/**/*.zh-CN.mdx",
46
+ ],
47
+ outputExtensions: (locale, { filePath }) => {
48
+ if (filePath.includes(".mdx")) {
49
+ if (locale === "en-US") return ".mdx";
50
+ return `.${locale}.mdx`;
51
+ } else {
52
+ if (locale === "en-US") return ".md";
53
+ return `.${locale}.md`;
54
+ }
55
+ },
56
+ },
57
+ });
@@ -1,5 +1,5 @@
1
1
  API_PREFIX="/api"
2
- API_TIMEOUT=60 * 1000
2
+ API_TIMEOUT=60000
3
3
 
4
4
  COOKIE_NAME='ywkf_jwt'
5
5
 
@@ -9,23 +9,6 @@ OUTPUT_PATH='dist'
9
9
  BASENAME='/'
10
10
 
11
11
  PUBLIC_PATH='/s{{{ name }}}/'
12
- VERSION_NOTIFY_TIME=30000
13
-
14
-
15
- # Sentry 配置 - (官网)
16
- # REACT_APP_SENTRY_DSN="https://95add4dc797c22aa2224494e4abd4d71@o4509466151878656.ingest.us.sentry.io/4509466153058304"
17
- # SENTRY_PROJECT="javascript-react"
18
- # SENTRY_ORG="test-x6m"
19
- # SENTRY_AUTH_TOKEN="d477b94cfe375f5b4e4e88ac4fc15a8be8557652cea424710b6f7f19be4f0b3d"
20
- # SENTRY_URL="https://test-x6m.sentry.io/"
21
12
 
22
13
  # 帮助中心
23
- HELP_CENTER_URL="https://im-support.gz4399.com/"
24
-
25
- # 是否开启Sentry
26
- SENTRY_ENABLED='{{SENTRY_ENABLED}}'
27
- REACT_APP_SENTRY_DSN='{{SENTRY_DSN}}'
28
- SENTRY_PROJECT='{{{ name }}}'
29
- SENTRY_ORG="sentry"
30
- SENTRY_AUTH_TOKEN="f13c66cd7a3629c8324cceb62fdb65aae1f337abbec4ebbadbe0a22447708994"
31
- SENTRY_URL="https://sentry.gz4399.com/"
14
+ HELP_CENTER_URL="https://im-support.gz4399.com/"
@@ -20,12 +20,12 @@ export default class jwt {
20
20
  }
21
21
 
22
22
  // 获取访问令牌
23
- static getAccessToken(accessTokenKey: string) {
24
- return Cookies.get(accessTokenKey);
23
+ static getAccessToken(accessTokenKey?: string) {
24
+ return Cookies.get(accessTokenKey ?? this.key);
25
25
  }
26
26
 
27
27
  // 清空访问令牌
28
- static clearAccessToken(accessTokenKey: string) {
29
- Cookies.remove(accessTokenKey);
28
+ static clearAccessToken(accessTokenKey?: string) {
29
+ Cookies.remove(accessTokenKey ?? this.key);
30
30
  }
31
31
  }
@@ -0,0 +1,67 @@
1
+ import { message as antdMessage } from "antd";
2
+ import type { AxiosError } from "axios";
3
+ import type { RequestConfig, ApiResponse } from "./types";
4
+
5
+ /**
6
+ * 错误处理器
7
+ */
8
+ export class ErrorHandler {
9
+ /**
10
+ * 处理请求错误
11
+ */
12
+ handle(error: any, config?: RequestConfig): void {
13
+ const messageConfig = config?.message;
14
+
15
+ // 如果明确禁用错误提示
16
+ if (messageConfig?.showError === false) {
17
+ return;
18
+ }
19
+
20
+ const axiosError = error as AxiosError<ApiResponse>;
21
+ let errorMessage = messageConfig?.errorMessage;
22
+
23
+ // 如果没有自定义错误消息,使用默认逻辑
24
+ if (!errorMessage) {
25
+ if (axiosError.response) {
26
+ const { status, data } = axiosError.response;
27
+
28
+ // 优先使用后端返回的消息
29
+ errorMessage = data?.message;
30
+
31
+ // 如果后端没有返回消息,使用默认消息
32
+ if (!errorMessage) {
33
+ errorMessage = this.getDefaultErrorMessage(status);
34
+ }
35
+ } else if (axiosError.request) {
36
+ errorMessage = "网络错误,请检查您的网络连接";
37
+ } else {
38
+ errorMessage = axiosError.message || "请求失败";
39
+ }
40
+ }
41
+
42
+ // 显示错误提示
43
+ antdMessage.error(errorMessage);
44
+ }
45
+
46
+ /**
47
+ * 获取默认错误消息
48
+ */
49
+ private getDefaultErrorMessage(status: number): string {
50
+ const errorMessages: Record<number, string> = {
51
+ 400: "请求参数错误",
52
+ 401: "未授权,请重新登录",
53
+ 403: "拒绝访问",
54
+ 404: "请求的资源不存在",
55
+ 405: "请求方法不允许",
56
+ 408: "请求超时",
57
+ 500: "服务器内部错误",
58
+ 502: "网关错误",
59
+ 503: "服务暂时不可用",
60
+ 504: "网关超时",
61
+ };
62
+
63
+ return errorMessages[status] || `请求失败 (${status})`;
64
+ }
65
+ }
66
+
67
+ export const errorHandler = new ErrorHandler();