nsgm-cli 2.1.14 → 2.1.15

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 (69) hide show
  1. package/README.md +49 -26
  2. package/client/components/ClientProviders.tsx +29 -0
  3. package/client/components/LanguageSwitcher.tsx +59 -0
  4. package/client/components/SSRSafeAntdProvider.tsx +24 -0
  5. package/client/components/SuppressHydrationWarnings.tsx +55 -0
  6. package/client/layout/index.tsx +82 -126
  7. package/client/styled/common.ts +0 -1
  8. package/client/styled/layout/index.ts +218 -104
  9. package/client/styled/template/manage.ts +88 -1
  10. package/client/utils/i18n.ts +68 -0
  11. package/client/utils/menu.tsx +42 -36
  12. package/client/utils/navigation.ts +58 -0
  13. package/client/utils/suppressWarnings.ts +32 -0
  14. package/eslint.config.js +17 -20
  15. package/generation/client/redux/reducers.ts +1 -1
  16. package/generation/client/utils/menu.tsx +36 -30
  17. package/generation/env +3 -0
  18. package/generation/next.config.js +7 -3
  19. package/generation/package.json +5 -2
  20. package/generation/tsconfig.json +6 -19
  21. package/lib/cli/commands/create.js +1 -1
  22. package/lib/cli/commands/delete.js +1 -1
  23. package/lib/cli/commands/init.js +6 -6
  24. package/lib/cli/utils/prompt.d.ts +1 -1
  25. package/lib/cli/utils/prompt.js +76 -117
  26. package/lib/constants.d.ts +8 -1
  27. package/lib/constants.js +17 -2
  28. package/lib/generate.js +1 -0
  29. package/lib/generate_create.js +14 -11
  30. package/lib/generate_delete.js +86 -9
  31. package/lib/generate_init.d.ts +6 -0
  32. package/lib/generate_init.js +125 -5
  33. package/lib/generators/file-generator.d.ts +48 -0
  34. package/lib/generators/file-generator.js +455 -0
  35. package/lib/generators/i18n-generator.d.ts +51 -0
  36. package/lib/generators/i18n-generator.js +320 -0
  37. package/lib/generators/page-generator.d.ts +6 -2
  38. package/lib/generators/page-generator.js +182 -156
  39. package/lib/generators/resolver-generator.d.ts +6 -4
  40. package/lib/generators/resolver-generator.js +114 -75
  41. package/lib/generators/service-generator.d.ts +4 -0
  42. package/lib/generators/service-generator.js +120 -6
  43. package/lib/tsconfig.build.tsbuildinfo +1 -1
  44. package/next-i18next.config.js +18 -0
  45. package/next.config.js +55 -16
  46. package/package.json +7 -2
  47. package/pages/_app.tsx +84 -35
  48. package/pages/_document.tsx +39 -2
  49. package/pages/_error.tsx +66 -0
  50. package/pages/index.tsx +46 -29
  51. package/pages/login.tsx +58 -33
  52. package/pages/template/manage.tsx +95 -109
  53. package/public/locales/en-US/common.json +48 -0
  54. package/public/locales/en-US/home.json +57 -0
  55. package/public/locales/en-US/layout.json +22 -0
  56. package/public/locales/en-US/login.json +13 -0
  57. package/public/locales/en-US/template.json +42 -0
  58. package/public/locales/ja-JP/common.json +48 -0
  59. package/public/locales/ja-JP/home.json +57 -0
  60. package/public/locales/ja-JP/layout.json +22 -0
  61. package/public/locales/ja-JP/login.json +13 -0
  62. package/public/locales/ja-JP/template.json +42 -0
  63. package/public/locales/zh-CN/common.json +48 -0
  64. package/public/locales/zh-CN/home.json +57 -0
  65. package/public/locales/zh-CN/layout.json +22 -0
  66. package/public/locales/zh-CN/login.json +13 -0
  67. package/public/locales/zh-CN/template.json +42 -0
  68. package/server/utils/validation.js +163 -0
  69. package/types/i18next.d.ts +10 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @type {import('next-i18next').UserConfig}
3
+ */
4
+ module.exports = {
5
+ i18n: {
6
+ defaultLocale: 'zh-CN',
7
+ locales: ['zh-CN', 'en-US', 'ja-JP'],
8
+ localeDetection: false, // 禁用自动语言检测
9
+ },
10
+ localePath: './public/locales',
11
+ /** To avoid issues when deploying to some platforms, we can configure the cache */
12
+ saveMissing: false,
13
+ strictMode: true,
14
+ serializeConfig: false,
15
+ react: {
16
+ useSuspense: false,
17
+ },
18
+ }
package/next.config.js CHANGED
@@ -4,6 +4,7 @@
4
4
  const { PHASE_DEVELOPMENT_SERVER, PHASE_EXPORT } = require('next/constants')
5
5
  const fs = require('fs')
6
6
  const path = require('path')
7
+ const { i18n } = require('./next-i18next.config')
7
8
 
8
9
  module.exports = (phase, defaultConfig, options) => {
9
10
  let projectConfig = null
@@ -38,10 +39,11 @@ module.exports = (phase, defaultConfig, options) => {
38
39
  if (phase === PHASE_DEVELOPMENT_SERVER) {
39
40
  prefix = ''
40
41
  }
41
-
42
+
42
43
  let configObj = {
43
44
  // target: 'serverless',
44
45
  // crossOrign: 'anonymous',
46
+ i18n,
45
47
  serverRuntimeConfig: {},
46
48
  publicRuntimeConfig: {
47
49
  version,
@@ -51,22 +53,32 @@ module.exports = (phase, defaultConfig, options) => {
51
53
  port,
52
54
  env,
53
55
  phase,
54
- isExport: (phase === PHASE_EXPORT)
56
+ isExport: phase === PHASE_EXPORT,
55
57
  },
56
- transpilePackages: ["antd", "@ant-design", "rc-util", "rc-pagination", "rc-picker", "rc-notification", "rc-tooltip", "rc-tree", "rc-table"],
58
+ transpilePackages: [
59
+ 'antd',
60
+ '@ant-design',
61
+ 'rc-util',
62
+ 'rc-pagination',
63
+ 'rc-picker',
64
+ 'rc-notification',
65
+ 'rc-tooltip',
66
+ 'rc-tree',
67
+ 'rc-table',
68
+ ],
57
69
  // Bundle 优化配置
58
70
  experimental: {
59
71
  optimizeCss: true, // 启用 CSS 优化(已安装 critters 依赖)
60
- esmExternals: true // 支持 ESM 外部依赖
72
+ esmExternals: true, // 支持 ESM 外部依赖
61
73
  },
62
74
  compiler: {
63
75
  removeConsole: phase !== PHASE_DEVELOPMENT_SERVER,
64
- styledComponents: true
76
+ styledComponents: true,
65
77
  },
66
78
  ...(phase === PHASE_DEVELOPMENT_SERVER && {
67
79
  devIndicators: {
68
- position: 'bottom-right'
69
- }
80
+ position: 'bottom-right',
81
+ },
70
82
  }),
71
83
  allowedDevOrigins: [
72
84
  'http://127.0.0.1:8080',
@@ -76,9 +88,39 @@ module.exports = (phase, defaultConfig, options) => {
76
88
  '127.0.0.1:8080',
77
89
  'localhost:8080',
78
90
  '127.0.0.1',
79
- 'localhost'
91
+ 'localhost',
80
92
  ],
81
93
  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
94
+ // 抑制服务端渲染时的 useLayoutEffect 警告
95
+ if (dev && isServer) {
96
+ const originalWarn = console.warn
97
+ const originalError = console.error
98
+
99
+ console.warn = (...args) => {
100
+ const warnMessage = args[0]
101
+ if (
102
+ typeof warnMessage === 'string' &&
103
+ (warnMessage.includes('useLayoutEffect does nothing on the server') ||
104
+ warnMessage.includes('Warning: useLayoutEffect does nothing on the server'))
105
+ ) {
106
+ return
107
+ }
108
+ originalWarn.apply(console, args)
109
+ }
110
+
111
+ console.error = (...args) => {
112
+ const errorMessage = args[0]
113
+ if (
114
+ typeof errorMessage === 'string' &&
115
+ (errorMessage.includes('useLayoutEffect does nothing on the server') ||
116
+ errorMessage.includes('Warning: useLayoutEffect does nothing on the server'))
117
+ ) {
118
+ return
119
+ }
120
+ originalError.apply(console, args)
121
+ }
122
+ }
123
+
82
124
  // 启用压缩
83
125
  if (!dev && !isServer) {
84
126
  config.optimization = {
@@ -134,10 +176,7 @@ module.exports = (phase, defaultConfig, options) => {
134
176
  }
135
177
 
136
178
  // 支持 TypeScript 路径映射
137
- config.resolve.modules = [
138
- path.resolve(__dirname, 'client'),
139
- 'node_modules'
140
- ]
179
+ config.resolve.modules = [path.resolve(__dirname, 'client'), 'node_modules']
141
180
 
142
181
  return config
143
182
  },
@@ -164,14 +203,14 @@ module.exports = (phase, defaultConfig, options) => {
164
203
  return [
165
204
  {
166
205
  source: prefix === '' ? '/' : prefix,
167
- destination: '/'
206
+ destination: '/',
168
207
  },
169
208
  {
170
209
  source: prefix + '/:slug*',
171
- destination: '/:slug*'
172
- }
210
+ destination: '/:slug*',
211
+ },
173
212
  ]
174
- }
213
+ },
175
214
  }
176
215
  }
177
216
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nsgm-cli",
3
- "version": "2.1.14",
3
+ "version": "2.1.15",
4
4
  "description": "A CLI tool to run Next/Style-components and Graphql/Mysql fullstack project",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -68,9 +68,11 @@
68
68
  "lib/*",
69
69
  "public/*",
70
70
  "scripts/*",
71
+ "types/*",
71
72
  "*.config.js",
72
73
  ".babelrc",
73
- "next-env.d.ts"
74
+ "next-env.d.ts",
75
+ "next-i18next.config.js"
74
76
  ],
75
77
  "homepage": "https://github.com/erishen/nsgm#readme",
76
78
  "dependencies": {
@@ -79,6 +81,9 @@
79
81
  "@types/bcrypt": "6.0.0",
80
82
  "@types/express-session": "1.18.2",
81
83
  "antd": "5.26.6",
84
+ "next-i18next": "15.3.0",
85
+ "react-i18next": "15.2.0",
86
+ "i18next": "24.1.0",
82
87
  "axios": "1.11.0",
83
88
  "bcrypt": "6.0.0",
84
89
  "body-parser": "2.2.0",
package/pages/_app.tsx CHANGED
@@ -1,11 +1,21 @@
1
+ // 必须在所有其他导入之前执行
2
+ import '@/utils/suppressWarnings'
3
+
1
4
  import React, { useEffect, useState } from 'react'
2
5
  import { Provider } from 'react-redux'
3
- import { ThemeProvider } from 'styled-components'
6
+ import { Spin } from 'antd'
7
+ import { appWithTranslation } from 'next-i18next'
8
+ import { useRouter } from 'next/router'
4
9
  import { useStore } from '@/redux/store'
5
- import { GlobalStyle, Loading } from '@/styled/common'
10
+ import { Loading } from '@/styled/common'
6
11
  import LayoutComponent from '@/layout'
12
+ import ClientProviders from '@/components/ClientProviders'
13
+ import SuppressHydrationWarnings from '@/components/SuppressHydrationWarnings'
14
+ import SSRSafeAntdProvider from '@/components/SSRSafeAntdProvider'
7
15
  import { login } from '@/utils/sso'
8
- import { Spin } from 'antd'
16
+ import { getAntdLocale } from '@/utils/i18n'
17
+ import { navigateToLogin } from '@/utils/navigation'
18
+ import nextI18NextConfig from '../next-i18next.config.js'
9
19
  import 'antd/dist/reset.css'
10
20
 
11
21
  const theme = {
@@ -16,16 +26,53 @@ const theme = {
16
26
 
17
27
  const App = ({ Component, pageProps }) => {
18
28
  const store = useStore(pageProps.initialReduxState)
29
+ const router = useRouter()
19
30
  const [ssoUser, setSsoUser] = useState(null)
20
31
  const [pageLoad, setPageLoad] = useState(false)
21
32
  const [loginChecked, setLoginChecked] = useState(false)
33
+ const [mounted, setMounted] = useState(false)
34
+ const [currentLocale, setCurrentLocale] = useState('zh-CN')
35
+
36
+ // 检查是否为特殊页面(登录页、错误页)
37
+ // 避免在服务器端访问 router.pathname
38
+ const isSpecialPage =
39
+ Component.displayName === 'ErrorPage' ||
40
+ Component.name === 'ErrorPage' ||
41
+ Component.displayName === 'LoginPage' ||
42
+ Component.name === 'LoginPage'
43
+
44
+ // Get Antd locale based on current locale
45
+ const antdLocale = getAntdLocale(currentLocale)
22
46
 
23
47
  useEffect(() => {
24
- // 检查当前路径是否为登录页
48
+ setMounted(true)
49
+ // 只在客户端设置当前语言
50
+ if (typeof window !== 'undefined') {
51
+ const locale = router.locale || 'zh-CN'
52
+ setCurrentLocale(locale)
53
+ }
54
+ }, [router.locale])
55
+
56
+ useEffect(() => {
57
+ if (!mounted) return
58
+
59
+ // 对于特殊页面,跳过登录检查
60
+ if (isSpecialPage) {
61
+ setLoginChecked(true)
62
+ setPageLoad(true)
63
+ return
64
+ }
65
+
66
+ // 检查当前路径是否为登录页或错误页
25
67
  const isLoginPage = typeof window !== 'undefined' && window.location.pathname === '/login'
68
+ const isErrorPage =
69
+ typeof window !== 'undefined' &&
70
+ (window.location.pathname === '/404' ||
71
+ window.location.pathname === '/500' ||
72
+ window.location.pathname === '/_error')
26
73
 
27
- // 如果是登录页,直接设置加载完成
28
- if (isLoginPage) {
74
+ // 如果是登录页或错误页,直接设置加载完成,不进行登录检查
75
+ if (isLoginPage || isErrorPage) {
29
76
  setLoginChecked(true)
30
77
  setPageLoad(true)
31
78
  return
@@ -34,9 +81,9 @@ const App = ({ Component, pageProps }) => {
34
81
  // 检查是否有登录凭证
35
82
  const hasLoginCookie = typeof window !== 'undefined' && document.cookie.includes('_cas_nsgm')
36
83
 
37
- // 如果没有登录凭证,直接跳转到登录页面
84
+ // 如果没有登录凭证,直接跳转到登录页面(保持当前语言)
38
85
  if (!hasLoginCookie && typeof window !== 'undefined') {
39
- window.location.href = `${window.location.origin}/login`
86
+ navigateToLogin(router)
40
87
  return
41
88
  }
42
89
 
@@ -51,40 +98,42 @@ const App = ({ Component, pageProps }) => {
51
98
  setTimeout(() => {
52
99
  setPageLoad(true)
53
100
  }, 100)
54
- }, [])
101
+ }, [mounted, isSpecialPage])
55
102
 
56
103
  return (
57
104
  <>
58
- <GlobalStyle whiteColor={true} />
59
- <ThemeProvider theme={theme}>
60
- <Provider store={store}>
61
- {!loginChecked ? (
62
- <Loading>
63
- <Spin size="large" />
64
- </Loading>
65
- ) : pageLoad ? (
66
- ssoUser ? (
67
- <LayoutComponent user={ssoUser}>
105
+ <SuppressHydrationWarnings />
106
+ <SSRSafeAntdProvider locale={antdLocale}>
107
+ <ClientProviders theme={theme} whiteColor={true}>
108
+ <Provider store={store}>
109
+ {isSpecialPage ? (
110
+ // 特殊页面(错误页、登录页)直接渲染,不使用 Layout
111
+ <Component {...pageProps} />
112
+ ) : !loginChecked ? (
113
+ <Loading>
114
+ <Spin size="large" />
115
+ </Loading>
116
+ ) : pageLoad ? (
117
+ ssoUser ? (
118
+ <LayoutComponent user={ssoUser}>
119
+ <Component {...pageProps} />
120
+ </LayoutComponent>
121
+ ) : (
68
122
  <Component {...pageProps} />
69
- </LayoutComponent>
123
+ )
70
124
  ) : (
71
- <Component {...pageProps} />
72
- )
73
- ) : (
74
- <Loading>
75
- <Spin size="large" />
76
- </Loading>
77
- )}
78
- </Provider>
79
- </ThemeProvider>
125
+ <Loading>
126
+ <Spin size="large" />
127
+ </Loading>
128
+ )}
129
+ </Provider>
130
+ </ClientProviders>
131
+ </SSRSafeAntdProvider>
80
132
  </>
81
133
  )
82
134
  }
83
135
 
84
- App.getInitialProps = async ({ Component, ctx }) => {
85
- return {
86
- pageProps: await Component?.getInitialProps(ctx),
87
- }
88
- }
136
+ // 移除 getInitialProps 以启用静态优化
137
+ // 如果需要页面级别的数据获取,请在各个页面中使用 getStaticProps 或 getServerSideProps
89
138
 
90
- export default App
139
+ export default appWithTranslation(App, nextI18NextConfig)
@@ -2,9 +2,12 @@ import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/do
2
2
  import React from 'react'
3
3
  import { ServerStyleSheet } from 'styled-components'
4
4
 
5
- const MyDocument = () => {
5
+ const MyDocument = (props) => {
6
+ // 从 props 中获取语言信息,如果没有则默认为 zh-CN
7
+ const locale = props.locale || 'zh-CN'
8
+
6
9
  return (
7
- <Html>
10
+ <Html lang={locale}>
8
11
  <title>NSGM CLI</title>
9
12
  <meta
10
13
  name="viewport"
@@ -44,6 +47,39 @@ const MyDocument = () => {
44
47
  -moz-osx-font-smoothing: grayscale;
45
48
  }
46
49
  `}</style>
50
+
51
+ {/* 抑制服务端渲染的 useLayoutEffect 警告 */}
52
+ <script
53
+ dangerouslySetInnerHTML={{
54
+ __html: `
55
+ if (typeof window === 'undefined') {
56
+ global.window = {};
57
+ }
58
+
59
+ // 在服务端抑制 useLayoutEffect 警告
60
+ if (typeof console !== 'undefined' && process.env.NODE_ENV === 'development') {
61
+ const originalError = console.error;
62
+ const originalWarn = console.warn;
63
+
64
+ console.error = function(...args) {
65
+ const message = args[0];
66
+ if (typeof message === 'string' && message.includes('useLayoutEffect does nothing on the server')) {
67
+ return;
68
+ }
69
+ originalError.apply(console, args);
70
+ };
71
+
72
+ console.warn = function(...args) {
73
+ const message = args[0];
74
+ if (typeof message === 'string' && message.includes('useLayoutEffect does nothing on the server')) {
75
+ return;
76
+ }
77
+ originalWarn.apply(console, args);
78
+ };
79
+ }
80
+ `,
81
+ }}
82
+ />
47
83
  </Head>
48
84
  <body>
49
85
  <Main />
@@ -69,6 +105,7 @@ MyDocument.getInitialProps = async (ctx: DocumentContext) => {
69
105
 
70
106
  return {
71
107
  ...initialProps,
108
+ locale: ctx.locale || 'zh-CN',
72
109
  styles: [initialProps.styles, sheet.getStyleElement()],
73
110
  }
74
111
  } finally {
@@ -0,0 +1,66 @@
1
+ import React from 'react'
2
+ import { Result, Button } from 'antd'
3
+ import { NextPageContext } from 'next'
4
+
5
+ interface ErrorProps {
6
+ statusCode?: number
7
+ }
8
+
9
+ function ErrorPage({ statusCode }: ErrorProps) {
10
+ const getErrorMessage = () => {
11
+ if (statusCode === 404) {
12
+ return '页面未找到'
13
+ } else if (statusCode === 500) {
14
+ return '服务器错误'
15
+ }
16
+ return '发生了未知错误'
17
+ }
18
+
19
+ const getErrorTitle = () => {
20
+ if (statusCode === 404) {
21
+ return '404'
22
+ } else if (statusCode === 500) {
23
+ return '500'
24
+ }
25
+ return '错误'
26
+ }
27
+
28
+ return (
29
+ <div
30
+ style={{
31
+ minHeight: '100vh',
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ padding: '20px',
36
+ }}
37
+ >
38
+ <Result
39
+ status={statusCode === 404 ? '404' : '500'}
40
+ title={getErrorTitle()}
41
+ subTitle={getErrorMessage()}
42
+ extra={
43
+ <Button
44
+ type="primary"
45
+ onClick={() => {
46
+ if (typeof window !== 'undefined') {
47
+ window.location.href = '/'
48
+ }
49
+ }}
50
+ >
51
+ 返回首页
52
+ </Button>
53
+ }
54
+ />
55
+ </div>
56
+ )
57
+ }
58
+
59
+ ErrorPage.getInitialProps = ({ res, err }: NextPageContext) => {
60
+ const statusCode = res ? res.statusCode : err ? err.statusCode : 404
61
+ return { statusCode }
62
+ }
63
+
64
+ ErrorPage.displayName = 'ErrorPage'
65
+
66
+ export default ErrorPage
package/pages/index.tsx CHANGED
@@ -3,14 +3,22 @@ import { Container } from '../client/styled/common'
3
3
  import React from 'react'
4
4
  import { Card, Typography, Divider, Row, Col, Tag } from 'antd'
5
5
  import { CodeOutlined, BookOutlined, DatabaseOutlined, SettingOutlined } from '@ant-design/icons'
6
+ import { useTranslation } from 'next-i18next'
7
+ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
6
8
 
7
9
  const { Title, Paragraph, Text } = Typography
8
10
 
9
11
  const Page = () => {
12
+ const { t } = useTranslation(['common', 'home'])
13
+
10
14
  return (
11
15
  <Container>
12
16
  <Typography style={{ padding: '24px' }}>
13
- <Title level={1}>NSGM CLI</Title>
17
+ <Row justify="space-between" align="middle" style={{ marginBottom: '24px' }}>
18
+ <Col>
19
+ <Title level={1}>{t('home:page.title')}</Title>
20
+ </Col>
21
+ </Row>
14
22
 
15
23
  <Paragraph>
16
24
  <Row gutter={[16, 16]}>
@@ -28,7 +36,7 @@ const Page = () => {
28
36
  </Col>
29
37
  </Row>
30
38
  </Paragraph>
31
- <Paragraph>全栈架构,代码模板生成,快速开发</Paragraph>
39
+ <Paragraph>{t('home:page.description')}</Paragraph>
32
40
 
33
41
  <Card style={{ marginBottom: '24px' }}>
34
42
  <Row gutter={[24, 16]}>
@@ -37,11 +45,11 @@ const Page = () => {
37
45
  type="inner"
38
46
  title={
39
47
  <>
40
- <DatabaseOutlined /> 数据库配置
48
+ <DatabaseOutlined /> {t('home:page.sections.database.title')}
41
49
  </>
42
50
  }
43
51
  >
44
- 数据库采用 Mysql, 配置见 mysql.config.js
52
+ {t('home:page.sections.database.description')}
45
53
  </Card>
46
54
  </Col>
47
55
  <Col xs={24} md={8}>
@@ -49,11 +57,11 @@ const Page = () => {
49
57
  type="inner"
50
58
  title={
51
59
  <>
52
- <SettingOutlined /> 项目配置
60
+ <SettingOutlined /> {t('home:page.sections.project.title')}
53
61
  </>
54
62
  }
55
63
  >
56
- 项目配置见 project.config.js
64
+ {t('home:page.sections.project.description')}
57
65
  </Card>
58
66
  </Col>
59
67
  <Col xs={24} md={8}>
@@ -61,66 +69,68 @@ const Page = () => {
61
69
  type="inner"
62
70
  title={
63
71
  <>
64
- <CodeOutlined /> 框架配置
72
+ <CodeOutlined /> {t('home:page.sections.framework.title')}
65
73
  </>
66
74
  }
67
75
  >
68
- Next 框架配置见 next.config.js
76
+ {t('home:page.sections.framework.description')}
69
77
  </Card>
70
78
  </Col>
71
79
  </Row>
72
80
  </Card>
73
81
 
74
82
  <Title level={2}>
75
- <BookOutlined /> 命令
83
+ <BookOutlined /> {t('home:page.commands.title')}
76
84
  </Title>
77
85
  <Divider />
78
86
 
79
87
  <Row gutter={[16, 16]}>
80
88
  <Col xs={24} md={8}>
81
89
  <Card hoverable>
82
- <Title level={4}>项目管理</Title>
90
+ <Title level={4}>{t('home:page.commands.categories.projectManagement.title')}</Title>
83
91
  <ul>
84
92
  <li>
85
- <Text strong>nsgm init</Text> - 初始化项目
93
+ <Text strong>nsgm init</Text> - {t('home:page.commands.categories.projectManagement.items.init')}
86
94
  </li>
87
95
  <li>
88
- <Text strong>nsgm upgrade</Text> - 升级项目基础文件
96
+ <Text strong>nsgm upgrade</Text> -{' '}
97
+ {t('home:page.commands.categories.projectManagement.items.upgrade')}
89
98
  </li>
90
99
  </ul>
91
100
  </Card>
92
101
  </Col>
93
102
  <Col xs={24} md={8}>
94
103
  <Card hoverable>
95
- <Title level={4}>模板操作</Title>
104
+ <Title level={4}>{t('home:page.commands.categories.templateOperations.title')}</Title>
96
105
  <ul>
97
106
  <li>
98
- <Text strong>nsgm create</Text> - 创建模板页面
107
+ <Text strong>nsgm create</Text> - {t('home:page.commands.categories.templateOperations.items.create')}
99
108
  </li>
100
109
  <li>
101
- <Text strong>nsgm delete</Text> - 删除模板页面
110
+ <Text strong>nsgm delete</Text> - {t('home:page.commands.categories.templateOperations.items.delete')}
102
111
  </li>
103
112
  <li>
104
- <Text strong>nsgm deletedb</Text> - 删除模板页面及数据库表
113
+ <Text strong>nsgm deletedb</Text> -{' '}
114
+ {t('home:page.commands.categories.templateOperations.items.deletedb')}
105
115
  </li>
106
116
  </ul>
107
117
  </Card>
108
118
  </Col>
109
119
  <Col xs={24} md={8}>
110
120
  <Card hoverable>
111
- <Title level={4}>运行与构建</Title>
121
+ <Title level={4}>{t('home:page.commands.categories.runBuild.title')}</Title>
112
122
  <ul>
113
123
  <li>
114
- <Text strong>nsgm dev</Text> - 开发模式
124
+ <Text strong>nsgm dev</Text> - {t('home:page.commands.categories.runBuild.items.dev')}
115
125
  </li>
116
126
  <li>
117
- <Text strong>nsgm start</Text> - 生产模式
127
+ <Text strong>nsgm start</Text> - {t('home:page.commands.categories.runBuild.items.start')}
118
128
  </li>
119
129
  <li>
120
- <Text strong>nsgm build</Text> - 编译
130
+ <Text strong>nsgm build</Text> - {t('home:page.commands.categories.runBuild.items.build')}
121
131
  </li>
122
132
  <li>
123
- <Text strong>nsgm export</Text> - 导出静态页面
133
+ <Text strong>nsgm export</Text> - {t('home:page.commands.categories.runBuild.items.export')}
124
134
  </li>
125
135
  </ul>
126
136
  </Card>
@@ -128,21 +138,19 @@ const Page = () => {
128
138
  </Row>
129
139
 
130
140
  <Title level={2} style={{ marginTop: '24px' }}>
131
- 参数
141
+ {t('home:page.parameters.title')}
132
142
  </Title>
133
143
  <Divider />
134
144
  <Card>
135
145
  <ul>
136
146
  <li>
137
- <Text strong>dictionary:</Text> 在 export/init 的时候使用, 默认 webapp, 譬如: nsgm export/init
138
- dictionary=webapp 或者 nsgm export/init webapp
147
+ <Text strong>dictionary:</Text> {t('home:page.parameters.items.dictionary')}
139
148
  </li>
140
149
  <li>
141
- <Text strong>controller:</Text> 在 create/delete 的时候使用, 必须有。譬如:nsgm create/delete math
150
+ <Text strong>controller:</Text> {t('home:page.parameters.items.controller')}
142
151
  </li>
143
152
  <li>
144
- <Text strong>action:</Text> 在 create/delete 的时候使用, 默认 manage, 跟在 controller 后面, 譬如 nsgm
145
- create/delete math test
153
+ <Text strong>action:</Text> {t('home:page.parameters.items.action')}
146
154
  </li>
147
155
  </ul>
148
156
  </Card>
@@ -151,8 +159,17 @@ const Page = () => {
151
159
  )
152
160
  }
153
161
 
154
- Page.getInitialProps = () => {
155
- return {}
162
+ export const getServerSideProps = async ({ locale }) => {
163
+ // 确保 locale 有默认值,避免 serverSideTranslations 报错
164
+ const currentLocale = locale || 'zh-CN'
165
+
166
+ return {
167
+ props: {
168
+ ...(await serverSideTranslations(currentLocale, ['common', 'home', 'layout'])),
169
+ },
170
+ }
156
171
  }
157
172
 
173
+ Page.displayName = 'HomePage'
174
+
158
175
  export default Page