Skip to content

Commit 93ff1c5

Browse files
authored
Merge pull request #406 from ZOOT-Plus/dev
Release to Production
2 parents 30c4f5a + 8196dc4 commit 93ff1c5

File tree

125 files changed

+6010
-1053
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+6010
-1053
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.eslintrc.js
22
do not edit these files
3+
generated

.eslintrc.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = {
1515
'plugin:import/recommended',
1616
'plugin:import/typescript',
1717
'plugin:react/jsx-runtime',
18-
"plugin:react-hooks/recommended"
18+
'plugin:react-hooks/recommended',
1919
],
2020
parser: '@typescript-eslint/parser',
2121
parserOptions: {
@@ -25,8 +25,8 @@ module.exports = {
2525
ecmaVersion: 'latest',
2626
sourceType: 'module',
2727

28-
project: './tsconfig.json',
29-
tsconfigRootDir: './',
28+
project: ['./tsconfig.json', './tsconfig.node.json'],
29+
tsconfigRootDir: __dirname,
3030
},
3131
plugins: ['react', '@typescript-eslint', 'prettier', 'import'],
3232
rules: {
@@ -36,7 +36,7 @@ module.exports = {
3636
'react/self-closing-comp': 'error',
3737
'no-unused-vars': 'off',
3838
eqeqeq: 'error',
39-
"react-hooks/exhaustive-deps": "error",
39+
'react-hooks/exhaustive-deps': 'error',
4040
},
4141
settings: {
4242
react: {

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ dist-ssr
2424
*.sw?
2525

2626
build/
27+
public/locales/*.json
28+
src/i18n/generated

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2025-05-11
2+
3+
- 添加 i18n 及英文翻译 [@Constrat](https://github.com/Constrat) [@guansss](https://github.com/guansss)
4+
15
## 2025-05-02
26

37
- 添加用户名重复校验 [@dragove](https://github.com/dragove)

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
# maa-copilot-frontend
1+
# zoot-plus-frontend
22

3-
MAA 作业站前端
3+
ZOOT Plus 前端
44

55
## 文档
66

7-
- ~~后端接口文档~~ (暂无,请参考 [maa-copilot-client](https://github.com/MaaAssistantArknights/maa-copilot-client-ts) 的 TS 类型,或者从后端 [Actions](https://github.com/MaaAssistantArknights/MaaBackendCenter/actions/workflows/openapi.yml) 的 Artifacts 里下载最新的 OpenAPI 文档)
8-
- 作业格式:[MAA 战斗流程协议](https://maa.plus/docs/zh-cn/protocol/copilot-schema.html)
7+
- ~~后端接口文档~~ (暂无,请参考 [zoot-plus-client](https://github.com/ZOOT-Plus/zoot-plus-client-ts) 的 TS 类型,或者从后端 [Actions](https://github.com/ZOOT-Plus/ZootPlusBackend/actions/workflows/openapi.yml) 的 Artifacts 里下载最新的 OpenAPI 文档)
8+
- 作业格式:[战斗流程协议](https://maa.plus/docs/zh-cn/protocol/copilot-schema.html)
99

10-
更新 maa-copilot-client 时,需要在 [Tags](https://github.com/MaaAssistantArknights/maa-copilot-client-ts/tags) 中复制版本号,然后替换掉 `package.json` 中的 `maa-copilot-client` 版本号,再运行 `yarn` 安装依赖
10+
更新 zoot-plus-client 时,需要在 [Tags](https://github.com/ZOOT-Plus/zoot-plus-client-ts/tags) 中复制版本号,然后替换掉 `package.json` 中的 `maa-copilot-client` 版本号,再运行 `yarn` 安装依赖
1111

1212
## 开发流程
1313

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"linkifyjs": "^3.0.5",
4646
"lodash-es": "^4.17.21",
4747
"maa-copilot-client": "https://github.com/MaaAssistantArknights/maa-copilot-client-ts.git#0.1.0-SNAPSHOT.824.f8ad839",
48+
"mitt": "^3.0.1",
4849
"normalize.css": "^8.0.1",
4950
"prettier": "^3.2.5",
5051
"react": "^18.0.0",

scripts/generate-translations.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { isObject } from 'lodash'
2+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
3+
import { join } from 'node:path'
4+
import { fileURLToPath } from 'node:url'
5+
import { inspect } from 'node:util'
6+
import { Plugin } from 'vite'
7+
8+
const translationsFile = fileURLToPath(
9+
new URL('../src/i18n/translations.json', import.meta.url),
10+
)
11+
const outputDir = fileURLToPath(
12+
new URL('../src/i18n/generated', import.meta.url),
13+
)
14+
15+
export function generateTranslations(): Plugin {
16+
splitTranslations()
17+
18+
return {
19+
name: 'generate-translations',
20+
apply: 'serve',
21+
configureServer(server) {
22+
server.watcher.on('change', (filePath) => {
23+
if (filePath === translationsFile) {
24+
try {
25+
splitTranslations()
26+
} catch (e) {
27+
console.error('Failed to generate translations:', e)
28+
}
29+
}
30+
})
31+
},
32+
}
33+
}
34+
35+
function splitTranslations() {
36+
if (!existsSync(outputDir)) {
37+
mkdirSync(outputDir, { recursive: true })
38+
}
39+
40+
const translationsJson = readFileSync(translationsFile, 'utf-8')
41+
const languages = Object.keys(
42+
JSON.parse(translationsJson).essentials.language,
43+
)
44+
const essentials: Record<string, Record<string, unknown>> = {}
45+
46+
for (const language of languages) {
47+
const languageTranslations = JSON.parse(translationsJson, (key, value) => {
48+
if (
49+
isObject(value) &&
50+
Object.keys(value).some((key) => languages.includes(key))
51+
) {
52+
return value[language] || '__NOT_TRANSLATED__'
53+
}
54+
return value
55+
})
56+
57+
essentials[language] = languageTranslations.essentials
58+
delete languageTranslations.essentials
59+
60+
writeTsFile(`${language}.ts`, languageTranslations)
61+
}
62+
63+
writeTsFile(`essentials.ts`, essentials)
64+
65+
console.log(`Translations generated for ${languages.join(', ')}`)
66+
}
67+
68+
function writeTsFile(filename: string, jsonObject: Record<string, unknown>) {
69+
const filePath = join(outputDir, filename)
70+
71+
const literalObject = inspect(jsonObject, {
72+
depth: 100,
73+
maxStringLength: Infinity,
74+
breakLength: Infinity,
75+
sorted: false,
76+
compact: false,
77+
})
78+
79+
const content = `// This file is auto-generated by generate-translations.ts, do not edit it directly.
80+
export default ${literalObject} as const`
81+
82+
if (existsSync(filePath) && readFileSync(filePath, 'utf-8') === content) {
83+
// skip writing to avoid unnecessary hot reloads
84+
return
85+
}
86+
writeFileSync(filePath, content, 'utf-8')
87+
}

src/App.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { authAtom } from 'store/auth'
66
import { TokenManager } from 'utils/token-manager'
77

88
import { GlobalErrorBoundary } from './components/GlobalErrorBoundary'
9+
import { I18NProvider } from './i18n/I18NProvider'
910
import { FCC } from './types'
1011

1112
// jotai 在没有 Provider 时会使用默认的 store
@@ -14,18 +15,18 @@ TokenManager.setAuthSetter((v) => getDefaultStore().set(authAtom, v))
1415

1516
export const App: FCC = ({ children }) => {
1617
return (
17-
<>
18-
<SWRConfig
19-
value={{
20-
focusThrottleInterval: 1000 * 60,
21-
errorRetryInterval: 1000 * 3,
22-
errorRetryCount: 3,
23-
}}
24-
>
25-
<GlobalErrorBoundary>
18+
<SWRConfig
19+
value={{
20+
focusThrottleInterval: 1000 * 60,
21+
errorRetryInterval: 1000 * 3,
22+
errorRetryCount: 3,
23+
}}
24+
>
25+
<GlobalErrorBoundary>
26+
<I18NProvider>
2627
<BrowserRouter>{children}</BrowserRouter>
27-
</GlobalErrorBoundary>
28-
</SWRConfig>
29-
</>
28+
</I18NProvider>
29+
</GlobalErrorBoundary>
30+
</SWRConfig>
3031
)
3132
}

src/apis/announcement.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import useSWR from 'swr'
22

3+
import { i18n } from '../i18n/i18n'
34
import mockFile from './mock/announcements.md?url'
45

56
const isMock = process.env.NODE_ENV === 'development'
@@ -20,7 +21,7 @@ export function useAnnouncement() {
2021
.then((res) => res.text())
2122
.catch((e) => {
2223
if ((e as Error).message === 'Failed to fetch') {
23-
throw new Error('网络错误')
24+
throw new Error(i18n.apis.announcement.network_error)
2425
}
2526

2627
throw e

src/apis/comment.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import useSWRInfinite from 'swr/infinite'
66

77
import { CommentApi } from 'utils/maa-copilot-client'
88

9+
import { i18n } from '../i18n/i18n'
910
import { CommentRating } from '../models/comment'
1011
import { Operation } from '../models/operation'
1112

@@ -35,7 +36,7 @@ export function useComments({
3536
}
3637

3738
if (!isFinite(+operationId)) {
38-
throw new Error('operationId is not a valid number')
39+
throw new Error(i18n.apis.comment.invalid_operation_id)
3940
}
4041

4142
return [
@@ -86,6 +87,7 @@ export async function sendComment(req: {
8687
copilotId: req.operationId,
8788
fromCommentId: req.fromCommentId,
8889
notification: false,
90+
commentStatus: 'ENABLED',
8991
},
9092
})
9193
}

0 commit comments

Comments
 (0)