@qlover/create-app 0.7.15 → 0.8.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 (340) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/configs/_common/.gitignore.template +6 -0
  3. package/dist/configs/_common/.prettierignore +17 -5
  4. package/dist/configs/_common/.vscode/settings.json +6 -1
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/templates/next-app/.env.template +1 -1
  8. package/dist/templates/next-app/README.en.md +0 -1
  9. package/dist/templates/next-app/README.md +0 -1
  10. package/dist/templates/next-app/config/Identifier/api.ts +5 -5
  11. package/dist/templates/next-app/config/Identifier/common/admint.table.ts +69 -0
  12. package/dist/templates/next-app/config/Identifier/common/common.ts +76 -0
  13. package/dist/templates/next-app/config/Identifier/common/index.ts +3 -0
  14. package/dist/templates/next-app/config/Identifier/{validator.ts → common/validators.ts} +5 -5
  15. package/dist/templates/next-app/config/Identifier/index.ts +2 -12
  16. package/dist/templates/next-app/config/Identifier/pages/index.ts +6 -0
  17. package/dist/templates/next-app/config/Identifier/pages/page.admin.home.ts +27 -0
  18. package/dist/templates/next-app/config/Identifier/pages/page.admin.locales.ts +266 -0
  19. package/dist/templates/next-app/config/Identifier/pages/page.admin.user.ts +293 -0
  20. package/dist/templates/{react-app/config/Identifier → next-app/config/Identifier/pages}/page.home.ts +15 -22
  21. package/dist/templates/next-app/config/Identifier/{page.login.ts → pages/page.login.ts} +28 -34
  22. package/dist/templates/next-app/config/Identifier/{page.register.ts → pages/page.register.ts} +30 -29
  23. package/dist/templates/next-app/config/adminNavs.ts +19 -0
  24. package/dist/templates/next-app/config/common.ts +22 -13
  25. package/dist/templates/next-app/config/i18n/HomeI18n.ts +5 -5
  26. package/dist/templates/next-app/config/i18n/admin18n.ts +61 -19
  27. package/dist/templates/next-app/config/i18n/i18nConfig.ts +2 -0
  28. package/dist/templates/next-app/config/i18n/i18nKeyScheam.ts +36 -0
  29. package/dist/templates/next-app/config/i18n/loginI18n.ts +22 -22
  30. package/dist/templates/next-app/config/i18n/register18n.ts +23 -24
  31. package/dist/templates/next-app/docs/en/index.md +0 -1
  32. package/dist/templates/next-app/docs/en/project-structure.md +0 -1
  33. package/dist/templates/next-app/docs/zh/index.md +0 -1
  34. package/dist/templates/next-app/docs/zh/project-structure.md +0 -1
  35. package/dist/templates/next-app/make/generateLocales.ts +19 -12
  36. package/dist/templates/next-app/migrations/schema/LocalesSchema.ts +15 -0
  37. package/dist/templates/next-app/migrations/sql/1694244000000.sql +11 -0
  38. package/dist/templates/next-app/package.json +7 -3
  39. package/dist/templates/next-app/public/locales/en.json +172 -207
  40. package/dist/templates/next-app/public/locales/zh.json +172 -207
  41. package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +153 -0
  42. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +48 -50
  43. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +2 -2
  44. package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +34 -0
  45. package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +40 -0
  46. package/dist/templates/next-app/src/app/api/admin/locales/route.ts +42 -0
  47. package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +32 -0
  48. package/dist/templates/next-app/src/app/api/locales/json/route.ts +44 -0
  49. package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +1 -13
  50. package/dist/templates/next-app/src/base/cases/Datetime.ts +18 -0
  51. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +12 -6
  52. package/dist/templates/next-app/src/base/cases/ResourceState.ts +17 -0
  53. package/dist/templates/next-app/src/base/cases/TranslateI18nInterface.ts +25 -0
  54. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +200 -0
  55. package/dist/templates/next-app/src/base/port/ZodBuilderInterface.ts +8 -0
  56. package/dist/templates/next-app/src/base/services/AdminLocalesService.ts +20 -0
  57. package/dist/templates/next-app/src/base/services/AdminPageEvent.ts +26 -0
  58. package/dist/templates/next-app/src/base/services/AdminPageScheduler.ts +42 -0
  59. package/dist/templates/next-app/src/base/services/ResourceService.ts +122 -0
  60. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +104 -0
  61. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +38 -5
  62. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +1 -1
  63. package/dist/templates/next-app/src/i18n/request.ts +30 -1
  64. package/dist/templates/next-app/src/server/PageParams.ts +2 -10
  65. package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +5 -0
  66. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +2 -0
  67. package/dist/templates/next-app/src/server/port/LocalesRepositoryInterface.ts +43 -0
  68. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +197 -0
  69. package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +122 -0
  70. package/dist/templates/next-app/src/server/sqlBridges/SupabaseBridge.ts +60 -11
  71. package/dist/templates/next-app/src/server/validators/ExtendedExecutorError.ts +6 -0
  72. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +131 -0
  73. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +2 -5
  74. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +32 -16
  75. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +2 -1
  76. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +28 -29
  77. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +2 -1
  78. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +17 -3
  79. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +5 -4
  80. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +5 -4
  81. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +3 -2
  82. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +1 -1
  83. package/dist/templates/next-app/src/uikit/components/EditableCell.tsx +118 -0
  84. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +5 -6
  85. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +1 -1
  86. package/dist/templates/next-app/src/uikit/components/With.tsx +2 -2
  87. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportButton.tsx +62 -0
  88. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +28 -0
  89. package/dist/templates/next-app/src/uikit/components/localesImportButton/import.module.css +6 -0
  90. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +8 -14
  91. package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +25 -0
  92. package/dist/templates/react-app/.prettierignore +17 -0
  93. package/dist/templates/react-app/README.en.md +71 -54
  94. package/dist/templates/react-app/README.md +35 -18
  95. package/dist/templates/react-app/__tests__/__mocks__/BootstrapTest.ts +14 -0
  96. package/dist/templates/react-app/__tests__/__mocks__/MockAppConfit.ts +1 -1
  97. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +2 -2
  98. package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +1 -1
  99. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +45 -0
  100. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +34 -0
  101. package/dist/templates/react-app/__tests__/__mocks__/components/TestRouter.tsx +46 -0
  102. package/dist/templates/react-app/__tests__/__mocks__/components/index.ts +12 -0
  103. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +1 -2
  104. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOC.ts +51 -0
  105. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +69 -0
  106. package/dist/templates/react-app/__tests__/setup/index.ts +1 -51
  107. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +51 -0
  108. package/dist/templates/react-app/__tests__/src/App.structure.test.tsx +115 -0
  109. package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +2 -2
  110. package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +1 -1
  111. package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +3 -5
  112. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +13 -2
  113. package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +1 -1
  114. package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +1 -1
  115. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +5 -5
  116. package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +1 -2
  117. package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +25 -15
  118. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +29 -15
  119. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +19 -9
  120. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +153 -0
  121. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +9 -7
  122. package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +4 -5
  123. package/dist/templates/react-app/__tests__/src/main.test.tsx +4 -4
  124. package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +68 -59
  125. package/dist/templates/react-app/config/IOCIdentifier.ts +8 -8
  126. package/dist/templates/react-app/config/Identifier/{common.error.ts → common/common.error.ts} +5 -5
  127. package/dist/templates/react-app/config/Identifier/{common.ts → common/common.ts} +9 -9
  128. package/dist/templates/react-app/config/Identifier/common/index.ts +2 -0
  129. package/dist/templates/react-app/config/Identifier/index.ts +1 -9
  130. package/dist/templates/react-app/config/Identifier/pages/index.ts +8 -0
  131. package/dist/templates/react-app/config/Identifier/{page.about.ts → pages/page.about.ts} +34 -26
  132. package/dist/templates/react-app/config/Identifier/{page.executor.ts → pages/page.executor.ts} +47 -39
  133. package/dist/templates/{next-app/config/Identifier → react-app/config/Identifier/pages}/page.home.ts +24 -23
  134. package/dist/templates/react-app/config/Identifier/pages/page.identifiter.ts +102 -0
  135. package/dist/templates/react-app/config/Identifier/{page.jsonStorage.ts → pages/page.jsonStorage.ts} +18 -11
  136. package/dist/templates/react-app/config/Identifier/{page.login.ts → pages/page.login.ts} +37 -27
  137. package/dist/templates/react-app/config/Identifier/{page.register.ts → pages/page.register.ts} +37 -25
  138. package/dist/templates/react-app/config/Identifier/{page.request.ts → pages/page.request.ts} +34 -44
  139. package/dist/templates/react-app/config/app.router.ts +66 -69
  140. package/dist/templates/react-app/config/i18n/PageI18nInterface.ts +51 -0
  141. package/dist/templates/react-app/config/i18n/aboutI18n.ts +42 -0
  142. package/dist/templates/react-app/config/i18n/executorI18n.ts +51 -0
  143. package/dist/templates/react-app/config/i18n/homeI18n.ts +24 -0
  144. package/dist/templates/react-app/config/i18n/i18nConfig.ts +30 -0
  145. package/dist/templates/react-app/config/i18n/identifiter18n.ts +30 -0
  146. package/dist/templates/react-app/config/i18n/jsonStorage18n.ts +27 -0
  147. package/dist/templates/react-app/config/i18n/login18n.ts +42 -0
  148. package/dist/templates/react-app/config/i18n/notFoundI18n.ts +34 -0
  149. package/dist/templates/react-app/config/i18n/register18n.ts +40 -0
  150. package/dist/templates/react-app/config/i18n/request18n.ts +41 -0
  151. package/dist/templates/react-app/config/theme.ts +14 -4
  152. package/dist/templates/react-app/docs/en/bootstrap.md +1670 -341
  153. package/dist/templates/react-app/docs/en/development-guide.md +1021 -345
  154. package/dist/templates/react-app/docs/en/env.md +1132 -278
  155. package/dist/templates/react-app/docs/en/i18n.md +858 -147
  156. package/dist/templates/react-app/docs/en/index.md +733 -104
  157. package/dist/templates/react-app/docs/en/ioc.md +1228 -287
  158. package/dist/templates/react-app/docs/en/playwright/e2e-tests.md +321 -0
  159. package/dist/templates/react-app/docs/en/playwright/index.md +19 -0
  160. package/dist/templates/react-app/docs/en/playwright/installation-summary.md +332 -0
  161. package/dist/templates/react-app/docs/en/playwright/overview.md +222 -0
  162. package/dist/templates/react-app/docs/en/playwright/quickstart.md +325 -0
  163. package/dist/templates/react-app/docs/en/playwright/reorganization-notes.md +340 -0
  164. package/dist/templates/react-app/docs/en/playwright/setup-complete.md +290 -0
  165. package/dist/templates/react-app/docs/en/playwright/testing-guide.md +565 -0
  166. package/dist/templates/react-app/docs/en/store.md +1194 -184
  167. package/dist/templates/react-app/docs/en/why-no-globals.md +797 -0
  168. package/dist/templates/react-app/docs/zh/bootstrap.md +1670 -341
  169. package/dist/templates/react-app/docs/zh/development-guide.md +1021 -345
  170. package/dist/templates/react-app/docs/zh/env.md +1132 -275
  171. package/dist/templates/react-app/docs/zh/i18n.md +858 -147
  172. package/dist/templates/react-app/docs/zh/index.md +717 -104
  173. package/dist/templates/react-app/docs/zh/ioc.md +1229 -287
  174. package/dist/templates/react-app/docs/zh/playwright/e2e-tests.md +321 -0
  175. package/dist/templates/react-app/docs/zh/playwright/index.md +19 -0
  176. package/dist/templates/react-app/docs/zh/playwright/installation-summary.md +332 -0
  177. package/dist/templates/react-app/docs/zh/playwright/overview.md +222 -0
  178. package/dist/templates/react-app/docs/zh/playwright/quickstart.md +325 -0
  179. package/dist/templates/react-app/docs/zh/playwright/reorganization-notes.md +340 -0
  180. package/dist/templates/react-app/docs/zh/playwright/setup-complete.md +290 -0
  181. package/dist/templates/react-app/docs/zh/playwright/testing-guide.md +565 -0
  182. package/dist/templates/react-app/docs/zh/store.md +1192 -184
  183. package/dist/templates/react-app/docs/zh/why-no-globals.md +797 -0
  184. package/dist/templates/react-app/e2e/App.spec.ts +319 -0
  185. package/dist/templates/react-app/e2e/fixtures/base.fixture.ts +40 -0
  186. package/dist/templates/react-app/e2e/main.spec.ts +20 -0
  187. package/dist/templates/react-app/e2e/utils/test-helpers.ts +19 -0
  188. package/dist/templates/react-app/eslint.config.mjs +247 -0
  189. package/dist/templates/react-app/makes/eslint-utils.mjs +195 -0
  190. package/dist/templates/react-app/makes/generateTs2LocalesOptions.ts +26 -0
  191. package/dist/templates/react-app/package.json +31 -3
  192. package/dist/templates/react-app/playwright.config.ts +79 -0
  193. package/dist/templates/react-app/public/locales/en/common.json +190 -179
  194. package/dist/templates/react-app/public/locales/zh/common.json +190 -179
  195. package/dist/templates/react-app/src/App.tsx +15 -42
  196. package/dist/templates/react-app/src/base/apis/AiApi.ts +5 -5
  197. package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +1 -1
  198. package/dist/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +1 -1
  199. package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +8 -8
  200. package/dist/templates/react-app/src/base/apis/feApi/FeApiType.ts +1 -1
  201. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +6 -6
  202. package/dist/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +1 -1
  203. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +12 -14
  204. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +1 -1
  205. package/dist/templates/react-app/src/base/cases/DialogHandler.ts +5 -2
  206. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +3 -3
  207. package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +3 -3
  208. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +2 -2
  209. package/dist/templates/react-app/src/base/cases/RequestLogger.ts +4 -4
  210. package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +1 -1
  211. package/dist/templates/react-app/src/base/cases/ResourceState.ts +23 -0
  212. package/dist/templates/react-app/src/base/cases/RouterLoader.ts +4 -4
  213. package/dist/templates/react-app/src/base/cases/TranslateI18nInterface.ts +26 -0
  214. package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +2 -3
  215. package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +1 -1
  216. package/dist/templates/react-app/src/base/port/IOCInterface.ts +36 -0
  217. package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +2 -1
  218. package/dist/templates/react-app/src/base/port/ProcesserExecutorInterface.ts +1 -1
  219. package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +2 -2
  220. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +9 -5
  221. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +1 -1
  222. package/dist/templates/react-app/src/base/services/I18nService.ts +29 -29
  223. package/dist/templates/react-app/src/base/services/IdentifierService.ts +143 -0
  224. package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +3 -3
  225. package/dist/templates/react-app/src/base/services/RouteService.ts +27 -8
  226. package/dist/templates/react-app/src/base/services/UserService.ts +8 -8
  227. package/dist/templates/react-app/src/base/types/Page.ts +14 -2
  228. package/dist/templates/react-app/src/base/types/global.d.ts +1 -1
  229. package/dist/templates/react-app/src/core/IOC.ts +5 -46
  230. package/dist/templates/react-app/src/core/bootstraps/{BootstrapApp.ts → BootstrapClient.ts} +44 -17
  231. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +14 -7
  232. package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +1 -1
  233. package/dist/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +1 -1
  234. package/dist/templates/react-app/src/core/clientIoc/ClientIOC.ts +40 -0
  235. package/dist/templates/react-app/src/core/{IocRegisterImpl.ts → clientIoc/ClientIOCRegister.ts} +35 -24
  236. package/dist/templates/react-app/src/core/globals.ts +9 -9
  237. package/dist/templates/react-app/src/main.tsx +4 -4
  238. package/dist/templates/react-app/src/pages/404.tsx +6 -3
  239. package/dist/templates/react-app/src/pages/500.tsx +5 -2
  240. package/dist/templates/react-app/src/pages/NoRouteFound.tsx +5 -0
  241. package/dist/templates/react-app/src/pages/auth/Layout.tsx +9 -6
  242. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +46 -56
  243. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +46 -58
  244. package/dist/templates/react-app/src/pages/base/AboutPage.tsx +35 -40
  245. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +51 -51
  246. package/dist/templates/react-app/src/pages/base/HomePage.tsx +14 -15
  247. package/dist/templates/react-app/src/pages/base/IdentifierPage.tsx +70 -11
  248. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +24 -25
  249. package/dist/templates/react-app/src/pages/base/Layout.tsx +2 -2
  250. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +3 -2
  251. package/dist/templates/react-app/src/pages/base/RequestPage.tsx +41 -59
  252. package/dist/templates/react-app/src/styles/css/antd-themes/{_default.css → _common/_default.css} +85 -0
  253. package/dist/templates/react-app/src/styles/css/antd-themes/{dark.css → _common/dark.css} +99 -0
  254. package/dist/templates/react-app/src/styles/css/antd-themes/_common/index.css +3 -0
  255. package/dist/templates/react-app/src/styles/css/antd-themes/{pink.css → _common/pink.css} +86 -0
  256. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +4 -3
  257. package/dist/templates/react-app/src/styles/css/antd-themes/menu/_default.css +108 -0
  258. package/dist/templates/react-app/src/styles/css/antd-themes/menu/dark.css +67 -0
  259. package/dist/templates/react-app/src/styles/css/antd-themes/menu/index.css +3 -0
  260. package/dist/templates/react-app/src/styles/css/antd-themes/menu/pink.css +67 -0
  261. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/_default.css +34 -0
  262. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/dark.css +31 -0
  263. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/index.css +3 -0
  264. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/pink.css +36 -0
  265. package/dist/templates/react-app/src/styles/css/antd-themes/table/_default.css +44 -0
  266. package/dist/templates/react-app/src/styles/css/antd-themes/table/dark.css +43 -0
  267. package/dist/templates/react-app/src/styles/css/antd-themes/table/index.css +3 -0
  268. package/dist/templates/react-app/src/styles/css/antd-themes/table/pink.css +43 -0
  269. package/dist/templates/react-app/src/styles/css/page.css +4 -3
  270. package/dist/templates/react-app/src/styles/css/themes/_default.css +1 -0
  271. package/dist/templates/react-app/src/styles/css/themes/dark.css +1 -0
  272. package/dist/templates/react-app/src/styles/css/themes/pink.css +1 -0
  273. package/dist/templates/react-app/src/styles/css/zIndex.css +1 -1
  274. package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +3 -3
  275. package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +2 -2
  276. package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +1 -1
  277. package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +3 -3
  278. package/dist/templates/react-app/src/uikit/components/AppRouterProvider.tsx +35 -0
  279. package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +15 -11
  280. package/dist/templates/react-app/src/uikit/components/BaseRouteProvider.tsx +14 -11
  281. package/dist/templates/react-app/src/uikit/components/BaseRouteSeo.tsx +18 -0
  282. package/dist/templates/react-app/src/uikit/components/BootstrapsProvider.tsx +13 -0
  283. package/dist/templates/react-app/src/uikit/components/ClientSeo.tsx +62 -0
  284. package/dist/templates/react-app/src/uikit/components/ComboProvider.tsx +38 -0
  285. package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +48 -27
  286. package/dist/templates/react-app/src/uikit/components/Loading.tsx +4 -2
  287. package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +4 -5
  288. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +34 -11
  289. package/dist/templates/react-app/src/uikit/components/ProcessExecutorProvider.tsx +9 -5
  290. package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +6 -3
  291. package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +97 -40
  292. package/dist/templates/react-app/src/uikit/components/UserAuthProvider.tsx +5 -5
  293. package/dist/templates/react-app/src/uikit/components/With.tsx +17 -0
  294. package/dist/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +17 -11
  295. package/dist/templates/react-app/src/uikit/contexts/IOCContext.ts +13 -0
  296. package/dist/templates/react-app/src/uikit/hooks/useAppTranslation.ts +26 -0
  297. package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +8 -11
  298. package/dist/templates/react-app/src/uikit/hooks/useI18nInterface.ts +25 -0
  299. package/dist/templates/react-app/src/uikit/hooks/useIOC.ts +35 -0
  300. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +3 -3
  301. package/dist/templates/react-app/src/uikit/hooks/useStrictEffect.ts +0 -1
  302. package/dist/templates/react-app/tsconfig.e2e.json +21 -0
  303. package/dist/templates/react-app/tsconfig.json +8 -1
  304. package/dist/templates/react-app/tsconfig.node.json +1 -1
  305. package/dist/templates/react-app/tsconfig.test.json +3 -1
  306. package/dist/templates/react-app/vite.config.ts +50 -34
  307. package/package.json +2 -1
  308. package/dist/configs/react-app/eslint.config.js +0 -94
  309. package/dist/templates/next-app/config/Identifier/common.error.ts +0 -41
  310. package/dist/templates/next-app/config/Identifier/common.ts +0 -69
  311. package/dist/templates/next-app/config/Identifier/page.about.ts +0 -181
  312. package/dist/templates/next-app/config/Identifier/page.admin.ts +0 -48
  313. package/dist/templates/next-app/config/Identifier/page.executor.ts +0 -272
  314. package/dist/templates/next-app/config/Identifier/page.identifiter.ts +0 -39
  315. package/dist/templates/next-app/config/Identifier/page.jsonStorage.ts +0 -72
  316. package/dist/templates/next-app/config/Identifier/page.request.ts +0 -182
  317. package/dist/templates/next-app/src/base/cases/ChatAction.ts +0 -21
  318. package/dist/templates/next-app/src/base/cases/FocusBarAction.ts +0 -36
  319. package/dist/templates/next-app/src/base/cases/RequestState.ts +0 -20
  320. package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +0 -85
  321. package/dist/templates/next-app/src/base/port/AsyncStateInterface.ts +0 -7
  322. package/dist/templates/next-app/src/base/services/AdminUserService.ts +0 -45
  323. package/dist/templates/next-app/src/uikit/components/ChatRoot.tsx +0 -17
  324. package/dist/templates/next-app/src/uikit/components/chat/ChatActionInterface.ts +0 -30
  325. package/dist/templates/next-app/src/uikit/components/chat/ChatFocusBar.tsx +0 -65
  326. package/dist/templates/next-app/src/uikit/components/chat/ChatMessages.tsx +0 -59
  327. package/dist/templates/next-app/src/uikit/components/chat/ChatWrap.tsx +0 -28
  328. package/dist/templates/next-app/src/uikit/components/chat/FocusBarActionInterface.ts +0 -19
  329. package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +0 -17
  330. package/dist/templates/next-app/src/uikit/hook/useStore.ts +0 -15
  331. package/dist/templates/react-app/__tests__/__mocks__/I18nService.ts +0 -13
  332. package/dist/templates/react-app/__tests__/src/App.test.tsx +0 -139
  333. package/dist/templates/react-app/config/Identifier/page.identifiter.ts +0 -39
  334. package/dist/templates/react-app/config/i18n.ts +0 -15
  335. package/dist/templates/react-app/docs/en/project-structure.md +0 -434
  336. package/dist/templates/react-app/docs/zh/project-structure.md +0 -434
  337. package/dist/templates/react-app/src/base/cases/RequestState.ts +0 -20
  338. package/dist/templates/react-app/src/base/port/AsyncStateInterface.ts +0 -7
  339. package/dist/templates/react-app/src/uikit/hooks/useDocumentTitle.ts +0 -15
  340. package/dist/templates/react-app/src/uikit/hooks/useStore.ts +0 -15
@@ -1,152 +1,568 @@
1
- # 国际化系统
1
+ # 国际化 (i18n)
2
2
 
3
- ## 概述
3
+ ## 📋 目录
4
4
 
5
- 国际化系统采用基于标识符别名的方案,结合 i18next TypeScript 注释,实现了一个类型安全、易维护的国际化解决方案。主要特点:
5
+ - [核心理念](#-核心理念) - 绝不硬编码,只用变量
6
+ - [什么是 i18n Key](#-什么是-i18n-key) - 唯一标识符
7
+ - [为什么使用 i18n Key](#-为什么使用-i18n-key) - 减少思考负担
8
+ - [项目中的实现](#-项目中的实现) - Bootstrap 插件
9
+ - [使用方式](#-使用方式) - 实战指南
10
+ - [最佳实践](#-最佳实践) - 7 条核心实践
11
+ - [常见问题](#-常见问题) - FAQ
6
12
 
7
- - **标识符别名**:使用常量而非字符串作为翻译键
8
- - **TypeScript 注释**:通过注释自动生成翻译资源
9
- - **类型安全**:完整的类型检查和自动补全
10
- - **路由集成**:支持 URL 路径中的语言参数
11
- - **自动生成**:自动从源码生成翻译文件
12
- - **开发友好**:完整的开发工具支持
13
+ ---
13
14
 
14
- ## 核心概念
15
+ ## 🎯 核心理念
15
16
 
16
- ### 1. 标识符别名
17
+ > **🚨 重要原则:项目中所有需要显示的文字、词语、句子,都必须使用 i18n Key,绝不允许硬编码文本!**
17
18
 
18
- 使用常量替代字符串键值,提供更好的类型安全和维护性:
19
+ > **⭐ 核心优势:开发者不需要记住复杂的翻译字符串,只需要知道变量名,IDE 会提供自动补全!**
20
+
21
+ ### 核心概念
22
+
23
+ ```
24
+ ❌ 硬编码文本 ✅ i18n Key(变量)
25
+ "登录" → BUTTON_LOGIN (不需要记住 'common:button.login')
26
+ "欢迎回来" → MESSAGE_WELCOME (不需要记住 'common:message.welcome')
27
+ "确定要删除吗?" → CONFIRM_DELETE (不需要记住 'common:confirm.delete')
28
+ ```
29
+
30
+ **每个文本都有唯一的标识符:**
19
31
 
20
32
  ```typescript
21
- // config/Identifier/common.ts
33
+ // ❌ 错误:直接写文字或字符串
34
+ <button>登录</button>
35
+ <h1>欢迎回来</h1>
36
+ <p>{t('common:confirm.delete')}</p> // 也不要写字符串
37
+
38
+ // ✅ 正确:使用 i18n Key 变量
39
+ <button>{t(BUTTON_LOGIN)}</button> // 只需记住变量名
40
+ <h1>{t(MESSAGE_WELCOME)}</h1> // IDE 会自动补全
41
+ <p>{t(CONFIRM_DELETE)}</p> // TypeScript 会检查拼写
42
+ ```
43
+
44
+ ---
45
+
46
+ ## 🔑 什么是 i18n Key
47
+
48
+ i18n Key 是**每个文本的唯一标识符**,就像每个人都有身份证号一样。
49
+
50
+ ### 基本概念
51
+
52
+ ```typescript
53
+ // i18n Key 定义(在 config/Identifier/ 目录下)
22
54
 
23
55
  /**
24
- * @description Theme switcher default theme label
25
- * @localZh 默认主题
26
- * @localEn Default Theme
56
+ * @description Login button text
57
+ * @localZh 登录
58
+ * @localEn Login
27
59
  */
28
- export const HEADER_THEME_DEFAULT = 'header.theme.default';
60
+ export const BUTTON_LOGIN = 'common:button.login';
61
+ // ↑ 常量名 ↑ 标识符字符串
62
+ // 用于代码中引用 存储在翻译文件中的键名
29
63
 
30
64
  /**
31
- * @description Theme switcher dark theme label
32
- * @localZh 暗色主题
33
- * @localEn Dark Theme
65
+ * @description Welcome message
66
+ * @localZh 欢迎回来,{{name}}!
67
+ * @localEn Welcome back, {{name}}!
34
68
  */
35
- export const HEADER_THEME_DARK = 'header.theme.dark';
69
+ export const MESSAGE_WELCOME = 'common:message.welcome';
36
70
  ```
37
71
 
38
- ### 2. 自动生成配置
72
+ **结构说明:**
73
+
74
+ ```
75
+ 'common:button.login'
76
+ ↑ ↑ ↑
77
+ 命名空间 分类 具体标识
78
+ ```
39
79
 
40
- `vite.config.ts` 中配置自动生成:
80
+ ### 命名规范
41
81
 
42
82
  ```typescript
43
- import ts2Locales from '@brain-toolkit/ts2locales/vite';
83
+ // 好的命名:清晰、结构化
84
+ export const BUTTON_LOGIN = 'common:button.login'; // 按钮文本
85
+ export const BUTTON_SUBMIT = 'common:button.submit'; // 按钮文本
86
+ export const MESSAGE_WELCOME = 'common:message.welcome'; // 消息文本
87
+ export const CONFIRM_DELETE = 'common:confirm.delete'; // 确认对话框
88
+ export const ERROR_NETWORK = 'common:error.network'; // 错误信息
89
+ export const PAGE_HOME_TITLE = 'common:page.home.title'; // 页面标题
90
+
91
+ // ❌ 不好的命名:模糊、无结构
92
+ export const TEXT1 = 'login';
93
+ export const MSG = 'welcome';
94
+ export const DELETE_CONFIRM = 'delete_confirm';
95
+ ```
44
96
 
45
- export default defineConfig({
46
- plugins: [
47
- ts2Locales({
48
- locales: ['en', 'zh'],
49
- options: [
50
- {
51
- source: './config/Identifier/common.ts',
52
- target: './public/locales/{{lng}}/common.json'
53
- },
54
- {
55
- source: './config/Identifier/error.ts',
56
- target: './public/locales/{{lng}}/common.json'
57
- }
58
- ]
59
- })
60
- ]
61
- });
97
+ ---
98
+
99
+ ## 🤔 为什么使用 i18n Key
100
+
101
+ ### 问题:硬编码文本的痛点
102
+
103
+ ```typescript
104
+ // ❌ 传统方式:文本硬编码
105
+
106
+ function LoginPage() {
107
+ return (
108
+ <div>
109
+ <h1>登录</h1>
110
+ <button>登录</button>
111
+ <p>欢迎回来!</p>
112
+ <span>忘记密码?</span>
113
+ </div>
114
+ );
115
+ }
116
+
117
+ // 😰 问题 1:国际化困难
118
+ // 如果要支持英文,需要修改大量代码
119
+
120
+ // 😰 问题 2:维护困难
121
+ // 同样的文本可能在多个地方使用,修改时容易遗漏
122
+
123
+ // 😰 问题 3:难以统一管理
124
+ // 无法统计项目中有多少个文本,哪些需要翻译
125
+
126
+ // 😰 问题 4:搜索困难
127
+ // 想找到某个文本在哪里使用,只能全局搜索字符串
128
+ ```
129
+
130
+ ### 解决方案:使用 i18n Key
131
+
132
+ ```typescript
133
+ // ✅ 使用 i18n Key
134
+
135
+ import { useAppTranslation } from '@/uikit/hooks/useAppTranslation';
136
+ import {
137
+ PAGE_LOGIN_TITLE,
138
+ BUTTON_LOGIN,
139
+ MESSAGE_WELCOME,
140
+ LINK_FORGOT_PASSWORD
141
+ } from '@config/Identifier';
142
+
143
+ function LoginPage() {
144
+ const { t } = useAppTranslation();
145
+
146
+ return (
147
+ <div>
148
+ <h1>{t(PAGE_LOGIN_TITLE)}</h1>
149
+ <button>{t(BUTTON_LOGIN)}</button>
150
+ <p>{t(MESSAGE_WELCOME)}</p>
151
+ <span>{t(LINK_FORGOT_PASSWORD)}</span>
152
+ </div>
153
+ );
154
+ }
155
+
156
+ // ✅ 优势 1:自动国际化
157
+ // 根据用户语言自动显示对应文本
158
+
159
+ // ✅ 优势 2:集中管理
160
+ // 所有文本在 Identifier 文件中统一管理
161
+
162
+ // ✅ 优势 3:易于维护
163
+ // 修改文本只需修改一处
164
+
165
+ // ✅ 优势 4:类型安全
166
+ // TypeScript 会检查 Key 是否存在
167
+
168
+ // ✅ 优势 5:减少思考 ⭐
169
+ // 开发者只需要知道 BUTTON_LOGIN 是一个变量
170
+ // 不需要记住 'common:button.login' 这个字符串
171
+ // IDE 会提供自动补全,写错了会立即报错
172
+ ```
173
+
174
+ ### 对比总结
175
+
176
+ | 特性 | 硬编码文本 | i18n Key |
177
+ | ------------ | --------------------- | ----------------------- |
178
+ | **国际化** | ❌ 困难(需要改代码) | ✅ 自动(切换语言即可) |
179
+ | **维护性** | ❌ 差(分散各处) | ✅ 好(集中管理) |
180
+ | **重用性** | ❌ 差(复制粘贴) | ✅ 好(引用 Key) |
181
+ | **搜索性** | ❌ 困难(字符串搜索) | ✅ 简单(搜索常量) |
182
+ | **统计性** | ❌ 不可能 | ✅ 容易(统计 Key) |
183
+ | **类型安全** | ❌ 没有 | ✅ 有(TypeScript) |
184
+ | **心智负担** | ❌ 高(需记住文字) | ✅ 低(只需记变量名) |
185
+ | **IDE 支持** | ❌ 无自动补全 | ✅ 完整自动补全 |
186
+
187
+ ### ⭐ 核心优势:减少开发者思考
188
+
189
+ **最重要的优势:开发者不需要关心具体的 i18n 字符串是什么!**
190
+
191
+ ```typescript
192
+ // ❌ 传统方式:需要记住复杂的字符串
193
+ function LoginPage() {
194
+ const { t } = useTranslation();
195
+
196
+ return (
197
+ <div>
198
+ {/* 😰 需要记住 'common:button.login' */}
199
+ <button>{t('common:button.login')}</button>
200
+
201
+ {/* 😰 需要记住 'page.login.title' */}
202
+ <h1>{t('page.login.title')}</h1>
203
+
204
+ {/* 😰 写错了也不会报错 */}
205
+ <p>{t('page.login.welcom')}</p> {/* welcom → welcome,拼写错误 */}
206
+ </div>
207
+ );
208
+ }
209
+
210
+ // ✅ i18n Key 方式:只需要知道变量名
211
+ import { BUTTON_LOGIN, PAGE_LOGIN_TITLE, PAGE_LOGIN_WELCOME } from '@config/Identifier';
212
+
213
+ function LoginPage() {
214
+ const { t } = useAppTranslation();
215
+
216
+ return (
217
+ <div>
218
+ {/* ✅ 只需要记住 BUTTON_LOGIN,IDE 会自动补全 */}
219
+ <button>{t(BUTTON_LOGIN)}</button>
220
+
221
+ {/* ✅ 输入 PAGE_ 后,IDE 会提示所有可用的 Key */}
222
+ <h1>{t(PAGE_LOGIN_TITLE)}</h1>
223
+
224
+ {/* ✅ 写错了 TypeScript 会立即报错 */}
225
+ <p>{t(PAGE_LOGIN_WELCOM)}</p> {/* ❌ TS 错误:找不到变量 */}
226
+ </div>
227
+ );
228
+ }
229
+ ```
230
+
231
+ **开发体验对比:**
232
+
233
+ ```typescript
234
+ // ❌ 使用字符串的开发流程:
235
+ // 1. 😰 查看翻译文件,找到对应的 key
236
+ // 2. 😰 记住 key 的完整路径(如 'common:page.login.title')
237
+ // 3. 😰 在代码中手动输入字符串
238
+ // 4. 😰 可能输入错误,但编译器不会报错
239
+ // 5. 😰 运行时才发现翻译没有生效
240
+
241
+ // ✅ 使用 i18n Key 的开发流程:
242
+ // 1. 😊 输入变量名前缀(如 BUTTON_)
243
+ // 2. 😊 IDE 自动提示所有可用的 Key
244
+ // 3. 😊 选择需要的 Key,IDE 自动补全
245
+ // 4. 😊 如果输入错误,TypeScript 立即报错
246
+ // 5. 😊 编译期就能确保 Key 的正确性
247
+ ```
248
+
249
+ **认知负担对比:**
250
+
251
+ | 开发者需要记住的内容 | 字符串方式 | i18n Key 方式 |
252
+ | -------------------- | --------------------------------------- | --------------- |
253
+ | **翻译文件结构** | ✅ 必须记住(如 `common:button.login`) | ❌ 不需要记 |
254
+ | **命名空间** | ✅ 必须记住(如 `common:`, `page:`) | ❌ 不需要记 |
255
+ | **字符串路径** | ✅ 必须记住完整路径 | ❌ 不需要记 |
256
+ | **变量名** | ❌ 没有变量 | ✅ 只需记变量名 |
257
+ | **IDE 提示** | ❌ 没有 | ✅ 完整提示 |
258
+
259
+ **实际开发场景:**
260
+
261
+ ```typescript
262
+ // 场景 1:新写一个按钮
263
+
264
+ // ❌ 字符串方式(需要思考很多)
265
+ // 1. 这个按钮的翻译 key 应该叫什么?
266
+ // 2. 应该放在哪个命名空间?common? page?
267
+ // 3. 路径应该是什么?button.submit? submit.button?
268
+ // 4. 最终写出:t('common:button.submit')
269
+ <button>{t('common:button.submit')}</button>
270
+
271
+ // ✅ i18n Key 方式(只需要搜索)
272
+ // 1. 输入 BUTTON_
273
+ // 2. IDE 显示所有可用的按钮 Key
274
+ // 3. 看到 BUTTON_SUBMIT,选择它
275
+ // 4. 完成!
276
+ <button>{t(BUTTON_SUBMIT)}</button>
277
+
278
+ // 场景 2:要改另一个页面的文字
279
+
280
+ // ❌ 字符串方式
281
+ // 1. 查看那个页面的代码
282
+ // 2. 找到 t('page.home.welcome')
283
+ // 3. 记住这个字符串
284
+ // 4. 在翻译文件中搜索并修改
285
+
286
+ // ✅ i18n Key 方式
287
+ // 1. 看到代码中的 PAGE_HOME_WELCOME
288
+ // 2. 直接跳转到定义(IDE 的 Go to Definition)
289
+ // 3. 修改注释中的翻译
290
+ // 4. 自动生成新的翻译文件
291
+ ```
292
+
293
+ **总结:**
294
+
295
+ 使用 i18n Key 让开发者:
296
+
297
+ - ✅ **不需要记住** 复杂的字符串路径
298
+ - ✅ **不需要记住** 翻译文件的结构
299
+ - ✅ **不需要关心** 具体的翻译键名
300
+ - ✅ **只需要知道** 这是一个变量
301
+ - ✅ **依靠 IDE** 提供的自动补全和类型检查
302
+ - ✅ **专注于** 业务逻辑,而不是翻译细节
303
+
304
+ > 💡 **核心理念:让开发者把精力用在业务逻辑上,而不是记忆翻译字符串!**
305
+
306
+ ---
307
+
308
+ ## 🛠️ 项目中的实现
309
+
310
+ ### 1. 文件结构
311
+
312
+ ```
313
+ config/
314
+ └── Identifier/ # i18n Key 定义目录
315
+ ├── index.ts # 导出所有 Key
316
+ ├── common/ # 公共文本
317
+ │ ├── index.ts
318
+ │ ├── common.ts # 通用文本(按钮、消息等)
319
+ │ └── common.error.ts # 错误信息
320
+ └── pages/ # 页面文本
321
+ ├── index.ts
322
+ ├── page.home.ts # 首页文本
323
+ ├── page.login.ts # 登录页文本
324
+ └── page.about.ts # 关于页文本
325
+
326
+ public/
327
+ └── locales/ # 生成的翻译文件
328
+ ├── zh/
329
+ │ └── common.json # 中文翻译
330
+ └── en/
331
+ └── common.json # 英文翻译
332
+
333
+ src/
334
+ └── uikit/
335
+ └── hooks/
336
+ └── useAppTranslation.ts # 翻译 Hook
62
337
  ```
63
338
 
64
- ### 3. i18n 服务配置
339
+ ### 2. i18n 配置
65
340
 
66
341
  ```typescript
67
- // config/i18n.ts
68
- export default {
69
- fallbackLng: 'en',
342
+ // config/i18n/i18nConfig.ts
343
+ export const i18nConfig = {
344
+ fallbackLng: 'en', // 默认语言
70
345
  debug: false,
71
346
  interpolation: {
72
347
  escapeValue: false
73
348
  },
74
- ns: ['common'],
349
+ ns: ['common'], // 命名空间
75
350
  defaultNS: 'common',
76
351
  backend: {
77
- loadPath: '/locales/{{lng}}/{{ns}}.json'
352
+ loadPath: '/locales/{{lng}}/{{ns}}.json' // 翻译文件路径
78
353
  },
79
- supportedLngs: ['en', 'zh']
354
+ supportedLngs: ['en', 'zh'] // 支持的语言
80
355
  } as const;
81
356
  ```
82
357
 
83
- ## 使用方式
358
+ ### 3. I18nService(Bootstrap 插件)
84
359
 
85
- ### 1. 定义翻译键
360
+ ```typescript
361
+ // src/base/services/I18nService.ts
362
+ export class I18nService implements BootstrapExecutorPlugin {
363
+ readonly pluginName = 'I18nService';
364
+
365
+ constructor(protected pathname: string) {
366
+ super(() => new I18nServiceState(i18n.language));
367
+ }
368
+
369
+ /**
370
+ * 在 Bootstrap 启动时初始化 i18n
371
+ */
372
+ onBefore(): void {
373
+ i18n
374
+ .use(HttpApi) // 加载翻译文件
375
+ .use(LanguageDetector) // 语言检测
376
+ .use(initReactI18next) // React 集成
377
+ .init(i18nConfig); // 初始化配置
378
+
379
+ // 添加自定义语言检测器(从 URL 路径检测)
380
+ const pathLanguageDetector = {
381
+ name: 'pathLanguageDetector',
382
+ lookup: () => {
383
+ const paths = this.pathname.split('/');
384
+ for (const path of paths) {
385
+ if (this.isValidLanguage(path)) {
386
+ return path; // 从 URL 中提取语言(如 /zh/home)
387
+ }
388
+ }
389
+ return fallbackLng;
390
+ },
391
+ cacheUserLanguage(lng: string) {
392
+ localStorage.setItem('i18nextLng', lng);
393
+ }
394
+ };
395
+
396
+ i18n.services.languageDetector.addDetector(pathLanguageDetector);
397
+ }
398
+
399
+ /**
400
+ * 切换语言
401
+ */
402
+ async changeLanguage(language: string): Promise<void> {
403
+ await i18n.changeLanguage(language);
404
+ localStorage.setItem('i18nextLng', language);
405
+ }
406
+
407
+ /**
408
+ * 翻译文本
409
+ */
410
+ t(key: string, params?: Record<string, unknown>): string {
411
+ return i18n.t(key, { lng: i18n.language, ...params });
412
+ }
413
+ }
414
+ ```
415
+
416
+ ### 4. 自动生成翻译文件
86
417
 
87
418
  ```typescript
88
- // config/Identifier/page.ts
419
+ // vite.config.ts
420
+ import ts2Locales from '@brain-toolkit/ts2locales/vite';
421
+
422
+ export default defineConfig({
423
+ plugins: [
424
+ // ✅ ts2locales 插件:自动从 TypeScript 注释生成翻译文件
425
+ ts2Locales({
426
+ locales: ['en', 'zh'],
427
+ options: generateTs2LocalesOptions() // 配置生成规则
428
+ })
429
+ ]
430
+ });
431
+ ```
432
+
433
+ **工作原理:**
434
+
435
+ ```typescript
436
+ // 1. 在 Identifier 文件中定义 Key
437
+ /**
438
+ * @description Login button text
439
+ * @localZh 登录
440
+ * @localEn Login
441
+ */
442
+ export const BUTTON_LOGIN = 'common:button.login';
443
+
444
+ // 2. ts2locales 插件自动生成翻译文件
445
+
446
+ // public/locales/zh/common.json
447
+ {
448
+ "button": {
449
+ "login": "登录"
450
+ }
451
+ }
452
+
453
+ // public/locales/en/common.json
454
+ {
455
+ "button": {
456
+ "login": "Login"
457
+ }
458
+ }
459
+ ```
460
+
461
+ ---
462
+
463
+ ## 📝 使用方式
464
+
465
+ ### 1. 定义 i18n Key
466
+
467
+ ```typescript
468
+ // config/Identifier/pages/page.home.ts
89
469
 
90
470
  /**
91
471
  * @description Home page title
92
472
  * @localZh 首页
93
473
  * @localEn Home
94
474
  */
95
- export const PAGE_HOME_TITLE = 'page.home.title';
475
+ export const PAGE_HOME_TITLE = 'common:page.home.title';
96
476
 
97
477
  /**
98
478
  * @description Home page welcome message
99
- * @localZh 欢迎来到我们的应用
100
- * @localEn Welcome to our application
479
+ * @localZh 欢迎来到我们的应用!
480
+ * @localEn Welcome to our application!
101
481
  */
102
- export const PAGE_HOME_WELCOME = 'page.home.welcome';
482
+ export const PAGE_HOME_WELCOME = 'common:page.home.welcome';
483
+
484
+ /**
485
+ * @description Home page description with user name
486
+ * @localZh 你好,{{name}}!今天是个好日子。
487
+ * @localEn Hello, {{name}}! Have a great day.
488
+ */
489
+ export const PAGE_HOME_GREETING = 'common:page.home.greeting';
103
490
  ```
104
491
 
105
- ### 2. 在组件中使用
492
+ **注释规范:**
493
+
494
+ - `@description`:说明文本用途(英文)
495
+ - `@localZh`:中文翻译
496
+ - `@localEn`:英文翻译
106
497
 
107
- ```tsx
108
- import { useTranslation } from 'react-i18next';
109
- import * as i18nKeys from '@config/Identifier/page';
498
+ ### 2. 在 UI 组件中使用
499
+
500
+ ```typescript
501
+ // src/pages/base/HomePage.tsx
502
+ import { useAppTranslation } from '@/uikit/hooks/useAppTranslation';
503
+ import {
504
+ PAGE_HOME_TITLE,
505
+ PAGE_HOME_WELCOME,
506
+ PAGE_HOME_GREETING
507
+ } from '@config/Identifier';
110
508
 
111
509
  function HomePage() {
112
- const { t } = useTranslation();
510
+ const { t } = useAppTranslation();
511
+ const userName = 'John';
113
512
 
114
513
  return (
115
514
  <div>
116
- <h1>{t(i18nKeys.PAGE_HOME_TITLE)}</h1>
117
- <p>{t(i18nKeys.PAGE_HOME_WELCOME)}</p>
515
+ {/* ✅ 简单文本 */}
516
+ <h1>{t(PAGE_HOME_TITLE)}</h1>
517
+ <p>{t(PAGE_HOME_WELCOME)}</p>
518
+
519
+ {/* ✅ 带参数的文本 */}
520
+ <p>{t(PAGE_HOME_GREETING, { name: userName })}</p>
118
521
  </div>
119
522
  );
120
523
  }
121
524
  ```
122
525
 
123
- ### 3. 带参数的翻译
526
+ ### 3. 在服务中使用
124
527
 
125
528
  ```typescript
126
- /**
127
- * @description Welcome user message
128
- * @localZh 欢迎,{{name}}!
129
- * @localEn Welcome, {{name}}!
130
- */
131
- export const USER_WELCOME = 'user.welcome';
132
-
133
- function Welcome({ username }: { username: string }) {
134
- const { t } = useTranslation();
135
- return <h1>{t(i18nKeys.USER_WELCOME, { name: username })}</h1>;
529
+ // src/base/services/UserService.ts
530
+ import { injectable, inject } from 'inversify';
531
+ import { I18nServiceInterface } from '@/base/port/I18nServiceInterface';
532
+ import { ERROR_USER_NOT_FOUND } from '@config/Identifier';
533
+
534
+ @injectable()
535
+ export class UserService {
536
+ constructor(
537
+ @inject(IOCIdentifier.I18nServiceInterface)
538
+ private i18n: I18nServiceInterface
539
+ ) {}
540
+
541
+ async getUser(id: string) {
542
+ const user = await this.api.getUserById(id);
543
+
544
+ if (!user) {
545
+ // ✅ 在服务中使用 i18n
546
+ throw new Error(this.i18n.t(ERROR_USER_NOT_FOUND, { id }));
547
+ }
548
+
549
+ return user;
550
+ }
136
551
  }
137
552
  ```
138
553
 
139
- ### 4. 语言切换
554
+ ### 4. 切换语言
140
555
 
141
- ```tsx
142
- import { IOC } from '@/core/IOC';
143
- import { I18nService } from '@/base/services/I18nService';
556
+ ```typescript
557
+ // src/uikit/components/LanguageSwitcher.tsx
558
+ import { useIOC } from '@/uikit/hooks/useIOC';
144
559
 
145
560
  function LanguageSwitcher() {
146
- const i18nService = IOC(I18nService);
561
+ const i18nService = useIOC('I18nServiceInterface');
147
562
  const currentLang = i18nService.getCurrentLanguage();
148
563
 
149
- const handleChange = (lang: string) => {
564
+ const handleChange = (lang: 'zh' | 'en') => {
565
+ // ✅ 切换语言
150
566
  i18nService.changeLanguage(lang);
151
567
  };
152
568
 
@@ -159,110 +575,405 @@ function LanguageSwitcher() {
159
575
  }
160
576
  ```
161
577
 
162
- ## 最佳实践
578
+ ### 5. 复杂场景
163
579
 
164
- ### 1. 标识符组织
580
+ #### 场景 1:动态文本
165
581
 
166
- - **命名规范**:
582
+ ```typescript
583
+ /**
584
+ * @description Upload progress message
585
+ * @localZh 已上传 {{current}} / {{total}} 个文件
586
+ * @localEn Uploaded {{current}} / {{total}} files
587
+ */
588
+ export const UPLOAD_PROGRESS = 'common:upload.progress';
167
589
 
168
- ```typescript
169
- // 页面相关
170
- PAGE_HOME_TITLE;
171
- PAGE_ABOUT_DESCRIPTION;
590
+ // 使用
591
+ <p>{t(UPLOAD_PROGRESS, { current: 3, total: 10 })}</p>
592
+ // 中文:已上传 3 / 10 个文件
593
+ // 英文:Uploaded 3 / 10 files
594
+ ```
172
595
 
173
- // 功能模块相关
174
- AUTH_LOGIN_BUTTON;
175
- AUTH_LOGOUT_CONFIRM;
596
+ #### 场景 2:复数形式
176
597
 
177
- // 公共组件相关
178
- COMMON_SUBMIT_BUTTON;
179
- COMMON_CANCEL_BUTTON;
180
- ```
598
+ ```typescript
599
+ /**
600
+ * @description Files count message
601
+ * @localZh {{count}} 个文件
602
+ * @localEn {{count}} file_plural
603
+ */
604
+ export const FILES_COUNT = 'common:files.count';
181
605
 
182
- - **文件组织**:
183
- ```
184
- config/
185
- ├── Identifier/
186
- │ ├── common.ts // 公共文本
187
- │ ├── page.ts // 页面文本
188
- │ ├── error.ts // 错误信息
189
- │ └── auth.ts // 认证相关
190
- ```
606
+ // 使用
607
+ <p>{t(FILES_COUNT, { count: 1 })}</p> // 1 file
608
+ <p>{t(FILES_COUNT, { count: 5 })}</p> // 5 files
609
+ ```
191
610
 
192
- ### 2. 注释规范
611
+ #### 场景 3:HTML 内容
193
612
 
194
613
  ```typescript
195
614
  /**
196
- * @description 简短的描述,说明文本用途
197
- * @localZh 中文翻译文本
198
- * @localEn 英文翻译文本
615
+ * @description Terms of service agreement
616
+ * @localZh 我已阅读并同意<a href="/terms">服务条款</a>
617
+ * @localEn I have read and agree to the <a href="/terms">Terms of Service</a>
199
618
  */
619
+ export const TERMS_AGREEMENT = 'common:terms.agreement';
620
+
621
+ // 使用(React 中需要使用 Trans 组件)
622
+ import { Trans } from 'react-i18next';
623
+
624
+ <Trans i18nKey={TERMS_AGREEMENT}>
625
+ I have read and agree to the <a href="/terms">Terms of Service</a>
626
+ </Trans>
627
+ ```
628
+
629
+ ---
630
+
631
+ ## 💎 最佳实践
632
+
633
+ ### 1. ✅ 所有文本都使用 i18n Key
634
+
635
+ ```typescript
636
+ // ❌ 错误:混用硬编码和 i18n Key
637
+ function LoginForm() {
638
+ return (
639
+ <form>
640
+ <h1>{t(PAGE_LOGIN_TITLE)}</h1>
641
+ <button>登录</button> {/* ❌ 硬编码 */}
642
+ <a href="/forgot">忘记密码?</a> {/* ❌ 硬编码 */}
643
+ </form>
644
+ );
645
+ }
646
+
647
+ // ✅ 正确:所有文本都使用 i18n Key
648
+ function LoginForm() {
649
+ return (
650
+ <form>
651
+ <h1>{t(PAGE_LOGIN_TITLE)}</h1>
652
+ <button>{t(BUTTON_LOGIN)}</button> {/* ✅ 使用 Key */}
653
+ <a href="/forgot">{t(LINK_FORGOT_PASSWORD)}</a> {/* ✅ 使用 Key */}
654
+ </form>
655
+ );
656
+ }
200
657
  ```
201
658
 
202
- ### 3. 类型安全
659
+ ### 2. ✅ 合理组织 i18n Key
203
660
 
204
661
  ```typescript
205
- // 使用类型导入确保键值存在
206
- import * as i18nKeys from '@config/Identifier';
662
+ // ✅ 好的组织:按功能模块分类
663
+ config/Identifier/
664
+ ├── common/
665
+ │ ├── common.ts # 通用文本(按钮、标签等)
666
+ │ └── common.error.ts # 错误信息
667
+ ├── pages/
668
+ │ ├── page.home.ts # 首页文本
669
+ │ ├── page.login.ts # 登录页文本
670
+ │ └── page.about.ts # 关于页文本
671
+ └── components/
672
+ ├── component.header.ts # 头部组件文本
673
+ └── component.footer.ts # 底部组件文本
674
+ ```
675
+
676
+ ### 3. ✅ 使用语义化的命名
207
677
 
208
- // 类型检查会捕获错误的键值
209
- t(i18nKeys.NONEXISTENT_KEY); // TS 错误
678
+ ```typescript
679
+ // 好的命名:清晰表达含义
680
+ export const BUTTON_SUBMIT = 'common:button.submit';
681
+ export const BUTTON_CANCEL = 'common:button.cancel';
682
+ export const ERROR_NETWORK = 'common:error.network';
683
+ export const MESSAGE_SUCCESS = 'common:message.success';
684
+
685
+ // ❌ 不好的命名:含义不清
686
+ export const BTN1 = 'btn1';
687
+ export const TEXT = 'text';
688
+ export const MSG = 'msg';
210
689
  ```
211
690
 
212
- ### 4. 动态内容
691
+ ### 4. ✅ 为 i18n Key 添加清晰的注释
213
692
 
214
693
  ```typescript
694
+ // ✅ 好的注释:清晰说明用途和上下文
695
+ /**
696
+ * @description Confirmation message when user tries to delete an item
697
+ * @localZh 确定要删除 {{itemName}} 吗?此操作不可撤销。
698
+ * @localEn Are you sure you want to delete {{itemName}}? This action cannot be undone.
699
+ */
700
+ export const CONFIRM_DELETE_ITEM = 'common:confirm.delete.item';
701
+
702
+ // ❌ 不好的注释:没有上下文信息
215
703
  /**
216
- * @description File upload progress
217
- * @localZh 已上传 {{count}} 个文件,共 {{total}} 个
218
- * @localEn Uploaded {{count}} files out of {{total}}
704
+ * @description Delete
705
+ * @localZh 删除
706
+ * @localEn Delete
219
707
  */
220
- export const UPLOAD_PROGRESS = 'upload.progress';
708
+ export const DELETE = 'delete';
709
+ ```
710
+
711
+ ### 5. ✅ 统一命名规范
712
+
713
+ ```typescript
714
+ // ✅ 推荐的命名规范:
715
+
716
+ // 按钮文本
717
+ export const BUTTON_LOGIN = 'common:button.login';
718
+ export const BUTTON_SUBMIT = 'common:button.submit';
719
+ export const BUTTON_CANCEL = 'common:button.cancel';
720
+
721
+ // 页面标题
722
+ export const PAGE_HOME_TITLE = 'common:page.home.title';
723
+ export const PAGE_ABOUT_TITLE = 'common:page.about.title';
724
+
725
+ // 错误信息
726
+ export const ERROR_NETWORK = 'common:error.network';
727
+ export const ERROR_INVALID_INPUT = 'common:error.invalid.input';
728
+
729
+ // 消息提示
730
+ export const MESSAGE_SUCCESS = 'common:message.success';
731
+ export const MESSAGE_WARNING = 'common:message.warning';
732
+
733
+ // 确认对话框
734
+ export const CONFIRM_DELETE = 'common:confirm.delete';
735
+ export const CONFIRM_LOGOUT = 'common:confirm.logout';
736
+
737
+ // 链接文本
738
+ export const LINK_FORGOT_PASSWORD = 'common:link.forgot.password';
739
+ export const LINK_PRIVACY_POLICY = 'common:link.privacy.policy';
740
+ ```
741
+
742
+ ### 6. ✅ 避免重复定义
743
+
744
+ ```typescript
745
+ // ❌ 错误:重复定义相同的文本
746
+ // config/Identifier/common/common.ts
747
+ export const BUTTON_OK_1 = 'common:button.ok1';
748
+
749
+ // config/Identifier/pages/page.home.ts
750
+ export const BUTTON_OK_2 = 'common:button.ok2';
751
+
752
+ // ✅ 正确:在公共文件中定义一次,多处引用
753
+ // config/Identifier/common/common.ts
754
+ export const BUTTON_OK = 'common:button.ok';
755
+
756
+ // 在各个页面中引用
757
+ import { BUTTON_OK } from '@config/Identifier';
758
+ ```
759
+
760
+ ### 7. ✅ 使用参数化文本
761
+
762
+ ```typescript
763
+ // ❌ 不好:为每种情况定义不同的 Key
764
+ export const WELCOME_USER_JOHN = 'common:welcome.john';
765
+ export const WELCOME_USER_MARY = 'common:welcome.mary';
766
+
767
+ // ✅ 好:使用参数
768
+ /**
769
+ * @description Welcome message with user name
770
+ * @localZh 欢迎,{{name}}!
771
+ * @localEn Welcome, {{name}}!
772
+ */
773
+ export const WELCOME_USER = 'common:welcome.user';
221
774
 
222
775
  // 使用
223
- t(i18nKeys.UPLOAD_PROGRESS, { count: 3, total: 10 });
776
+ <h1>{t(WELCOME_USER, { name: 'John' })}</h1>
777
+ <h1>{t(WELCOME_USER, { name: 'Mary' })}</h1>
224
778
  ```
225
779
 
226
- ## 工作流程
780
+ ---
227
781
 
228
- 1. **开发阶段**:
229
- - 在 Identifier 文件中定义翻译键和注释
230
- - 使用标识符别名在代码中引用翻译
231
- - 运行开发服务器时自动生成翻译文件
782
+ ## ❓ 常见问题
232
783
 
233
- 2. **构建阶段**:
234
- - 自动检查翻译完整性
235
- - 生成生产环境翻译文件
236
- - 优化翻译资源加载
784
+ ### Q1: 为什么不能直接写文字?
237
785
 
238
- 3. **运行时**:
239
- - 根据 URL 检测语言
240
- - 按需加载翻译资源
241
- - 响应语言切换事件
786
+ **A:**
242
787
 
243
- ## 常见问题
788
+ ```typescript
789
+ // ❌ 直接写文字的问题
790
+ <button>登录</button>
244
791
 
245
- ### 1. 翻译未生效
792
+ // 问题 1:无法国际化
793
+ // 如果用户切换到英文,文字不会改变
246
794
 
247
- 检查以下几点:
795
+ // 问题 2:难以维护
796
+ // 如果要把"登录"改成"立即登录",需要找到所有地方修改
248
797
 
249
- - 确保标识符正确导出
250
- - 检查注释格式是否规范
251
- - 验证翻译文件是否生成
252
- - 确认语言检测正确
798
+ // 问题 3:无法统一管理
799
+ // 无法知道项目中有多少个"登录"按钮
253
800
 
254
- ### 2. 类型错误
801
+ // 使用 i18n Key 的优势
802
+ <button>{t(BUTTON_LOGIN)}</button>
255
803
 
256
- 可能的解决方案:
804
+ // ✅ 自动国际化:切换语言自动显示对应文本
805
+ // ✅ 易于维护:只需修改一处定义
806
+ // ✅ 统一管理:所有文本集中在 Identifier 文件中
807
+ ```
808
+
809
+ ### Q2: 如何知道某个 Key 在哪里使用?
810
+
811
+ **A:**
812
+
813
+ ```bash
814
+ # 搜索常量名
815
+ grep -r "BUTTON_LOGIN" src/
257
816
 
258
- - 检查标识符导入路径
259
- - 确保使用正确的标识符
260
- - 更新 TypeScript 类型定义
817
+ # 或者在 IDE 中使用 "Find Usages" 功能
818
+ # 可以快速找到所有使用该 Key 的地方
819
+ ```
820
+
821
+ ### Q3: 忘记定义 i18n Key 怎么办?
261
822
 
262
- ### 3. 性能优化
823
+ **A:** TypeScript 会在编译时报错:
824
+
825
+ ```typescript
826
+ // ❌ 使用未定义的 Key
827
+ <button>{t(BUTTON_NOT_EXIST)}</button>
828
+ // TypeScript 错误:Cannot find name 'BUTTON_NOT_EXIST'
829
+
830
+ // ✅ 正确:先定义 Key
831
+ export const BUTTON_NOT_EXIST = 'common:button.not.exist';
832
+ ```
833
+
834
+ ### Q4: 如何处理动态文本?
835
+
836
+ **A:** 使用参数:
837
+
838
+ ```typescript
839
+ /**
840
+ * @description User greeting with name and time
841
+ * @localZh {{name}},{{time}}好!
842
+ * @localEn Good {{time}}, {{name}}!
843
+ */
844
+ export const GREETING_WITH_TIME = 'common:greeting.with.time';
845
+
846
+ // 使用
847
+ <h1>{t(GREETING_WITH_TIME, { name: 'John', time: 'morning' })}</h1>
848
+ // 中文:John,morning好!
849
+ // 英文:Good morning, John!
850
+ ```
851
+
852
+ ### Q5: 翻译文件是如何生成的?
853
+
854
+ **A:**
855
+
856
+ ```
857
+ 1. 开发者在 Identifier 文件中定义 Key 和翻译注释
858
+
859
+ 2. ts2locales 插件扫描 TypeScript 文件
860
+
861
+ 3. 从注释中提取 @localZh 和 @localEn
862
+
863
+ 4. 自动生成 public/locales/{lng}/common.json
864
+
865
+ 5. i18next 在运行时加载这些 JSON 文件
866
+ ```
867
+
868
+ ### Q6: 可以在代码中直接使用翻译后的文本吗?
869
+
870
+ **A:**
871
+
872
+ ```typescript
873
+ // ❌ 不推荐:跳过 i18n 系统
874
+ const loginText = '登录'; // 硬编码
875
+ <button>{loginText}</button>
876
+
877
+ // ✅ 推荐:始终使用 i18n Key
878
+ const loginText = t(BUTTON_LOGIN); // 通过 i18n 系统
879
+ <button>{loginText}</button>
880
+ ```
881
+
882
+ ### Q7: 如何测试 i18n?
883
+
884
+ **A:**
885
+
886
+ ```typescript
887
+ // __tests__/i18n.test.ts
888
+ import { I18nService } from '@/base/services/I18nService';
889
+ import { BUTTON_LOGIN } from '@config/Identifier';
890
+
891
+ describe('I18n', () => {
892
+ let i18nService: I18nService;
893
+
894
+ beforeEach(() => {
895
+ i18nService = new I18nService('/zh/home');
896
+ i18nService.onBefore();
897
+ });
898
+
899
+ it('should translate to Chinese', () => {
900
+ i18nService.changeLanguage('zh');
901
+ expect(i18nService.t(BUTTON_LOGIN)).toBe('登录');
902
+ });
903
+
904
+ it('should translate to English', () => {
905
+ i18nService.changeLanguage('en');
906
+ expect(i18nService.t(BUTTON_LOGIN)).toBe('Login');
907
+ });
908
+
909
+ it('should support parameters', () => {
910
+ const result = i18nService.t(WELCOME_USER, { name: 'John' });
911
+ expect(result).toContain('John');
912
+ });
913
+ });
914
+ ```
915
+
916
+ ---
917
+
918
+ ## 📚 相关文档
919
+
920
+ - [项目架构设计](./index.md) - 了解整体架构
921
+ - [Bootstrap 启动器](./bootstrap.md) - I18nService 是 Bootstrap 插件之一
922
+ - [配置驱动开发](./#配置驱动开发) - i18n Key 是配置驱动的实践
923
+
924
+ ---
925
+
926
+ ## 🎉 总结
927
+
928
+ 国际化系统的核心理念:
929
+
930
+ 1. **唯一标识符** 🔑 - 每个文本都有唯一的 i18n Key
931
+ 2. **绝不硬编码** 🚫 - 项目中所有文本都必须使用 i18n Key
932
+ 3. **减少思考** 🧠 - 开发者只需知道变量名,不需要记住字符串路径
933
+ 4. **集中管理** 📦 - 所有 Key 定义在 Identifier 目录中
934
+ 5. **自动生成** ⚡ - 翻译文件从注释自动生成
935
+ 6. **类型安全** 🔒 - TypeScript 提供完整的类型检查
936
+ 7. **易于维护** 🛠️ - 修改翻译只需修改一处
937
+ 8. **IDE 友好** 💻 - 完整的自动补全和类型提示
938
+
939
+ **记住两个核心原则:**
940
+
941
+ 1. **永远不要在代码中直接写文字!**
942
+ 2. **开发者不需要记住翻译字符串,只需要知道变量名!**
943
+
944
+ ```typescript
945
+ // ❌ 永远不要这样做
946
+ <button>登录</button>
947
+ <h1>欢迎回来</h1>
948
+ <p>{t('common:confirm.delete')}</p> // 也不要直接写字符串
949
+
950
+ // ✅ 始终这样做
951
+ <button>{t(BUTTON_LOGIN)}</button>
952
+ <h1>{t(MESSAGE_WELCOME)}</h1>
953
+ <p>{t(CONFIRM_DELETE)}</p> // 使用变量,IDE 会自动补全
954
+ ```
955
+
956
+ **开发流程:**
957
+
958
+ ```typescript
959
+ // 1. 输入变量名前缀
960
+ t(BUTTON_
961
+
962
+ // 2. IDE 自动提示所有可用的 Key
963
+ // BUTTON_LOGIN
964
+ // BUTTON_SUBMIT
965
+ // BUTTON_CANCEL
966
+ // ...
967
+
968
+ // 3. 选择需要的 Key,完成!
969
+ t(BUTTON_LOGIN)
970
+
971
+ // 不需要记住 'common:button.login'
972
+ // 不需要关心翻译文件的结构
973
+ // 不需要担心拼写错误(TypeScript 会检查)
974
+ ```
263
975
 
264
- 开发建议:
976
+ ---
265
977
 
266
- - 合理拆分翻译文件
267
- - 使用翻译资源预加载
268
- - 避免重复翻译键
978
+ **问题反馈:**
979
+ 如果你对国际化系统有任何疑问或建议,请在团队频道中讨论或提交 Issue。