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
package/pages/login.tsx CHANGED
@@ -2,10 +2,15 @@ import MarkdownIt from 'markdown-it'
2
2
  import _ from 'lodash'
3
3
  import { LoginContainer } from '../client/styled/common'
4
4
  // import getConfig from 'next/config'
5
- import React, { useState } from 'react'
5
+ import React, { useState, useEffect } from 'react'
6
6
  import { Input, Button, Form, Typography, message } from 'antd'
7
7
  import { UserOutlined, LockOutlined } from '@ant-design/icons'
8
8
  import { directLogin } from '../client/utils/sso'
9
+ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
10
+ import { useTranslation } from 'next-i18next'
11
+ import { useRouter } from 'next/router'
12
+ import LanguageSwitcher from '@/components/LanguageSwitcher'
13
+ import { navigateToHome } from '@/utils/navigation'
9
14
 
10
15
  const md = new MarkdownIt({
11
16
  html: true,
@@ -19,11 +24,18 @@ const md = new MarkdownIt({
19
24
 
20
25
  const renderArr: any = []
21
26
 
22
- renderArr.push('Login')
27
+ renderArr.push('NSGM')
23
28
 
24
29
  const Page = ({ html }) => {
30
+ const { t } = useTranslation(['login'])
31
+ const router = useRouter()
25
32
  const [userName, setUserName] = useState('')
26
33
  const [userPassword, setUserPassword] = useState('')
34
+ const [mounted, setMounted] = useState(false)
35
+
36
+ useEffect(() => {
37
+ setMounted(true)
38
+ }, [])
27
39
 
28
40
  const createMarkup = () => {
29
41
  return {
@@ -32,35 +44,36 @@ const Page = ({ html }) => {
32
44
  }
33
45
 
34
46
  const doLogin = () => {
35
- if (typeof window !== 'undefined') {
36
- if (userName === '') {
37
- message.error('请输入用户名')
38
- return
39
- }
40
- if (userPassword === '') {
41
- message.error('请输入密码')
42
- return
47
+ if (!mounted || typeof window === 'undefined') return
48
+
49
+ if (userName === '') {
50
+ message.error(t('login:login.errors.usernameRequired'))
51
+ return
52
+ }
53
+ if (userPassword === '') {
54
+ message.error(t('login:login.errors.passwordRequired'))
55
+ return
56
+ }
57
+
58
+ const result = directLogin(userName, userPassword, (user) => {
59
+ if (user && mounted) {
60
+ // 跳转到首页,保持当前语言设置,强制添加语言前缀避免自动检测
61
+ navigateToHome(router, true)
43
62
  }
63
+ })
44
64
 
45
- const result = directLogin(userName, userPassword, (user) => {
46
- if (user) {
47
- window.location.href = window.location.origin
65
+ // 检查是否是 Promise
66
+ if (result && typeof (result as any).then === 'function') {
67
+ ;(result as Promise<any>).then((loginResult) => {
68
+ if (!loginResult.success) {
69
+ message.error(loginResult.message)
48
70
  }
49
71
  })
50
-
51
- // 检查是否是 Promise
52
- if (result && typeof (result as any).then === 'function') {
53
- ;(result as Promise<any>).then((loginResult) => {
54
- if (!loginResult.success) {
55
- message.error(loginResult.message)
56
- }
57
- })
58
- } else {
59
- // 直接返回的结果
60
- const syncResult = result as { success: boolean; message?: string }
61
- if (!syncResult.success) {
62
- message.error(syncResult.message || '登录失败')
63
- }
72
+ } else {
73
+ // 直接返回的结果
74
+ const syncResult = result as { success: boolean; message?: string }
75
+ if (!syncResult.success) {
76
+ message.error(syncResult.message || t('login:login.errors.loginFailed'))
64
77
  }
65
78
  }
66
79
  }
@@ -75,15 +88,18 @@ const Page = ({ html }) => {
75
88
 
76
89
  return (
77
90
  <LoginContainer>
91
+ <div style={{ position: 'absolute', top: '20px', right: '20px' }}>
92
+ <LanguageSwitcher />
93
+ </div>
78
94
  <div dangerouslySetInnerHTML={createMarkup()} />
79
95
  <Typography.Title level={3} style={{ textAlign: 'center', marginBottom: 24 }}>
80
- 系统登录
96
+ {t('login:login.title')}
81
97
  </Typography.Title>
82
98
  <Form layout="vertical" style={{ width: '100%' }}>
83
99
  <Form.Item>
84
100
  <Input
85
101
  prefix={<UserOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}
86
- placeholder="用户名"
102
+ placeholder={t('login:login.username')}
87
103
  size="large"
88
104
  value={userName}
89
105
  onChange={doChangeName}
@@ -93,7 +109,7 @@ const Page = ({ html }) => {
93
109
  <Form.Item>
94
110
  <Input.Password
95
111
  prefix={<LockOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}
96
- placeholder="密码"
112
+ placeholder={t('login:login.password')}
97
113
  size="large"
98
114
  value={userPassword}
99
115
  onChange={doChangePassword}
@@ -102,7 +118,7 @@ const Page = ({ html }) => {
102
118
  </Form.Item>
103
119
  <Form.Item>
104
120
  <Button type="primary" onClick={doLogin} size="large" block>
105
- 登录
121
+ {t('login:login.loginButton')}
106
122
  </Button>
107
123
  </Form.Item>
108
124
  </Form>
@@ -110,15 +126,24 @@ const Page = ({ html }) => {
110
126
  )
111
127
  }
112
128
 
113
- Page.getInitialProps = () => {
129
+ export const getServerSideProps = async ({ locale }) => {
130
+ // 确保 locale 有默认值,避免 serverSideTranslations 报错
131
+ const currentLocale = locale || 'zh-CN'
132
+
133
+ // 处理 markdown 内容
114
134
  let html = ''
115
135
  _.each(renderArr, (item) => {
116
136
  html += md.render(item)
117
137
  })
118
138
 
119
139
  return {
120
- html,
140
+ props: {
141
+ html,
142
+ ...(await serverSideTranslations(currentLocale, ['common', 'layout', 'login'])),
143
+ },
121
144
  }
122
145
  }
123
146
 
147
+ Page.displayName = 'LoginPage'
148
+
124
149
  export default Page
@@ -1,7 +1,18 @@
1
1
  import React, { useState, useEffect } from 'react'
2
- import { ConfigProvider, Table, Modal, Button, Input, Space, Upload, message } from 'antd'
3
- import { Container, SearchRow, ModalContainer } from '@/styled/template/manage'
4
- import styled from 'styled-components'
2
+ import { ConfigProvider, Modal, Space, Upload, message } from 'antd'
3
+ import {
4
+ Container,
5
+ SearchRow,
6
+ ModalContainer,
7
+ StyledButton,
8
+ StyledInput,
9
+ StyledTable,
10
+ ModalTitle,
11
+ ModalInput,
12
+ IconWrapper,
13
+ RoundedButton,
14
+ GlobalStyle,
15
+ } from '@/styled/template/manage'
5
16
  import { useDispatch, useSelector } from 'react-redux'
6
17
  import {
7
18
  addTemplate,
@@ -14,7 +25,9 @@ import {
14
25
  import { getTemplateService } from '@/service/template/manage'
15
26
  import { RootState, AppDispatch } from '@/redux/store'
16
27
  import _ from 'lodash'
17
- import locale from 'antd/lib/locale/zh_CN'
28
+ import { useTranslation } from 'next-i18next'
29
+ import { getAntdLocale } from '@/utils/i18n'
30
+ import { useRouter } from 'next/router'
18
31
  import { handleXSS, checkModalObj } from '@/utils/common'
19
32
  import { UploadOutlined } from '@ant-design/icons'
20
33
  import ExcelJS from 'exceljs'
@@ -23,78 +36,10 @@ import { createCSRFUploadProps } from '@/utils/fetch'
23
36
 
24
37
  const pageSize = 100
25
38
 
26
- const keyTitles = {
27
- name: '名称',
28
- }
29
-
30
- // styled-components
31
- const StyledButton = styled(Button)<{ $primary?: boolean; $export?: boolean; $import?: boolean; $danger?: boolean }>`
32
- display: flex;
33
- align-items: center;
34
- border-radius: 6px;
35
- box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
36
- ${(props) =>
37
- props.$export &&
38
- `
39
- background-color: #f6ffed;
40
- color: #52c41a;
41
- border-color: #b7eb8f;
42
- box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
43
- transition: all 0.3s ease;
44
- `}
45
- ${(props) =>
46
- props.$import &&
47
- `
48
- background-color: #e6f7ff;
49
- color: #1890ff;
50
- border-color: #91d5ff;
51
- box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
52
- transition: all 0.3s ease;
53
- `}
54
- ${(props) =>
55
- props.$danger &&
56
- `
57
- background-color: #fff1f0;
58
- border-color: #ffa39e;
59
- box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
60
- transition: all 0.3s ease;
61
- `}
62
- `
63
- const StyledInput = styled(Input)`
64
- width: 200px;
65
- border-radius: 6px;
66
- box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
67
- `
68
- const StyledTable = styled(Table)`
69
- margin-top: 16px;
70
- border-radius: 8px;
71
- overflow: hidden;
72
-
73
- .styled-pagination {
74
- margin-top: 16px;
75
- margin-bottom: 16px;
76
- }
77
- `
78
- const ModalTitle = styled.div`
79
- color: #1890ff;
80
- font-weight: 500;
81
- `
82
- const ModalInput = styled(Input)`
83
- border-radius: 4px;
84
- `
85
- const IconWrapper = styled.i`
86
- margin-right: 5px;
87
- `
88
- const RoundedButton = styled(Button)`
89
- border-radius: 4px;
90
- `
91
- const GlobalStyle = styled.div`
92
- .rounded-button {
93
- border-radius: 4px;
94
- }
95
- `
96
-
97
39
  const Page = ({ template }) => {
40
+ const { t } = useTranslation(['common', 'template'])
41
+ const router = useRouter()
42
+ const antdLocale = getAntdLocale(router.locale || 'zh-CN')
98
43
  const dispatch = useDispatch<AppDispatch>()
99
44
  const [isModalVisiable, setIsModalVisible] = useState(false)
100
45
  const [modalId, setModalId] = useState(0)
@@ -102,10 +47,39 @@ const Page = ({ template }) => {
102
47
  const [searchName, setSearchName] = useState('')
103
48
  const [batchDelIds, setBatchDelIds] = useState([])
104
49
 
50
+ const keyTitles = {
51
+ name: t('template:template.fields.name'),
52
+ }
53
+
105
54
  useEffect(() => {
106
55
  dispatch(updateSSRTemplate(template))
107
56
  }, [dispatch])
108
57
 
58
+ useEffect(() => {
59
+ // 管理弹窗打开时的滚动条显示
60
+ if (isModalVisiable) {
61
+ // 记录原始样式
62
+ const originalStyle = window.getComputedStyle(document.body).overflow
63
+ const originalPaddingRight = window.getComputedStyle(document.body).paddingRight
64
+
65
+ // 设置定时器,在 Modal 设置样式后覆盖
66
+ const timer = setTimeout(() => {
67
+ document.body.style.overflow = 'auto'
68
+ document.body.style.paddingRight = '0px'
69
+ }, 0)
70
+
71
+ return () => {
72
+ clearTimeout(timer)
73
+ // 清理时恢复原始样式
74
+ document.body.style.overflow = originalStyle
75
+ document.body.style.paddingRight = originalPaddingRight
76
+ }
77
+ }
78
+
79
+ // 当弹窗关闭时,不需要清理函数
80
+ return undefined
81
+ }, [isModalVisiable])
82
+
109
83
  const templateManage = useSelector((state: RootState) => state.templateManage)
110
84
 
111
85
  if (!templateManage.firstLoadFlag) {
@@ -122,7 +96,7 @@ const Page = ({ template }) => {
122
96
  const dataSource = templateItems
123
97
  const columns: any = [
124
98
  {
125
- title: 'ID',
99
+ title: t('template:template.fields.id'),
126
100
  dataIndex: 'id',
127
101
  key: 'id',
128
102
  sorter: (a: any, b: any) => a.id - b.id,
@@ -132,7 +106,7 @@ const Page = ({ template }) => {
132
106
  align: 'center',
133
107
  },
134
108
  {
135
- title: keyTitles.name,
109
+ title: t('template:template.fields.name'),
136
110
  dataIndex: 'name',
137
111
  key: 'name',
138
112
  sorter: (a: any, b: any) => a.name.length - b.name.length,
@@ -142,7 +116,7 @@ const Page = ({ template }) => {
142
116
  ellipsis: true,
143
117
  },
144
118
  {
145
- title: '操作',
119
+ title: t('template:template.fields.actions'),
146
120
  dataIndex: '',
147
121
  width: '25%',
148
122
  align: 'center',
@@ -156,7 +130,7 @@ const Page = ({ template }) => {
156
130
  updateTemplate(record)
157
131
  }}
158
132
  >
159
- 修改
133
+ {t('template:template.buttons.edit')}
160
134
  </RoundedButton>
161
135
  <RoundedButton
162
136
  danger
@@ -166,7 +140,7 @@ const Page = ({ template }) => {
166
140
  deleteTemplate(id)
167
141
  }}
168
142
  >
169
- 删除
143
+ {t('template:template.buttons.delete')}
170
144
  </RoundedButton>
171
145
  </Space>
172
146
  )
@@ -197,10 +171,10 @@ const Page = ({ template }) => {
197
171
 
198
172
  const deleteTemplate = (id: number) => {
199
173
  Modal.confirm({
200
- title: '提示',
201
- content: '确认删除吗',
202
- okText: '确认',
203
- cancelText: '取消',
174
+ title: t('common:common.warning'),
175
+ content: t('template:template.messages.confirmDelete'),
176
+ okText: t('template:template.buttons.confirm'),
177
+ cancelText: t('template:template.buttons.cancel'),
204
178
  onOk: () => {
205
179
  dispatch(delTemplate(id))
206
180
  Modal.destroyAll()
@@ -284,18 +258,18 @@ const Page = ({ template }) => {
284
258
  // 导出失败
285
259
  })
286
260
  } else {
287
- message.info('没有数据无需导出')
261
+ message.info(t('template:template.messages.noData'))
288
262
  }
289
263
  }
290
264
 
291
265
  const uploadProps = createCSRFUploadProps('/rest/template/import', {
292
266
  name: 'file',
293
267
  onSuccess: (fileName) => {
294
- message.success(`${fileName} 文件上传成功`)
268
+ message.success(`${fileName} ${t('template:template.messages.uploadSuccess')}`)
295
269
  window.location.reload()
296
270
  },
297
271
  onError: (fileName) => {
298
- message.error(`${fileName} 文件上传失败`)
272
+ message.error(`${fileName} ${t('template:template.messages.uploadFailed')}`)
299
273
  },
300
274
  beforeUpload: (file) => {
301
275
  // 可以在这里添加文件类型、大小等验证
@@ -303,12 +277,12 @@ const Page = ({ template }) => {
303
277
  file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
304
278
  file.type === 'application/vnd.ms-excel'
305
279
  if (!isExcel) {
306
- message.error('只能上传 Excel 文件!')
280
+ message.error(t('template:template.messages.onlyExcel'))
307
281
  return false
308
282
  }
309
283
  const isLt2M = file.size / 1024 / 1024 < 2
310
284
  if (!isLt2M) {
311
- message.error('文件大小不能超过 2MB!')
285
+ message.error(t('template:template.messages.fileSizeLimit'))
312
286
  return false
313
287
  }
314
288
  return true
@@ -318,55 +292,55 @@ const Page = ({ template }) => {
318
292
  const batchDeleteTemplate = () => {
319
293
  if (batchDelIds.length > 0) {
320
294
  Modal.confirm({
321
- title: '提示',
322
- content: '确认批量删除吗',
323
- okText: '确认',
324
- cancelText: '取消',
295
+ title: t('common:common.warning'),
296
+ content: t('template:template.messages.confirmBatchDelete'),
297
+ okText: t('template:template.buttons.confirm'),
298
+ cancelText: t('template:template.buttons.cancel'),
325
299
  onOk: () => {
326
300
  dispatch(batchDelTemplate(batchDelIds))
327
301
  Modal.destroyAll()
328
302
  },
329
303
  })
330
304
  } else {
331
- message.info('没有数据不能批量删除')
305
+ message.info(t('template:template.messages.noDataBatchDelete'))
332
306
  }
333
307
  }
334
308
 
335
309
  return (
336
310
  <Container>
337
311
  <GlobalStyle />
338
- <div className="page-title">Template 管理</div>
339
- <ConfigProvider locale={locale}>
312
+ <div className="page-title">{t('template:template.title')}</div>
313
+ <ConfigProvider locale={antdLocale}>
340
314
  <SearchRow>
341
315
  <Space size="middle" wrap>
342
316
  <Space size="small">
343
317
  <StyledButton type="primary" onClick={createTemplate} $primary>
344
318
  <IconWrapper className="fa fa-plus"></IconWrapper>
345
- 新增
319
+ {t('template:template.buttons.add')}
346
320
  </StyledButton>
347
321
  <StyledInput
348
322
  value={searchName}
349
- placeholder="请输入名称搜索"
323
+ placeholder={t('template:template.placeholders.enterName')}
350
324
  allowClear
351
325
  onChange={(e) => setSearchName(e.target.value)}
352
326
  onPressEnter={doSearch}
353
327
  />
354
328
  <StyledButton type="primary" onClick={doSearch} $primary>
355
329
  <IconWrapper className="fa fa-search"></IconWrapper>
356
- 搜索
330
+ {t('template:template.buttons.search')}
357
331
  </StyledButton>
358
332
  </Space>
359
333
  <Space size="small">
360
334
  <StyledButton onClick={exportTemplate} icon={<UploadOutlined rotate={180} />} $export>
361
- 导出
335
+ {t('template:template.buttons.export')}
362
336
  </StyledButton>
363
337
  <Upload {...uploadProps}>
364
338
  <StyledButton icon={<UploadOutlined />} $import>
365
- 导入
339
+ {t('template:template.buttons.import')}
366
340
  </StyledButton>
367
341
  </Upload>
368
342
  <StyledButton danger onClick={batchDeleteTemplate} $danger>
369
- 批量删除
343
+ {t('template:template.buttons.batchDelete')}
370
344
  </StyledButton>
371
345
  </Space>
372
346
  </Space>
@@ -385,7 +359,7 @@ const Page = ({ template }) => {
385
359
  pageSize: pageSize,
386
360
  showSizeChanger: false,
387
361
  showQuickJumper: true,
388
- showTotal: (total) => `共 ${total} 条记录`,
362
+ showTotal: (total) => t('template:template.pagination.total', { total }),
389
363
  onChange: (page, pageSize) => {
390
364
  dispatch(searchTemplate(page - 1, pageSize, { name: handleXSS(searchName) }))
391
365
  },
@@ -393,12 +367,16 @@ const Page = ({ template }) => {
393
367
  }}
394
368
  />
395
369
  <Modal
396
- title={<ModalTitle>{`${modalId == 0 ? '新增' : '修改'} Template`}</ModalTitle>}
370
+ title={
371
+ <ModalTitle>
372
+ {modalId == 0 ? t('template:template.modal.addTitle') : t('template:template.modal.editTitle')}
373
+ </ModalTitle>
374
+ }
397
375
  open={isModalVisiable}
398
376
  onOk={handleOk}
399
377
  onCancel={handleCancel}
400
- okText="确认"
401
- cancelText="取消"
378
+ okText={t('template:template.buttons.confirm')}
379
+ cancelText={t('template:template.buttons.cancel')}
402
380
  centered
403
381
  maskClosable={false}
404
382
  destroyOnHidden
@@ -410,7 +388,7 @@ const Page = ({ template }) => {
410
388
  <label>{keyTitles.name}:</label>
411
389
  <ModalInput
412
390
  value={modalName}
413
- placeholder="请输入名称"
391
+ placeholder={t('template:template.placeholders.inputName')}
414
392
  allowClear
415
393
  autoFocus
416
394
  onChange={(e) => setModalName(e.target.value)}
@@ -423,7 +401,9 @@ const Page = ({ template }) => {
423
401
  )
424
402
  }
425
403
 
426
- Page.getInitialProps = async () => {
404
+ export async function getServerSideProps(context) {
405
+ const { serverSideTranslations } = await import('next-i18next/serverSideTranslations')
406
+
427
407
  let template = null
428
408
 
429
409
  await getTemplateService(0, pageSize).then((res: any) => {
@@ -431,8 +411,14 @@ Page.getInitialProps = async () => {
431
411
  template = data.template
432
412
  })
433
413
 
414
+ const { locale } = context
415
+ const translations = await serverSideTranslations(locale || 'zh-CN', ['common', 'template', 'layout', 'login'])
416
+
434
417
  return {
435
- template,
418
+ props: {
419
+ template,
420
+ ...translations,
421
+ },
436
422
  }
437
423
  }
438
424
 
@@ -0,0 +1,48 @@
1
+ {
2
+ "common": {
3
+ "confirm": "Confirm",
4
+ "cancel": "Cancel",
5
+ "save": "Save",
6
+ "edit": "Edit",
7
+ "delete": "Delete",
8
+ "add": "Add",
9
+ "search": "Search",
10
+ "reset": "Reset",
11
+ "submit": "Submit",
12
+ "back": "Back",
13
+ "next": "Next",
14
+ "previous": "Previous",
15
+ "loading": "Loading...",
16
+ "success": "Success",
17
+ "error": "Error",
18
+ "warning": "Warning",
19
+ "info": "Info",
20
+ "yes": "Yes",
21
+ "no": "No",
22
+ "close": "Close",
23
+ "refresh": "Refresh"
24
+ },
25
+ "navigation": {
26
+ "home": "Home",
27
+ "dashboard": "Dashboard",
28
+ "settings": "Settings",
29
+ "profile": "Profile",
30
+ "logout": "Logout"
31
+ },
32
+ "form": {
33
+ "required": "This field is required",
34
+ "invalidEmail": "Please enter a valid email address",
35
+ "invalidPhone": "Please enter a valid phone number",
36
+ "passwordTooShort": "Password must be at least 8 characters",
37
+ "confirmPassword": "Confirm Password",
38
+ "passwordMismatch": "Passwords do not match"
39
+ },
40
+ "messages": {
41
+ "saveSuccess": "Save successful",
42
+ "deleteSuccess": "Delete successful",
43
+ "updateSuccess": "Update successful",
44
+ "createSuccess": "Create successful",
45
+ "operationFailed": "Operation failed",
46
+ "networkError": "Network error, please try again later"
47
+ }
48
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "page": {
3
+ "title": "NSGM CLI",
4
+ "description": "Full-stack architecture, code template generation, rapid development",
5
+ "sections": {
6
+ "database": {
7
+ "title": "Database Configuration",
8
+ "description": "Database uses Mysql, see mysql.config.js for configuration"
9
+ },
10
+ "project": {
11
+ "title": "Project Configuration",
12
+ "description": "See project.config.js for project configuration"
13
+ },
14
+ "framework": {
15
+ "title": "Framework Configuration",
16
+ "description": "See next.config.js for Next framework configuration"
17
+ }
18
+ },
19
+ "commands": {
20
+ "title": "Commands",
21
+ "categories": {
22
+ "projectManagement": {
23
+ "title": "Project Management",
24
+ "items": {
25
+ "init": "Initialize project",
26
+ "upgrade": "Upgrade project base files"
27
+ }
28
+ },
29
+ "templateOperations": {
30
+ "title": "Template Operations",
31
+ "items": {
32
+ "create": "Create template page",
33
+ "delete": "Delete template page",
34
+ "deletedb": "Delete template page and database table"
35
+ }
36
+ },
37
+ "runBuild": {
38
+ "title": "Run & Build",
39
+ "items": {
40
+ "dev": "Development mode",
41
+ "start": "Production mode",
42
+ "build": "Build",
43
+ "export": "Export static pages"
44
+ }
45
+ }
46
+ }
47
+ },
48
+ "parameters": {
49
+ "title": "Parameters",
50
+ "items": {
51
+ "dictionary": "Used in export/init, default webapp",
52
+ "controller": "Required when using create/delete",
53
+ "action": "Used in create/delete, default manage"
54
+ }
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "layout": {
3
+ "userActions": {
4
+ "logout": "Logout",
5
+ "profile": "Profile",
6
+ "settings": "Account Settings",
7
+ "notifications": "Notifications",
8
+ "user": "User"
9
+ },
10
+ "navigation": {
11
+ "home": "Home",
12
+ "dashboard": "Dashboard",
13
+ "management": "Management",
14
+ "system": "System"
15
+ },
16
+ "menu": {
17
+ "introduction": "Introduction",
18
+ "template": "Template",
19
+ "template1": "Template 1"
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "login": {
3
+ "title": "System Login",
4
+ "username": "Username",
5
+ "password": "Password",
6
+ "loginButton": "Login",
7
+ "errors": {
8
+ "usernameRequired": "Please enter username",
9
+ "passwordRequired": "Please enter password",
10
+ "loginFailed": "Login failed"
11
+ }
12
+ }
13
+ }