From a575f998796e3c32728b8f43afe993f6d1fb5215 Mon Sep 17 00:00:00 2001 From: GZTime Date: Wed, 24 Jan 2024 02:49:06 +0800 Subject: [PATCH] refactor: i18n for account pages --- src/GZCTF/ClientApp/.vscode/settings.json | 14 +- src/GZCTF/ClientApp/package.json | 1 + src/GZCTF/ClientApp/pnpm-lock.yaml | 220 +++++++++++------- src/GZCTF/ClientApp/src/App.tsx | 2 +- .../src/components/ActionIconWithConfirm.tsx | 2 +- .../ClientApp/src/components/AppHeader.tsx | 2 +- .../ClientApp/src/components/AppNavbar.tsx | 60 ++--- .../src/components/ChallengeCard.tsx | 2 +- .../src/components/ChallengeDetailModal.tsx | 90 +++---- .../src/components/ChallengePanel.tsx | 2 +- src/GZCTF/ClientApp/src/components/Empty.tsx | 2 +- .../ClientApp/src/components/GameCard.tsx | 2 +- .../src/components/GameJoinModal.tsx | 2 +- .../src/components/GameNoticePanel.tsx | 2 +- .../ClientApp/src/components/HintList.tsx | 2 +- .../src/components/InstanceEntry.tsx | 2 +- .../src/components/MobilePostCard.tsx | 2 +- .../components/MobileScoreboardItemModal.tsx | 2 +- .../src/components/MobileScoreboardTable.tsx | 2 +- .../src/components/PasswordChangeModal.tsx | 2 +- .../ClientApp/src/components/PostCard.tsx | 2 +- .../ClientApp/src/components/RecentGame.tsx | 2 +- .../src/components/RecentGameSlide.tsx | 2 +- .../src/components/ScoreboardItemModal.tsx | 2 +- .../src/components/ScoreboardTable.tsx | 2 +- .../src/components/StrengthPasswordInput.tsx | 48 ++-- .../ClientApp/src/components/TeamCard.tsx | 2 +- .../src/components/TeamCreateModal.tsx | 2 +- .../src/components/TeamEditModal.tsx | 32 +-- .../ClientApp/src/components/TeamRank.tsx | 2 +- .../ClientApp/src/components/TimeLine.tsx | 2 +- .../ClientApp/src/components/TrafficItems.tsx | 2 +- .../src/components/WithGameMonitor.tsx | 2 +- .../ClientApp/src/components/WithGameTab.tsx | 2 +- .../src/components/WithWiderScreen.tsx | 2 +- .../src/components/WriteupSubmitModal.tsx | 2 +- .../admin/AttachmentRemoteEditModal.tsx | 2 +- .../admin/AttachmentUploadModal.tsx | 2 +- .../src/components/admin/BloodBonusModel.tsx | 2 +- .../components/admin/ChallengeCreateModal.tsx | 2 +- .../components/admin/ChallengeEditCard.tsx | 2 +- .../admin/ChallengePreviewModal.tsx | 2 +- .../src/components/admin/FlagCreateModal.tsx | 2 +- .../src/components/admin/FlagEditPanel.tsx | 2 +- .../src/components/admin/GameCreateModal.tsx | 2 +- .../components/admin/GameNoticeEditModal.tsx | 2 +- .../src/components/admin/PDFViewer.tsx | 2 +- .../admin/ParticipationStatusControl.tsx | 2 +- .../src/components/admin/ScoreFunc.tsx | 2 +- .../src/components/admin/TeamEditModal.tsx | 2 +- .../src/components/admin/UserEditModal.tsx | 2 +- .../src/components/admin/WithAdminTab.tsx | 2 +- .../ClientApp/src/locales/zh_CN/account.json | 119 ++++++++++ .../src/locales/zh_CN/challenge.json | 1 + .../ClientApp/src/locales/zh_CN/common.json | 53 +++++ .../ClientApp/src/locales/zh_CN/config.json | 1 + .../ClientApp/src/locales/zh_CN/game.json | 1 + .../ClientApp/src/locales/zh_CN/post.json | 1 + src/GZCTF/ClientApp/src/main.tsx | 4 +- src/GZCTF/ClientApp/src/pages/Index.tsx | 2 +- src/GZCTF/ClientApp/src/pages/Teams.tsx | 2 +- src/GZCTF/ClientApp/src/pages/[...all].tsx | 2 +- .../ClientApp/src/pages/account/Confirm.tsx | 26 +-- .../ClientApp/src/pages/account/Login.tsx | 32 +-- .../ClientApp/src/pages/account/Profile.tsx | 65 +++--- .../ClientApp/src/pages/account/Recovery.tsx | 24 +- .../ClientApp/src/pages/account/Register.tsx | 76 +++--- .../ClientApp/src/pages/account/Reset.tsx | 21 +- .../ClientApp/src/pages/account/Verify.tsx | 24 +- .../ClientApp/src/pages/admin/Configs.tsx | 2 +- .../ClientApp/src/pages/admin/Instances.tsx | 2 +- src/GZCTF/ClientApp/src/pages/admin/Logs.tsx | 2 +- src/GZCTF/ClientApp/src/pages/admin/Teams.tsx | 2 +- src/GZCTF/ClientApp/src/pages/admin/Users.tsx | 2 +- .../ClientApp/src/pages/admin/games/Index.tsx | 2 +- .../src/pages/admin/games/[id]/Info.tsx | 6 +- .../src/pages/admin/games/[id]/Notices.tsx | 2 +- .../src/pages/admin/games/[id]/Review.tsx | 2 +- .../src/pages/admin/games/[id]/Writeups.tsx | 2 +- .../admin/games/[id]/challenges/Index.tsx | 2 +- .../games/[id]/challenges/[chalId]/Flags.tsx | 2 +- .../games/[id]/challenges/[chalId]/Index.tsx | 31 +-- src/GZCTF/ClientApp/src/pages/games/Index.tsx | 2 +- .../ClientApp/src/pages/games/[id]/Index.tsx | 2 +- .../pages/games/[id]/monitor/CheatInfo.tsx | 2 +- .../src/pages/games/[id]/monitor/Events.tsx | 2 +- .../pages/games/[id]/monitor/Submissions.tsx | 2 +- .../src/pages/games/[id]/monitor/Traffic.tsx | 2 +- src/GZCTF/ClientApp/src/pages/posts/Index.tsx | 2 +- .../src/pages/posts/[postId]/Index.tsx | 2 +- .../src/pages/posts/[postId]/edit.tsx | 2 +- .../src/resources/strings.en-US.json | 24 -- .../src/resources/strings.ja-JP.json | 24 -- .../ClientApp/src/resources/strings.json | 24 -- src/GZCTF/ClientApp/src/utils/I18n.tsx | 48 ---- src/GZCTF/ClientApp/src/utils/useUser.tsx | 2 +- src/GZCTF/ClientApp/src/vite-env.d.ts | 6 + src/GZCTF/ClientApp/vite.config.mts | 7 +- 98 files changed, 678 insertions(+), 543 deletions(-) create mode 100644 src/GZCTF/ClientApp/src/locales/zh_CN/account.json create mode 100644 src/GZCTF/ClientApp/src/locales/zh_CN/challenge.json create mode 100644 src/GZCTF/ClientApp/src/locales/zh_CN/common.json create mode 100644 src/GZCTF/ClientApp/src/locales/zh_CN/config.json create mode 100644 src/GZCTF/ClientApp/src/locales/zh_CN/game.json create mode 100644 src/GZCTF/ClientApp/src/locales/zh_CN/post.json delete mode 100644 src/GZCTF/ClientApp/src/resources/strings.en-US.json delete mode 100644 src/GZCTF/ClientApp/src/resources/strings.ja-JP.json delete mode 100644 src/GZCTF/ClientApp/src/resources/strings.json delete mode 100644 src/GZCTF/ClientApp/src/utils/I18n.tsx diff --git a/src/GZCTF/ClientApp/.vscode/settings.json b/src/GZCTF/ClientApp/.vscode/settings.json index e8e74c90f..82558c4d5 100644 --- a/src/GZCTF/ClientApp/.vscode/settings.json +++ b/src/GZCTF/ClientApp/.vscode/settings.json @@ -1,3 +1,15 @@ { - "liveServer.settings.root": "/build" + "liveServer.settings.root": "/build", + "i18n-ally.namespace": true, + "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}", + "i18n-ally.keystyle": "nested", + "i18n-ally.extract.keyPrefix": "{fileNameWithoutExt}", + "i18n-ally.localesPaths": [ + "src/locales" + ], + "i18n-ally.extract.autoDetect": true, + "i18n-ally.extract.keyMaxLength": 20, + "i18n-ally.extract.keygenStyle": "snake_case", + "i18n-ally.sortKeys": true, + "i18n-ally.sourceLanguage": "zh" } diff --git a/src/GZCTF/ClientApp/package.json b/src/GZCTF/ClientApp/package.json index 338c501df..7837faa10 100644 --- a/src/GZCTF/ClientApp/package.json +++ b/src/GZCTF/ClientApp/package.json @@ -52,6 +52,7 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.23.3", + "@kainstar/vite-plugin-i18next-loader": "^1.0.2", "@nabla/vite-plugin-eslint": "^2.0.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/katex": "^0.16.7", diff --git a/src/GZCTF/ClientApp/pnpm-lock.yaml b/src/GZCTF/ClientApp/pnpm-lock.yaml index 7f085576f..1aad369ed 100644 --- a/src/GZCTF/ClientApp/pnpm-lock.yaml +++ b/src/GZCTF/ClientApp/pnpm-lock.yaml @@ -121,6 +121,9 @@ devDependencies: '@babel/eslint-parser': specifier: ^7.23.3 version: 7.23.3(@babel/core@7.23.7)(eslint@8.56.0) + '@kainstar/vite-plugin-i18next-loader': + specifier: ^1.0.2 + version: 1.0.2(vite@5.0.12) '@nabla/vite-plugin-eslint': specifier: ^2.0.2 version: 2.0.2(eslint@8.56.0)(vite@5.0.12) @@ -355,7 +358,7 @@ packages: engines: {node: '>=6.9.0'} /@babel/helpers@7.23.8: - resolution: { integrity: sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== } + resolution: {integrity: sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.15 @@ -400,7 +403,7 @@ packages: dev: true /@babel/runtime@7.23.8: - resolution: { integrity: sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== } + resolution: {integrity: sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 @@ -554,7 +557,7 @@ packages: dev: false /@esbuild/aix-ppc64@0.19.11: - resolution: { integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g== } + resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] @@ -563,7 +566,7 @@ packages: optional: true /@esbuild/android-arm64@0.19.11: - resolution: { integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q== } + resolution: {integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -572,7 +575,7 @@ packages: optional: true /@esbuild/android-arm@0.19.11: - resolution: { integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw== } + resolution: {integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -581,7 +584,7 @@ packages: optional: true /@esbuild/android-x64@0.19.11: - resolution: { integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg== } + resolution: {integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -590,7 +593,7 @@ packages: optional: true /@esbuild/darwin-arm64@0.19.11: - resolution: { integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ== } + resolution: {integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -599,7 +602,7 @@ packages: optional: true /@esbuild/darwin-x64@0.19.11: - resolution: { integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g== } + resolution: {integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -608,7 +611,7 @@ packages: optional: true /@esbuild/freebsd-arm64@0.19.11: - resolution: { integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA== } + resolution: {integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -617,7 +620,7 @@ packages: optional: true /@esbuild/freebsd-x64@0.19.11: - resolution: { integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw== } + resolution: {integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -626,7 +629,7 @@ packages: optional: true /@esbuild/linux-arm64@0.19.11: - resolution: { integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg== } + resolution: {integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -635,7 +638,7 @@ packages: optional: true /@esbuild/linux-arm@0.19.11: - resolution: { integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q== } + resolution: {integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -644,7 +647,7 @@ packages: optional: true /@esbuild/linux-ia32@0.19.11: - resolution: { integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA== } + resolution: {integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -653,7 +656,7 @@ packages: optional: true /@esbuild/linux-loong64@0.19.11: - resolution: { integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg== } + resolution: {integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -662,7 +665,7 @@ packages: optional: true /@esbuild/linux-mips64el@0.19.11: - resolution: { integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg== } + resolution: {integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -671,7 +674,7 @@ packages: optional: true /@esbuild/linux-ppc64@0.19.11: - resolution: { integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA== } + resolution: {integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -680,7 +683,7 @@ packages: optional: true /@esbuild/linux-riscv64@0.19.11: - resolution: { integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ== } + resolution: {integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -689,7 +692,7 @@ packages: optional: true /@esbuild/linux-s390x@0.19.11: - resolution: { integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q== } + resolution: {integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -698,7 +701,7 @@ packages: optional: true /@esbuild/linux-x64@0.19.11: - resolution: { integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA== } + resolution: {integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -707,7 +710,7 @@ packages: optional: true /@esbuild/netbsd-x64@0.19.11: - resolution: { integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ== } + resolution: {integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -716,7 +719,7 @@ packages: optional: true /@esbuild/openbsd-x64@0.19.11: - resolution: { integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw== } + resolution: {integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -725,7 +728,7 @@ packages: optional: true /@esbuild/sunos-x64@0.19.11: - resolution: { integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ== } + resolution: {integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -734,7 +737,7 @@ packages: optional: true /@esbuild/win32-arm64@0.19.11: - resolution: { integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ== } + resolution: {integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -743,7 +746,7 @@ packages: optional: true /@esbuild/win32-ia32@0.19.11: - resolution: { integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg== } + resolution: {integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -752,7 +755,7 @@ packages: optional: true /@esbuild/win32-x64@0.19.11: - resolution: { integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw== } + resolution: {integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -802,13 +805,13 @@ packages: dev: true /@floating-ui/core@1.5.3: - resolution: { integrity: sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q== } + resolution: {integrity: sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==} dependencies: '@floating-ui/utils': 0.2.1 dev: false /@floating-ui/dom@1.5.4: - resolution: { integrity: sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ== } + resolution: {integrity: sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==} dependencies: '@floating-ui/core': 1.5.3 '@floating-ui/utils': 0.2.1 @@ -839,11 +842,11 @@ packages: dev: false /@floating-ui/utils@0.2.1: - resolution: { integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== } + resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} dev: false /@humanwhocodes/config-array@0.11.14: - resolution: { integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== } + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 2.0.2 @@ -859,7 +862,7 @@ packages: dev: true /@humanwhocodes/object-schema@2.0.2: - resolution: { integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== } + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} dev: true /@jridgewell/gen-mapping@0.3.3: @@ -882,11 +885,22 @@ packages: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} /@jridgewell/trace-mapping@0.3.22: - resolution: { integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== } + resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + /@kainstar/vite-plugin-i18next-loader@1.0.2(vite@5.0.12): + resolution: {integrity: sha512-fQgnGe4AFHsYECJHF+gXSA4hCS1l9ZrzVVIxaBByboJM7MKRVoelaItjebuRIt6gVAFzvcfKhwctueBCh3PCsA==} + peerDependencies: + vite: '>=4' + dependencies: + dot-prop: 8.0.2 + globby: 13.2.2 + js-yaml: 4.1.0 + vite: 5.0.12(@types/node@20.11.5) + dev: true + /@mantine/carousel@6.0.21(@mantine/core@6.0.21)(@mantine/hooks@6.0.21)(embla-carousel-react@7.1.0)(react@18.2.0): resolution: {integrity: sha512-cQAQ5RlVhSrYA8aez/euzs5nQKcGcwxVTS/gf46GEZ0gcDJXlymZPbc2OopH/WDczEaMWOF7wz8R9+uG1hYNCg==} peerDependencies: @@ -1046,7 +1060,7 @@ packages: optional: true /@marsidev/react-turnstile@0.4.1(react-dom@18.2.0)(react@18.2.0): - resolution: { integrity: sha512-uZusUW9mPr0csWpls8bApe5iuRK0YK7H1PCKqfM4djW3OA9GB9rU68irjk7xRO8qlHyj0aDTeVu9tTLPExBO4Q== } + resolution: {integrity: sha512-uZusUW9mPr0csWpls8bApe5iuRK0YK7H1PCKqfM4djW3OA9GB9rU68irjk7xRO8qlHyj0aDTeVu9tTLPExBO4Q==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -1231,12 +1245,12 @@ packages: dev: false /@remix-run/router@1.14.2: - resolution: { integrity: sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg== } + resolution: {integrity: sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==} engines: {node: '>=14.0.0'} dev: false /@rollup/rollup-android-arm-eabi@4.9.6: - resolution: { integrity: sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg== } + resolution: {integrity: sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==} cpu: [arm] os: [android] requiresBuild: true @@ -1244,7 +1258,7 @@ packages: optional: true /@rollup/rollup-android-arm64@4.9.6: - resolution: { integrity: sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw== } + resolution: {integrity: sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==} cpu: [arm64] os: [android] requiresBuild: true @@ -1252,7 +1266,7 @@ packages: optional: true /@rollup/rollup-darwin-arm64@4.9.6: - resolution: { integrity: sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw== } + resolution: {integrity: sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==} cpu: [arm64] os: [darwin] requiresBuild: true @@ -1260,7 +1274,7 @@ packages: optional: true /@rollup/rollup-darwin-x64@4.9.6: - resolution: { integrity: sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog== } + resolution: {integrity: sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==} cpu: [x64] os: [darwin] requiresBuild: true @@ -1268,7 +1282,7 @@ packages: optional: true /@rollup/rollup-linux-arm-gnueabihf@4.9.6: - resolution: { integrity: sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ== } + resolution: {integrity: sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==} cpu: [arm] os: [linux] requiresBuild: true @@ -1276,7 +1290,7 @@ packages: optional: true /@rollup/rollup-linux-arm64-gnu@4.9.6: - resolution: { integrity: sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ== } + resolution: {integrity: sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==} cpu: [arm64] os: [linux] requiresBuild: true @@ -1284,7 +1298,7 @@ packages: optional: true /@rollup/rollup-linux-arm64-musl@4.9.6: - resolution: { integrity: sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ== } + resolution: {integrity: sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==} cpu: [arm64] os: [linux] requiresBuild: true @@ -1292,7 +1306,7 @@ packages: optional: true /@rollup/rollup-linux-riscv64-gnu@4.9.6: - resolution: { integrity: sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA== } + resolution: {integrity: sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==} cpu: [riscv64] os: [linux] requiresBuild: true @@ -1300,7 +1314,7 @@ packages: optional: true /@rollup/rollup-linux-x64-gnu@4.9.6: - resolution: { integrity: sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw== } + resolution: {integrity: sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==} cpu: [x64] os: [linux] requiresBuild: true @@ -1308,7 +1322,7 @@ packages: optional: true /@rollup/rollup-linux-x64-musl@4.9.6: - resolution: { integrity: sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ== } + resolution: {integrity: sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==} cpu: [x64] os: [linux] requiresBuild: true @@ -1316,7 +1330,7 @@ packages: optional: true /@rollup/rollup-win32-arm64-msvc@4.9.6: - resolution: { integrity: sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA== } + resolution: {integrity: sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==} cpu: [arm64] os: [win32] requiresBuild: true @@ -1324,7 +1338,7 @@ packages: optional: true /@rollup/rollup-win32-ia32-msvc@4.9.6: - resolution: { integrity: sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ== } + resolution: {integrity: sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==} cpu: [ia32] os: [win32] requiresBuild: true @@ -1332,7 +1346,7 @@ packages: optional: true /@rollup/rollup-win32-x64-msvc@4.9.6: - resolution: { integrity: sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ== } + resolution: {integrity: sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==} cpu: [x64] os: [win32] requiresBuild: true @@ -1375,7 +1389,7 @@ packages: dev: true /@types/babel__generator@7.6.8: - resolution: { integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== } + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} dependencies: '@babel/types': 7.23.6 dev: true @@ -1388,7 +1402,7 @@ packages: dev: true /@types/babel__traverse@7.20.5: - resolution: { integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== } + resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} dependencies: '@babel/types': 7.23.6 dev: true @@ -1400,7 +1414,7 @@ packages: dev: true /@types/eslint@8.56.2: - resolution: { integrity: sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== } + resolution: {integrity: sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==} dependencies: '@types/estree': 1.0.5 '@types/json-schema': 7.0.15 @@ -1423,7 +1437,7 @@ packages: dev: true /@types/node@20.11.5: - resolution: { integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w== } + resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==} dependencies: undici-types: 5.26.5 dev: true @@ -1446,7 +1460,7 @@ packages: dev: true /@types/react@18.2.48: - resolution: { integrity: sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w== } + resolution: {integrity: sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==} dependencies: '@types/prop-types': 15.7.11 '@types/scheduler': 0.16.8 @@ -1464,7 +1478,7 @@ packages: dev: true /@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1)(eslint@8.56.0)(typescript@5.3.3): - resolution: { integrity: sha512-roQScUGFruWod9CEyoV5KlCYrubC/fvG8/1zXuT0WTcxX87GnMMmnksMwSg99lo1xiKrBzw2icsJPMAw1OtKxg== } + resolution: {integrity: sha512-roQScUGFruWod9CEyoV5KlCYrubC/fvG8/1zXuT0WTcxX87GnMMmnksMwSg99lo1xiKrBzw2icsJPMAw1OtKxg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -1493,7 +1507,7 @@ packages: dev: true /@typescript-eslint/parser@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: { integrity: sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ== } + resolution: {integrity: sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1514,7 +1528,7 @@ packages: dev: true /@typescript-eslint/scope-manager@6.19.1: - resolution: { integrity: sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w== } + resolution: {integrity: sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: '@typescript-eslint/types': 6.19.1 @@ -1522,7 +1536,7 @@ packages: dev: true /@typescript-eslint/type-utils@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: { integrity: sha512-0vdyld3ecfxJuddDjACUvlAeYNrHP/pDeQk2pWBR2ESeEzQhg52DF53AbI9QCBkYE23lgkhLCZNkHn2hEXXYIg== } + resolution: {integrity: sha512-0vdyld3ecfxJuddDjACUvlAeYNrHP/pDeQk2pWBR2ESeEzQhg52DF53AbI9QCBkYE23lgkhLCZNkHn2hEXXYIg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1542,12 +1556,12 @@ packages: dev: true /@typescript-eslint/types@6.19.1: - resolution: { integrity: sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg== } + resolution: {integrity: sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==} engines: {node: ^16.0.0 || >=18.0.0} dev: true /@typescript-eslint/typescript-estree@6.19.1(typescript@5.3.3): - resolution: { integrity: sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA== } + resolution: {integrity: sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1569,7 +1583,7 @@ packages: dev: true /@typescript-eslint/utils@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: { integrity: sha512-JvjfEZuP5WoMqwh9SPAPDSHSg9FBHHGhjPugSRxu5jMfjvBpq5/sGTD+9M9aQ5sh6iJ8AY/Kk/oUYVEMAPwi7w== } + resolution: {integrity: sha512-JvjfEZuP5WoMqwh9SPAPDSHSg9FBHHGhjPugSRxu5jMfjvBpq5/sGTD+9M9aQ5sh6iJ8AY/Kk/oUYVEMAPwi7w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1588,7 +1602,7 @@ packages: dev: true /@typescript-eslint/visitor-keys@6.19.1: - resolution: { integrity: sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ== } + resolution: {integrity: sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: '@typescript-eslint/types': 6.19.1 @@ -1637,7 +1651,7 @@ packages: dev: true /acorn@8.11.3: - resolution: { integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== } + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} hasBin: true dev: true @@ -1733,7 +1747,7 @@ packages: dev: true /axios@1.6.5: - resolution: { integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== } + resolution: {integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==} dependencies: follow-redirects: 1.15.5 form-data: 4.0.0 @@ -1808,7 +1822,7 @@ packages: engines: {node: '>=6'} /caniuse-lite@1.0.30001579: - resolution: { integrity: sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA== } + resolution: {integrity: sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==} /canvas@2.11.2: resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} @@ -1878,7 +1892,7 @@ packages: dev: false /clsx@2.1.0: - resolution: { integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== } + resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==} engines: {node: '>=6'} dev: false @@ -2060,8 +2074,8 @@ packages: optional: true /dequal@2.0.3: - resolution: { integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} dev: false /detect-libc@2.0.2: @@ -2100,6 +2114,13 @@ packages: csstype: 3.1.3 dev: false + /dot-prop@8.0.2: + resolution: {integrity: sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ==} + engines: {node: '>=16'} + dependencies: + type-fest: 3.13.1 + dev: true + /echarts-for-react@3.0.2(echarts@5.4.3)(react@18.2.0): resolution: {integrity: sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==} peerDependencies: @@ -2120,7 +2141,7 @@ packages: dev: false /electron-to-chromium@1.4.642: - resolution: { integrity: sha512-M4+u22ZJGpk4RY7tne6W+APkZhnnhmAH48FNl8iEFK2lEgob+U5rUQsIqQhvAwCXYpfd3H20pHK/ENsCvwTbsA== } + resolution: {integrity: sha512-M4+u22ZJGpk4RY7tne6W+APkZhnnhmAH48FNl8iEFK2lEgob+U5rUQsIqQhvAwCXYpfd3H20pHK/ENsCvwTbsA==} /embla-carousel-autoplay@7.1.0(embla-carousel@7.1.0): resolution: {integrity: sha512-nYfgSGn3ek44OzwO0t/Ptuxq4PNPD5l7Y9X7JjLYI/DN1uGjqxz9L73YYqR6YCRDnTYJ88s9fep48dzBnSG4vQ==} @@ -2175,7 +2196,7 @@ packages: dev: true /esbuild@0.19.11: - resolution: { integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA== } + resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==} engines: {node: '>=12'} hasBin: true requiresBuild: true @@ -2400,7 +2421,7 @@ packages: dev: true /fastq@1.16.0: - resolution: { integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== } + resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} dependencies: reusify: 1.0.4 dev: true @@ -2467,7 +2488,7 @@ packages: dev: true /follow-redirects@1.15.5: - resolution: { integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== } + resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -2612,6 +2633,17 @@ packages: slash: 3.0.0 dev: true + /globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.0 + merge2: 1.4.1 + slash: 4.0.0 + dev: true + /globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} dev: true @@ -2709,7 +2741,7 @@ packages: dev: false /i18next@23.7.18: - resolution: { integrity: sha512-b9N2KjRCYQNlUvE1Kc83g8knyUkL5NiZQOp9BsTR/v/LXk6Fzz+doOzTg2/826XK28mCgBkYLNAtixjE58qpCw== } + resolution: {integrity: sha512-b9N2KjRCYQNlUvE1Kc83g8knyUkL5NiZQOp9BsTR/v/LXk6Fzz+doOzTg2/826XK28mCgBkYLNAtixjE58qpCw==} dependencies: '@babel/runtime': 7.23.8 dev: false @@ -2942,7 +2974,7 @@ packages: hasBin: true /jsonc-parser@3.2.1: - resolution: { integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== } + resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} dev: true /katex@0.16.9: @@ -3148,7 +3180,7 @@ packages: optional: true /mlly@1.5.0: - resolution: { integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ== } + resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==} dependencies: acorn: 8.11.3 pathe: 1.1.2 @@ -3407,7 +3439,7 @@ packages: optional: true /pathe@1.1.2: - resolution: { integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== } + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} dev: true /pdfjs-dist@3.11.174: @@ -3438,7 +3470,7 @@ packages: dev: true /postcss@8.4.33: - resolution: { integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== } + resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 @@ -3458,7 +3490,7 @@ packages: dev: true /prettier@3.2.4: - resolution: { integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ== } + resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==} engines: {node: '>=14'} hasBin: true dev: true @@ -3538,7 +3570,7 @@ packages: dev: false /react-i18next@14.0.1(i18next@23.7.18)(react-dom@18.2.0)(react@18.2.0): - resolution: { integrity: sha512-TMV8hFismBmpMdIehoFHin/okfvgjFhp723RYgIqB4XyhDobVMyukyM3Z8wtTRmajyFMZrBl/OaaXF2P6WjUAw== } + resolution: {integrity: sha512-TMV8hFismBmpMdIehoFHin/okfvgjFhp723RYgIqB4XyhDobVMyukyM3Z8wtTRmajyFMZrBl/OaaXF2P6WjUAw==} peerDependencies: i18next: '>= 23.2.3' react: '>= 16.8.0' @@ -3562,7 +3594,7 @@ packages: dev: false /react-pdf@7.7.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): - resolution: { integrity: sha512-704ObLnRDm5lixL4e6NXNLaincBHGNLo+NGdbO3rEXE963NlNzwLxFpmKcbdXHAMQL4rYJQWb1L0w5IL6y8Osw== } + resolution: {integrity: sha512-704ObLnRDm5lixL4e6NXNLaincBHGNLo+NGdbO3rEXE963NlNzwLxFpmKcbdXHAMQL4rYJQWb1L0w5IL6y8Osw==} peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -3629,7 +3661,7 @@ packages: dev: false /react-router-dom@6.21.3(react-dom@18.2.0)(react@18.2.0): - resolution: { integrity: sha512-kNzubk7n4YHSrErzjLK72j0B5i969GsuCGazRl3G6j1zqZBLjuSlYBdVdkDOgzGdPIffUOc9nmgiadTEVoq91g== } + resolution: {integrity: sha512-kNzubk7n4YHSrErzjLK72j0B5i969GsuCGazRl3G6j1zqZBLjuSlYBdVdkDOgzGdPIffUOc9nmgiadTEVoq91g==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' @@ -3642,7 +3674,7 @@ packages: dev: false /react-router@6.21.3(react@18.2.0): - resolution: { integrity: sha512-a0H638ZXULv1OdkmiK6s6itNhoy33ywxmUFT/xtSoVyf9VnC7n7+VT4LjVzdIHSaF5TIh9ylUgxMXksHTgGrKg== } + resolution: {integrity: sha512-a0H638ZXULv1OdkmiK6s6itNhoy33ywxmUFT/xtSoVyf9VnC7n7+VT4LjVzdIHSaF5TIh9ylUgxMXksHTgGrKg==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' @@ -3765,7 +3797,7 @@ packages: glob: 7.2.3 /rollup@4.9.6: - resolution: { integrity: sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg== } + resolution: {integrity: sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: @@ -3827,7 +3859,7 @@ packages: dev: false /set-function-length@1.2.0: - resolution: { integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== } + resolution: {integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==} engines: {node: '>= 0.4'} dependencies: define-data-property: 1.1.1 @@ -3942,6 +3974,11 @@ packages: engines: {node: '>=8'} dev: true + /slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + dev: true + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -4127,8 +4164,8 @@ packages: dev: true /tsconfck@3.0.1(typescript@5.3.3): - resolution: { integrity: sha512-7ppiBlF3UEddCLeI1JRx5m2Ryq+xk4JrZuq4EuYXykipebaq1dV0Fhgr1hb7CkmHt32QSgOZlcqVLEtHBG4/mg== } - engines: { node: ^18 || >=20 } + resolution: {integrity: sha512-7ppiBlF3UEddCLeI1JRx5m2Ryq+xk4JrZuq4EuYXykipebaq1dV0Fhgr1hb7CkmHt32QSgOZlcqVLEtHBG4/mg==} + engines: {node: ^18 || >=20} hasBin: true peerDependencies: typescript: ^5.0.0 @@ -4158,6 +4195,11 @@ packages: engines: {node: '>=10'} dev: true + /type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + dev: true + /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} @@ -4212,7 +4254,7 @@ packages: dev: false /use-callback-ref@1.3.1(@types/react@18.2.48)(react@18.2.0): - resolution: { integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ== } + resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==} engines: {node: '>=10'} peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -4315,7 +4357,7 @@ packages: dev: true /vite-plugin-prismjs@0.0.11(prismjs@1.29.0): - resolution: { integrity: sha512-20NBQxg/zH+3FTrlU6BQTob720xkuXNYtrx7psAQ4E6pMcRDeLEK77QU9kXURU587+f2To7ASH1JVTGbXVV/vQ== } + resolution: {integrity: sha512-20NBQxg/zH+3FTrlU6BQTob720xkuXNYtrx7psAQ4E6pMcRDeLEK77QU9kXURU587+f2To7ASH1JVTGbXVV/vQ==} engines: {node: '>=12.0.0'} dependencies: '@babel/core': 7.23.7 @@ -4340,7 +4382,7 @@ packages: dev: true /vite-tsconfig-paths@4.3.1(typescript@5.3.3)(vite@5.0.12): - resolution: { integrity: sha512-cfgJwcGOsIxXOLU/nELPny2/LUD/lcf1IbfyeKTv2bsupVbTH/xpFtdQlBmIP1GEK2CjjLxYhFfB+QODFAx5aw== } + resolution: {integrity: sha512-cfgJwcGOsIxXOLU/nELPny2/LUD/lcf1IbfyeKTv2bsupVbTH/xpFtdQlBmIP1GEK2CjjLxYhFfB+QODFAx5aw==} peerDependencies: vite: '*' peerDependenciesMeta: @@ -4357,7 +4399,7 @@ packages: dev: true /vite@5.0.12(@types/node@20.11.5): - resolution: { integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w== } + resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -4398,13 +4440,13 @@ packages: dev: false /warning@4.0.3: - resolution: { integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== } + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} dependencies: loose-envify: 1.4.0 dev: false /web-streams-polyfill@3.3.2: - resolution: { integrity: sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== } + resolution: {integrity: sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==} engines: {node: '>= 8'} dev: true diff --git a/src/GZCTF/ClientApp/src/App.tsx b/src/GZCTF/ClientApp/src/App.tsx index 0a135593f..557150a0b 100644 --- a/src/GZCTF/ClientApp/src/App.tsx +++ b/src/GZCTF/ClientApp/src/App.tsx @@ -10,10 +10,10 @@ import { useLocalStorage } from '@mantine/hooks' import { ModalsProvider } from '@mantine/modals' import { Notifications } from '@mantine/notifications' import { FC, Suspense } from 'react' +import { useTranslation } from 'react-i18next' import { useRoutes } from 'react-router-dom' import { SWRConfig } from 'swr' import routes from '~react-pages' -import { useTranslation } from '@Utils/I18n' import { ThemeOverride } from '@Utils/ThemeOverride' import { useBanner, useLocalStorageCache } from '@Utils/useConfig' import { fetcher } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/ActionIconWithConfirm.tsx b/src/GZCTF/ClientApp/src/components/ActionIconWithConfirm.tsx index beb004f02..fa1e075d0 100644 --- a/src/GZCTF/ClientApp/src/components/ActionIconWithConfirm.tsx +++ b/src/GZCTF/ClientApp/src/components/ActionIconWithConfirm.tsx @@ -10,7 +10,7 @@ import { } from '@mantine/core' import { Icon } from '@mdi/react' import { FC, useState } from 'react' -import { useTranslation } from '@Utils/I18n' +import { useTranslation } from 'react-i18next' export interface ActionIconWithConfirmProps { iconPath: string diff --git a/src/GZCTF/ClientApp/src/components/AppHeader.tsx b/src/GZCTF/ClientApp/src/components/AppHeader.tsx index cea055042..c0dc95242 100644 --- a/src/GZCTF/ClientApp/src/components/AppHeader.tsx +++ b/src/GZCTF/ClientApp/src/components/AppHeader.tsx @@ -9,9 +9,9 @@ import { } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Link, useLocation, useNavigate } from 'react-router-dom' import LogoHeader from '@Components/LogoHeader' -import { useTranslation } from '@Utils/I18n' import { useIsMobile } from '@Utils/ThemeOverride' import { useLocalStorageCache } from '@Utils/useConfig' import { useLoginOut, useUser } from '@Utils/useUser' diff --git a/src/GZCTF/ClientApp/src/components/AppNavbar.tsx b/src/GZCTF/ClientApp/src/components/AppNavbar.tsx index 5970c1d99..c8f1321b5 100644 --- a/src/GZCTF/ClientApp/src/components/AppNavbar.tsx +++ b/src/GZCTF/ClientApp/src/components/AppNavbar.tsx @@ -25,9 +25,9 @@ import { } from '@mdi/js' import { Icon } from '@mdi/react' import React, { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Link, useLocation, useNavigate } from 'react-router-dom' import MainIcon from '@Components/icon/MainIcon' -import { useTranslation } from '@Utils/I18n' import { useLocalStorageCache } from '@Utils/useConfig' import { useLoginOut, useUser } from '@Utils/useUser' import { Role } from '@Api' @@ -88,15 +88,6 @@ interface NavbarItem { admin?: boolean } -const items: NavbarItem[] = [ - { icon: mdiHomeVariantOutline, label: t('主页'), link: '/' }, - { icon: mdiNoteTextOutline, label: t('文章'), link: '/posts' }, - { icon: mdiFlagOutline, label: t('赛事'), link: '/games' }, - { icon: mdiAccountGroupOutline, label: t('队伍'), link: '/teams' }, - { icon: mdiInformationOutline, label: t('关于'), link: '/about' }, - { icon: mdiWrenchOutline, label: t('管理'), link: '/admin/games', admin: true }, -] - export interface NavbarLinkProps { icon: string label?: string @@ -122,29 +113,37 @@ const NavbarLink: FC = (props: NavbarLinkProps) => { ) } -const getLabel = (path: string) => - items.find((item) => - item.link === '/' - ? path === '/' - : item.link.startsWith('/admin') - ? path.startsWith('/admin') - : path.startsWith(item.link) - )?.label - const AppNavbar: FC = () => { const location = useLocation() const navigate = useNavigate() const { classes } = useStyles() - - const [active, setActive] = useState(getLabel(location.pathname) ?? '') const { colorScheme, toggleColorScheme } = useMantineColorScheme() const logout = useLoginOut() const { clearLocalCache } = useLocalStorageCache() const { user, error } = useUser() - const { t } = useTranslation() + const items: NavbarItem[] = [ + { icon: mdiHomeVariantOutline, label: t('common.tab.home'), link: '/' }, + { icon: mdiNoteTextOutline, label: t('common.tab.post'), link: '/posts' }, + { icon: mdiFlagOutline, label: t('common.tab.game'), link: '/games' }, + { icon: mdiAccountGroupOutline, label: t('common.tab.team'), link: '/teams' }, + { icon: mdiInformationOutline, label: t('common.tab.about'), link: '/about' }, + { icon: mdiWrenchOutline, label: t('common.tab.manage'), link: '/admin/games', admin: true }, + ] + + const getLabel = (path: string) => + items.find((item) => + item.link === '/' + ? path === '/' + : item.link.startsWith('/admin') + ? path.startsWith('/admin') + : path.startsWith(item.link) + )?.label + + const [active, setActive] = useState(getLabel(location.pathname) ?? '') + useEffect(() => { if (location.pathname === '/') { setActive(items[0].label) @@ -184,7 +183,10 @@ const AppNavbar: FC = () => { {/* Color Mode */} @@ -219,18 +221,22 @@ const AppNavbar: FC = () => { to="/account/profile" icon={} > - 用户信息 + {t('common.tab.account.profile')} }> - 清除缓存 + {t('common.tab.account.clean_cache')} }> - 登出 + {t('common.tab.account.logout')} ) : ( - + = (props) => { diff --git a/src/GZCTF/ClientApp/src/components/ChallengePanel.tsx b/src/GZCTF/ClientApp/src/components/ChallengePanel.tsx index 89e564e22..a587650ca 100644 --- a/src/GZCTF/ClientApp/src/components/ChallengePanel.tsx +++ b/src/GZCTF/ClientApp/src/components/ChallengePanel.tsx @@ -18,12 +18,12 @@ import { mdiFileUploadOutline, mdiFlagOutline, mdiPuzzle } from '@mdi/js' import { Icon } from '@mdi/react' import dayjs from 'dayjs' import React, { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import ChallengeCard from '@Components/ChallengeCard' import ChallengeDetailModal from '@Components/ChallengeDetailModal' import Empty from '@Components/Empty' import WriteupSubmitModal from '@Components/WriteupSubmitModal' -import { useTranslation } from '@Utils/I18n' import { ChallengeTagLabelMap, SubmissionTypeIconMap } from '@Utils/Shared' import { useGame } from '@Utils/useGame' import api, { ChallengeInfo, ChallengeTag, SubmissionType } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/Empty.tsx b/src/GZCTF/ClientApp/src/components/Empty.tsx index 26df7c8e7..ab993b074 100644 --- a/src/GZCTF/ClientApp/src/components/Empty.tsx +++ b/src/GZCTF/ClientApp/src/components/Empty.tsx @@ -2,7 +2,7 @@ import { MantineNumberSize, Stack, Text } from '@mantine/core' import { mdiInbox } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, ReactNode } from 'react' -import { useTranslation } from '@Utils/I18n' +import { useTranslation } from 'react-i18next' interface EmptyProps { bordered?: boolean diff --git a/src/GZCTF/ClientApp/src/components/GameCard.tsx b/src/GZCTF/ClientApp/src/components/GameCard.tsx index 30df0943a..26e93dd98 100644 --- a/src/GZCTF/ClientApp/src/components/GameCard.tsx +++ b/src/GZCTF/ClientApp/src/components/GameCard.tsx @@ -13,8 +13,8 @@ import { import { mdiChevronTripleRight, mdiFlagOutline } from '@mdi/js' import { Icon } from '@mdi/react' import { FC } from 'react' +import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' -import { useTranslation } from '@Utils/I18n' import { getGameStatus } from '@Utils/useGame' import { BasicGameInfoModel } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/GameJoinModal.tsx b/src/GZCTF/ClientApp/src/components/GameJoinModal.tsx index ab19466c9..adbdf0e89 100644 --- a/src/GZCTF/ClientApp/src/components/GameJoinModal.tsx +++ b/src/GZCTF/ClientApp/src/components/GameJoinModal.tsx @@ -3,8 +3,8 @@ import { showNotification } from '@mantine/notifications' import { mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' -import { useTranslation } from '@Utils/I18n' import { useGame } from '@Utils/useGame' import { useTeams } from '@Utils/useUser' import { GameJoinModel } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/GameNoticePanel.tsx b/src/GZCTF/ClientApp/src/components/GameNoticePanel.tsx index aa7214cc7..c3c72ffc4 100644 --- a/src/GZCTF/ClientApp/src/components/GameNoticePanel.tsx +++ b/src/GZCTF/ClientApp/src/components/GameNoticePanel.tsx @@ -5,10 +5,10 @@ import { Icon } from '@mdi/react' import * as signalR from '@microsoft/signalr' import dayjs from 'dayjs' import { FC, useEffect, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import Empty from '@Components/Empty' import { InlineMarkdownRender } from '@Components/MarkdownRender' -import { useTranslation } from '@Utils/I18n' import { NoticTypeIconMap } from '@Utils/Shared' import api, { GameNotice, NoticeType } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/HintList.tsx b/src/GZCTF/ClientApp/src/components/HintList.tsx index a07ad7b64..01578570c 100644 --- a/src/GZCTF/ClientApp/src/components/HintList.tsx +++ b/src/GZCTF/ClientApp/src/components/HintList.tsx @@ -10,7 +10,7 @@ import { import { mdiClose, mdiPlus } from '@mdi/js' import { Icon } from '@mdi/react' import { FC } from 'react' -import { useTranslation } from '@Utils/I18n' +import { useTranslation } from 'react-i18next' interface HintListProps extends TextInputProps { hints: string[] diff --git a/src/GZCTF/ClientApp/src/components/InstanceEntry.tsx b/src/GZCTF/ClientApp/src/components/InstanceEntry.tsx index 55fb39e03..44841939e 100644 --- a/src/GZCTF/ClientApp/src/components/InstanceEntry.tsx +++ b/src/GZCTF/ClientApp/src/components/InstanceEntry.tsx @@ -23,7 +23,7 @@ import { Icon } from '@mdi/react' import dayjs from 'dayjs' import duration from 'dayjs/plugin/duration' import { FC, useEffect, useState } from 'react' -import { useTranslation } from '@Utils/I18n' +import { useTranslation } from 'react-i18next' import { getProxyUrl } from '@Utils/Shared' import { useTooltipStyles } from '@Utils/ThemeOverride' import { ClientFlagContext } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/MobilePostCard.tsx b/src/GZCTF/ClientApp/src/components/MobilePostCard.tsx index 636c11039..efecee056 100644 --- a/src/GZCTF/ClientApp/src/components/MobilePostCard.tsx +++ b/src/GZCTF/ClientApp/src/components/MobilePostCard.tsx @@ -3,11 +3,11 @@ import { mdiPencilOutline, mdiPinOffOutline, mdiPinOutline } from '@mdi/js' import { Icon } from '@mdi/react' import dayjs from 'dayjs' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import MarkdownRender from '@Components/MarkdownRender' import { PostCardProps } from '@Components/PostCard' import { RequireRole } from '@Components/WithRole' -import { useTranslation } from '@Utils/I18n' import { useUserRole } from '@Utils/useUser' import { Role } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/MobileScoreboardItemModal.tsx b/src/GZCTF/ClientApp/src/components/MobileScoreboardItemModal.tsx index 565ea9053..f0ddc1f02 100644 --- a/src/GZCTF/ClientApp/src/components/MobileScoreboardItemModal.tsx +++ b/src/GZCTF/ClientApp/src/components/MobileScoreboardItemModal.tsx @@ -16,8 +16,8 @@ import { } from '@mantine/core' import dayjs from 'dayjs' import { FC } from 'react' +import { useTranslation } from 'react-i18next' import TeamRadarMap from '@Components/TeamRadarMap' -import { useTranslation } from '@Utils/I18n' import { BonusLabel } from '@Utils/Shared' import { useTableStyles } from '@Utils/ThemeOverride' import { ChallengeInfo, ScoreboardItem, SubmissionType } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/MobileScoreboardTable.tsx b/src/GZCTF/ClientApp/src/components/MobileScoreboardTable.tsx index 062196800..616f74c46 100644 --- a/src/GZCTF/ClientApp/src/components/MobileScoreboardTable.tsx +++ b/src/GZCTF/ClientApp/src/components/MobileScoreboardTable.tsx @@ -1,9 +1,9 @@ import { Avatar, Box, Group, Input, Pagination, Paper, Select, Stack, Table } from '@mantine/core' import React, { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import MobileScoreboardItemModal from '@Components/MobileScoreboardItemModal' import { ScoreboardProps, useScoreboardStyles } from '@Components/ScoreboardTable' -import { useTranslation } from '@Utils/I18n' import { BloodBonus } from '@Utils/Shared' import { useGameScoreboard } from '@Utils/useGame' import { ScoreboardItem, SubmissionType } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/PasswordChangeModal.tsx b/src/GZCTF/ClientApp/src/components/PasswordChangeModal.tsx index df348e5a9..90da1c19f 100644 --- a/src/GZCTF/ClientApp/src/components/PasswordChangeModal.tsx +++ b/src/GZCTF/ClientApp/src/components/PasswordChangeModal.tsx @@ -4,10 +4,10 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck, mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import { FC } from 'react' +import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import StrengthPasswordInput from '@Components/StrengthPasswordInput' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import api from '@Api' const PasswordChangeModal: FC = (props) => { diff --git a/src/GZCTF/ClientApp/src/components/PostCard.tsx b/src/GZCTF/ClientApp/src/components/PostCard.tsx index 899d08b33..c2671ba01 100644 --- a/src/GZCTF/ClientApp/src/components/PostCard.tsx +++ b/src/GZCTF/ClientApp/src/components/PostCard.tsx @@ -14,10 +14,10 @@ import { mdiPencilOutline, mdiPinOffOutline, mdiPinOutline } from '@mdi/js' import { Icon } from '@mdi/react' import dayjs from 'dayjs' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Link, useNavigate } from 'react-router-dom' import MarkdownRender from '@Components/MarkdownRender' import { RequireRole } from '@Components/WithRole' -import { useTranslation } from '@Utils/I18n' import { useUserRole } from '@Utils/useUser' import { PostInfoModel, Role } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/RecentGame.tsx b/src/GZCTF/ClientApp/src/components/RecentGame.tsx index ac14f36e0..daebf00aa 100644 --- a/src/GZCTF/ClientApp/src/components/RecentGame.tsx +++ b/src/GZCTF/ClientApp/src/components/RecentGame.tsx @@ -13,9 +13,9 @@ import { mdiFlagOutline } from '@mdi/js' import { Icon } from '@mdi/react' import dayjs from 'dayjs' import { FC } from 'react' +import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' import { GameColorMap, GameStatus } from '@Components/GameCard' -import { useTranslation } from '@Utils/I18n' import { getGameStatus } from '@Utils/useGame' import { BasicGameInfoModel } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/RecentGameSlide.tsx b/src/GZCTF/ClientApp/src/components/RecentGameSlide.tsx index 6955c2f34..7421dc9b9 100644 --- a/src/GZCTF/ClientApp/src/components/RecentGameSlide.tsx +++ b/src/GZCTF/ClientApp/src/components/RecentGameSlide.tsx @@ -1,10 +1,10 @@ import { Badge, createStyles, Group, Paper, Stack, Title } from '@mantine/core' import dayjs from 'dayjs' import { FC } from 'react' +import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' import { GameColorMap, GameStatus } from '@Components/GameCard' import { RecentGameProps } from '@Components/RecentGame' -import { useTranslation } from '@Utils/I18n' import { getGameStatus } from '@Utils/useGame' const useStyles = createStyles((theme) => ({ diff --git a/src/GZCTF/ClientApp/src/components/ScoreboardItemModal.tsx b/src/GZCTF/ClientApp/src/components/ScoreboardItemModal.tsx index 81b1bbcf9..32342a2cd 100644 --- a/src/GZCTF/ClientApp/src/components/ScoreboardItemModal.tsx +++ b/src/GZCTF/ClientApp/src/components/ScoreboardItemModal.tsx @@ -15,8 +15,8 @@ import { } from '@mantine/core' import dayjs from 'dayjs' import { FC } from 'react' +import { useTranslation } from 'react-i18next' import TeamRadarMap from '@Components/TeamRadarMap' -import { useTranslation } from '@Utils/I18n' import { BloodsTypes, BonusLabel } from '@Utils/Shared' import { useTableStyles } from '@Utils/ThemeOverride' import { ChallengeInfo, ScoreboardItem, SubmissionType } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/ScoreboardTable.tsx b/src/GZCTF/ClientApp/src/components/ScoreboardTable.tsx index 95c844db5..a3d4057e7 100644 --- a/src/GZCTF/ClientApp/src/components/ScoreboardTable.tsx +++ b/src/GZCTF/ClientApp/src/components/ScoreboardTable.tsx @@ -16,9 +16,9 @@ import { import { Icon } from '@mdi/react' import dayjs from 'dayjs' import React, { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import ScoreboardItemModal from '@Components/ScoreboardItemModal' -import { useTranslation } from '@Utils/I18n' import { BloodBonus, BloodsTypes, ChallengeTagLabelMap, SubmissionTypeIconMap } from '@Utils/Shared' import { useTooltipStyles } from '@Utils/ThemeOverride' import { useGameScoreboard } from '@Utils/useGame' diff --git a/src/GZCTF/ClientApp/src/components/StrengthPasswordInput.tsx b/src/GZCTF/ClientApp/src/components/StrengthPasswordInput.tsx index 54c192804..b2a7a27e5 100644 --- a/src/GZCTF/ClientApp/src/components/StrengthPasswordInput.tsx +++ b/src/GZCTF/ClientApp/src/components/StrengthPasswordInput.tsx @@ -3,7 +3,7 @@ import { useDisclosure } from '@mantine/hooks' import { mdiCheck, mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import React, { FC } from 'react' -import { useTranslation } from '@Utils/I18n' +import { useTranslation } from 'react-i18next' import { useIsMobile } from '@Utils/ThemeOverride' const PasswordRequirement: FC<{ meets: boolean; label: string }> = ({ meets, label }) => { @@ -17,25 +17,6 @@ const PasswordRequirement: FC<{ meets: boolean; label: string }> = ({ meets, lab ) } -const requirements = [ - { re: /[0-9]/, label: t('包含数字') }, - { re: /[a-z]/, label: t('包含小写字母') }, - { re: /[A-Z]/, label: t('包含大写字母') }, - { re: /[`$&+,:;=?@#|'<>.^*()%!-]/, label: t('包含特殊字符') }, -] - -const getStrength = (password: string) => { - let multiplier = password.length > 5 ? 0 : 1 - - requirements.forEach((requirement) => { - if (!requirement.re.test(password)) { - multiplier += 1 - } - }) - - return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 0) -} - interface StrengthPasswordInputProps { value: string disabled?: boolean @@ -51,8 +32,31 @@ const StrengthPasswordInput: FC = (props) => { const { t } = useTranslation() + const requirements = [ + { re: /[0-9]/, label: t('account.password.include_number') }, + { re: /[a-z]/, label: t('account.password.include_lowercase') }, + { re: /[A-Z]/, label: t('account.password.include_uppercase') }, + { re: /[`$&+,:;=?@#|'<>.^*()%!-]/, label: t('account.password.include_symbol') }, + ] + + const getStrength = (password: string) => { + let multiplier = password.length > 5 ? 0 : 1 + + requirements.forEach((requirement) => { + if (!requirement.re.test(password)) { + multiplier += 1 + } + }) + + return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 0) + } + const checks = [ - = 6} />, + = 6} + />, ...requirements.map((requirement, index) => ( = (props) => { = (props) => { setTeamInfo(null) mutateTeams( teams?.filter((x) => x.id !== teamInfo.id), - {revalidate: false} + { revalidate: false } ) props.onClose() }) @@ -461,8 +461,8 @@ const TeamEditModal: FC = (props) => { onReject={() => { showNotification({ color: 'red', - title: '文件获取失败', - message: '请检查文件格式和大小', + title: t('common.error.file_invalid.title'), + message: t('common.error.file_invalid.message'), icon: , }) }} @@ -479,10 +479,10 @@ const TeamEditModal: FC = (props) => { ) : ( - 拖放图片或点击此处以选择头像 + {t('account.placeholder.drop_zone.content')} - 请选择小于 3MB 的图片 + {t('account.placeholder.drop_zone.hint')} )} diff --git a/src/GZCTF/ClientApp/src/components/TeamRank.tsx b/src/GZCTF/ClientApp/src/components/TeamRank.tsx index bbd2cb68c..3932109e8 100644 --- a/src/GZCTF/ClientApp/src/components/TeamRank.tsx +++ b/src/GZCTF/ClientApp/src/components/TeamRank.tsx @@ -17,8 +17,8 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck, mdiExclamationThick, mdiKey } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useEffect } from 'react' +import { useTranslation } from 'react-i18next' import { useNavigate, useParams } from 'react-router-dom' -import { useTranslation } from '@Utils/I18n' import { useIsMobile } from '@Utils/ThemeOverride' import api from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/TimeLine.tsx b/src/GZCTF/ClientApp/src/components/TimeLine.tsx index 4da7d00ed..17cd9038b 100644 --- a/src/GZCTF/ClientApp/src/components/TimeLine.tsx +++ b/src/GZCTF/ClientApp/src/components/TimeLine.tsx @@ -2,8 +2,8 @@ import { useMantineTheme } from '@mantine/core' import dayjs from 'dayjs' import ReactEcharts from 'echarts-for-react' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' -import { useTranslation } from '@Utils/I18n' import { getGameStatus, useGame, useGameScoreboard } from '@Utils/useGame' interface TimeLineProps { diff --git a/src/GZCTF/ClientApp/src/components/TrafficItems.tsx b/src/GZCTF/ClientApp/src/components/TrafficItems.tsx index 754af4536..1eda12a97 100644 --- a/src/GZCTF/ClientApp/src/components/TrafficItems.tsx +++ b/src/GZCTF/ClientApp/src/components/TrafficItems.tsx @@ -2,8 +2,8 @@ import { Avatar, Badge, Group, rem, Stack, Text, useMantineTheme } from '@mantin import { mdiFileDownloadOutline, mdiMenuRight } from '@mdi/js' import { Icon } from '@mdi/react' import dayjs from 'dayjs' +import { useTranslation } from 'react-i18next' import { SelectableItem, SelectableItemComponent } from '@Components/ScrollSelect' -import { useTranslation } from '@Utils/I18n' import { ChallengeTagLabelMap, HunamizeSize } from '@Utils/Shared' import { ChallengeTag, diff --git a/src/GZCTF/ClientApp/src/components/WithGameMonitor.tsx b/src/GZCTF/ClientApp/src/components/WithGameMonitor.tsx index 256c2bdfa..8fc6245d5 100644 --- a/src/GZCTF/ClientApp/src/components/WithGameMonitor.tsx +++ b/src/GZCTF/ClientApp/src/components/WithGameMonitor.tsx @@ -8,11 +8,11 @@ import { } from '@mdi/js' import { Icon } from '@mdi/react' import React, { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useLocation, useNavigate, useParams } from 'react-router-dom' import WithGameTab from '@Components/WithGameTab' import WithNavBar from '@Components/WithNavbar' import WithRole from '@Components/WithRole' -import { useTranslation } from '@Utils/I18n' import { Role } from '@Api' const pages = [ diff --git a/src/GZCTF/ClientApp/src/components/WithGameTab.tsx b/src/GZCTF/ClientApp/src/components/WithGameTab.tsx index 7c9171e1a..c9370fed8 100644 --- a/src/GZCTF/ClientApp/src/components/WithGameTab.tsx +++ b/src/GZCTF/ClientApp/src/components/WithGameTab.tsx @@ -5,11 +5,11 @@ import { Icon } from '@mdi/react' import dayjs from 'dayjs' import duration from 'dayjs/plugin/duration' import React, { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useLocation, useNavigate, useParams } from 'react-router-dom' import CustomProgress from '@Components/CustomProgress' import IconTabs from '@Components/IconTabs' import { RequireRole } from '@Components/WithRole' -import { useTranslation } from '@Utils/I18n' import { getGameStatus, useGame } from '@Utils/useGame' import { usePageTitle } from '@Utils/usePageTitle' import { useUserRole } from '@Utils/useUser' diff --git a/src/GZCTF/ClientApp/src/components/WithWiderScreen.tsx b/src/GZCTF/ClientApp/src/components/WithWiderScreen.tsx index c75c8596d..0b66acca3 100644 --- a/src/GZCTF/ClientApp/src/components/WithWiderScreen.tsx +++ b/src/GZCTF/ClientApp/src/components/WithWiderScreen.tsx @@ -1,8 +1,8 @@ import { Stack, Text, Title } from '@mantine/core' import { useViewportSize } from '@mantine/hooks' import { FC } from 'react' +import { useTranslation } from 'react-i18next' import IconWiderScreenRequired from '@Components/icon/WiderScreenRequiredIcon' -import { useTranslation } from '@Utils/I18n' interface WithWiderScreenProps extends React.PropsWithChildren { minWidth?: number diff --git a/src/GZCTF/ClientApp/src/components/WriteupSubmitModal.tsx b/src/GZCTF/ClientApp/src/components/WriteupSubmitModal.tsx index b743363b4..afcfeaa32 100644 --- a/src/GZCTF/ClientApp/src/components/WriteupSubmitModal.tsx +++ b/src/GZCTF/ClientApp/src/components/WriteupSubmitModal.tsx @@ -18,9 +18,9 @@ import { mdiCheck, mdiExclamationThick, mdiFileDocumentOutline, mdiFileHidden } import { Icon } from '@mdi/react' import dayjs from 'dayjs' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import MarkdownRender from '@Components/MarkdownRender' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import { HunamizeSize } from '@Utils/Shared' import { useUploadStyles } from '@Utils/ThemeOverride' import { OnceSWRConfig } from '@Utils/useConfig' diff --git a/src/GZCTF/ClientApp/src/components/admin/AttachmentRemoteEditModal.tsx b/src/GZCTF/ClientApp/src/components/admin/AttachmentRemoteEditModal.tsx index e75655254..37077e9e2 100644 --- a/src/GZCTF/ClientApp/src/components/admin/AttachmentRemoteEditModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/AttachmentRemoteEditModal.tsx @@ -3,9 +3,9 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import { useEditChallenge } from '@Utils/useEdit' import api, { FileType, FlagCreateModel } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/admin/AttachmentUploadModal.tsx b/src/GZCTF/ClientApp/src/components/admin/AttachmentUploadModal.tsx index 8213f35c6..3e87c0b31 100644 --- a/src/GZCTF/ClientApp/src/components/admin/AttachmentUploadModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/AttachmentUploadModal.tsx @@ -18,9 +18,9 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck, mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import { useUploadStyles } from '@Utils/ThemeOverride' import { useEditChallenge } from '@Utils/useEdit' import api, { FileType } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/admin/BloodBonusModel.tsx b/src/GZCTF/ClientApp/src/components/admin/BloodBonusModel.tsx index f94f43c96..bf29f498d 100644 --- a/src/GZCTF/ClientApp/src/components/admin/BloodBonusModel.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/BloodBonusModel.tsx @@ -1,7 +1,7 @@ import { Button, Group, Modal, ModalProps, NumberInput, Stack, Text } from '@mantine/core' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' -import { useTranslation } from '@Utils/I18n' import { BloodBonus } from '@Utils/Shared' import { OnceSWRConfig } from '@Utils/useConfig' import api, { SubmissionType } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/admin/ChallengeCreateModal.tsx b/src/GZCTF/ClientApp/src/components/admin/ChallengeCreateModal.tsx index 7fbb89e97..eba0615ec 100644 --- a/src/GZCTF/ClientApp/src/components/admin/ChallengeCreateModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/ChallengeCreateModal.tsx @@ -4,9 +4,9 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useNavigate, useParams } from 'react-router-dom' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import { ChallengeTagItem, ChallengeTagLabelMap, diff --git a/src/GZCTF/ClientApp/src/components/admin/ChallengeEditCard.tsx b/src/GZCTF/ClientApp/src/components/admin/ChallengeEditCard.tsx index cf8810177..8c8e7fb55 100644 --- a/src/GZCTF/ClientApp/src/components/admin/ChallengeEditCard.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/ChallengeEditCard.tsx @@ -12,8 +12,8 @@ import { import { mdiDatabaseEditOutline, mdiPuzzleEditOutline } from '@mdi/js' import { Icon } from '@mdi/react' import { Dispatch, FC, SetStateAction, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useNavigate, useParams } from 'react-router-dom' -import { useTranslation } from '@Utils/I18n' import { ChallengeTagLabelMap } from '@Utils/Shared' import { ChallengeInfoModel, ChallengeTag } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/admin/ChallengePreviewModal.tsx b/src/GZCTF/ClientApp/src/components/admin/ChallengePreviewModal.tsx index d48dded61..509f64711 100644 --- a/src/GZCTF/ClientApp/src/components/admin/ChallengePreviewModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/ChallengePreviewModal.tsx @@ -19,10 +19,10 @@ import { mdiCheck, mdiDownload, mdiLightbulbOnOutline } from '@mdi/js' import { Icon } from '@mdi/react' import dayjs from 'dayjs' import React, { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { FlagPlaceholders } from '@Components/ChallengeDetailModal' import InstanceEntry from '@Components/InstanceEntry' import MarkdownRender, { InlineMarkdownRender } from '@Components/MarkdownRender' -import { useTranslation } from '@Utils/I18n' import { ChallengeTagItemProps } from '@Utils/Shared' import { useTooltipStyles } from '@Utils/ThemeOverride' import { useTypographyStyles } from '@Utils/useTypographyStyles' diff --git a/src/GZCTF/ClientApp/src/components/admin/FlagCreateModal.tsx b/src/GZCTF/ClientApp/src/components/admin/FlagCreateModal.tsx index c195df56f..2c420dab5 100644 --- a/src/GZCTF/ClientApp/src/components/admin/FlagCreateModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/FlagCreateModal.tsx @@ -4,9 +4,9 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import { useEditChallenge } from '@Utils/useEdit' import api from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/admin/FlagEditPanel.tsx b/src/GZCTF/ClientApp/src/components/admin/FlagEditPanel.tsx index 48c74dafd..b4d586871 100644 --- a/src/GZCTF/ClientApp/src/components/admin/FlagEditPanel.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/FlagEditPanel.tsx @@ -13,7 +13,7 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck, mdiDeleteOutline } from '@mdi/js' import { Icon } from '@mdi/react' import { FC } from 'react' -import { useTranslation } from '@Utils/I18n' +import { useTranslation } from 'react-i18next' import { Attachment, FlagInfoModel } from '@Api' interface FlagCardProps { diff --git a/src/GZCTF/ClientApp/src/components/admin/GameCreateModal.tsx b/src/GZCTF/ClientApp/src/components/admin/GameCreateModal.tsx index fd52ef60b..9112cadfe 100644 --- a/src/GZCTF/ClientApp/src/components/admin/GameCreateModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/GameCreateModal.tsx @@ -6,9 +6,9 @@ import { mdiCheck, mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import dayjs from 'dayjs' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import api, { GameInfoModel } from '@Api' interface GameCreateModalProps extends ModalProps { diff --git a/src/GZCTF/ClientApp/src/components/admin/GameNoticeEditModal.tsx b/src/GZCTF/ClientApp/src/components/admin/GameNoticeEditModal.tsx index 8d8abe816..c78012854 100644 --- a/src/GZCTF/ClientApp/src/components/admin/GameNoticeEditModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/GameNoticeEditModal.tsx @@ -3,9 +3,9 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck, mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import api, { GameNotice } from '@Api' interface GameNoticeEditModalProps extends ModalProps { diff --git a/src/GZCTF/ClientApp/src/components/admin/PDFViewer.tsx b/src/GZCTF/ClientApp/src/components/admin/PDFViewer.tsx index a0629894b..6b25f174f 100644 --- a/src/GZCTF/ClientApp/src/components/admin/PDFViewer.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/PDFViewer.tsx @@ -1,10 +1,10 @@ import { createStyles, Paper, ScrollArea, Stack } from '@mantine/core' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Document, Page, pdfjs } from 'react-pdf' import 'react-pdf/dist/esm/Page/AnnotationLayer.css' import 'react-pdf/dist/esm/Page/TextLayer.css' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/build/pdf.worker.js', diff --git a/src/GZCTF/ClientApp/src/components/admin/ParticipationStatusControl.tsx b/src/GZCTF/ClientApp/src/components/admin/ParticipationStatusControl.tsx index 996b14d85..c4ec839cb 100644 --- a/src/GZCTF/ClientApp/src/components/admin/ParticipationStatusControl.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/ParticipationStatusControl.tsx @@ -1,7 +1,7 @@ import { Group, GroupProps, MantineNumberSize, useMantineTheme } from '@mantine/core' import { FC } from 'react' +import { useTranslation } from 'react-i18next' import { ActionIconWithConfirm } from '@Components/ActionIconWithConfirm' -import { useTranslation } from '@Utils/I18n' import { ParticipationStatusMap } from '@Utils/Shared' import { ParticipationStatus } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/admin/ScoreFunc.tsx b/src/GZCTF/ClientApp/src/components/admin/ScoreFunc.tsx index 31467ad3e..0510bf5b1 100644 --- a/src/GZCTF/ClientApp/src/components/admin/ScoreFunc.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/ScoreFunc.tsx @@ -1,7 +1,7 @@ import { useMantineTheme } from '@mantine/core' import ReactEcharts from 'echarts-for-react' import { FC } from 'react' -import { useTranslation } from '@Utils/I18n' +import { useTranslation } from 'react-i18next' interface ScoreFuncProps { originalScore: number diff --git a/src/GZCTF/ClientApp/src/components/admin/TeamEditModal.tsx b/src/GZCTF/ClientApp/src/components/admin/TeamEditModal.tsx index 0f4c317bb..dc67225a7 100644 --- a/src/GZCTF/ClientApp/src/components/admin/TeamEditModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/TeamEditModal.tsx @@ -17,8 +17,8 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck, mdiLockOutline, mdiStar } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import api, { AdminTeamModel, TeamInfoModel } from '@Api' interface TeamEditModalProps extends ModalProps { diff --git a/src/GZCTF/ClientApp/src/components/admin/UserEditModal.tsx b/src/GZCTF/ClientApp/src/components/admin/UserEditModal.tsx index 6b8b22af5..a957097f7 100644 --- a/src/GZCTF/ClientApp/src/components/admin/UserEditModal.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/UserEditModal.tsx @@ -20,8 +20,8 @@ import { mdiCheck } from '@mdi/js' import { Icon } from '@mdi/react' import dayjs from 'dayjs' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import { useUser } from '@Utils/useUser' import api, { AdminUserInfoModel, Role, UserInfoModel } from '@Api' diff --git a/src/GZCTF/ClientApp/src/components/admin/WithAdminTab.tsx b/src/GZCTF/ClientApp/src/components/admin/WithAdminTab.tsx index 710b3eae8..6ec70b687 100644 --- a/src/GZCTF/ClientApp/src/components/admin/WithAdminTab.tsx +++ b/src/GZCTF/ClientApp/src/components/admin/WithAdminTab.tsx @@ -9,9 +9,9 @@ import { } from '@mdi/js' import { Icon } from '@mdi/react' import React, { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useLocation, useNavigate } from 'react-router-dom' import IconTabs from '@Components/IconTabs' -import { useTranslation } from '@Utils/I18n' import { usePageTitle } from '@Utils/usePageTitle' const pages = [ diff --git a/src/GZCTF/ClientApp/src/locales/zh_CN/account.json b/src/GZCTF/ClientApp/src/locales/zh_CN/account.json new file mode 100644 index 000000000..644e41109 --- /dev/null +++ b/src/GZCTF/ClientApp/src/locales/zh_CN/account.json @@ -0,0 +1,119 @@ +{ + "label": { + "bio": "描述", + "email": "邮箱", + "phone": "手机号", + "password": "密码", + "username": "用户名", + "email_new": "新邮箱", + "real_name": "真实姓名", + "student_id": "学工号", + "password_old": "原密码", + "password_retype": "确认密码", + "username_or_email": "用户名或邮箱" + }, + "title": { + "login": "登录", + "reset": "重置密码", + "verify": "账户验证", + "confirm": "邮箱验证", + "profile": "个人资料", + "register": "注册", + "recovery": "找回帐号" + }, + "anchor": { + "login": "准备好登录?", + "recovery": "忘记密码?", + "register": "还没有账户?" + }, + "button": { + "login": "登录", + "reset": "重置密码", + "recovery": "发送重置邮件", + "register": "注册", + "save_avatar": "保存头像", + "save_profile": "保存个人信息", + "update_email": "更改邮箱", + "confirm_email": "确认邮箱", + "change_password": "更改密码", + "verify_account": "验证账户" + }, + "content": { + "welcome": "{{decodeEmail}}, 你好👋", + "link_check": "请检查链接是否正确后再次访问", + "link_invalid": "Ouch! 你的链接好像有问题", + "confirm": { + "message": "请点击下方按钮确认更改邮箱。" + }, + "verify": { + "message": "请点击下方按钮验证账户。" + }, + "profile": { + "update_email_note": "更改邮箱后,您将不能通过原邮箱登录。
一封邮件将发送至新邮箱,点击其中链接完成验证。" + } + }, + "password": { + "empty": "密码不能为空", + "not_match": "重复密码有误", + "min_length": "至少 6 个字符", + "include_number": "包含数字", + "include_symbol": "包含特殊字符", + "include_lowercase": "包含小写字母", + "include_uppercase": "包含大写字母" + }, + "placeholder": { + "bio": "这个人很懒,什么都没有写", + "drop_zone": { + "hint": "请选择小于 3MB 的图片", + "content": "拖放图片或点击此处以选择头像" + } + }, + "notification": { + "login": { + "banned": "用户已被禁用", + "invalid": "用户名或密码错误", + "success": { + "title": "登录成功", + "message": "跳转回登录前页面" + } + }, + "logout": { + "logged_out": "登出成功" + }, + "captcha": { + "not_valid": "请等待验证码……", + "request_sent": { + "title": "请求已发送……", + "message": "等待服务器验证" + } + }, + "confirm": { + "failed": "邮箱验证失败", + "success": "邮箱已验证" + }, + "verify": { + "failed": "账户验证失败", + "success": "账户已验证,请登录" + }, + "reset": { + "success": { + "title": "密码已重置", + "message": "请重新登录" + } + }, + "profile": { + "avatar_uploaded": "头像已更新", + "profile_updated": "个人信息已更新", + "avatar_uploading": "正在上传头像", + "password_updated": "密码已修改,请重新登录", + "avatar_upload_failed": "头像更新失败" + }, + "register": { + "logged_in": "注册成功", + "request_sent": { + "title": "注册请求已发送", + "message": "请等待管理员审核激活~" + } + } + } +} diff --git a/src/GZCTF/ClientApp/src/locales/zh_CN/challenge.json b/src/GZCTF/ClientApp/src/locales/zh_CN/challenge.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/src/GZCTF/ClientApp/src/locales/zh_CN/challenge.json @@ -0,0 +1 @@ +{} diff --git a/src/GZCTF/ClientApp/src/locales/zh_CN/common.json b/src/GZCTF/ClientApp/src/locales/zh_CN/common.json new file mode 100644 index 000000000..0100710f2 --- /dev/null +++ b/src/GZCTF/ClientApp/src/locales/zh_CN/common.json @@ -0,0 +1,53 @@ +{ + "tab": { + "game": "赛事", + "home": "主页", + "post": "文章", + "team": "战队", + "about": "关于", + "theme": { + "dark": "深色", + "light": "浅色", + "switch_to": "切换到{{theme}}主题" + }, + "manage": "管理", + "account": { + "login": "登录", + "logout": "登出", + "profile": "用户信息", + "clean_cache": "清除缓存" + }, + "language": { + "en": "English", + "zh": "中文", + "switch_to": "切换到{{language}}" + } + }, + "email": { + "sent": { + "title": "邮件已发送", + "message": "请检查你的邮箱及垃圾邮件~" + } + }, + "error": { + "try_later": "请稍后再试", + "check_input": "请检查输入", + "encountered": "遇到了问题", + "param_error": "参数错误,请检查", + "file_invalid": { + "title": "文件加载失败", + "message": "请检查文件格式和大小" + }, + "param_invalid": "参数无效", + "param_missing": "缺少参数", + "param_unknown": "未知参数", + "invalid_username_or_password": "用户名或密码错误" + }, + "modal": { + "cancel": "取消", + "delete": "删除", + "confirm": "确认", + "confirm_delete": "确认删除", + "confirm_update": "确认修改" + } +} diff --git a/src/GZCTF/ClientApp/src/locales/zh_CN/config.json b/src/GZCTF/ClientApp/src/locales/zh_CN/config.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/src/GZCTF/ClientApp/src/locales/zh_CN/config.json @@ -0,0 +1 @@ +{} diff --git a/src/GZCTF/ClientApp/src/locales/zh_CN/game.json b/src/GZCTF/ClientApp/src/locales/zh_CN/game.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/src/GZCTF/ClientApp/src/locales/zh_CN/game.json @@ -0,0 +1 @@ +{} diff --git a/src/GZCTF/ClientApp/src/locales/zh_CN/post.json b/src/GZCTF/ClientApp/src/locales/zh_CN/post.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/src/GZCTF/ClientApp/src/locales/zh_CN/post.json @@ -0,0 +1 @@ +{} diff --git a/src/GZCTF/ClientApp/src/main.tsx b/src/GZCTF/ClientApp/src/main.tsx index 55a058119..a71363276 100644 --- a/src/GZCTF/ClientApp/src/main.tsx +++ b/src/GZCTF/ClientApp/src/main.tsx @@ -5,14 +5,14 @@ import { StrictMode } from 'react' import ReactDOM from 'react-dom/client' import { initReactI18next } from 'react-i18next' import { BrowserRouter as Router } from 'react-router-dom' -import { resources } from './utils/I18n' +import resources from 'virtual:i18next-loader' i18n .use(LanguageDetector) .use(initReactI18next) .init({ resources, - fallbackLng: 'zh-CN', + fallbackLng: 'zh_CN', interpolation: { escapeValue: false, }, diff --git a/src/GZCTF/ClientApp/src/pages/Index.tsx b/src/GZCTF/ClientApp/src/pages/Index.tsx index b13671e3c..eb2a93210 100644 --- a/src/GZCTF/ClientApp/src/pages/Index.tsx +++ b/src/GZCTF/ClientApp/src/pages/Index.tsx @@ -2,6 +2,7 @@ import { createStyles, Group, Stack, Title } from '@mantine/core' import { mdiFlagCheckered } from '@mdi/js' import { Icon } from '@mdi/react' import { FC } from 'react' +import { Trans, useTranslation } from 'react-i18next' import MobilePostCard from '@Components/MobilePostCard' import PostCard from '@Components/PostCard' import RecentGame from '@Components/RecentGame' @@ -9,7 +10,6 @@ import RecentGameCarousel from '@Components/RecentGameCarousel' import StickyHeader from '@Components/StickyHeader' import WithNavBar from '@Components/WithNavbar' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { Trans, useTranslation } from '@Utils/I18n' import { useIsMobile } from '@Utils/ThemeOverride' import { usePageTitle } from '@Utils/usePageTitle' import api, { PostInfoModel } from '@Api' diff --git a/src/GZCTF/ClientApp/src/pages/Teams.tsx b/src/GZCTF/ClientApp/src/pages/Teams.tsx index 2f8a1b34b..d408f1fb7 100644 --- a/src/GZCTF/ClientApp/src/pages/Teams.tsx +++ b/src/GZCTF/ClientApp/src/pages/Teams.tsx @@ -15,6 +15,7 @@ import { showNotification } from '@mantine/notifications' import { mdiAccountMultiplePlus, mdiCheck, mdiClose, mdiHumanGreetingVariant } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import LogoHeader from '@Components/LogoHeader' import TeamCard from '@Components/TeamCard' import TeamCreateModal from '@Components/TeamCreateModal' @@ -22,7 +23,6 @@ import TeamEditModal from '@Components/TeamEditModal' import WithNavBar from '@Components/WithNavbar' import WithRole from '@Components/WithRole' import { showErrorNotification } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import { useIsMobile } from '@Utils/ThemeOverride' import { usePageTitle } from '@Utils/usePageTitle' import { useTeams, useUser } from '@Utils/useUser' diff --git a/src/GZCTF/ClientApp/src/pages/[...all].tsx b/src/GZCTF/ClientApp/src/pages/[...all].tsx index 172e48451..7b13a5777 100644 --- a/src/GZCTF/ClientApp/src/pages/[...all].tsx +++ b/src/GZCTF/ClientApp/src/pages/[...all].tsx @@ -1,9 +1,9 @@ import { Stack, Text, Title } from '@mantine/core' import { FC, useEffect } from 'react' +import { useTranslation } from 'react-i18next' import { useLocation, useNavigate } from 'react-router-dom' import WithNavBar from '@Components/WithNavbar' import Icon404 from '@Components/icon/404Icon' -import { useTranslation } from '@Utils/I18n' import { usePageTitle } from '@Utils/usePageTitle' const Error404: FC = () => { diff --git a/src/GZCTF/ClientApp/src/pages/account/Confirm.tsx b/src/GZCTF/ClientApp/src/pages/account/Confirm.tsx index cb14d220b..59b9469a9 100644 --- a/src/GZCTF/ClientApp/src/pages/account/Confirm.tsx +++ b/src/GZCTF/ClientApp/src/pages/account/Confirm.tsx @@ -3,9 +3,9 @@ import { showNotification } from '@mantine/notifications' import { mdiCheck, mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useLocation, useNavigate } from 'react-router-dom' import AccountView from '@Components/AccountView' -import { Trans, useTranslation } from '@Utils/I18n' import { usePageTitle } from '@Utils/usePageTitle' import api from '@Api' @@ -19,7 +19,7 @@ const Confirm: FC = () => { const { t } = useTranslation() const decodeEmail = window.atob(email ?? '') - usePageTitle(t('Page_ConfirmEmail')) + usePageTitle(t('account.title.confirm')) const verify = async (event: React.FormEvent) => { event.preventDefault() @@ -27,8 +27,8 @@ const Confirm: FC = () => { if (!token || !email) { showNotification({ color: 'red', - title: t('Email_ConfirmFailed'), - message: t('Param_Missing'), + title: t('account.notification.confirm.failed'), + message: t('common.error.param_missing'), icon: , }) return @@ -40,7 +40,7 @@ const Confirm: FC = () => { .then(() => { showNotification({ color: 'teal', - title: t('Email_Confirm'), + title: t('account.notification.confirm.success'), message: window.atob(email), icon: , }) @@ -49,8 +49,8 @@ const Confirm: FC = () => { .catch(() => { showNotification({ color: 'red', - title: t('Email_ConfirmFailed'), - message: t('Param_Error'), + title: t('account.notification.confirm.failed'), + message: t('common.error.param_error'), icon: , }) }) @@ -64,24 +64,22 @@ const Confirm: FC = () => { {email && token ? ( <> - - {{ decodeEmail }}, 你好👋 - + {t('account.content.welcome', { decodeEmail })} - + {t('account.content.confirm.message')} ) : ( <> - + {t('account.content.link_invalid')} - + {t('account.content.link_check')} )} diff --git a/src/GZCTF/ClientApp/src/pages/account/Login.tsx b/src/GZCTF/ClientApp/src/pages/account/Login.tsx index aa337bd7a..acb394910 100644 --- a/src/GZCTF/ClientApp/src/pages/account/Login.tsx +++ b/src/GZCTF/ClientApp/src/pages/account/Login.tsx @@ -4,10 +4,10 @@ import { showNotification, updateNotification } from '@mantine/notifications' import { mdiCheck, mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Link, useNavigate, useSearchParams } from 'react-router-dom' import AccountView from '@Components/AccountView' import Captcha, { useCaptchaRef } from '@Components/Captcha' -import { useTranslation } from '@Utils/I18n' import { usePageTitle } from '@Utils/usePageTitle' import { useUser } from '@Utils/useUser' import api from '@Api' @@ -26,7 +26,7 @@ const Login: FC = () => { const { t } = useTranslation() - usePageTitle('登录') + usePageTitle(t('account.title.login')) useEffect(() => { if (needRedirect && user) { @@ -43,8 +43,8 @@ const Login: FC = () => { if (uname.length === 0 || pwd.length < 6) { showNotification({ color: 'red', - title: '请检查输入', - message: '无效的用户名或密码', + title: t('account.notification.login.invalid'), + message: t('common.error.check_input'), icon: , }) setDisabled(false) @@ -56,8 +56,8 @@ const Login: FC = () => { if (!valid) { showNotification({ color: 'orange', - title: '请等待验证码……', - message: '请稍后重试', + title: t('account.notification.captcha.not_valid'), + message: t('common.error.try_later'), loading: true, }) return @@ -68,8 +68,8 @@ const Login: FC = () => { showNotification({ color: 'orange', id: 'login-status', - title: '请求已发送……', - message: '等待服务器验证', + title: t('account.notification.captcha.request_sent.title'), + message: t('account.notification.captcha.request_sent.message'), loading: true, autoClose: false, }) @@ -84,8 +84,8 @@ const Login: FC = () => { updateNotification({ id: 'login-status', color: 'teal', - title: '登录成功', - message: '跳转回登录前页面', + title: t('account.notification.login.success.title'), + message: t('account.notification.login.success.message'), icon: , }) setNeedRedirect(true) @@ -94,7 +94,7 @@ const Login: FC = () => { updateNotification({ id: 'login-status', color: 'red', - title: '遇到了问题', + title: t('common.error.encountered'), message: `${err.response.data.title}`, icon: , }) @@ -107,7 +107,7 @@ const Login: FC = () => { { /> { component={Link} to="/account/recovery" > - 忘记密码? + {t('account.anchor.recovery')} diff --git a/src/GZCTF/ClientApp/src/pages/account/Profile.tsx b/src/GZCTF/ClientApp/src/pages/account/Profile.tsx index 218ceb183..eb4184fe8 100644 --- a/src/GZCTF/ClientApp/src/pages/account/Profile.tsx +++ b/src/GZCTF/ClientApp/src/pages/account/Profile.tsx @@ -21,10 +21,10 @@ import { notifications, showNotification, updateNotification } from '@mantine/no import { mdiCheck, mdiClose } from '@mdi/js' import { Icon } from '@mdi/react' import { FC, useEffect, useState } from 'react' +import { Trans, useTranslation } from 'react-i18next' import PasswordChangeModal from '@Components/PasswordChangeModal' import WithNavBar from '@Components/WithNavbar' import { showErrorNotification, tryGetErrorMsg } from '@Utils/ApiErrorHandler' -import { useTranslation } from '@Utils/I18n' import { ACCEPT_IMAGE_MIME_TYPE, useIsMobile } from '@Utils/ThemeOverride' import { usePageTitle } from '@Utils/usePageTitle' import { useUser } from '@Utils/useUser' @@ -54,7 +54,7 @@ const Profile: FC = () => { const { t } = useTranslation() - usePageTitle('个人信息') + usePageTitle(t('account.title.profile')) useEffect(() => { setProfile({ @@ -74,7 +74,7 @@ const Profile: FC = () => { showNotification({ id: 'upload-avatar', color: 'orange', - message: '正在上传头像', + message: t('account.notification.profile.avatar_uploading'), loading: true, autoClose: false, }) @@ -87,7 +87,7 @@ const Profile: FC = () => { updateNotification({ id: 'upload-avatar', color: 'teal', - message: '头像已更新', + message: t('account.notification.profile.avatar_uploaded'), icon: , autoClose: true, }) @@ -99,7 +99,7 @@ const Profile: FC = () => { updateNotification({ id: 'upload-avatar', color: 'red', - title: '头像更新失败', + title: t('account.notification.profile.avatar_upload_failed'), message: tryGetErrorMsg(err, t), icon: , autoClose: true, @@ -117,8 +117,7 @@ const Profile: FC = () => { .then(() => { showNotification({ color: 'teal', - title: '更改成功', - message: '个人信息已更新', + message: t('account.notification.profile.profile_updated'), icon: , }) mutate({ ...user }) @@ -137,8 +136,8 @@ const Profile: FC = () => { if (res.data.data) { showNotification({ color: 'teal', - title: '验证邮件已发送', - message: '请检查你的邮箱及垃圾邮件~', + title: t('common.email.sent.title'), + message: t('common.email.sent.message'), icon: , }) } else { @@ -151,12 +150,12 @@ const Profile: FC = () => { const context = ( <> - 个人信息 + {t('account.title.profile')} { { readOnly /> { onChange={(event) => setProfile({ ...profile, phone: event.target.value })} /> { onChange={(event) => setProfile({ ...profile, stdNumber: event.target.value })} /> { />