@qlover/create-app 0.10.0 → 0.10.2

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 (125) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/config/IOCIdentifier.ts +2 -2
  5. package/dist/templates/next-app/config/Identifier/common/common.ts +14 -0
  6. package/dist/templates/next-app/config/Identifier/pages/index.ts +1 -0
  7. package/dist/templates/next-app/config/Identifier/pages/page.about.ts +20 -0
  8. package/dist/templates/next-app/config/common.ts +1 -1
  9. package/dist/templates/next-app/config/cookies.ts +23 -0
  10. package/dist/templates/next-app/config/i18n/AboutI18n.ts +14 -0
  11. package/dist/templates/next-app/config/i18n/i18nConfig.ts +3 -1
  12. package/dist/templates/next-app/config/i18n/index.ts +1 -0
  13. package/dist/templates/next-app/config/i18n/loginI18n.ts +8 -0
  14. package/dist/templates/next-app/config/theme.ts +4 -0
  15. package/dist/templates/next-app/eslint.config.mjs +4 -1
  16. package/dist/templates/next-app/migrations/schema/UserSchema.ts +17 -3
  17. package/dist/templates/next-app/next.config.ts +1 -0
  18. package/dist/templates/next-app/package.json +15 -7
  19. package/dist/templates/next-app/public/locales/en.json +5 -0
  20. package/dist/templates/next-app/public/locales/zh.json +5 -0
  21. package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +37 -0
  22. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +30 -6
  23. package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +1 -1
  24. package/dist/templates/next-app/src/app/[locale]/layout.tsx +47 -10
  25. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
  26. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +22 -10
  27. package/dist/templates/next-app/src/app/[locale]/page.tsx +23 -8
  28. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +21 -9
  29. package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +7 -28
  30. package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +7 -34
  31. package/dist/templates/next-app/src/app/api/admin/locales/route.ts +12 -34
  32. package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +7 -26
  33. package/dist/templates/next-app/src/app/api/admin/users/route.ts +14 -33
  34. package/dist/templates/next-app/src/app/api/locales/json/route.ts +13 -25
  35. package/dist/templates/next-app/src/app/api/user/login/route.ts +6 -46
  36. package/dist/templates/next-app/src/app/api/user/logout/route.ts +5 -24
  37. package/dist/templates/next-app/src/app/api/user/register/route.ts +6 -45
  38. package/dist/templates/next-app/src/app/manifest.ts +16 -0
  39. package/dist/templates/next-app/src/app/robots.txt +2 -0
  40. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +1 -2
  41. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +12 -2
  42. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +4 -5
  43. package/dist/templates/next-app/src/base/cases/RouterService.ts +5 -5
  44. package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +44 -29
  45. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +1 -2
  46. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +22 -0
  47. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +22 -10
  48. package/dist/templates/next-app/src/base/port/IOCInterface.ts +9 -0
  49. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +17 -9
  50. package/dist/templates/next-app/src/base/services/ResourceService.ts +3 -4
  51. package/dist/templates/next-app/src/base/services/UserService.ts +37 -13
  52. package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +8 -7
  53. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +15 -26
  54. package/dist/templates/next-app/src/base/types/{PageProps.ts → AppPageRouter.ts} +4 -1
  55. package/dist/templates/next-app/src/base/types/PagesRouter.ts +9 -0
  56. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +19 -5
  57. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +2 -2
  58. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -1
  59. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +33 -11
  60. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +8 -5
  61. package/dist/templates/next-app/src/core/globals.ts +2 -1
  62. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +29 -10
  63. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +6 -7
  64. package/dist/templates/next-app/src/i18n/loadMessages.ts +103 -0
  65. package/dist/templates/next-app/src/i18n/request.ts +3 -22
  66. package/dist/templates/next-app/src/pages/[locale]/about.tsx +61 -0
  67. package/dist/templates/next-app/src/pages/_app.tsx +50 -0
  68. package/dist/templates/next-app/src/pages/_document.tsx +13 -0
  69. package/dist/templates/next-app/src/{middleware.ts → proxy.ts} +2 -1
  70. package/dist/templates/next-app/src/server/AppPageRouteParams.ts +94 -0
  71. package/dist/templates/next-app/src/server/NextApiServer.ts +53 -0
  72. package/dist/templates/next-app/src/server/PagesRouteParams.ts +136 -0
  73. package/dist/templates/next-app/src/server/{sqlBridges/SupabaseBridge.ts → SupabaseBridge.ts} +2 -0
  74. package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -3
  75. package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +74 -0
  76. package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +39 -0
  77. package/dist/templates/next-app/src/server/controllers/LocalesController.ts +33 -0
  78. package/dist/templates/next-app/src/server/controllers/UserController.ts +77 -0
  79. package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +8 -0
  80. package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +21 -0
  81. package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +11 -0
  82. package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +10 -0
  83. package/dist/templates/next-app/src/server/port/{ParamsHandlerInterface.ts → RouteParamsnHandlerInterface.ts} +9 -2
  84. package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -2
  85. package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +8 -0
  86. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +1 -1
  87. package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +2 -2
  88. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +8 -2
  89. package/dist/templates/next-app/src/{base → server}/services/AdminLocalesService.ts +2 -2
  90. package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +25 -10
  91. package/dist/templates/next-app/src/server/services/ApiUserService.ts +5 -2
  92. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +4 -2
  93. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +1 -1
  94. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +10 -10
  95. package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -44
  96. package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -44
  97. package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -44
  98. package/dist/templates/next-app/src/styles/css/index.css +1 -1
  99. package/dist/templates/next-app/src/styles/css/scrollbar.css +34 -0
  100. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +34 -11
  101. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +69 -39
  102. package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +64 -0
  103. package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +42 -0
  104. package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +34 -0
  105. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +1 -2
  106. package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +17 -0
  107. package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +112 -0
  108. package/dist/templates/next-app/src/uikit/{components → components-app}/LanguageSwitcher.tsx +15 -19
  109. package/dist/templates/next-app/src/uikit/{components → components-app}/ThemeSwitcher.tsx +53 -52
  110. package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +98 -0
  111. package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +93 -0
  112. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +16 -4
  113. package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +32 -0
  114. package/dist/templates/next-app/tsconfig.json +3 -2
  115. package/dist/templates/react-app/tsconfig.app.json +1 -3
  116. package/dist/templates/react-app/tsconfig.node.json +0 -4
  117. package/dist/templates/react-app/tsconfig.test.json +1 -3
  118. package/package.json +1 -1
  119. package/dist/templates/next-app/src/server/PageParams.ts +0 -66
  120. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +0 -80
  121. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +0 -65
  122. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +0 -58
  123. package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +0 -21
  124. /package/dist/templates/next-app/{src/app/[locale] → public}/favicon.ico +0 -0
  125. /package/dist/templates/next-app/src/uikit/{components → components-app}/LogoutButton.tsx +0 -0
@@ -1,4 +1,7 @@
1
- import type { PageWithParams } from '@/server/PageParams';
1
+ /**
2
+ * 该文件主要用于 /src/app 目录下页面路由的类型定义
3
+ */
4
+ import type { PageWithParams } from '@/server/AppPageRouteParams';
2
5
 
3
6
  export interface PageParamsProps extends PageWithParams {
4
7
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 该文件主要用于 /src/pages 目录下页面路由的类型定义
3
+ */
4
+ import type { AppProps } from 'next/app';
5
+ import type { NextRouter } from 'next/router';
6
+
7
+ export type PagesRouterProps = AppProps & {
8
+ router: NextRouter;
9
+ };
@@ -1,13 +1,15 @@
1
1
  import 'reflect-metadata';
2
2
  import { Bootstrap } from '@qlover/corekit-bridge';
3
3
  import { isObject } from 'lodash';
4
+ import type { IocRegisterOptions } from '@/base/port/IOCInterface';
4
5
  import { browserGlobalsName } from '@config/common';
5
6
  import type { IOCIdentifierMap } from '@config/IOCIdentifier';
6
7
  import { BootstrapsRegistry } from './BootstrapsRegistry';
7
8
  import * as globals from '../globals';
8
9
  import type {
9
10
  IOCContainerInterface,
10
- IOCFunctionInterface
11
+ IOCFunctionInterface,
12
+ IOCRegisterInterface
11
13
  } from '@qlover/corekit-bridge';
12
14
 
13
15
  export type BootstrapAppArgs = {
@@ -23,23 +25,31 @@ export type BootstrapAppArgs = {
23
25
  * IOC容器
24
26
  */
25
27
  IOC: IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface>;
28
+
29
+ register?: IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>;
26
30
  };
27
31
 
28
32
  export class BootstrapClient {
33
+ static lastTime = 0;
29
34
  static async main(args: BootstrapAppArgs): Promise<BootstrapAppArgs> {
30
- const { root, IOC } = args;
35
+ const { logger, appConfig } = globals;
36
+
37
+ if (BootstrapClient.lastTime) {
38
+ return args;
39
+ }
40
+
41
+ const { root, IOC, register } = args;
31
42
 
32
43
  if (!isObject(root)) {
33
44
  throw new Error('root is not an object');
34
45
  }
35
46
 
36
- const { logger, appConfig } = globals;
37
-
38
47
  const bootstrap = new Bootstrap({
39
48
  root,
40
49
  logger,
41
50
  ioc: {
42
- manager: IOC
51
+ manager: IOC,
52
+ register: register
43
53
  },
44
54
  globalOptions: {
45
55
  sources: globals,
@@ -53,6 +63,10 @@ export class BootstrapClient {
53
63
  const bootstrapsRegistry = new BootstrapsRegistry(args);
54
64
 
55
65
  await bootstrap.use(bootstrapsRegistry.register()).start();
66
+
67
+ BootstrapClient.lastTime = Date.now();
68
+
69
+ logger.info('BootstrapClient starup success,', BootstrapClient.lastTime);
56
70
  } catch (error) {
57
71
  logger.error(`${appConfig.appName} starup error:`, error);
58
72
  }
@@ -4,8 +4,7 @@ import {
4
4
  type BootstrapContextValue,
5
5
  type BootstrapExecutorPlugin,
6
6
  type IOCContainerInterface,
7
- type IOCFunctionInterface,
8
- type LoggerInterface
7
+ type IOCFunctionInterface
9
8
  } from '@qlover/corekit-bridge';
10
9
  import {
11
10
  AsyncExecutor,
@@ -16,6 +15,7 @@ import {
16
15
  import type { ServerInterface } from '@/server/port/ServerInterface';
17
16
  import { I, type IOCIdentifierMapServer } from '@config/IOCIdentifier';
18
17
  import { ServerIOC } from '../serverIoc/ServerIOC';
18
+ import type { LoggerInterface } from '@qlover/logger';
19
19
 
20
20
  export interface BootstrapServerContextValue extends BootstrapContextValue {
21
21
  IOC: IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>;
@@ -41,7 +41,6 @@ export class BootstrapsRegistry {
41
41
  }
42
42
 
43
43
  bootstrapList.push(IocIdentifierTest);
44
- // TODO: 需要使用到
45
44
 
46
45
  return bootstrapList;
47
46
  }
@@ -1,21 +1,34 @@
1
1
  import {
2
2
  createIOCFunction,
3
3
  type IOCContainerInterface,
4
- type IOCFunctionInterface
4
+ type IOCFunctionInterface,
5
+ type IOCRegisterInterface
5
6
  } from '@qlover/corekit-bridge';
6
7
  import { InversifyContainer } from '@/base/cases/InversifyContainer';
7
- import type { IOCInterface } from '@/base/port/IOCInterface';
8
+ import type {
9
+ IOCInterface,
10
+ IocRegisterOptions
11
+ } from '@/base/port/IOCInterface';
8
12
  import type { IOCIdentifierMap } from '@config/IOCIdentifier';
9
13
  import { ClientIOCRegister } from './ClientIOCRegister';
10
- import { appConfig } from '../globals';
14
+ import { logger } from '../globals';
11
15
 
12
- export class ClientIOC
13
- implements IOCInterface<IOCIdentifierMap, IOCContainerInterface>
14
- {
16
+ export class ClientIOC implements IOCInterface<
17
+ IOCIdentifierMap,
18
+ IOCContainerInterface
19
+ > {
15
20
  protected ioc: IOCFunctionInterface<
16
21
  IOCIdentifierMap,
17
22
  IOCContainerInterface
18
23
  > | null = null;
24
+ protected registers = 0;
25
+
26
+ constructor(
27
+ protected readonly iocRegister?: IOCRegisterInterface<
28
+ IOCContainerInterface,
29
+ IocRegisterOptions
30
+ >
31
+ ) {}
19
32
 
20
33
  create(): IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface> {
21
34
  if (this.ioc) {
@@ -24,13 +37,22 @@ export class ClientIOC
24
37
 
25
38
  this.ioc = createIOCFunction<IOCIdentifierMap>(new InversifyContainer());
26
39
 
27
- const register = new ClientIOCRegister({
28
- appConfig: appConfig
29
- });
40
+ return this.ioc;
41
+ }
42
+
43
+ register(options: IocRegisterOptions): void {
44
+ if (this.registers > 0) {
45
+ return;
46
+ }
47
+
48
+ if (!this.ioc) {
49
+ return;
50
+ }
30
51
 
31
- register.register(this.ioc.implemention!, this.ioc);
52
+ logger.info('ClientIOC register');
53
+ new ClientIOCRegister(options).register(this.ioc.implemention!, this.ioc);
32
54
 
33
- return this.ioc;
55
+ this.registers++;
34
56
  }
35
57
  }
36
58
 
@@ -1,7 +1,9 @@
1
+ import { CookieStorage } from '@qlover/corekit-bridge';
1
2
  import { RouterService } from '@/base/cases/RouterService';
2
3
  import type { IocRegisterOptions } from '@/base/port/IOCInterface';
3
4
  import { I18nService } from '@/base/services/I18nService';
4
5
  import { UserService } from '@/base/services/UserService';
6
+ import { cookiesConfig } from '@config/cookies';
5
7
  import { IOCIdentifier as I } from '@config/IOCIdentifier';
6
8
  import { dialogHandler, logger, JSON } from '../globals';
7
9
  import type {
@@ -10,9 +12,10 @@ import type {
10
12
  IOCRegisterInterface
11
13
  } from '@qlover/corekit-bridge';
12
14
 
13
- export class ClientIOCRegister
14
- implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
15
- {
15
+ export class ClientIOCRegister implements IOCRegisterInterface<
16
+ IOCContainerInterface,
17
+ IocRegisterOptions
18
+ > {
16
19
  constructor(protected options: IocRegisterOptions) {}
17
20
 
18
21
  /**
@@ -33,7 +36,7 @@ export class ClientIOCRegister
33
36
  // ioc.bind(I.AntdStaticApiInterface, dialogHandler);
34
37
  // ioc.bind(I.LocalStorage, globals.localStorage);
35
38
  // ioc.bind(I.LocalStorageEncrypt, localStorageEncrypt);
36
- // ioc.bind(I.CookieStorage, globals.cookieStorage);
39
+ ioc.bind(I.CookieStorage, new CookieStorage(cookiesConfig));
37
40
  }
38
41
 
39
42
  /**
@@ -86,7 +89,7 @@ export class ClientIOCRegister
86
89
  /**
87
90
  * @override
88
91
  */
89
- register(
92
+ public register(
90
93
  ioc: IOCContainerInterface,
91
94
  _manager: IOCManagerInterface<IOCContainerInterface>
92
95
  ): void {
@@ -1,8 +1,9 @@
1
1
  import 'reflect-metadata';
2
2
 
3
3
  // ! global variables, don't import any dependencies and don't have side effects
4
- import { ColorFormatter, ConsoleHandler, Logger } from '@qlover/corekit-bridge';
4
+ import { ColorFormatter } from '@qlover/corekit-bridge';
5
5
  import { JSONSerializer } from '@qlover/fe-corekit';
6
+ import { Logger, ConsoleHandler } from '@qlover/logger';
6
7
  import { AppConfig } from '@/base/cases/AppConfig';
7
8
  import { DialogHandler } from '@/base/cases/DialogHandler';
8
9
  import { loggerStyles } from '@config/common';
@@ -5,15 +5,21 @@ import {
5
5
  } from '@qlover/corekit-bridge';
6
6
  import { AppConfig } from '@/base/cases/AppConfig';
7
7
  import { InversifyContainer } from '@/base/cases/InversifyContainer';
8
- import type { IOCInterface } from '@/base/port/IOCInterface';
8
+ import type {
9
+ IOCInterface,
10
+ IocRegisterOptions
11
+ } from '@/base/port/IOCInterface';
9
12
  import type { IOCIdentifierMapServer } from '@config/IOCIdentifier';
10
13
  import { ServerIOCRegister } from './ServerIOCRegister';
11
14
 
12
- export class ServerIOC
13
- implements IOCInterface<IOCIdentifierMapServer, IOCContainerInterface>
14
- {
15
+ export class ServerIOC implements IOCInterface<
16
+ IOCIdentifierMapServer,
17
+ IOCContainerInterface
18
+ > {
15
19
  static instance: ServerIOC | null = null;
16
20
 
21
+ protected registers = 0;
22
+
17
23
  protected ioc: IOCFunctionInterface<
18
24
  IOCIdentifierMapServer,
19
25
  IOCContainerInterface
@@ -37,16 +43,29 @@ export class ServerIOC
37
43
  return this.ioc;
38
44
  }
39
45
 
40
- this.ioc = createIOCFunction<IOCIdentifierMapServer>(
41
- new InversifyContainer()
42
- );
46
+ this.ioc = createIOCFunction(new InversifyContainer());
43
47
 
44
- const register = new ServerIOCRegister({
48
+ // 注册默认依赖
49
+ this.register({
45
50
  appConfig: new AppConfig()
46
51
  });
47
52
 
48
- register.register(this.ioc.implemention!, this.ioc);
49
-
50
53
  return this.ioc;
51
54
  }
55
+
56
+ register(options: IocRegisterOptions): void {
57
+ if (this.registers > 0) {
58
+ console.debug('ServerIOC: ioc already registered');
59
+ return;
60
+ }
61
+
62
+ if (!this.ioc) {
63
+ console.debug('ServerIOC: ioc not initialized');
64
+ return;
65
+ }
66
+
67
+ new ServerIOCRegister(options).register(this.ioc.implemention!, this.ioc);
68
+
69
+ this.registers++;
70
+ }
52
71
  }
@@ -1,18 +1,17 @@
1
1
  import {
2
- ConsoleHandler,
3
- Logger,
4
- TimestampFormatter,
5
2
  type IOCContainerInterface,
6
3
  type IOCManagerInterface,
7
4
  type IOCRegisterInterface
8
5
  } from '@qlover/corekit-bridge';
6
+ import { Logger, ConsoleHandler, TimestampFormatter } from '@qlover/logger';
9
7
  import type { IocRegisterOptions } from '@/base/port/IOCInterface';
10
- import { SupabaseBridge } from '@/server/sqlBridges/SupabaseBridge';
8
+ import { SupabaseBridge } from '@/server/SupabaseBridge';
11
9
  import { IOCIdentifier as I } from '@config/IOCIdentifier';
12
10
 
13
- export class ServerIOCRegister
14
- implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
15
- {
11
+ export class ServerIOCRegister implements IOCRegisterInterface<
12
+ IOCContainerInterface,
13
+ IocRegisterOptions
14
+ > {
16
15
  constructor(protected options: IocRegisterOptions) {}
17
16
 
18
17
  /**
@@ -0,0 +1,103 @@
1
+ import { useApiLocales } from '@config/common';
2
+
3
+ /**
4
+ * 加载 i18n 消息的公共方法
5
+ * 支持从 API 加载或从 JSON 文件加载
6
+ *
7
+ * @param locale - 要加载的语言代码
8
+ * @param namespace - 可选的命名空间(单个字符串或字符串数组),如果提供则只返回该命名空间下的消息,保留命名空间前缀
9
+ * @returns Promise<Record<string, string>> 返回翻译消息对象
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // 加载所有消息
14
+ * const allMessages = await loadMessages('en');
15
+ *
16
+ * // 加载单个命名空间
17
+ * const commonMessages = await loadMessages('en', 'common');
18
+ *
19
+ * // 加载多个命名空间
20
+ * const messages = await loadMessages('en', ['common', 'page_home']);
21
+ * ```
22
+ */
23
+ export async function loadMessages(
24
+ locale: string,
25
+ namespace?: string | string[]
26
+ ): Promise<Record<string, string>> {
27
+ let allMessages: Record<string, string>;
28
+
29
+ // 如果配置了使用 API 加载本地化数据
30
+ if (useApiLocales) {
31
+ try {
32
+ const app_host = process.env.APP_HOST;
33
+ const localeUrl = `${app_host}/api/locales/json?locale=${locale}`;
34
+ const response = await fetch(localeUrl);
35
+
36
+ if (!response.ok) {
37
+ throw new Error(
38
+ `Failed to fetch locale from API: ${response.statusText}`
39
+ );
40
+ }
41
+
42
+ allMessages = await response.json();
43
+ } catch (error) {
44
+ console.warn(`Failed to load locale from API for ${locale}`, error);
45
+ // 如果 API 加载失败,继续尝试从文件加载
46
+ allMessages = (await import(`../../public/locales/${locale}.json`))
47
+ .default;
48
+ }
49
+ } else {
50
+ allMessages = (await import(`../../public/locales/${locale}.json`)).default;
51
+ }
52
+
53
+ // 如果指定了命名空间,进行过滤
54
+ return filterMessagesByNamespace(allMessages, namespace);
55
+ }
56
+
57
+ /**
58
+ * 根据命名空间过滤消息,保留命名空间前缀
59
+ *
60
+ * @param messages - 所有消息对象
61
+ * @param namespace - 可选的命名空间(单个字符串或字符串数组),如果提供则只返回该命名空间下的消息,保留命名空间前缀
62
+ * @returns 过滤后的消息对象
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const allMessages = { "common:save": "Save", "common:cancel": "Cancel", "page_home:title": "Home" };
67
+ *
68
+ * // 单个命名空间
69
+ * const commonMessages = filterMessagesByNamespace(allMessages, "common");
70
+ * // 返回: { "common:save": "Save", "common:cancel": "Cancel" }
71
+ *
72
+ * // 多个命名空间
73
+ * const messages = filterMessagesByNamespace(allMessages, ["common", "page_home"]);
74
+ * // 返回: { "common:save": "Save", "common:cancel": "Cancel", "page_home:title": "Home" }
75
+ * ```
76
+ */
77
+ export function filterMessagesByNamespace(
78
+ messages: Record<string, string>,
79
+ namespace?: string | string[]
80
+ ): Record<string, string> {
81
+ // 如果没有指定命名空间,返回所有消息
82
+ if (!namespace) {
83
+ return messages;
84
+ }
85
+
86
+ // 将单个字符串转换为数组,统一处理
87
+ const namespaces = Array.isArray(namespace) ? namespace : [namespace];
88
+ const filteredMessages: Record<string, string> = {};
89
+
90
+ // 遍历所有命名空间
91
+ for (const ns of namespaces) {
92
+ const namespacePrefix = `${ns}:`;
93
+
94
+ for (const [key, value] of Object.entries(messages)) {
95
+ if (key.startsWith(namespacePrefix)) {
96
+ // 保留完整的键名(包括命名空间前缀)
97
+ filteredMessages[key] = value;
98
+ }
99
+ }
100
+ }
101
+
102
+ return filteredMessages;
103
+ }
@@ -1,7 +1,7 @@
1
1
  // src/i18n/request.ts
2
2
 
3
3
  import { getRequestConfig } from 'next-intl/server';
4
- import { useApiLocales } from '@config/common';
4
+ import { loadMessages } from './loadMessages';
5
5
  import { routing, type Locale } from './routing';
6
6
 
7
7
  // Export a function to configure next-intl on each request (server-side)
@@ -14,30 +14,11 @@ export default getRequestConfig(async ({ requestLocale }) => {
14
14
  locale = routing.defaultLocale;
15
15
  }
16
16
 
17
- // use api locales
18
- if (useApiLocales) {
19
- const app_host = process.env.APP_HOST;
20
- const localeUrl = `${app_host}/api/locales/json?locale=${locale}`;
21
- const response = await fetch(localeUrl);
22
- const data = await response.json();
23
- return {
24
- locale,
25
- messages: data,
26
- // 将 MISSING_MESSAGE 错误转换为警告
27
- onError: (error) => {
28
- if (error.message.includes('MISSING_MESSAGE')) {
29
- console.warn(`[i18n] Missing translation: ${error.message}`);
30
- return error.message; // 返回 key 作为 fallback 文本
31
- }
32
- throw error; // 其他错误仍然抛出
33
- }
34
- };
35
- }
17
+ const messages = await loadMessages(locale);
36
18
 
37
- // Dynamically import the translation messages for the selected locale
38
19
  return {
39
20
  locale,
40
- messages: (await import(`../../public/locales/${locale}.json`)).default,
21
+ messages,
41
22
  // 将 MISSING_MESSAGE 错误转换为警告
42
23
  onError: (error) => {
43
24
  if (error.message.includes('MISSING_MESSAGE')) {
@@ -0,0 +1,61 @@
1
+ import { useMemo } from 'react';
2
+ import { PagesRouteParams } from '@/server/PagesRouteParams';
3
+ import type { PagesRouteParamsType } from '@/server/PagesRouteParams';
4
+ import { ClientSeo } from '@/uikit/components/ClientSeo';
5
+ import { PagesRoutePage } from '@/uikit/components-pages/PagesRoutePage';
6
+ import { useI18nInterface } from '@/uikit/hook/useI18nInterface';
7
+ import { aboutI18n, i18nConfig } from '@config/i18n';
8
+ import { COMMON_ADMIN_TITLE } from '@config/Identifier';
9
+ import type { GetStaticPropsContext } from 'next';
10
+
11
+ interface AboutProps {
12
+ messages: Record<string, string>;
13
+ }
14
+
15
+ const namespace = 'page_about';
16
+
17
+ export default function About({}: AboutProps) {
18
+ const i18nInterface = useMemo(() => {
19
+ return {
20
+ ...aboutI18n,
21
+ adminTitle: COMMON_ADMIN_TITLE
22
+ };
23
+ }, []);
24
+ const seoMetadata = useI18nInterface(i18nInterface);
25
+
26
+ return (
27
+ <PagesRoutePage
28
+ data-testid="AboutPage"
29
+ tt={{ title: seoMetadata.title, adminTitle: seoMetadata.adminTitle }}
30
+ showAdminButton={false}
31
+ >
32
+ <ClientSeo i18nInterface={seoMetadata} />
33
+ <div data-testid="About" className="bg-primary h-screen">
34
+ {seoMetadata.title}
35
+ </div>
36
+ </PagesRoutePage>
37
+ );
38
+ }
39
+
40
+ export async function getStaticProps({
41
+ params
42
+ }: GetStaticPropsContext<PagesRouteParamsType>) {
43
+ const pageParams = new PagesRouteParams(params);
44
+
45
+ const messages = await pageParams.getI18nMessages(namespace);
46
+
47
+ return {
48
+ props: {
49
+ messages
50
+ }
51
+ };
52
+ }
53
+
54
+ export async function getStaticPaths() {
55
+ return {
56
+ paths: i18nConfig.supportedLngs.map((locale) => ({
57
+ params: { locale }
58
+ })),
59
+ fallback: false
60
+ };
61
+ }
@@ -0,0 +1,50 @@
1
+ import dynamic from 'next/dynamic';
2
+ import { withRouter } from 'next/router';
3
+ import { NextIntlClientProvider } from 'next-intl';
4
+ import '@/styles/css/index.css';
5
+ import type { PagesRouterProps } from '@/base/types/PagesRouter';
6
+ import { BootstrapsProvider } from '@/uikit/components/BootstrapsProvider';
7
+ import { IOCProvider } from '@/uikit/components/IOCProvider';
8
+ import { i18nConfig } from '@config/i18n';
9
+ import { themeConfig } from '@config/theme';
10
+
11
+ /**
12
+ * 动态导入 ClientRootProvider,禁用 SSR,确保只在客户端渲染
13
+ *
14
+ * 为什么需要禁用 SSR?
15
+ * 1. ClientRootProvider 内部使用了 AntdRegistry(来自 @ant-design/nextjs-registry),
16
+ * 该组件主要针对 App Router 设计,在 Pages Router 的 SSR 环境中可能存在兼容性问题
17
+ * 2. ClientRootProvider 使用了 useIOC() hook,该 hook 在服务端渲染时可能无法正常工作
18
+ * 3. ThemeProvider 和 AntdThemeProvider 可能依赖浏览器 API(如 localStorage、window 等),
19
+ * 在服务端渲染时会导致错误或页面一直处于加载状态
20
+ *
21
+ * 通过在 Pages Router 中使用 dynamic import 并设置 ssr: false,
22
+ * 可以确保 ClientRootProvider 只在客户端渲染,避免 SSR 相关的问题
23
+ */
24
+ const ClientRootProvider = dynamic(
25
+ () =>
26
+ import('@/uikit/components/ClientRootProvider').then(
27
+ (mod) => mod.ClientRootProvider
28
+ ),
29
+ {
30
+ ssr: false
31
+ }
32
+ );
33
+
34
+ function App({ Component, pageProps, router }: PagesRouterProps) {
35
+ const locale = (router.query.locale as string) || i18nConfig.fallbackLng;
36
+
37
+ return (
38
+ <IOCProvider data-testid="IOCProvider">
39
+ <NextIntlClientProvider locale={locale} messages={pageProps.messages}>
40
+ <BootstrapsProvider>
41
+ <ClientRootProvider themeConfig={themeConfig}>
42
+ <Component {...pageProps} />
43
+ </ClientRootProvider>
44
+ </BootstrapsProvider>
45
+ </NextIntlClientProvider>
46
+ </IOCProvider>
47
+ );
48
+ }
49
+
50
+ export default withRouter(App);
@@ -0,0 +1,13 @@
1
+ import { Html, Head, Main, NextScript } from 'next/document';
2
+
3
+ export default function Document() {
4
+ return (
5
+ <Html data-testid="PagesRootLayout">
6
+ <Head />
7
+ <body>
8
+ <Main />
9
+ <NextScript />
10
+ </body>
11
+ </Html>
12
+ );
13
+ }
@@ -20,6 +20,7 @@ export const config = {
20
20
  // - Next.js internals (_next/*)
21
21
  // - Static files (*.svg, *.png, *.jpg, *.jpeg, *.gif, *.ico)
22
22
  // - Other static assets and special files
23
- '/((?!api|_next|.*\\.(?:svg|png|jpg|jpeg|gif|ico)|favicon.ico|sitemap.xml|sitemap-0.xml).*)'
23
+ // - Manifest file (manifest.webmanifest)
24
+ '/((?!api|_next|.*\\.(?:svg|png|jpg|jpeg|gif|ico)|favicon.ico|sitemap.xml|sitemap-0.xml|manifest.webmanifest).*)'
24
25
  ]
25
26
  };