@nocobase/portal-template-default 1.0.8 → 1.0.10

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 (262) hide show
  1. package/.env.example +5 -0
  2. package/.github/workflows/publish.yml +106 -0
  3. package/.gitignore +33 -0
  4. package/AGENTS.md +19 -0
  5. package/README.MD +61 -180
  6. package/package.json +3 -5
  7. package/scripts/build-html.mjs +12 -0
  8. package/src/App.css +2 -0
  9. package/src/App.tsx +35 -186
  10. package/src/app/development.tsx +154 -0
  11. package/src/app/extension.ts +5 -0
  12. package/src/app/extensions.tsx +18 -4
  13. package/src/app/routes.tsx +65 -0
  14. package/src/components/access-control/acl-bootstrap.tsx +11 -3
  15. package/src/components/access-control/acl-gate.tsx +7 -6
  16. package/src/components/access-control/can-access.tsx +17 -0
  17. package/src/components/access-control/navigate-to-accessible-resource.tsx +8 -3
  18. package/src/components/app-shell/sidebar.tsx +21 -5
  19. package/src/components/auth/auto-redirect-provider.tsx +2 -1
  20. package/src/extensions/nocobase-acl/README.md +74 -0
  21. package/{registry → src/extensions}/nocobase-acl/components/acl-boundary.tsx +22 -25
  22. package/{registry → src/extensions}/nocobase-acl/components/role-switcher.tsx +26 -23
  23. package/src/extensions/nocobase-acl/demo/acl-preview-provider.tsx +81 -0
  24. package/{registry → src/extensions}/nocobase-acl/demo/boundary-api.tsx +58 -0
  25. package/{registry → src/extensions}/nocobase-acl/demo/index.tsx +243 -105
  26. package/{registry → src/extensions}/nocobase-acl/demo/prompt-generator.tsx +3 -1
  27. package/src/extensions/nocobase-acl/extension.tsx +73 -0
  28. package/{registry → src/extensions}/nocobase-ai/README.md +21 -5
  29. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-composer.tsx +3 -1
  30. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-messages.tsx +38 -3
  31. package/{registry → src/extensions}/nocobase-ai/components/chat/tool-call-card.tsx +1 -9
  32. package/{registry → src/extensions}/nocobase-ai/components/index.ts +5 -0
  33. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +17 -0
  34. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +35 -0
  35. package/src/extensions/nocobase-ai/components/surfaces/chat-surface.tsx +108 -0
  36. package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +2 -2
  37. package/{registry → src/extensions}/nocobase-ai/demo/configuration-gate.tsx +4 -1
  38. package/{registry → src/extensions}/nocobase-ai/demo/container-showcase.tsx +2 -2
  39. package/{registry → src/extensions}/nocobase-ai/demo/floating.tsx +45 -15
  40. package/{registry → src/extensions}/nocobase-ai/demo/index.tsx +116 -74
  41. package/{registry → src/extensions}/nocobase-ai/demo/prompt-generator.tsx +3 -3
  42. package/{registry → src/extensions}/nocobase-ai/demo/tool-cards.tsx +2 -3
  43. package/src/extensions/nocobase-ai/extension.tsx +160 -0
  44. package/{registry → src/extensions}/nocobase-ai/global-ai-chat.tsx +12 -28
  45. package/{registry → src/extensions}/nocobase-ai/providers/ai-provider.tsx +14 -1
  46. package/{registry → src/extensions}/nocobase-ai/providers/chat-context.tsx +1 -0
  47. package/{registry → src/extensions}/nocobase-ai/providers/chat-provider.tsx +29 -8
  48. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-attachments.ts +6 -1
  49. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-work-context.ts +4 -1
  50. package/{registry → src/extensions}/nocobase-route-surfaces/README.md +2 -1
  51. package/{registry → src/extensions}/nocobase-route-surfaces/demo/index.tsx +33 -7
  52. package/{registry → src/extensions}/nocobase-route-surfaces/demo/scenarios.ts +20 -14
  53. package/{registry → src/extensions}/nocobase-route-surfaces/extension.tsx +55 -52
  54. package/src/{pages/users → extensions/nocobase-users-example}/create.tsx +4 -4
  55. package/src/{pages/users → extensions/nocobase-users-example}/edit.tsx +5 -9
  56. package/src/extensions/nocobase-users-example/extension.tsx +109 -0
  57. package/src/{pages/users → extensions/nocobase-users-example}/form-context.ts +11 -9
  58. package/src/extensions/nocobase-users-example/layout.tsx +16 -0
  59. package/src/{pages/users → extensions/nocobase-users-example}/list.tsx +1 -1
  60. package/src/extensions/nocobase-users-example/locales/en-US.ts +77 -0
  61. package/src/extensions/nocobase-users-example/locales/index.ts +8 -0
  62. package/src/extensions/nocobase-users-example/locales/zh-CN.ts +73 -0
  63. package/src/extensions/nocobase-users-example/optional-ai.ts +67 -0
  64. package/src/{pages/users → extensions/nocobase-users-example}/role-detail.tsx +4 -15
  65. package/src/{pages/users → extensions/nocobase-users-example}/routes.ts +1 -4
  66. package/src/extensions/nocobase-users-example/show.tsx +266 -0
  67. package/src/lib/nocobase/acl/context.ts +32 -0
  68. package/src/lib/nocobase/acl/evaluator.ts +186 -0
  69. package/src/lib/nocobase/acl/hooks.ts +40 -0
  70. package/src/lib/nocobase/acl/index.ts +4 -1
  71. package/src/lib/nocobase/acl/menu.ts +6 -6
  72. package/src/lib/nocobase/acl/record-permissions.ts +37 -7
  73. package/src/lib/nocobase/acl/store-provider.tsx +14 -0
  74. package/src/lib/nocobase/acl/store.ts +92 -86
  75. package/src/lib/nocobase/acl/types.ts +52 -32
  76. package/src/lib/nocobase/auth-session.ts +128 -0
  77. package/src/lib/nocobase/client.ts +48 -50
  78. package/src/locales/en-US.ts +0 -78
  79. package/src/locales/index.ts +2 -7
  80. package/src/locales/zh-CN.ts +0 -74
  81. package/src/providers/access-control.ts +43 -8
  82. package/src/providers/auth-callback.ts +1 -0
  83. package/src/providers/auth.ts +24 -26
  84. package/src/providers/constants.ts +0 -4
  85. package/src/providers/data.ts +14 -26
  86. package/src/providers/runtime-config.ts +40 -0
  87. package/src/vite-env.d.ts +9 -0
  88. package/tests/auth-session-regression.mjs +101 -0
  89. package/tsconfig.json +2 -1
  90. package/vite.config.ts +9 -1
  91. package/registry/nocobase-acl/README.md +0 -19
  92. package/registry/nocobase-acl/extension.tsx +0 -71
  93. package/registry/nocobase-acl/tests/acl-regression.mjs +0 -314
  94. package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  95. package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  96. package/registry/nocobase-ai/extension.tsx +0 -158
  97. package/registry/nocobase-ai/tests/business-report-regression.mjs +0 -70
  98. package/registry/nocobase-ai/tests/form-filler-regression.mjs +0 -129
  99. package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +0 -128
  100. package/registry/nocobase-ai/tests/page-context-regression.mjs +0 -48
  101. package/registry/nocobase-ai/tests/stream-regression.mjs +0 -207
  102. package/registry/nocobase-auth-cas/README.md +0 -6
  103. package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +0 -41
  104. package/registry/nocobase-auth-cas/demo.tsx +0 -20
  105. package/registry/nocobase-auth-cas/extension.tsx +0 -46
  106. package/registry/nocobase-auth-cas/index.ts +0 -1
  107. package/registry/nocobase-auth-cas/use-cas-sign-in.ts +0 -29
  108. package/registry/nocobase-auth-dingtalk/README.md +0 -7
  109. package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +0 -154
  110. package/registry/nocobase-auth-dingtalk/demo.tsx +0 -23
  111. package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +0 -45
  112. package/registry/nocobase-auth-dingtalk/extension.tsx +0 -55
  113. package/registry/nocobase-auth-dingtalk/index.ts +0 -4
  114. package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +0 -64
  115. package/registry/nocobase-auth-ldap/README.md +0 -6
  116. package/registry/nocobase-auth-ldap/demo.tsx +0 -21
  117. package/registry/nocobase-auth-ldap/extension.tsx +0 -46
  118. package/registry/nocobase-auth-ldap/index.ts +0 -1
  119. package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +0 -69
  120. package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +0 -5
  121. package/registry/nocobase-auth-oidc/README.md +0 -11
  122. package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +0 -13
  123. package/registry/nocobase-auth-oidc/demo.tsx +0 -20
  124. package/registry/nocobase-auth-oidc/extension.tsx +0 -51
  125. package/registry/nocobase-auth-oidc/index.ts +0 -1
  126. package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +0 -44
  127. package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +0 -58
  128. package/registry/nocobase-auth-saml/README.md +0 -6
  129. package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +0 -13
  130. package/registry/nocobase-auth-saml/demo.tsx +0 -20
  131. package/registry/nocobase-auth-saml/extension.tsx +0 -51
  132. package/registry/nocobase-auth-saml/index.ts +0 -1
  133. package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +0 -43
  134. package/registry/nocobase-auth-saml/use-saml-sign-in.ts +0 -51
  135. package/registry/nocobase-auth-sms/README.md +0 -7
  136. package/registry/nocobase-auth-sms/demo.tsx +0 -25
  137. package/registry/nocobase-auth-sms/extension.tsx +0 -46
  138. package/registry/nocobase-auth-sms/index.ts +0 -1
  139. package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +0 -107
  140. package/registry/nocobase-auth-sms/use-sms-sign-in.ts +0 -88
  141. package/registry/nocobase-auth-wecom/README.md +0 -6
  142. package/registry/nocobase-auth-wecom/auto-login-provider.tsx +0 -21
  143. package/registry/nocobase-auth-wecom/demo.tsx +0 -23
  144. package/registry/nocobase-auth-wecom/extension.tsx +0 -49
  145. package/registry/nocobase-auth-wecom/index.ts +0 -1
  146. package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +0 -62
  147. package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +0 -70
  148. package/registry/nocobase-i18n/README.md +0 -44
  149. package/registry/nocobase-i18n/components/index.ts +0 -1
  150. package/registry/nocobase-i18n/components/language-switcher.tsx +0 -138
  151. package/registry/nocobase-i18n/demo/index.tsx +0 -209
  152. package/registry/nocobase-i18n/demo/prompt-generator.tsx +0 -132
  153. package/registry/nocobase-i18n/extension.tsx +0 -45
  154. package/registry/nocobase-i18n/index.ts +0 -7
  155. package/registry/nocobase-i18n/locales/en-US.ts +0 -39
  156. package/registry/nocobase-i18n/locales/index.ts +0 -8
  157. package/registry/nocobase-i18n/locales/zh-CN.ts +0 -35
  158. package/registry/nocobase-i18n/provider.tsx +0 -44
  159. package/registry/nocobase-i18n/server-resources.ts +0 -166
  160. package/registry/nocobase-i18n/tests/i18n-regression.mjs +0 -51
  161. package/registry.config.json +0 -302
  162. package/scripts/registry.mjs +0 -164
  163. package/src/components/access-control/resource-access-guard.tsx +0 -21
  164. package/src/components/access-control/use-acl-runtime.ts +0 -28
  165. package/src/lib/nocobase/acl/action.ts +0 -147
  166. package/src/pages/users/layout.tsx +0 -15
  167. package/src/pages/users/show.tsx +0 -278
  168. /package/{registry → src/extensions}/nocobase-acl/components/index.ts +0 -0
  169. /package/{registry → src/extensions}/nocobase-acl/components/role-options.ts +0 -0
  170. /package/{registry → src/extensions}/nocobase-acl/demo/components.tsx +0 -0
  171. /package/{registry → src/extensions}/nocobase-acl/demo/lazy-route.tsx +0 -0
  172. /package/{registry → src/extensions}/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -0
  173. /package/{registry → src/extensions}/nocobase-acl/demo/scenario-section.tsx +0 -0
  174. /package/{registry → src/extensions}/nocobase-acl/index.ts +0 -0
  175. /package/{registry → src/extensions}/nocobase-ai/adapters/react-hook-form.ts +0 -0
  176. /package/{registry → src/extensions}/nocobase-ai/assets/nocobase-ai-chat.svg +0 -0
  177. /package/{registry → src/extensions}/nocobase-ai/components/ai-root-provider.tsx +0 -0
  178. /package/{registry → src/extensions}/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -0
  179. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-attachment.tsx +0 -0
  180. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-compact.tsx +0 -0
  181. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-empty-state.tsx +0 -0
  182. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-header.tsx +0 -0
  183. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -0
  184. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-message.tsx +0 -0
  185. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-window.tsx +0 -0
  186. /package/{registry → src/extensions}/nocobase-ai/components/chat/conversation-list.tsx +0 -0
  187. /package/{registry → src/extensions}/nocobase-ai/components/chat/markdown-message.tsx +0 -0
  188. /package/{registry → src/extensions}/nocobase-ai/components/chat/model-select-options.tsx +0 -0
  189. /package/{registry → src/extensions}/nocobase-ai/components/chat/reasoning-panel.tsx +0 -0
  190. /package/{registry → src/extensions}/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -0
  191. /package/{registry → src/extensions}/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -0
  192. /package/{registry → src/extensions}/nocobase-ai/components/chat/work-context-chip.tsx +0 -0
  193. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/ai-form.tsx +0 -0
  194. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -0
  195. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-inline.tsx +0 -0
  196. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-page.tsx +0 -0
  197. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -0
  198. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -0
  199. /package/{registry → src/extensions}/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -0
  200. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-dialog.tsx +0 -0
  201. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-renderer.tsx +0 -0
  202. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-utils.tsx +0 -0
  203. /package/{registry → src/extensions}/nocobase-ai/components/tools/chart-renderer.tsx +0 -0
  204. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-preview.tsx +0 -0
  205. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -0
  206. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -0
  207. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -0
  208. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -0
  209. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime.ts +0 -0
  210. /package/{registry → src/extensions}/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -0
  211. /package/{registry → src/extensions}/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -0
  212. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -0
  213. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -0
  214. /package/{registry → src/extensions}/nocobase-ai/components/tools/workflow-renderer.tsx +0 -0
  215. /package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -0
  216. /package/{registry → src/extensions}/nocobase-ai/demo/interaction-showcase.tsx +0 -0
  217. /package/{registry → src/extensions}/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -0
  218. /package/{registry → src/extensions}/nocobase-ai/demo/page-context.tsx +0 -0
  219. /package/{registry → src/extensions}/nocobase-ai/demo/page-element-showcase.tsx +0 -0
  220. /package/{registry → src/extensions}/nocobase-ai/demo/prompt-card.tsx +0 -0
  221. /package/{registry → src/extensions}/nocobase-ai/demo/shortcut.tsx +0 -0
  222. /package/{registry → src/extensions}/nocobase-ai/index.ts +0 -0
  223. /package/{registry → src/extensions}/nocobase-ai/providers/avatars.ts +0 -0
  224. /package/{registry → src/extensions}/nocobase-ai/providers/chat-controller.ts +0 -0
  225. /package/{registry → src/extensions}/nocobase-ai/providers/chat-message-utils.ts +0 -0
  226. /package/{registry → src/extensions}/nocobase-ai/providers/chat-reducer.ts +0 -0
  227. /package/{registry → src/extensions}/nocobase-ai/providers/chat-task-utils.ts +0 -0
  228. /package/{registry → src/extensions}/nocobase-ai/providers/chat-transport.ts +0 -0
  229. /package/{registry → src/extensions}/nocobase-ai/providers/form-registry.tsx +0 -0
  230. /package/{registry → src/extensions}/nocobase-ai/providers/frontend-tool-registry.tsx +0 -0
  231. /package/{registry → src/extensions}/nocobase-ai/providers/index.ts +0 -0
  232. /package/{registry → src/extensions}/nocobase-ai/providers/model.ts +0 -0
  233. /package/{registry → src/extensions}/nocobase-ai/providers/page-context.tsx +0 -0
  234. /package/{registry → src/extensions}/nocobase-ai/providers/stream-coalescer.ts +0 -0
  235. /package/{registry → src/extensions}/nocobase-ai/providers/stream-event-utils.ts +0 -0
  236. /package/{registry → src/extensions}/nocobase-ai/providers/stream-parser.ts +0 -0
  237. /package/{registry → src/extensions}/nocobase-ai/providers/sub-agent-stream.ts +0 -0
  238. /package/{registry → src/extensions}/nocobase-ai/providers/types.ts +0 -0
  239. /package/{registry → src/extensions}/nocobase-ai/providers/ui-message-stream.ts +0 -0
  240. /package/{registry → src/extensions}/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -0
  241. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-message-actions.ts +0 -0
  242. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-runtime.ts +0 -0
  243. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-catalog.ts +0 -0
  244. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-history.ts +0 -0
  245. /package/{registry → src/extensions}/nocobase-ai/services/index.ts +0 -0
  246. /package/{registry → src/extensions}/nocobase-ai/services/nocobase-ai-service.ts +0 -0
  247. /package/{registry → src/extensions}/nocobase-ai/services/types.ts +0 -0
  248. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/lazy-route.tsx +0 -0
  249. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/prompt-generator.tsx +0 -0
  250. /package/{registry → src/extensions}/nocobase-route-surfaces/index.ts +0 -0
  251. /package/{registry → src/extensions}/nocobase-route-surfaces/route-dialog.tsx +0 -0
  252. /package/{registry → src/extensions}/nocobase-route-surfaces/route-drawer.tsx +0 -0
  253. /package/{registry → src/extensions}/nocobase-route-surfaces/route-page.tsx +0 -0
  254. /package/{registry → src/extensions}/nocobase-route-surfaces/route-surface-context.ts +0 -0
  255. /package/{registry → src/extensions}/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +0 -0
  256. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-close.ts +0 -0
  257. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-state.ts +0 -0
  258. /package/src/{pages/users → extensions/nocobase-users-example}/form-fields.tsx +0 -0
  259. /package/src/{pages/users → extensions/nocobase-users-example}/index.ts +0 -0
  260. /package/src/{pages/users → extensions/nocobase-users-example}/role-badges.tsx +0 -0
  261. /package/src/{pages/users → extensions/nocobase-users-example}/role-utils.ts +0 -0
  262. /package/src/{pages/users → extensions/nocobase-users-example}/types.ts +0 -0
@@ -1,45 +0,0 @@
1
- import type { AppExtension } from "@/app/extension";
2
- import { LoadingState } from "@/components/app-shell/loading-state";
3
- import { Languages } from "lucide-react";
4
- import { lazy, Suspense } from "react";
5
- import { Route } from "react-router";
6
- import { LanguageUserMenuItems } from "./components";
7
- import "./locales";
8
- import { NocoBaseI18nBootstrap } from "./provider";
9
-
10
- const I18nDemoPage = lazy(() =>
11
- import("./demo").then((module) => ({ default: module.I18nDemoPage }))
12
- );
13
-
14
- const nocobaseI18nExtension: AppExtension = {
15
- id: "nocobase-i18n",
16
- Provider: NocoBaseI18nBootstrap,
17
- UserMenuItems: LanguageUserMenuItems,
18
- resources: [
19
- {
20
- name: "i18n-demo",
21
- list: "/i18n",
22
- meta: {
23
- label: "Internationalization",
24
- i18nKey: "navigation.demo",
25
- i18nOptions: { ns: "nocobase-i18n" },
26
- icon: <Languages />,
27
- description: "Optional frontend internationalization for the Starter.",
28
- acl: { type: "authenticated" },
29
- },
30
- },
31
- ],
32
- routes: (
33
- <Route
34
- key="nocobase-i18n-demo"
35
- path="/i18n"
36
- element={
37
- <Suspense fallback={<LoadingState className="min-h-80" />}>
38
- <I18nDemoPage />
39
- </Suspense>
40
- }
41
- />
42
- ),
43
- };
44
-
45
- export default nocobaseI18nExtension;
@@ -1,7 +0,0 @@
1
- export * from "./components";
2
- export * from "./provider";
3
- export * from "./server-resources";
4
- export {
5
- registerLocaleResources,
6
- useEnabledLocales,
7
- } from "@/providers/i18n";
@@ -1,39 +0,0 @@
1
- const enUS = {
2
- "language.label": "Language",
3
- "language.current": "Current language",
4
- "language.switchError": "Unable to switch language.",
5
- "navigation.group": "Localization",
6
- "navigation.demo": "Internationalization",
7
- "demo.title": "Internationalization",
8
- "demo.description":
9
- "Optional NocoBase server translations and language controls built on the Starter's internationalization runtime.",
10
- "demo.switcher.title": "Language switcher",
11
- "demo.switcher.description":
12
- "The same reusable component is also integrated into the signed-in user menu.",
13
- "demo.switcher.unavailable.title": "Only one language is enabled",
14
- "demo.switcher.unavailable.description":
15
- "Enable another language in NocoBase system settings to display the switcher.",
16
- "demo.frontend.title": "Application UI translations",
17
- "demo.frontend.description":
18
- "The Starter provides the runtime while application and Registry components keep their messages in local resource files.",
19
- "demo.frontend.actions": "Common actions",
20
- "demo.server.title": "NocoBase metadata translations",
21
- "demo.server.description":
22
- "Collection and field labels are loaded from dynamic server namespaces such as lm-collections.",
23
- "demo.currentLocale": "Current locale: {{locale}}",
24
- "demo.prompt.title": "Prompt generator",
25
- "demo.prompt.description":
26
- "Describe a complete multilingual application scene and generate an implementation prompt.",
27
- "demo.prompt.settings": "Scene settings",
28
- "demo.prompt.scene": "Application scene",
29
- "demo.prompt.languages": "Languages",
30
- "demo.prompt.copy": "Copy prompt",
31
- "demo.prompt.copied": "Copied",
32
- "demo.prompt.copyError":
33
- "Clipboard access failed. Select the prompt and copy it manually.",
34
- "demo.prompt.output": "Generated prompt",
35
- "demo.prompt.outputDescription":
36
- "Updates as you change the scene and language requirements.",
37
- } as const;
38
-
39
- export default enUS;
@@ -1,8 +0,0 @@
1
- import { registerLocaleResources } from "@/providers/i18n";
2
- import enUS from "./en-US";
3
- import zhCN from "./zh-CN";
4
-
5
- registerLocaleResources("nocobase-i18n", {
6
- "en-US": enUS,
7
- "zh-CN": zhCN,
8
- });
@@ -1,35 +0,0 @@
1
- const zhCN = {
2
- "language.label": "语言",
3
- "language.current": "当前语言",
4
- "language.switchError": "无法切换语言。",
5
- "navigation.group": "本地化",
6
- "navigation.demo": "国际化",
7
- "demo.title": "国际化",
8
- "demo.description":
9
- "基于 Starter 国际化运行时的可选 NocoBase 服务端翻译与语言切换能力。",
10
- "demo.switcher.title": "语言切换器",
11
- "demo.switcher.description": "同一个可复用组件也会集成到登录用户菜单中。",
12
- "demo.switcher.unavailable.title": "当前仅启用一种语言",
13
- "demo.switcher.unavailable.description":
14
- "请在 NocoBase 系统设置中启用其他语言,语言切换器才会显示。",
15
- "demo.frontend.title": "应用 UI 文案翻译",
16
- "demo.frontend.description":
17
- "Starter 提供运行时,应用和 Registry 组件分别维护自己的本地语言资源。",
18
- "demo.frontend.actions": "常用操作",
19
- "demo.server.title": "NocoBase 元数据翻译",
20
- "demo.server.description":
21
- "数据表和字段名称从 lm-collections 等服务端动态 namespace 加载。",
22
- "demo.currentLocale": "当前语言:{{locale}}",
23
- "demo.prompt.title": "提示词生成器",
24
- "demo.prompt.description": "描述完整的多语言应用场景并生成实现提示词。",
25
- "demo.prompt.settings": "场景设置",
26
- "demo.prompt.scene": "应用场景",
27
- "demo.prompt.languages": "语言",
28
- "demo.prompt.copy": "复制提示词",
29
- "demo.prompt.copied": "已复制",
30
- "demo.prompt.copyError": "无法访问剪贴板,请手动选中并复制提示词。",
31
- "demo.prompt.output": "生成的提示词",
32
- "demo.prompt.outputDescription": "修改场景和语言要求时会实时更新。",
33
- } as const;
34
-
35
- export default zhCN;
@@ -1,44 +0,0 @@
1
- import { useEffect, useState, type PropsWithChildren } from "react";
2
-
3
- import { LoadingState } from "@/components/app-shell/loading-state";
4
- import { nocobaseClient } from "@/lib/nocobase/client";
5
- import { setLocalePersistence } from "@/providers/i18n";
6
- import { useSystemSettings } from "@/providers/system-settings";
7
- import { loadServerLocaleResources } from "./server-resources";
8
-
9
- export function NocoBaseI18nBootstrap({ children }: PropsWithChildren) {
10
- const { settings } = useSystemSettings();
11
- const [ready, setReady] = useState(false);
12
-
13
- useEffect(
14
- () =>
15
- setLocalePersistence(async (locale) => {
16
- if (!nocobaseClient.getToken()) return;
17
- await nocobaseClient.action("users", "updateLang", {
18
- method: "POST",
19
- body: { appLang: locale },
20
- });
21
- }),
22
- []
23
- );
24
-
25
- useEffect(() => {
26
- let active = true;
27
- void loadServerLocaleResources(settings)
28
- .catch((error) => {
29
- console.warn("Unable to load NocoBase locale resources", error);
30
- })
31
- .finally(() => {
32
- if (active) setReady(true);
33
- });
34
- return () => {
35
- active = false;
36
- };
37
- }, [settings]);
38
-
39
- if (!ready) {
40
- return <LoadingState className="min-h-80" />;
41
- }
42
-
43
- return children;
44
- }
@@ -1,166 +0,0 @@
1
- import { nocobaseClient } from "@/lib/nocobase/client";
2
- import {
3
- type LocaleSystemSettings,
4
- applyDocumentLocale,
5
- getCurrentLocale,
6
- i18n,
7
- registerLocale,
8
- setEnabledLocales,
9
- } from "@/providers/i18n";
10
-
11
- type ServerLanguagePayload = {
12
- lang?: string;
13
- resources?: Record<string, Record<string, string>>;
14
- };
15
-
16
- const serverResourceNamespaces = new Set(["lm-collections"]);
17
- const loadedServerResourceNamespaces = new Set<string>();
18
- const namespaceLoadPromises = new Map<string, Promise<void>>();
19
- let cachedServerResources: ServerLanguagePayload["resources"] = {};
20
- let serverResourcesPromise: Promise<void> | undefined;
21
- let bootstrapStarted = false;
22
- let bootstrapComplete = false;
23
-
24
- function mergeNamespace(namespace: string, locale?: string) {
25
- const resource = cachedServerResources?.[namespace];
26
- if (!locale || !resource) return false;
27
- i18n.addResourceBundle(locale, namespace, resource, true, false);
28
- loadedServerResourceNamespaces.add(namespace);
29
- return true;
30
- }
31
-
32
- async function requestServerResources(
33
- namespaces: string[],
34
- requestedLocale?: string
35
- ) {
36
- const payload = await nocobaseClient.action<ServerLanguagePayload>(
37
- "app",
38
- "getLang",
39
- {
40
- method: "GET",
41
- query: {
42
- ...(requestedLocale ? { locale: requestedLocale } : {}),
43
- ...(namespaces.length ? { ns: namespaces.join(",") } : {}),
44
- },
45
- includeRole: false,
46
- withAclMeta: false,
47
- }
48
- );
49
-
50
- cachedServerResources = {
51
- ...cachedServerResources,
52
- ...payload.resources,
53
- };
54
- for (const namespace of namespaces) {
55
- mergeNamespace(namespace, payload.lang);
56
- loadedServerResourceNamespaces.add(namespace);
57
- }
58
-
59
- return payload;
60
- }
61
-
62
- async function ensureServerResourceNamespaces(namespaces: string[]) {
63
- const pending: Promise<void>[] = [];
64
- const missing: string[] = [];
65
-
66
- for (const namespace of namespaces) {
67
- if (loadedServerResourceNamespaces.has(namespace)) continue;
68
- if (mergeNamespace(namespace, getCurrentLocale())) continue;
69
- const existing = namespaceLoadPromises.get(namespace);
70
- if (existing) pending.push(existing);
71
- else missing.push(namespace);
72
- }
73
-
74
- if (missing.length) {
75
- const request = requestServerResources(
76
- missing,
77
- nocobaseClient.getStoredLocale() ?? getCurrentLocale()
78
- ).then(() => undefined);
79
- for (const namespace of missing) {
80
- namespaceLoadPromises.set(namespace, request);
81
- }
82
- const cleanup = () => {
83
- for (const namespace of missing) {
84
- if (namespaceLoadPromises.get(namespace) === request) {
85
- namespaceLoadPromises.delete(namespace);
86
- }
87
- }
88
- };
89
- void request.then(cleanup, cleanup);
90
- pending.push(request);
91
- }
92
-
93
- await Promise.all(pending);
94
- }
95
-
96
- export function registerServerResourceNamespace(namespace: string) {
97
- if (!namespace) return;
98
- serverResourceNamespaces.add(namespace);
99
-
100
- if (
101
- mergeNamespace(namespace, getCurrentLocale()) ||
102
- !bootstrapStarted ||
103
- !bootstrapComplete
104
- ) {
105
- return;
106
- }
107
-
108
- void ensureServerResourceNamespaces([namespace]).catch((error) => {
109
- console.warn(
110
- `Unable to load NocoBase locale namespace ${namespace}`,
111
- error
112
- );
113
- });
114
- }
115
-
116
- export function getServerResourceNamespaces() {
117
- return [...serverResourceNamespaces];
118
- }
119
-
120
- export function loadServerLocaleResources(settings?: LocaleSystemSettings) {
121
- if (serverResourcesPromise) return serverResourcesPromise;
122
-
123
- bootstrapStarted = true;
124
- const requestedLocale = nocobaseClient.getStoredLocale();
125
- const initialNamespaces = [...serverResourceNamespaces];
126
- const configuredLocales = Array.isArray(settings?.enabledLanguages)
127
- ? settings.enabledLanguages
128
- : [];
129
- if (configuredLocales.length) setEnabledLocales(configuredLocales);
130
- const languagePromise = requestServerResources(
131
- initialNamespaces,
132
- requestedLocale
133
- );
134
-
135
- serverResourcesPromise = languagePromise
136
- .then(async (payload) => {
137
- const serverLocale = payload.lang;
138
-
139
- if (!configuredLocales.length && serverLocale) {
140
- setEnabledLocales([serverLocale]);
141
- }
142
-
143
- if (serverLocale) {
144
- registerLocale({ locale: serverLocale, label: serverLocale });
145
- nocobaseClient.setLocale(serverLocale);
146
- await i18n.changeLanguage(serverLocale);
147
- applyDocumentLocale(serverLocale);
148
- }
149
-
150
- bootstrapComplete = true;
151
- const lateNamespaces = [...serverResourceNamespaces].filter(
152
- (namespace) => !loadedServerResourceNamespaces.has(namespace)
153
- );
154
- if (lateNamespaces.length) {
155
- await ensureServerResourceNamespaces(lateNamespaces);
156
- }
157
- })
158
- .catch((error) => {
159
- bootstrapComplete = true;
160
- if (!configuredLocales.length) setEnabledLocales([getCurrentLocale()]);
161
- serverResourcesPromise = undefined;
162
- throw error;
163
- });
164
-
165
- return serverResourcesPromise;
166
- }
@@ -1,51 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createServer } from "vite";
3
-
4
- const server = await createServer({
5
- appType: "custom",
6
- logLevel: "silent",
7
- server: { middlewareMode: true },
8
- });
9
-
10
- try {
11
- await server.ssrLoadModule("/src/locales/index.ts");
12
- const {
13
- applySystemLocale,
14
- getCurrentLocale,
15
- i18n,
16
- registerLocaleResources,
17
- translate,
18
- } = await server.ssrLoadModule("/src/providers/i18n/runtime.ts");
19
- const { resolveTranslatableText } =
20
- await server.ssrLoadModule("/src/lib/i18n.ts");
21
-
22
- await i18n.changeLanguage("en-US");
23
- assert.equal(translate("resources.users", { ns: "app" }, "Users"), "Users");
24
-
25
- await i18n.changeLanguage("zh-CN");
26
- assert.equal(translate("resources.users", { ns: "app" }, "Users"), "用户");
27
- assert.equal(resolveTranslatableText('{{t("Admin")}}'), "管理员");
28
- assert.equal(
29
- resolveTranslatableText("Full permissions", { ns: "starter" }),
30
- "全部权限"
31
- );
32
-
33
- registerLocaleResources("example-feature", {
34
- "en-US": { title: "Example" },
35
- "zh-CN": { title: "示例" },
36
- });
37
- assert.equal(
38
- translate("title", { ns: "example-feature" }, "Example"),
39
- "示例"
40
- );
41
-
42
- await applySystemLocale({
43
- appLang: "en-US",
44
- enabledLanguages: ["en-US", "zh-CN"],
45
- });
46
- assert.equal(getCurrentLocale(), "en-US");
47
-
48
- console.log("NocoBase i18n regression tests passed");
49
- } finally {
50
- await server.close();
51
- }