From 85df8eb09dfc317a48acdf4b2a8c43714435e5f2 Mon Sep 17 00:00:00 2001
From: elvis liao <1219585136@qq.com>
Date: Thu, 26 Sep 2024 17:53:44 +0800
Subject: [PATCH 1/8] feat: Add i18n
---
ui/package-lock.json | 141 +++++++++++++++++++++++++++++
ui/package.json | 6 +-
ui/src/components/LocaleToggle.tsx | 33 +++++++
ui/src/i18n/index.ts | 33 +++++++
ui/src/i18n/locales/en.json | 11 +++
ui/src/i18n/locales/zh.json | 118 ++++++++++++++++++++++++
6 files changed, 341 insertions(+), 1 deletion(-)
create mode 100644 ui/src/components/LocaleToggle.tsx
create mode 100644 ui/src/i18n/index.ts
create mode 100644 ui/src/i18n/locales/en.json
create mode 100644 ui/src/i18n/locales/zh.json
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 12c292c..18c5eb8 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -27,6 +27,9 @@
"@radix-ui/react-tooltip": "^1.1.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
+ "i18next": "^23.15.1",
+ "i18next-browser-languagedetector": "^8.0.0",
+ "i18next-http-backend": "^2.6.1",
"jszip": "^3.10.1",
"lucide-react": "^0.417.0",
"moment": "^2.30.1",
@@ -34,6 +37,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.1",
+ "react-i18next": "^15.0.2",
"react-router-dom": "^6.25.1",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7",
@@ -382,6 +386,17 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.25.6",
+ "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.25.6.tgz",
+ "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/template": {
"version": "7.24.7",
"resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.24.7.tgz",
@@ -2959,6 +2974,14 @@
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
+ "node_modules/cross-fetch": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/cross-fetch/-/cross-fetch-4.0.0.tgz",
+ "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
+ "dependencies": {
+ "node-fetch": "^2.6.12"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3697,6 +3720,52 @@
"node": ">= 0.4"
}
},
+ "node_modules/html-parse-stringify": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
+ "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
+ "dependencies": {
+ "void-elements": "3.1.0"
+ }
+ },
+ "node_modules/i18next": {
+ "version": "23.15.1",
+ "resolved": "https://registry.npmmirror.com/i18next/-/i18next-23.15.1.tgz",
+ "integrity": "sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://locize.com"
+ },
+ {
+ "type": "individual",
+ "url": "https://locize.com/i18next.html"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+ }
+ ],
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/i18next-browser-languagedetector": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz",
+ "integrity": "sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/i18next-http-backend": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmmirror.com/i18next-http-backend/-/i18next-http-backend-2.6.1.tgz",
+ "integrity": "sha512-rCilMAnlEQNeKOZY1+x8wLM5IpYOj10guGvEpeC59tNjj6MMreLIjIW8D1RclhD3ifLwn6d/Y9HEM1RUE6DSog==",
+ "dependencies": {
+ "cross-fetch": "4.0.0"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz",
@@ -4112,6 +4181,25 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
"node_modules/node-releases": {
"version": "2.0.18",
"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz",
@@ -4553,6 +4641,27 @@
"react": "^16.8.0 || ^17 || ^18 || ^19"
}
},
+ "node_modules/react-i18next": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.0.2.tgz",
+ "integrity": "sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.25.0",
+ "html-parse-stringify": "^3.0.1"
+ },
+ "peerDependencies": {
+ "i18next": ">= 23.2.3",
+ "react": ">= 16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -4692,6 +4801,11 @@
"node": ">=8.10.0"
}
},
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ },
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
@@ -5140,6 +5254,11 @@
"node": ">=8.0"
}
},
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
"node_modules/ts-api-utils": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
@@ -5357,6 +5476,28 @@
}
}
},
+ "node_modules/void-elements": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz",
+ "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
diff --git a/ui/package.json b/ui/package.json
index 1c954e6..97e3db9 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -40,7 +40,11 @@
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7",
"vaul": "^0.9.1",
- "zod": "^3.23.8"
+ "zod": "^3.23.8",
+ "i18next": "^23.15.1",
+ "i18next-browser-languagedetector": "^8.0.0",
+ "i18next-http-backend": "^2.6.1",
+ "react-i18next": "^15.0.2"
},
"devDependencies": {
"@types/node": "^22.0.0",
diff --git a/ui/src/components/LocaleToggle.tsx b/ui/src/components/LocaleToggle.tsx
new file mode 100644
index 0000000..01ec02e
--- /dev/null
+++ b/ui/src/components/LocaleToggle.tsx
@@ -0,0 +1,33 @@
+import { Languages } from "lucide-react";
+
+import { useTranslation } from "react-i18next";
+
+import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+
+export default function LocaleToggle() {
+ const { i18n } = useTranslation()
+
+ return (
+
+
+
+
+
+ {Object.keys(i18n.store.data).map(key => (
+ i18n.changeLanguage(key)}>
+ {i18n.store.data[key].name as string}
+
+ ))}
+
+
+ );
+}
diff --git a/ui/src/i18n/index.ts b/ui/src/i18n/index.ts
new file mode 100644
index 0000000..eaed9c9
--- /dev/null
+++ b/ui/src/i18n/index.ts
@@ -0,0 +1,33 @@
+import i18n from 'i18next';
+import { initReactI18next } from 'react-i18next';
+import LanguageDetector from 'i18next-browser-languagedetector';
+
+import zh from './locales/zh.json'
+import en from './locales/en.json'
+
+
+i18n
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ resources: {
+ zh: {
+ name: '简体中文',
+ translation: zh
+ },
+ en: {
+ name: 'English',
+ translation: en
+ }
+ },
+ fallbackLng: 'zh',
+ debug: true,
+ interpolation: {
+ escapeValue: false,
+ },
+ backend: {
+ loadPath: '/locales/{{lng}}.json',
+ }
+ });
+
+export default i18n;
diff --git a/ui/src/i18n/locales/en.json b/ui/src/i18n/locales/en.json
new file mode 100644
index 0000000..26e64d2
--- /dev/null
+++ b/ui/src/i18n/locales/en.json
@@ -0,0 +1,11 @@
+{
+ "username": "username",
+ "password": "password",
+ "login.submit": "login.submit",
+ "login.username.no.empty.message": "login.username.no.empty.message",
+ "login.password.length.message": "login.password.length.message",
+ "menu.dashboard": "menu.dashboard",
+ "menu.domain.management": "menu.domain.management",
+ "menu.auth.management": "menu.auth.management",
+ "menu.deploy.log": "menu.deploy.log"
+}
\ No newline at end of file
diff --git a/ui/src/i18n/locales/zh.json b/ui/src/i18n/locales/zh.json
new file mode 100644
index 0000000..e26e661
--- /dev/null
+++ b/ui/src/i18n/locales/zh.json
@@ -0,0 +1,118 @@
+{
+ "username": "用户名",
+ "password": "密码",
+ "email": "邮箱",
+ "logout": "退出登录",
+ "setting": "设置",
+ "account": "账户",
+ "template": "模版",
+ "save": "保存",
+ "no.data": "暂无数据",
+ "status": "状态",
+ "operation": "操作",
+ "enable": "启用",
+ "disable": "禁用",
+ "deploy": "部署",
+ "download": "下载",
+ "delete": "删除",
+ "cancel": "取消",
+ "confirm": "确认",
+ "edit": "编辑",
+ "operation.succeed": "操作成功",
+ "save.succeed": "保存成功",
+ "save.failed": "保存失败",
+ "login.submit": "登录",
+ "login.username.no.empty.message": "请输入正确的邮箱地址",
+ "login.password.length.message": "密码至少10个字符",
+ "menu": {
+ "auth.management": "授权管理"
+ },
+ "theme": {
+ "light": "浅色",
+ "dark": "暗黑",
+ "system": "系统"
+ },
+ "dashboard": "控制面板",
+ "dashboard.all": "所有",
+ "dashboard.near.expired": "即将过期",
+ "dashboard.enabled": "启用中",
+ "dashboard.not.enabled": "未启用",
+ "dashboard.unit": "个",
+ "deployment.log": {
+ "name": "部署历史",
+ "empty": "你暂未创建任何部署,请先添加域名进行部署吧!",
+ "status": "状态",
+ "stage": "阶段",
+ "last.execution.time": "最近执行时间",
+ "detail.button.text": "日志",
+ "detail": "部署详情"
+ },
+ "pagination": {
+ "next": "下一页",
+ "prev": "上一页"
+ },
+ "domain": "域名",
+ "domain.add": "新增域名",
+ "domain.delete": "删除域名",
+ "domain.management": {
+ "name": "域名列表",
+ "start.deploy.succeed.tips": "已发起部署,请稍后查看部署日志。",
+ "execution.failed": "执行失败",
+ "execution.failed.tips": "执行失败,请在 <1>部署历史1> 查看详情。",
+ "empty": "请添加域名开始部署证书吧。",
+ "expiry.date": "有效期限",
+ "expiry.date1": "有效期 {{date}} 天",
+ "expiry.date2": "{{date}} 到期",
+ "last.execution.time": "最近执行时间",
+ "last.execution.status": "最近执行状态",
+ "last.execution.stage": "最近执行阶段",
+ "enable": "是否启用",
+ "start.deploying": "立即部署",
+ "forced.deployment": "强行部署",
+ "delete.confirm": "确定要删除域名吗?",
+ "edit": {
+ "title": "编辑域名",
+ "domain.verify.tips": "域名",
+ "dns.verify.tips": "请选择DNS服务商授权配置",
+ "target.type.verify.tips": "请选择部署服务类型"
+ }
+ },
+ "setting.notify.menu": "消息推送",
+ "setting.submit": "确认修改",
+ "setting.account.email": {
+ "valid.message": "请输入正确的邮箱地址",
+ "placeholder": "请输入邮箱",
+ "change.succeed": "修改账户邮箱成功",
+ "change.failed": "修改账户邮箱失败"
+ },
+ "setting.account.log.back.in": "请重新登录",
+ "setting.password": {
+ "length.message": "密码至少10个字符",
+ "not.match": "两次密码不一致",
+ "change.succeed": "修改密码成功",
+ "change.failed": "修改密码失败",
+ "current.password": "当前密码",
+ "new.password": "新密码",
+ "confirm.password": "确认密码"
+ },
+ "setting.notify": {
+ "template": {
+ "save.succeed": "通知模板保存成功",
+ "variables.tips": {
+ "title": "可选的变量, COUNT:即将过期张数",
+ "content": "可选的变量, COUNT:即将过期张数,DOMAINS:域名列表"
+ }
+ },
+ "config": {
+ "enable": "是否启用",
+ "save.succeed": "配置保存成功",
+ "save.failed": "配置保存失败",
+ "save.failed.url.not.valid": "Url格式不正确"
+ }
+ },
+ "deploy.progress": {
+ "check": "检查",
+ "apply": "获取",
+ "deploy": "部署"
+ }
+}
\ No newline at end of file
From 0abb030889ce36cbea7e68aaf884fab5fda6f506 Mon Sep 17 00:00:00 2001
From: elvis liao <1219585136@qq.com>
Date: Thu, 26 Sep 2024 17:57:30 +0800
Subject: [PATCH 2/8] wip: i18n chinese
---
ui/src/components/ThemeToggle.tsx | 9 ++-
.../components/certimate/DeployProgress.tsx | 76 ++++++++++++++-----
ui/src/components/notify/DingTalk.tsx | 14 ++--
ui/src/components/notify/NotifyTemplate.tsx | 12 +--
ui/src/components/notify/Telegram.tsx | 14 ++--
ui/src/components/notify/Webhook.tsx | 18 +++--
ui/src/components/ui/form.tsx | 5 +-
ui/src/components/ui/pagination.tsx | 53 +++++++------
ui/src/pages/DashboardLayout.tsx | 26 ++++---
ui/src/pages/SettingLayout.tsx | 10 ++-
ui/src/pages/dashboard/Dashboard.tsx | 60 ++++++++++-----
ui/src/pages/domains/Edit.tsx | 8 +-
ui/src/pages/domains/Home.tsx | 73 +++++++++---------
ui/src/pages/history/History.tsx | 26 ++++---
ui/src/pages/login/Login.tsx | 20 +++--
ui/src/pages/setting/Account.tsx | 18 +++--
ui/src/pages/setting/Notify.tsx | 5 +-
ui/src/pages/setting/Password.tsx | 26 ++++---
18 files changed, 289 insertions(+), 184 deletions(-)
diff --git a/ui/src/components/ThemeToggle.tsx b/ui/src/components/ThemeToggle.tsx
index 4ec3d9c..a772948 100644
--- a/ui/src/components/ThemeToggle.tsx
+++ b/ui/src/components/ThemeToggle.tsx
@@ -1,4 +1,5 @@
import { Moon, Sun } from "lucide-react";
+import { useTranslation } from 'react-i18next'
import { Button } from "@/components/ui/button";
import {
@@ -11,6 +12,8 @@ import { useTheme } from "./ThemeProvider";
export function ThemeToggle() {
const { setTheme } = useTheme();
+ const { t } = useTranslation();
+
return (
@@ -23,13 +26,13 @@ export function ThemeToggle() {
setTheme("light")}>
- 浅色
+ {t('theme.light')}
setTheme("dark")}>
- 暗黑
+ {t('theme.dark')}
setTheme("system")}>
- 系统
+ {t('theme.system')}
diff --git a/ui/src/components/certimate/DeployProgress.tsx b/ui/src/components/certimate/DeployProgress.tsx
index 863da79..b96dc2b 100644
--- a/ui/src/components/certimate/DeployProgress.tsx
+++ b/ui/src/components/certimate/DeployProgress.tsx
@@ -1,3 +1,5 @@
+import { useTranslation } from "react-i18next";
+
import { Separator } from "../ui/separator";
type DeployProgressProps = {
@@ -6,26 +8,40 @@ type DeployProgressProps = {
};
const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
+ const { t } = useTranslation();
+
let rs = <> >;
if (phase === "check") {
if (phaseSuccess) {
rs = (
-
检查
+
+ {t('deploy.progress.check')}
+
-
获取
+
+ {t('deploy.progress.apply')}
+
-
部署
+
+ {t('deploy.progress.deploy')}
+
);
} else {
rs = (
-
检查
+
+ {t('deploy.progress.check')}
+
-
获取
+
+ {t('deploy.progress.apply')}
+
-
部署
+
+ {t('deploy.progress.deploy')}
+
);
}
@@ -35,21 +51,33 @@ const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
if (phaseSuccess) {
rs = (
-
检查
+
+ {t('deploy.progress.check')}
+
-
获取
+
+ {t('deploy.progress.apply')}
+
-
部署
+
+ {t('deploy.progress.deploy')}
+
);
} else {
rs = (
-
检查
+
+ {t('deploy.progress.check')}
+
-
获取
+
+ {t('deploy.progress.apply')}
+
-
部署
+
+ {t('deploy.progress.deploy')}
+
);
}
@@ -59,21 +87,33 @@ const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
if (phaseSuccess) {
rs = (
-
检查
+
+ {t('deploy.progress.check')}
+
-
获取
+
+ {t('deploy.progress.apply')}
+
-
部署
+
+ {t('deploy.progress.deploy')}
+
);
} else {
rs = (
-
检查
+
+ {t('deploy.progress.check')}
+
-
获取
+
+ {t('deploy.progress.apply')}
+
-
部署
+
+ {t('deploy.progress.deploy')}
+
);
}
diff --git a/ui/src/components/notify/DingTalk.tsx b/ui/src/components/notify/DingTalk.tsx
index 2e2a58f..37b3f32 100644
--- a/ui/src/components/notify/DingTalk.tsx
+++ b/ui/src/components/notify/DingTalk.tsx
@@ -8,6 +8,7 @@ import { useEffect, useState } from "react";
import { update } from "@/repository/settings";
import { getErrMessage } from "@/lib/error";
import { useToast } from "../ui/use-toast";
+import { useTranslation } from 'react-i18next'
type DingTalkSetting = {
id: string;
@@ -17,6 +18,7 @@ type DingTalkSetting = {
const DingTalk = () => {
const { config, setChannels } = useNotify();
+ const { t } = useTranslation();
const [dingtalk, setDingtalk] = useState({
id: config.id ?? "",
@@ -70,15 +72,15 @@ const DingTalk = () => {
setChannels(resp);
toast({
- title: "保存成功",
- description: "配置保存成功",
+ title: t('save.succeed'),
+ description: t('setting.notify.config.save.succeed'),
});
} catch (e) {
const msg = getErrMessage(e);
toast({
- title: "保存失败",
- description: "配置保存失败:" + msg,
+ title: t('save.failed'),
+ description: `${t('setting.notify.config.save.failed')}: ${msg}`,
variant: "destructive",
});
}
@@ -127,7 +129,7 @@ const DingTalk = () => {
});
}}
/>
-
+
@@ -136,7 +138,7 @@ const DingTalk = () => {
handleSaveClick();
}}
>
- 保存
+ {t('save')}
diff --git a/ui/src/components/notify/NotifyTemplate.tsx b/ui/src/components/notify/NotifyTemplate.tsx
index 2a761b0..2412ee0 100644
--- a/ui/src/components/notify/NotifyTemplate.tsx
+++ b/ui/src/components/notify/NotifyTemplate.tsx
@@ -9,6 +9,7 @@ import {
} from "@/domain/settings";
import { getSetting, update } from "@/repository/settings";
import { useToast } from "../ui/use-toast";
+import { useTranslation } from 'react-i18next'
const NotifyTemplate = () => {
const [id, setId] = useState("");
@@ -17,6 +18,7 @@ const NotifyTemplate = () => {
]);
const { toast } = useToast();
+ const { t } = useTranslation();
useEffect(() => {
const featchData = async () => {
@@ -66,8 +68,8 @@ const NotifyTemplate = () => {
}
toast({
- title: "保存成功",
- description: "通知模板保存成功",
+ title: t('save.succeed'),
+ description: t('setting.notify.template.save.succeed'),
});
};
@@ -81,7 +83,7 @@ const NotifyTemplate = () => {
/>
- 可选的变量, COUNT:即将过期张数
+ {t('setting.notify.template.variables.tips.title')}
- 可选的变量, COUNT:即将过期张数,DOMAINS:域名列表
+ {t('setting.notify.template.variables.tips.content')}
-
+
);
diff --git a/ui/src/components/notify/Telegram.tsx b/ui/src/components/notify/Telegram.tsx
index 5091946..339a24d 100644
--- a/ui/src/components/notify/Telegram.tsx
+++ b/ui/src/components/notify/Telegram.tsx
@@ -8,6 +8,7 @@ import { useEffect, useState } from "react";
import { update } from "@/repository/settings";
import { getErrMessage } from "@/lib/error";
import { useToast } from "../ui/use-toast";
+import { useTranslation } from "react-i18next";
type TelegramSetting = {
id: string;
@@ -17,6 +18,7 @@ type TelegramSetting = {
const Telegram = () => {
const { config, setChannels } = useNotify();
+ const { t } = useTranslation();
const [telegram, setTelegram] = useState({
id: config.id ?? "",
@@ -70,15 +72,15 @@ const Telegram = () => {
setChannels(resp);
toast({
- title: "保存成功",
- description: "配置保存成功",
+ title: t('save.succeed'),
+ description: t('setting.notify.config.save.succeed'),
});
} catch (e) {
const msg = getErrMessage(e);
toast({
- title: "保存失败",
- description: "配置保存失败:" + msg,
+ title: t('save.failed'),
+ description: `${t('setting.notify.config.save.failed')}: ${msg}`,
variant: "destructive",
});
}
@@ -128,7 +130,7 @@ const Telegram = () => {
});
}}
/>
-
+
@@ -137,7 +139,7 @@ const Telegram = () => {
handleSaveClick();
}}
>
- 保存
+ {t('save')}
diff --git a/ui/src/components/notify/Webhook.tsx b/ui/src/components/notify/Webhook.tsx
index f2844a7..af8468c 100644
--- a/ui/src/components/notify/Webhook.tsx
+++ b/ui/src/components/notify/Webhook.tsx
@@ -9,6 +9,7 @@ import { update } from "@/repository/settings";
import { getErrMessage } from "@/lib/error";
import { useToast } from "../ui/use-toast";
import { isValidURL } from "@/lib/url";
+import { useTranslation } from 'react-i18next'
type WebhookSetting = {
id: string;
@@ -18,6 +19,7 @@ type WebhookSetting = {
const Webhook = () => {
const { config, setChannels } = useNotify();
+ const { t } = useTranslation();
const [webhook, setWebhook] = useState({
id: config.id ?? "",
@@ -59,8 +61,8 @@ const Webhook = () => {
webhook.data.url = webhook.data.url.trim();
if (!isValidURL(webhook.data.url)) {
toast({
- title: "保存失败",
- description: "Url格式不正确",
+ title: t('save.failed'),
+ description: t('setting.notify.config.save.failed.url.not.valid'),
variant: "destructive",
});
return;
@@ -79,15 +81,15 @@ const Webhook = () => {
setChannels(resp);
toast({
- title: "保存成功",
- description: "配置保存成功",
+ title: t('save.succeed'),
+ description: t('setting.notify.config.save.succeed'),
});
} catch (e) {
const msg = getErrMessage(e);
toast({
- title: "保存失败",
- description: "配置保存失败:" + msg,
+ title: t('save.failed'),
+ description: `${t('setting.notify.config.save.failed')}: ${msg}`,
variant: "destructive",
});
}
@@ -123,7 +125,7 @@ const Webhook = () => {
});
}}
/>
-
+
@@ -132,7 +134,7 @@ const Webhook = () => {
handleSaveClick();
}}
>
- 保存
+ {t('save')}
diff --git a/ui/src/components/ui/form.tsx b/ui/src/components/ui/form.tsx
index 4603f8b..d91f6b8 100644
--- a/ui/src/components/ui/form.tsx
+++ b/ui/src/components/ui/form.tsx
@@ -12,6 +12,7 @@ import {
import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
+import { useTranslation } from "react-i18next"
const Form = FormProvider
@@ -145,7 +146,9 @@ const FormMessage = React.forwardRef<
React.HTMLAttributes
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
- const body = error ? String(error?.message) : children
+ const { t } = useTranslation()
+
+ const body = error ? t(String(error?.message)) : children
if (!body) {
return null
diff --git a/ui/src/components/ui/pagination.tsx b/ui/src/components/ui/pagination.tsx
index d6b1fcf..bbaa801 100644
--- a/ui/src/components/ui/pagination.tsx
+++ b/ui/src/components/ui/pagination.tsx
@@ -3,6 +3,7 @@ import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
import { cn } from "@/lib/utils";
import { ButtonProps, buttonVariants } from "@/components/ui/button";
+import { useTranslation } from "react-i18next";
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
@@ -138,7 +141,7 @@ export default function Dashboard() {
)}
>
- 控制面板
+ {t('dashboard')}
- 域名列表
+ {t('domain.management.name')}
- 授权管理
+ {t('menu.auth.management')}
- 部署历史
+ {t('deployment.log.name')}
+
- 账户
+ {t('account')}
- 偏好设置
+ {t('setting')}
- 退出
+ {t('logout')}
diff --git a/ui/src/pages/SettingLayout.tsx b/ui/src/pages/SettingLayout.tsx
index 6ddacc2..66bca44 100644
--- a/ui/src/pages/SettingLayout.tsx
+++ b/ui/src/pages/SettingLayout.tsx
@@ -4,11 +4,13 @@ import { KeyRound, Megaphone, UserRound } from "lucide-react";
import { useEffect, useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
+import { useTranslation } from "react-i18next";
const SettingLayout = () => {
const location = useLocation();
const [tabValue, setTabValue] = useState("account");
const navigate = useNavigate();
+ const { t } = useTranslation();
useEffect(() => {
const pathname = location.pathname;
@@ -20,7 +22,7 @@ const SettingLayout = () => {
- 偏好设置
+ {t('setting')}
@@ -33,7 +35,7 @@ const SettingLayout = () => {
className="px-5"
>
- 账户
+ {t('account')}
{
className="px-5"
>
- 密码
+ {t('password')}
{
className="px-5"
>
- 消息推送
+ {t('setting.notify.menu')}
diff --git a/ui/src/pages/dashboard/Dashboard.tsx b/ui/src/pages/dashboard/Dashboard.tsx
index 4bd72b3..1502e12 100644
--- a/ui/src/pages/dashboard/Dashboard.tsx
+++ b/ui/src/pages/dashboard/Dashboard.tsx
@@ -24,12 +24,14 @@ import {
} from "lucide-react";
import { useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
+import { useTranslation } from "react-i18next";
const Dashboard = () => {
const [statistic, setStatistic] = useState();
const [deployments, setDeployments] = useState();
const navigate = useNavigate();
+ const { t } = useTranslation();
useEffect(() => {
const fetchStatistic = async () => {
@@ -55,7 +57,7 @@ const Dashboard = () => {
return (
-
控制面板
+
{t('dashboard')}
@@ -63,7 +65,9 @@ const Dashboard = () => {
-
所有
+
+ {t('dashboard.all')}
+
{statistic?.total ? (
@@ -74,7 +78,9 @@ const Dashboard = () => {
0
)}
-
个
+
+ {t("dashboard.unit")}
+
@@ -84,7 +90,9 @@ const Dashboard = () => {
-
即将过期
+
+ {t('dashboard.near.expired')}
+
{statistic?.expired ? (
@@ -95,7 +103,9 @@ const Dashboard = () => {
0
)}
-
个
+
+ {t("dashboard.unit")}
+
@@ -109,7 +119,9 @@ const Dashboard = () => {
/>
-
启用中
+
+ {t('dashboard.enabled')}
+
{statistic?.enabled ? (
@@ -120,7 +132,9 @@ const Dashboard = () => {
0
)}
-
个
+
+ {t("dashboard.unit")}
+
@@ -130,7 +144,7 @@ const Dashboard = () => {
-
未启用
+
{t('dashboard.not.enabled')}
{statistic?.disabled ? (
@@ -144,19 +158,23 @@ const Dashboard = () => {
0
)}
-
个
+
+ {t("dashboard.unit")}
+
-
部署历史
+
+ {t('deployment.log.name')}
+
{deployments?.length == 0 ? (
<>
- 暂无数据
+ {t('no.data')}
@@ -164,7 +182,7 @@ const Dashboard = () => {
{" "}
- 你暂未创建任何部署,请先添加域名进行部署吧!
+ {t('deployment.log.empty')}
@@ -173,7 +191,7 @@ const Dashboard = () => {
navigate("/edit");
}}
>
- 添加域名
+ {t('domain.add')}
@@ -182,16 +200,16 @@ const Dashboard = () => {
) : (
<>
-
域名
+
{t('domain')}
-
状态
-
阶段
-
最近执行时间
+
{t('deployment.log.status')}
+
{t('deployment.log.stage')}
+
{t('deployment.log.last.execution.time')}
-
操作
+
{t('operation')}
- 部署历史
+ {t('deployment.log.name')}
{deployments?.map((deployment) => (
@@ -218,14 +236,14 @@ const Dashboard = () => {
{deployment.expand.domain?.domain}-{deployment.id}
- 部署详情
+ {t('deployment.log.detail')}
diff --git a/ui/src/pages/domains/Edit.tsx b/ui/src/pages/domains/Edit.tsx
index af03ecc..3a4c423 100644
--- a/ui/src/pages/domains/Edit.tsx
+++ b/ui/src/pages/domains/Edit.tsx
@@ -38,6 +38,7 @@ import EmailsEdit from "@/components/certimate/EmailsEdit";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
import { EmailsSetting } from "@/domain/settings";
+import { useTranslation } from "react-i18next";
const Edit = () => {
const {
@@ -47,6 +48,7 @@ const Edit = () => {
const [domain, setDomain] = useState
();
const location = useLocation();
+ const { t } = useTranslation();
const [tab, setTab] = useState<"base" | "advance">("base");
@@ -69,15 +71,15 @@ const Edit = () => {
const formSchema = z.object({
id: z.string().optional(),
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
- message: "请输入正确的域名",
+ message: t('domain.management.edit.domain.verify.tips'),
}),
email: z.string().email().optional(),
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
- message: "请选择DNS服务商授权配置",
+ message: t('domain.management.edit.dns.verify.tips'),
}),
targetAccess: z.string().optional(),
targetType: z.string().regex(/^[a-zA-Z0-9-]+$/, {
- message: "请选择部署服务类型",
+ message: t('domain.management.edit.target.type.verify.tips'),
}),
variables: z.string().optional(),
group: z.string().optional(),
diff --git a/ui/src/pages/domains/Home.tsx b/ui/src/pages/domains/Home.tsx
index 162eaf6..5150a17 100644
--- a/ui/src/pages/domains/Home.tsx
+++ b/ui/src/pages/domains/Home.tsx
@@ -35,11 +35,13 @@ import { TooltipContent, TooltipProvider } from "@radix-ui/react-tooltip";
import { Earth } from "lucide-react";
import { useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
+import { useTranslation, Trans } from "react-i18next";
const Home = () => {
const toast = useToast();
const navigate = useNavigate();
+ const { t } = useTranslation()
const location = useLocation();
const query = new URLSearchParams(location.search);
@@ -127,23 +129,22 @@ const Home = () => {
await save(domain);
toast.toast({
- title: "操作成功",
- description: "已发起部署,请稍后查看部署日志。",
+ title: t('operation.succeed'),
+ description: t('domain.management.start.deploy.succeed.tips'),
});
} catch (e) {
toast.toast({
- title: "执行失败",
+ title: t('domain.management.execution.failed'),
description: (
- <>
- 执行失败,请查看
+ // 这里的 text 只是占位作用,实际文案在 src/i18n/locales/[lang].json
+
+ text1
- 部署日志
-
- 查看详情。
- >
+ >text2
+ text3
+
),
variant: "destructive",
});
@@ -175,8 +176,10 @@ const Home = () => {
-
域名列表
-
+
{t('domain.management.name')}
+
{!domains.length ? (
@@ -187,26 +190,26 @@ const Home = () => {
- 请添加域名开始部署证书吧。
+ {t('domain.management.empty')}
>
) : (
<>
-
域名
-
有效期限
-
最近执行状态
-
最近执行阶段
-
最近执行时间
-
是否启用
-
操作
+
{t('domain')}
+
{t('domain.management.expiry.date')}
+
{t('domain.management.last.execution.status')}
+
{t('domain.management.last.execution.stage')}
+
{t('domain.management.last.execution.time')}
+
{t('domain.management.enable')}
+
{t('operation')}
- 域名
+ {t('domain')}
{domains.map((domain) => (
@@ -221,8 +224,8 @@ const Home = () => {
{domain.expiredAt ? (
<>
-
有效期90天
-
{getDate(domain.expiredAt)}到期
+
{t('domain.management.expiry.date1', { date: 90 })}
+
{t('domain.management.expiry.date2', { date: getDate(domain.expiredAt) })}
>
) : (
"---"
@@ -266,7 +269,7 @@ const Home = () => {
- {domain.enabled ? "禁用" : "启用"}
+ {domain.enabled ? t('disable') : t('enable')}
@@ -278,7 +281,7 @@ const Home = () => {
className="p-0"
onClick={() => handleHistoryClick(domain.id)}
>
- 部署历史
+ {t('deployment.log.name')}
@@ -287,7 +290,7 @@ const Home = () => {
className="p-0"
onClick={() => handleRightNowClick(domain)}
>
- 立即部署
+ {t('domain.management.start.deploying')}
@@ -304,7 +307,7 @@ const Home = () => {
className="p-0"
onClick={() => handleForceClick(domain)}
>
- 强行部署
+ {t('domain.management.forced.deployment')}
@@ -315,7 +318,7 @@ const Home = () => {
className="p-0"
onClick={() => handleDownloadClick(domain)}
>
- 下载
+ {t('download')}
@@ -325,24 +328,24 @@ const Home = () => {
- 删除域名
+ {t('domain.delete')}
- 确定要删除域名吗?
+ {t('domain.management.delete.confirm')}
- 取消
+ {t('cancel')}
{
handleDeleteClick(domain.id);
}}
>
- 确认
+ {t('confirm')}
@@ -354,7 +357,7 @@ const Home = () => {
className="p-0"
onClick={() => handleEditClick(domain.id)}
>
- 编辑
+ {t('edit')}
>
)}
diff --git a/ui/src/pages/history/History.tsx b/ui/src/pages/history/History.tsx
index 3db4db4..94dc756 100644
--- a/ui/src/pages/history/History.tsx
+++ b/ui/src/pages/history/History.tsx
@@ -17,11 +17,13 @@ import { list } from "@/repository/deployment";
import { Smile } from "lucide-react";
import { useEffect, useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
+import { useTranslation } from "react-i18next";
const History = () => {
const navigate = useNavigate();
const [deployments, setDeployments] = useState();
const [searchParams] = useSearchParams();
+ const { t } = useTranslation();
const domain = searchParams.get("domain");
useEffect(() => {
@@ -38,11 +40,11 @@ const History = () => {
return (
- 部署历史
+ {t('deployment.log.name')}
{!deployments?.length ? (
<>
- 暂无数据
+ {t('no.data')}
@@ -50,7 +52,7 @@ const History = () => {
{" "}
- 你暂未创建任何部署,请先添加域名进行部署吧!
+ {t('deployment.log.empty')}
@@ -59,7 +61,7 @@ const History = () => {
navigate("/");
}}
>
- 添加域名
+ {t('domain.add')}
@@ -68,16 +70,16 @@ const History = () => {
) : (
<>
-
域名
+
{t('domain')}
-
状态
-
阶段
-
最近执行时间
+
{t('deployment.log.status')}
+
{t('deployment.log.stage')}
+
{t('deployment.log.last.execution.time')}
-
操作
+
{t('operation')}
- 部署历史
+ {t('deployment.log.name')}
{deployments?.map((deployment) => (
@@ -104,14 +106,14 @@ const History = () => {
{deployment.expand.domain?.domain}-{deployment.id}
- 部署详情
+ {t('deployment.log.detail')}
diff --git a/ui/src/pages/login/Login.tsx b/ui/src/pages/login/Login.tsx
index d023a9b..9df8aa3 100644
--- a/ui/src/pages/login/Login.tsx
+++ b/ui/src/pages/login/Login.tsx
@@ -1,3 +1,8 @@
+import { useForm } from "react-hook-form";
+import { useNavigate } from "react-router-dom";
+import { z } from "zod";
+import { useTranslation } from 'react-i18next'
+
import { Button } from "@/components/ui/button";
import {
Form,
@@ -11,20 +16,19 @@ import { Input } from "@/components/ui/input";
import { getErrMessage } from "@/lib/error";
import { getPb } from "@/repository/api";
import { zodResolver } from "@hookform/resolvers/zod";
-import { useForm } from "react-hook-form";
-import { useNavigate } from "react-router-dom";
-import { z } from "zod";
const formSchema = z.object({
username: z.string().email({
- message: "请输入正确的邮箱地址",
+ message: "login.username.no.empty.message",
}),
password: z.string().min(10, {
- message: "密码至少10个字符",
+ message: "login.password.length.message",
}),
});
const Login = () => {
+ const { t } = useTranslation()
+
const form = useForm
>({
resolver: zodResolver(formSchema),
defaultValues: {
@@ -61,7 +65,7 @@ const Login = () => {
name="username"
render={({ field }) => (
- 用户名
+ {t('username')}
@@ -76,7 +80,7 @@ const Login = () => {
name="password"
render={({ field }) => (
- 密码
+ {t('password')}
@@ -86,7 +90,7 @@ const Login = () => {
)}
/>
-
+
diff --git a/ui/src/pages/setting/Account.tsx b/ui/src/pages/setting/Account.tsx
index 65e6e62..c839f70 100644
--- a/ui/src/pages/setting/Account.tsx
+++ b/ui/src/pages/setting/Account.tsx
@@ -15,16 +15,18 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
+import { useTranslation } from "react-i18next";
import { z } from "zod";
const formSchema = z.object({
- email: z.string().email("请输入正确的邮箱"),
+ email: z.string().email("setting.account.email.valid.message"),
});
const Account = () => {
const { toast } = useToast();
const navigate = useNavigate();
+ const { t } = useTranslation();
const [changed, setChanged] = useState(false);
@@ -43,8 +45,8 @@ const Account = () => {
getPb().authStore.clear();
toast({
- title: "修改账户邮箱功",
- description: "请重新登录",
+ title: t("setting.account.email.change.succeed"),
+ description: t("setting.account.log.back.in"),
});
setTimeout(() => {
navigate("/login");
@@ -52,7 +54,7 @@ const Account = () => {
} catch (e) {
const message = getErrMessage(e);
toast({
- title: "修改账户邮箱失败",
+ title: t("setting.account.email.change.failed"),
description: message,
variant: "destructive",
});
@@ -72,10 +74,10 @@ const Account = () => {
name="email"
render={({ field }) => (
- 邮箱
+ {t('email')}
{
@@ -92,10 +94,10 @@ const Account = () => {
{changed ? (
-
+
) : (
)}
diff --git a/ui/src/pages/setting/Notify.tsx b/ui/src/pages/setting/Notify.tsx
index ecdec18..a727611 100644
--- a/ui/src/pages/setting/Notify.tsx
+++ b/ui/src/pages/setting/Notify.tsx
@@ -9,15 +9,18 @@ import {
AccordionTrigger,
} from "@/components/ui/accordion";
import { NotifyProvider } from "@/providers/notify";
+import { useTranslation } from "react-i18next";
const Notify = () => {
+ const { t } = useTranslation();
+
return (
<>
- 模板
+ {t('template')}
diff --git a/ui/src/pages/setting/Password.tsx b/ui/src/pages/setting/Password.tsx
index 8733a5c..c72b15c 100644
--- a/ui/src/pages/setting/Password.tsx
+++ b/ui/src/pages/setting/Password.tsx
@@ -14,29 +14,31 @@ import { getPb } from "@/repository/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
+import { useTranslation } from "react-i18next";
import { z } from "zod";
const formSchema = z
.object({
oldPassword: z.string().min(10, {
- message: "密码至少10个字符",
+ message: "setting.password.length.message",
}),
newPassword: z.string().min(10, {
- message: "密码至少10个字符",
+ message: "setting.password.length.message",
}),
confirmPassword: z.string().min(10, {
- message: "密码至少10个字符",
+ message: "setting.password.length.message",
}),
})
.refine((data) => data.newPassword === data.confirmPassword, {
- message: "两次密码不一致",
+ message: "setting.password.not.match",
path: ["confirmPassword"],
});
const Password = () => {
const { toast } = useToast();
const navigate = useNavigate();
+ const { t } = useTranslation();
const form = useForm>({
resolver: zodResolver(formSchema),
@@ -66,8 +68,8 @@ const Password = () => {
getPb().authStore.clear();
toast({
- title: "修改密码成功",
- description: "请重新登录",
+ title: t('setting.password.change.succeed'),
+ description: t("setting.account.log.back.in"),
});
setTimeout(() => {
navigate("/login");
@@ -75,7 +77,7 @@ const Password = () => {
} catch (e) {
const message = getErrMessage(e);
toast({
- title: "修改密码失败",
+ title: t('setting.password.change.failed'),
description: message,
variant: "destructive",
});
@@ -95,9 +97,9 @@ const Password = () => {
name="oldPassword"
render={({ field }) => (
- 当前密码
+ {t('setting.password.current.password')}
-
+
@@ -110,7 +112,7 @@ const Password = () => {
name="newPassword"
render={({ field }) => (
- 新密码
+ {t('setting.password.new.password')}
{
name="confirmPassword"
render={({ field }) => (
- 确认密码
+ {t('setting.password.confirm.password')}
{
)}
/>
-
+
From b3f1e1e444f18a93d68107112fb4f694d213ddd5 Mon Sep 17 00:00:00 2001
From: Elvis Liao
Date: Thu, 26 Sep 2024 19:47:50 +0800
Subject: [PATCH 3/8] chore: import i18n
---
ui/src/main.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/ui/src/main.tsx b/ui/src/main.tsx
index 92acf52..6039f41 100644
--- a/ui/src/main.tsx
+++ b/ui/src/main.tsx
@@ -4,6 +4,7 @@ import "./global.css";
import { RouterProvider } from "react-router-dom";
import { router } from "./router.tsx";
import { ThemeProvider } from "./components/ThemeProvider.tsx";
+import "@/i18n";
ReactDOM.createRoot(document.getElementById("root")!).render(
From e820e5599bc289e910ed85a2a6f34708ea145bbf Mon Sep 17 00:00:00 2001
From: Elvis Liao
Date: Fri, 27 Sep 2024 00:42:40 +0800
Subject: [PATCH 4/8] wip: i18n
- Change locales json to one-dimensional format
---
ui/src/components/certimate/EmailsEdit.tsx | 14 +-
ui/src/components/certimate/Version.tsx | 5 +-
ui/src/i18n/locales/en.json | 103 +++++++++++-
ui/src/i18n/locales/zh.json | 172 +++++++++++----------
ui/src/pages/access/Access.tsx | 35 +++--
ui/src/pages/domains/Edit.tsx | 72 ++++-----
6 files changed, 259 insertions(+), 142 deletions(-)
diff --git a/ui/src/components/certimate/EmailsEdit.tsx b/ui/src/components/certimate/EmailsEdit.tsx
index d1d2fc9..6da10ea 100644
--- a/ui/src/components/certimate/EmailsEdit.tsx
+++ b/ui/src/components/certimate/EmailsEdit.tsx
@@ -10,6 +10,7 @@ import {
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
import {
Form,
FormControl,
@@ -39,9 +40,10 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
} = useConfig();
const [open, setOpen] = useState(false);
+ const { t } = useTranslation();
const formSchema = z.object({
- email: z.string().email(),
+ email: z.string().email("email.valid.message"),
});
const form = useForm>({
@@ -54,7 +56,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
const onSubmit = async (data: z.infer) => {
if ((emails.content as EmailsSetting).emails.includes(data.email)) {
form.setError("email", {
- message: "邮箱已存在",
+ message: "email.already.exist",
});
return;
}
@@ -100,7 +102,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
- 添加邮箱
+ {t('email.add')}
@@ -118,9 +120,9 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
name="email"
render={({ field }) => (
- 邮箱
+ {t('email')}
-
+
@@ -129,7 +131,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
/>
-
+
diff --git a/ui/src/components/certimate/Version.tsx b/ui/src/components/certimate/Version.tsx
index 47bfa7c..997ab11 100644
--- a/ui/src/components/certimate/Version.tsx
+++ b/ui/src/components/certimate/Version.tsx
@@ -1,9 +1,12 @@
import { BookOpen } from "lucide-react";
+import { useTranslation } from "react-i18next";
import { Separator } from "../ui/separator";
import { version } from "@/domain/version";
const Version = () => {
+ const { t } = useTranslation()
+
return (
@@ -14,7 +17,7 @@ const Version = () => {
className="flex items-center"
>
-
文档
+
{t('document')}
部署历史1> 查看详情。",
- "empty": "请添加域名开始部署证书吧。",
- "expiry.date": "有效期限",
- "expiry.date1": "有效期 {{date}} 天",
- "expiry.date2": "{{date}} 到期",
- "last.execution.time": "最近执行时间",
- "last.execution.status": "最近执行状态",
- "last.execution.stage": "最近执行阶段",
- "enable": "是否启用",
- "start.deploying": "立即部署",
- "forced.deployment": "强行部署",
- "delete.confirm": "确定要删除域名吗?",
- "edit": {
- "title": "编辑域名",
- "domain.verify.tips": "域名",
- "dns.verify.tips": "请选择DNS服务商授权配置",
- "target.type.verify.tips": "请选择部署服务类型"
- }
- },
+ "domain.not.empty.verify.message": "请输入域名",
+ "domain.management.name": "域名列表",
+ "domain.management.start.deploy.succeed.tips": "已发起部署,请稍后查看部署日志。",
+ "domain.management.execution.failed": "执行失败",
+ "domain.management.execution.failed.tips": "执行失败,请在 <1>部署历史1> 查看详情。",
+ "domain.management.empty": "请添加域名开始部署证书吧。",
+ "domain.management.expiry.date": "有效期限",
+ "domain.management.expiry.date1": "有效期 {{date}} 天",
+ "domain.management.expiry.date2": "{{date}} 到期",
+ "domain.management.last.execution.time": "最近执行时间",
+ "domain.management.last.execution.status": "最近执行状态",
+ "domain.management.last.execution.stage": "最近执行阶段",
+ "domain.management.enable": "是否启用",
+ "domain.management.start.deploying": "立即部署",
+ "domain.management.forced.deployment": "强行部署",
+ "domain.management.delete.confirm": "确定要删除域名吗?",
+ "domain.management.edit.title": "编辑域名",
+ "domain.management.edit.dns.access.label": "DNS 服务商授权配置",
+ "domain.management.edit.dns.access.not.empty.message": "请选择DNS服务商授权配置",
+ "domain.management.edit.access.label": "服务商授权配置",
+ "domain.management.edit.access.not.empty.message": "请选择授权配置",
+ "domain.management.edit.target.type": "部署服务类型",
+ "domain.management.edit.target.type.not.empty.message": "请选择部署服务类型",
+ "domain.management.edit.succeed.tips": "域名编辑成功",
+ "domain.management.edit.target.access": "部署服务商授权配置",
+ "domain.management.edit.target.access.content.label": "服务商授权配置",
+ "domain.management.edit.target.access.not.empty.message": "请选择授权配置",
+ "domain.management.edit.target.access.verify.msg": "部署授权和部署授权组至少选一个",
+ "domain.management.edit.group.label": "部署配置组(用于将一个域名证书部署到多个 ssh 主机)",
+ "domain.management.edit.group.not.empty.message": "请选择分组",
+ "domain.management.edit.email.not.empty.message": "请选择邮箱",
+ "domain.management.edit.email.description": "(申请证书需要提供邮箱)",
+ "domain.management.edit.variables.placeholder": "可在SSH部署中使用,形如:\nkey=val;\nkey2=val2;",
+ "domain.management.edit.dns.placeholder": "自定义域名服务器,多个用分号隔开,如:\n8.8.8.8;\n8.8.4.4;",
+ "domain.management.add.succeed.tips": "域名添加成功",
+ "email.add": "添加邮箱",
+ "email.list": "邮箱列表",
+ "email.valid.message": "请输入正确的邮箱地址",
+ "email.already.exist": "邮箱已存在",
+ "email.not.empty.message": "请输入邮箱",
"setting.notify.menu": "消息推送",
"setting.submit": "确认修改",
- "setting.account.email": {
- "valid.message": "请输入正确的邮箱地址",
- "placeholder": "请输入邮箱",
- "change.succeed": "修改账户邮箱成功",
- "change.failed": "修改账户邮箱失败"
- },
+ "setting.account.email.valid.message": "请输入正确的邮箱地址",
+ "setting.account.email.placeholder": "请输入邮箱",
+ "setting.account.email.change.succeed": "修改账户邮箱成功",
+ "setting.account.email.change.failed": "修改账户邮箱失败",
"setting.account.log.back.in": "请重新登录",
- "setting.password": {
- "length.message": "密码至少10个字符",
- "not.match": "两次密码不一致",
- "change.succeed": "修改密码成功",
- "change.failed": "修改密码失败",
- "current.password": "当前密码",
- "new.password": "新密码",
- "confirm.password": "确认密码"
- },
- "setting.notify": {
- "template": {
- "save.succeed": "通知模板保存成功",
- "variables.tips": {
- "title": "可选的变量, COUNT:即将过期张数",
- "content": "可选的变量, COUNT:即将过期张数,DOMAINS:域名列表"
- }
- },
- "config": {
- "enable": "是否启用",
- "save.succeed": "配置保存成功",
- "save.failed": "配置保存失败",
- "save.failed.url.not.valid": "Url格式不正确"
- }
- },
- "deploy.progress": {
- "check": "检查",
- "apply": "获取",
- "deploy": "部署"
- }
+ "setting.password.length.message": "密码至少10个字符",
+ "setting.password.not.match": "两次密码不一致",
+ "setting.password.change.succeed": "修改密码成功",
+ "setting.password.change.failed": "修改密码失败",
+ "setting.password.current.password": "当前密码",
+ "setting.password.new.password": "新密码",
+ "setting.password.confirm.password": "确认密码",
+ "setting.notify.template.save.succeed": "通知模板保存成功",
+ "setting.notify.template.variables.tips.title": "可选的变量, COUNT:即将过期张数",
+ "setting.notify.template.variables.tips.content": "可选的变量, COUNT:即将过期张数,DOMAINS:域名列表",
+ "setting.notify.config.enable": "是否启用",
+ "setting.notify.config.save.succeed": "配置保存成功",
+ "setting.notify.config.save.failed": "配置保存失败",
+ "setting.notify.config.save.failed.url.not.valid": "Url格式不正确",
+ "deploy.progress.check": "检查",
+ "deploy.progress.apply": "获取",
+ "deploy.progress.deploy": "部署",
+ "access.management": "授权管理",
+ "access.add": "添加授权",
+ "access.list": "授权列表",
+ "access.type": "服务商",
+ "access.empty": "请添加授权开始部署证书吧。",
+ "access.group.management": "授权组管理",
+ "access.group.add": "添加授权组"
}
\ No newline at end of file
diff --git a/ui/src/pages/access/Access.tsx b/ui/src/pages/access/Access.tsx
index cbeb267..3f955f2 100644
--- a/ui/src/pages/access/Access.tsx
+++ b/ui/src/pages/access/Access.tsx
@@ -9,6 +9,7 @@ import { Access as AccessType, accessTypeMap } from "@/domain/access";
import { convertZulu2Beijing } from "@/lib/time";
import { useConfig } from "@/providers/config";
import { remove } from "@/repository/access";
+import { t } from "i18next";
import { Key } from "lucide-react";
import { useLocation, useNavigate } from "react-router-dom";
@@ -46,11 +47,11 @@ const Access = () => {
return (
-
授权管理
+
{t('access.management')}
{tab != "access_group" ? (
-
添加授权} op="add" />
+ {t('access.add')}} op="add" />
) : (
- 添加授权组} />
+ {t('access.group.add')}} />
)}
@@ -66,7 +67,7 @@ const Access = () => {
handleTabItemClick("access");
}}
>
- 授权管理
+ {t('access.management')}
{
handleTabItemClick("access_group");
}}
>
- 授权组管理
+ {t('access.group.management')}
@@ -85,10 +86,10 @@ const Access = () => {
- 请添加授权开始部署证书吧。
+ {t('access.empty')}
添加授权}
+ trigger={}
op="add"
className="mt-3"
/>
@@ -96,15 +97,15 @@ const Access = () => {
) : (
<>
-
名称
-
服务商
+
{t('name')}
+
{t('access.type')}
-
创建时间
-
更新时间
-
操作
+
{t('create.time')}
+
{t('update.time')}
+
{t('operation')}
- 授权列表
+ {t('access.list')}
{accesses
.filter((item) => {
@@ -128,18 +129,18 @@ const Access = () => {
- 创建于{" "}
+ {t('created.in')}{" "}
{access.created && convertZulu2Beijing(access.created)}
- 更新于{" "}
+ {t('updated.in')}{" "}
{access.updated && convertZulu2Beijing(access.updated)}
- 编辑
+ {t('edit')}
}
op="edit"
@@ -153,7 +154,7 @@ const Access = () => {
handleDelete(access);
}}
>
- 删除
+ {t('delete')}
diff --git a/ui/src/pages/domains/Edit.tsx b/ui/src/pages/domains/Edit.tsx
index 3a4c423..771b96b 100644
--- a/ui/src/pages/domains/Edit.tsx
+++ b/ui/src/pages/domains/Edit.tsx
@@ -71,15 +71,15 @@ const Edit = () => {
const formSchema = z.object({
id: z.string().optional(),
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
- message: t('domain.management.edit.domain.verify.tips'),
+ message: 'domain.not.empty.verify.message',
}),
- email: z.string().email().optional(),
+ email: z.string().email('email.valid.message').optional(),
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
- message: t('domain.management.edit.dns.verify.tips'),
+ message: 'domain.management.edit.dns.access.not.empty.message',
}),
targetAccess: z.string().optional(),
targetType: z.string().regex(/^[a-zA-Z0-9-]+$/, {
- message: t('domain.management.edit.target.type.verify.tips'),
+ message: 'domain.management.edit.target.type.not.empty.message',
}),
variables: z.string().optional(),
group: z.string().optional(),
@@ -140,11 +140,11 @@ const Edit = () => {
if (group == "" && targetAccess == "") {
form.setError("group", {
type: "manual",
- message: "部署授权和部署授权组至少选一个",
+ message: 'domain.management.edit.target.access.verify.msg',
});
form.setError("targetAccess", {
type: "manual",
- message: "部署授权和部署授权组至少选一个",
+ message: 'domain.management.edit.target.access.verify.msg',
});
return;
}
@@ -164,13 +164,13 @@ const Edit = () => {
try {
await save(req);
- let description = "域名编辑成功";
+ let description = t('domain.management.edit.succeed.tips');
if (req.id == "") {
- description = "域名添加成功";
+ description = t('domain.management.add.succeed.tips');
}
toast({
- title: "成功",
+ title: t('succeed'),
description,
});
navigate("/domains");
@@ -195,7 +195,7 @@ const Edit = () => {
- {domain?.id ? "编辑" : "新增"}域名
+ {domain?.id ? t('domain.edit') : t('domain.add')}
@@ -208,7 +208,7 @@ const Edit = () => {
setTab("base");
}}
>
- 基础设置
+ {t('basic.setting')}
{
setTab("advance");
}}
>
- 高级设置
+ {t('advanced.setting')}
@@ -234,9 +234,9 @@ const Edit = () => {
name="domain"
render={({ field }) => (
- 域名
+ {t('domain')}
-
+
@@ -249,12 +249,12 @@ const Edit = () => {
render={({ field }) => (
- Email(申请证书需要提供邮箱)
+ {t('email') + t('domain.management.edit.email.description')}
- 新增
+ {t('add')}
}
/>
@@ -268,11 +268,11 @@ const Edit = () => {
}}
>
-
+
- 邮箱列表
+ {t('email.list')}
{(emails.content as EmailsSetting).emails.map(
(item) => (
@@ -295,12 +295,12 @@ const Edit = () => {
render={({ field }) => (
- DNS 服务商授权配置
+ {t('domain.management.edit.dns.access.label')}
- 新增
+ {t('add')}
}
op="add"
@@ -315,11 +315,11 @@ const Edit = () => {
}}
>
-
+
- 服务商授权配置
+ {t('domain.management.edit.access.label')}
{accesses
.filter((item) => item.usage != "deploy")
.map((item) => (
@@ -351,7 +351,7 @@ const Edit = () => {
name="targetType"
render={({ field }) => (
- 部署服务类型
+ {t('domain.management.edit.target.type')}
>
@@ -151,7 +149,7 @@ const AccessGroupList = () => {
);
}}
>
- 所有授权
+ {t('access.all')}
@@ -159,14 +157,14 @@ const AccessGroupList = () => {
@@ -175,21 +173,21 @@ const AccessGroupList = () => {
- 删除组
+ {t('access.group.delete')}
- 确定要删除部署授权组吗?
+ {t('access.group.delete.confirm')}
- 取消
+ {t('cancel')}
{
@@ -198,7 +196,7 @@ const AccessGroupList = () => {
);
}}
>
- 确认
+ {t('confirm')}
diff --git a/ui/src/components/certimate/AccessNamesiloForm.tsx b/ui/src/components/certimate/AccessNamesiloForm.tsx
index ac2abe1..d70d57b 100644
--- a/ui/src/components/certimate/AccessNamesiloForm.tsx
+++ b/ui/src/components/certimate/AccessNamesiloForm.tsx
@@ -1,5 +1,6 @@
import { Input } from "@/components/ui/input";
import { useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -28,11 +29,12 @@ const AccessNamesiloForm = ({
onAfterReq: () => void;
}) => {
const { addAccess, updateAccess } = useConfig();
+ const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
- name: z.string().min(1).max(64),
+ name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
configType: accessFormType,
- apiKey: z.string().min(1).max(64),
+ apiKey: z.string().min(1, 'access.form.namesilo.api.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
});
let config: NamesiloConfig = {
@@ -44,14 +46,13 @@ const AccessNamesiloForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
- name: data?.name,
+ name: data?.name || '',
configType: "namesilo",
apiKey: config.apiKey,
},
});
const onSubmit = async (data: z.infer) => {
- console.log(data);
const req: Access = {
id: data.id as string,
name: data.name,
@@ -106,9 +107,9 @@ const AccessNamesiloForm = ({
name="name"
render={({ field }) => (
- 名称
+ {t('name')}
-
+
@@ -121,7 +122,7 @@ const AccessNamesiloForm = ({
name="id"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -136,7 +137,7 @@ const AccessNamesiloForm = ({
name="configType"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -151,9 +152,9 @@ const AccessNamesiloForm = ({
name="apiKey"
render={({ field }) => (
- NAMESILO_API_KEY
+ {t('access.form.namesilo.api.key')}
-
+
@@ -162,7 +163,7 @@ const AccessNamesiloForm = ({
/>
-
+
diff --git a/ui/src/components/certimate/AccessQiniuForm.tsx b/ui/src/components/certimate/AccessQiniuForm.tsx
index 7b58f07..125f7ae 100644
--- a/ui/src/components/certimate/AccessQiniuForm.tsx
+++ b/ui/src/components/certimate/AccessQiniuForm.tsx
@@ -1,5 +1,6 @@
import { Input } from "@/components/ui/input";
import { useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -29,12 +30,13 @@ const AccessQiniuForm = ({
onAfterReq: () => void;
}) => {
const { addAccess, updateAccess } = useConfig();
+ const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
- name: z.string().min(1).max(64),
+ name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
configType: accessFormType,
- accessKey: z.string().min(1).max(64),
- secretKey: z.string().min(1).max(64),
+ accessKey: z.string().min(1, 'access.form.access.key.not.empty').max(64),
+ secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64),
});
let config: QiniuConfig = {
@@ -47,7 +49,7 @@ const AccessQiniuForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
- name: data?.name,
+ name: data?.name || '',
configType: "qiniu",
accessKey: config.accessKey,
secretKey: config.secretKey,
@@ -111,9 +113,9 @@ const AccessQiniuForm = ({
name="name"
render={({ field }) => (
- 名称
+ {t('name')}
-
+
@@ -126,7 +128,7 @@ const AccessQiniuForm = ({
name="id"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -141,7 +143,7 @@ const AccessQiniuForm = ({
name="configType"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -156,9 +158,9 @@ const AccessQiniuForm = ({
name="accessKey"
render={({ field }) => (
- AccessKey
+ {t('access.form.access.key')}
-
+
@@ -171,9 +173,9 @@ const AccessQiniuForm = ({
name="secretKey"
render={({ field }) => (
- SecretKey
+ {t('access.form.secret.key')}
-
+
@@ -184,7 +186,7 @@ const AccessQiniuForm = ({
-
+
diff --git a/ui/src/components/certimate/AccessSSHForm.tsx b/ui/src/components/certimate/AccessSSHForm.tsx
index b7af5b4..5e4696f 100644
--- a/ui/src/components/certimate/AccessSSHForm.tsx
+++ b/ui/src/components/certimate/AccessSSHForm.tsx
@@ -24,6 +24,7 @@ import { ClientResponseError } from "pocketbase";
import { PbErrorData } from "@/domain/base";
import { readFileContent } from "@/lib/file";
import { useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
import {
Select,
SelectContent,
@@ -53,6 +54,7 @@ const AccessSSHForm = ({
const fileInputRef = useRef(null);
const [fileName, setFileName] = useState("");
+ const { t } = useTranslation();
const originGroup = data ? (data.group ? data.group : "") : "";
@@ -62,25 +64,25 @@ const AccessSSHForm = ({
const formSchema = z.object({
id: z.string().optional(),
- name: z.string().min(1).max(64),
+ name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
configType: accessFormType,
host: z.string().refine(
(str) => {
return ipReg.test(str) || domainReg.test(str);
},
{
- message: "请输入正确的域名或IP",
+ message: "zod.rule.ssh.host",
}
),
group: z.string().optional(),
- port: z.string().min(1).max(5),
- username: z.string().min(1).max(64),
- password: z.string().min(0).max(64),
- key: z.string().min(0).max(20480),
+ port: z.string().min(1, 'access.form.ssh.port.not.empty').max(5, t('zod.rule.string.max', { max: 5 })),
+ username: z.string().min(1, 'username.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
+ password: z.string().min(0, 'password.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
+ key: z.string().min(0, 'access.form.ssh.key.not.empty').max(20480, t('zod.rule.string.max', { max: 20480 })),
keyFile: z.any().optional(),
- command: z.string().min(1).max(2048),
- certPath: z.string().min(0).max(2048),
- keyPath: z.string().min(0).max(2048),
+ command: z.string().min(1, 'access.form.ssh.command.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
+ certPath: z.string().min(0, 'access.form.ssh.cert.path.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
+ keyPath: z.string().min(0, 'access.form.ssh.key.path.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
});
let config: SSHConfig = {
@@ -100,7 +102,7 @@ const AccessSSHForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
- name: data?.name,
+ name: data?.name || '',
configType: "ssh",
group: data?.group,
host: config.host,
@@ -219,9 +221,9 @@ const AccessSSHForm = ({
name="name"
render={({ field }) => (
- 名称
+ {t('name')}
-
+
@@ -235,12 +237,12 @@ const AccessSSHForm = ({
render={({ field }) => (
- 授权配置组(用于将一个域名证书部署到多个 ssh 主机)
+ {t('access.form.ssh.group.label')}
- 新增
+ {t('add')}
}
/>
@@ -255,7 +257,7 @@ const AccessSSHForm = ({
}}
>
-
+
@@ -295,7 +297,7 @@ const AccessSSHForm = ({
name="id"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -310,7 +312,7 @@ const AccessSSHForm = ({
name="configType"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -325,9 +327,9 @@ const AccessSSHForm = ({
name="host"
render={({ field }) => (
- 服务器HOST
+ {t('access.form.ssh.host')}
-
+
@@ -340,10 +342,10 @@ const AccessSSHForm = ({
name="port"
render={({ field }) => (
- SSH端口
+ {t('access.form.ssh.port')}
@@ -360,9 +362,9 @@ const AccessSSHForm = ({
name="username"
render={({ field }) => (
- 用户名
+ {t('username')}
-
+
@@ -375,10 +377,10 @@ const AccessSSHForm = ({
name="password"
render={({ field }) => (
- 密码
+ {t('password')}
@@ -394,9 +396,9 @@ const AccessSSHForm = ({
name="key"
render={({ field }) => (
- Key(使用证书登录)
+ {t('access.form.ssh.key')}
-
+
@@ -409,7 +411,7 @@ const AccessSSHForm = ({
name="keyFile"
render={({ field }) => (
- Key(使用证书登录)
+ {t('access.form.ssh.key')}
(
- 证书上传路径
+ {t('access.form.ssh.cert.path')}
-
+
@@ -458,9 +460,9 @@ const AccessSSHForm = ({
name="keyPath"
render={({ field }) => (
- 私钥上传路径
+ {t('access.form.ssh.key.path')}
-
+
@@ -473,9 +475,9 @@ const AccessSSHForm = ({
name="command"
render={({ field }) => (
- Command
+ {t('access.form.ssh.command')}
-
+
@@ -486,7 +488,7 @@ const AccessSSHForm = ({
-
+
diff --git a/ui/src/components/certimate/AccessTencentForm.tsx b/ui/src/components/certimate/AccessTencentForm.tsx
index 0df3f6b..fee1151 100644
--- a/ui/src/components/certimate/AccessTencentForm.tsx
+++ b/ui/src/components/certimate/AccessTencentForm.tsx
@@ -1,5 +1,6 @@
import { Input } from "@/components/ui/input";
import { useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -28,12 +29,13 @@ const AccessTencentForm = ({
onAfterReq: () => void;
}) => {
const { addAccess, updateAccess } = useConfig();
+ const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
- name: z.string().min(1).max(64),
+ name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
configType: accessFormType,
- secretId: z.string().min(1).max(64),
- secretKey: z.string().min(1).max(64),
+ secretId: z.string().min(1, 'access.form.secret.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
+ secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
});
let config: TencentConfig = {
@@ -46,7 +48,7 @@ const AccessTencentForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
- name: data?.name,
+ name: data?.name || '',
configType: "tencent",
secretId: config.secretId,
secretKey: config.secretKey,
@@ -108,9 +110,9 @@ const AccessTencentForm = ({
name="name"
render={({ field }) => (
- 名称
+ {t('name')}
-
+
@@ -123,7 +125,7 @@ const AccessTencentForm = ({
name="id"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -138,7 +140,7 @@ const AccessTencentForm = ({
name="configType"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -153,9 +155,9 @@ const AccessTencentForm = ({
name="secretId"
render={({ field }) => (
- SecretId
+ {t('access.form.secret.id')}
-
+
@@ -168,9 +170,9 @@ const AccessTencentForm = ({
name="secretKey"
render={({ field }) => (
- SecretKey
+ {t('access.form.secret.key')}
-
+
@@ -179,7 +181,7 @@ const AccessTencentForm = ({
/>
-
+
diff --git a/ui/src/components/certimate/AccessWebhookFrom.tsx b/ui/src/components/certimate/AccessWebhookFrom.tsx
index 9a33cd5..c7947aa 100644
--- a/ui/src/components/certimate/AccessWebhookFrom.tsx
+++ b/ui/src/components/certimate/AccessWebhookFrom.tsx
@@ -1,5 +1,6 @@
import { Input } from "@/components/ui/input";
import { useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -28,11 +29,12 @@ const WebhookForm = ({
onAfterReq: () => void;
}) => {
const { addAccess, updateAccess } = useConfig();
+ const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
- name: z.string().min(1).max(64),
+ name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
configType: accessFormType,
- url: z.string().url(),
+ url: z.string().url('zod.rule.url'),
});
let config: WebhookConfig = {
@@ -44,14 +46,13 @@ const WebhookForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
- name: data?.name,
+ name: data?.name || '',
configType: "webhook",
url: config.url,
},
});
const onSubmit = async (data: z.infer) => {
- console.log(data);
const req: Access = {
id: data.id as string,
name: data.name,
@@ -106,9 +107,9 @@ const WebhookForm = ({
name="name"
render={({ field }) => (
- 名称
+ {t('name')}
-
+
@@ -121,7 +122,7 @@ const WebhookForm = ({
name="id"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -136,7 +137,7 @@ const WebhookForm = ({
name="configType"
render={({ field }) => (
- 配置类型
+ {t('access.form.config.field')}
@@ -151,9 +152,9 @@ const WebhookForm = ({
name="url"
render={({ field }) => (
- Webhook Url
+ {t('access.form.webhook.url')}
-
+
@@ -162,7 +163,7 @@ const WebhookForm = ({
/>
-
+
diff --git a/ui/src/components/notify/DingTalk.tsx b/ui/src/components/notify/DingTalk.tsx
index 37b3f32..fecd790 100644
--- a/ui/src/components/notify/DingTalk.tsx
+++ b/ui/src/components/notify/DingTalk.tsx
@@ -102,7 +102,7 @@ const DingTalk = () => {
}}
/>
{
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index b8acb40..db6dc65 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -1,14 +1,14 @@
import { z } from "zod";
export const accessTypeMap: Map = new Map([
- ["tencent", ["腾讯云", "/imgs/providers/tencent.svg"]],
- ["aliyun", ["阿里云", "/imgs/providers/aliyun.svg"]],
- ["cloudflare", ["Cloudflare", "/imgs/providers/cloudflare.svg"]],
- ["namesilo", ["Namesilo", "/imgs/providers/namesilo.svg"]],
- ["godaddy", ["GoDaddy", "/imgs/providers/godaddy.svg"]],
- ["qiniu", ["七牛云", "/imgs/providers/qiniu.svg"]],
- ["ssh", ["SSH部署", "/imgs/providers/ssh.svg"]],
- ["webhook", ["Webhook", "/imgs/providers/webhook.svg"]],
+ ["tencent", ["tencent", "/imgs/providers/tencent.svg"]],
+ ["aliyun", ["aliyun", "/imgs/providers/aliyun.svg"]],
+ ["cloudflare", ["cloudflare", "/imgs/providers/cloudflare.svg"]],
+ ["namesilo", ["namesilo", "/imgs/providers/namesilo.svg"]],
+ ["godaddy", ["go.daddy", "/imgs/providers/godaddy.svg"]],
+ ["qiniu", ["qiniu", "/imgs/providers/qiniu.svg"]],
+ ["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
+ ["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
]);
export const getProviderInfo = (t: string) => {
@@ -26,7 +26,7 @@ export const accessFormType = z.union(
z.literal("namesilo"),
z.literal("godaddy"),
],
- { message: "请选择云服务商" }
+ { message: "access.not.empty" }
);
type AccessUsage = "apply" | "deploy" | "all";
diff --git a/ui/src/domain/domain.ts b/ui/src/domain/domain.ts
index 0bb190f..f8e69ef 100644
--- a/ui/src/domain/domain.ts
+++ b/ui/src/domain/domain.ts
@@ -40,13 +40,13 @@ export const getLastDeployment = (domain: Domain): Deployment | undefined => {
};
export const targetTypeMap: Map = new Map([
- ["aliyun-cdn", ["阿里云-CDN", "/imgs/providers/aliyun.svg"]],
- ["aliyun-oss", ["阿里云-OSS", "/imgs/providers/aliyun.svg"]],
- ["aliyun-dcdn", ["阿里云-DCDN", "/imgs/providers/aliyun.svg"]],
- ["tencent-cdn", ["腾讯云-CDN", "/imgs/providers/tencent.svg"]],
- ["ssh", ["SSH部署", "/imgs/providers/ssh.svg"]],
- ["qiniu-cdn", ["七牛云-CDN", "/imgs/providers/qiniu.svg"]],
- ["webhook", ["Webhook", "/imgs/providers/webhook.svg"]],
+ ["aliyun-cdn", ["aliyun.cdn", "/imgs/providers/aliyun.svg"]],
+ ["aliyun-oss", ["aliyun.oss", "/imgs/providers/aliyun.svg"]],
+ ["aliyun-dcdn", ["aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
+ ["tencent-cdn", ["tencent.cdn", "/imgs/providers/tencent.svg"]],
+ ["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
+ ["qiniu-cdn", ["qiniu.cdn", "/imgs/providers/qiniu.svg"]],
+ ["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
]);
export const targetTypeKeys = Array.from(targetTypeMap.keys());
diff --git a/ui/src/i18n/locales/en.json b/ui/src/i18n/locales/en.json
index abd0c36..a06d2fb 100644
--- a/ui/src/i18n/locales/en.json
+++ b/ui/src/i18n/locales/en.json
@@ -1,6 +1,8 @@
{
"username": "username",
+ "username.not.empty": "username.not.empty",
"password": "password",
+ "password.not.empty": "password.not.empty",
"email": "email",
"logout": "logout",
"setting": "setting",
@@ -21,11 +23,37 @@
"succeed": "succeed",
"add": "add",
"document": "document",
+ "variables": "variables",
+ "dns": "dns",
+ "name": "name",
+ "create.time": "create.time",
+ "update.time": "update.time",
+ "created.in": "created.in",
+ "updated.in": "updated.in",
"basic.setting": "basic.setting",
"advanced.setting": "advanced.setting",
"operation.succeed": "operation.succeed",
"save.succeed": "save.succeed",
"save.failed": "save.failed",
+ "delete.failed": "delete.failed",
+ "ding.talk": "ding.talk",
+ "telegram": "telegram",
+ "webhook": "webhook",
+ "tencent": "tencent",
+ "tencent.cdn": "tencent.cdn",
+ "aliyun": "aliyun",
+ "aliyun.cdn": "aliyun.cdn",
+ "aliyun.oss": "aliyun.oss",
+ "aliyun.dcdn": "aliyun.dcdn",
+ "qiniu": "qiniu",
+ "qiniu.cdn": "qiniu.cdn",
+ "cloudflare": "cloudflare",
+ "namesilo": "namesilo",
+ "go.daddy": "go.daddy",
+ "ssh": "ssh",
+ "zod.rule.string.max": "zod.rule.string.max",
+ "zod.rule.url": "zod.rule.url",
+ "zod.rule.ssh.host": "zod.rule.ssh.host",
"login.submit": "login.submit",
"login.username.no.empty.message": "login.username.no.empty.message",
"login.password.length.message": "login.password.length.message",
@@ -68,13 +96,23 @@
"domain.management.forced.deployment": "domain.management.forced.deployment",
"domain.management.delete.confirm": "domain.management.delete.confirm",
"domain.management.edit.title": "domain.management.edit.title",
- "domain.management.edit.dns.label": "domain.management.edit.dns.label",
- "domain.management.edit.dns.verify.tips": "domain.management.edit.dns.verify.tips",
- "domain.management.edit.target.type.verify.tips": "domain.management.edit.target.type.verify.tips",
+ "domain.management.edit.dns.access.label": "domain.management.edit.dns.access.label",
+ "domain.management.edit.dns.access.not.empty.message": "domain.management.edit.dns.access.not.empty.message",
+ "domain.management.edit.access.label": "domain.management.edit.access.label",
+ "domain.management.edit.access.not.empty.message": "domain.management.edit.access.not.empty.message",
+ "domain.management.edit.target.type": "domain.management.edit.target.type",
+ "domain.management.edit.target.type.not.empty.message": "domain.management.edit.target.type.not.empty.message",
"domain.management.edit.succeed.tips": "domain.management.edit.succeed.tips",
+ "domain.management.edit.target.access": "domain.management.edit.target.access",
+ "domain.management.edit.target.access.content.label": "domain.management.edit.target.access.content.label",
+ "domain.management.edit.target.access.not.empty.message": "domain.management.edit.target.access.not.empty.message",
"domain.management.edit.target.access.verify.msg": "domain.management.edit.target.access.verify.msg",
+ "domain.management.edit.group.label": "domain.management.edit.group.label",
+ "domain.management.edit.group.not.empty.message": "domain.management.edit.group.not.empty.message",
"domain.management.edit.email.not.empty.message": "domain.management.edit.email.not.empty.message",
"domain.management.edit.email.description": "domain.management.edit.email.description",
+ "domain.management.edit.variables.placeholder": "domain.management.edit.variables.placeholder",
+ "domain.management.edit.dns.placeholder": "domain.management.edit.dns.placeholder",
"domain.management.add.succeed.tips": "domain.management.add.succeed.tips",
"email.add": "email.add",
"email.list": "email.list",
@@ -104,5 +142,61 @@
"setting.notify.config.save.failed.url.not.valid": "setting.notify.config.save.failed.url.not.valid",
"deploy.progress.check": "deploy.progress.check",
"deploy.progress.apply": "deploy.progress.apply",
- "deploy.progress.deploy": "deploy.progress.deploy"
+ "deploy.progress.deploy": "deploy.progress.deploy",
+ "access.management": "access.management",
+ "access.add": "access.add",
+ "access.edit": "access.edit",
+ "access.all": "access.all",
+ "access.list": "access.list",
+ "access.type": "access.type",
+ "access.type.not.empty": "access.type.not.empty",
+ "access.not.empty": "access.not.empty",
+ "access.empty": "access.empty",
+ "access.group.management": "access.group.management",
+ "access.group.add": "access.group.add",
+ "access.group.not.empty": "access.group.not.empty",
+ "access.group.name": "access.group.name",
+ "access.group.name.not.empty": "access.group.name.not.empty",
+ "access.group.delete": "access.group.delete",
+ "access.group.delete.confirm": "access.group.delete.confirm",
+ "access.group.domain.empty": "access.group.domain.empty",
+ "access.group.empty": "access.group.empty",
+ "access.group.total": "access.group.total",
+ "access.form.name.not.empty": "access.form.name.not.empty",
+ "access.form.config.field": "access.form.config.field",
+ "access.form.access.key.id": "access.form.access.key.id",
+ "access.form.access.key.id.not.empty": "access.form.access.key.id.not.empty",
+ "access.form.access.key.secret": "access.form.access.key.secret",
+ "access.form.access.key.secret.not.empty": "access.form.access.key.secret.not.empty",
+ "access.form.cloud.dns.api.token": "access.form.cloud.dns.api.token",
+ "access.form.cloud.dns.api.token.not.empty": "access.form.cloud.dns.api.token.not.empty",
+ "access.form.go.daddy.api.key": "access.form.go.daddy.api.key",
+ "access.form.go.daddy.api.key.not.empty": "access.form.go.daddy.api.key.not.empty",
+ "access.form.go.daddy.api.secret": "access.form.go.daddy.api.secret",
+ "access.form.go.daddy.api.secret.not.empty": "access.form.go.daddy.api.secret.not.empty",
+ "access.form.namesilo.api.key": "access.form.namesilo.api.key",
+ "access.form.namesilo.api.key.not.empty": "access.form.namesilo.api.key.not.empty",
+ "access.form.secret.id": "access.form.secret.id",
+ "access.form.secret.id.not.empty": "access.form.secret.id.not.empty",
+ "access.form.secret.key": "access.form.secret.key",
+ "access.form.secret.key.not.empty": "access.form.secret.key.not.empty",
+ "access.form.access.key": "access.form.access.key",
+ "access.form.access.key.not.empty": "access.form.access.key.not.empty",
+ "access.form.webhook.url": "access.form.webhook.url",
+ "access.form.webhook.url.not.empty": "access.form.webhook.url.not.empty",
+ "access.form.ssh.group.label": "access.form.ssh.group.label",
+ "access.form.ssh.host": "access.form.ssh.host",
+ "access.form.ssh.host.not.empty": "access.form.ssh.host.not.empty",
+ "access.form.ssh.port": "access.form.ssh.port",
+ "access.form.ssh.port.not.empty": "access.form.ssh.port.not.empty",
+ "access.form.ssh.key": "access.form.ssh.key",
+ "access.form.ssh.key.not.empty": "access.form.ssh.key.not.empty",
+ "access.form.ssh.key.file.not.empty": "access.form.ssh.key.file.not.empty",
+ "access.form.ssh.cert.path": "access.form.ssh.cert.path",
+ "access.form.ssh.cert.path.not.empty": "access.form.ssh.cert.path.not.empty",
+ "access.form.ssh.key.path": "access.form.ssh.key.path",
+ "access.form.ssh.key.path.not.empty": "access.form.ssh.key.path.not.empty",
+ "access.form.ssh.command": "access.form.ssh.command",
+ "access.form.ssh.command.not.empty": "access.form.ssh.command.not.empty",
+ "access.form.ding.access.token.placeholder": "access.form.ding.access.token.placeholder"
}
\ No newline at end of file
diff --git a/ui/src/i18n/locales/zh.json b/ui/src/i18n/locales/zh.json
index 1fee469..385699d 100644
--- a/ui/src/i18n/locales/zh.json
+++ b/ui/src/i18n/locales/zh.json
@@ -1,6 +1,8 @@
{
"username": "用户名",
+ "username.not.empty": "请输入用户名",
"password": "密码",
+ "password.not.empty": "请输入密码",
"email": "邮箱",
"logout": "退出登录",
"setting": "设置",
@@ -33,6 +35,25 @@
"operation.succeed": "操作成功",
"save.succeed": "保存成功",
"save.failed": "保存失败",
+ "delete.failed": "删除失败",
+ "ding.talk": "钉钉",
+ "telegram": "Telegram",
+ "webhook": "Webhook",
+ "tencent": "腾讯云",
+ "tencent.cdn": "腾讯云-CDN",
+ "aliyun": "阿里云",
+ "aliyun.cdn": "阿里云-CDN",
+ "aliyun.oss": "阿里云-OSS",
+ "aliyun.dcdn": "阿里云-DCDN",
+ "qiniu": "七牛云",
+ "qiniu.cdn": "七牛云-CDN",
+ "cloudflare": "Cloudflare",
+ "namesilo": "Namesilo",
+ "go.daddy": "GoDaddy",
+ "ssh": "SSH 部署",
+ "zod.rule.string.max": "请输入不超过 {{max}} 个字符",
+ "zod.rule.url": "请输入有效的 url 地址",
+ "zod.rule.ssh.host": "请输入正确的域名或IP",
"login.submit": "登录",
"login.username.no.empty.message": "请输入正确的邮箱地址",
"login.password.length.message": "密码至少10个字符",
@@ -124,9 +145,58 @@
"deploy.progress.deploy": "部署",
"access.management": "授权管理",
"access.add": "添加授权",
+ "access.edit": "编辑授权",
+ "access.all": "所有授权",
"access.list": "授权列表",
"access.type": "服务商",
+ "access.type.not.empty": "请选择服务商",
+ "access.not.empty": "请选择云服务商",
"access.empty": "请添加授权开始部署证书吧。",
"access.group.management": "授权组管理",
- "access.group.add": "添加授权组"
+ "access.group.add": "添加授权组",
+ "access.group.not.empty": "请选择分组",
+ "access.group.name": "组名",
+ "access.group.name.not.empty": "请输入组名",
+ "access.group.delete": "删除组",
+ "access.group.delete.confirm": "确定要删除部署授权组吗?",
+ "access.group.domain.empty": "请添加域名开始部署证书吧。",
+ "access.group.empty": "暂无部署授权配置,请添加后开始使用吧",
+ "access.group.total": "共有 {{total}} 个部署授权配置",
+ "access.form.name.not.empty": "请输入授权名称",
+ "access.form.config.field": "配置类型",
+ "access.form.access.key.id": "AccessKeyId",
+ "access.form.access.key.id.not.empty": "请输入 AccessKeyId",
+ "access.form.access.key.secret": "AccessKeySecret",
+ "access.form.access.key.secret.not.empty": "请输入 AccessKeySecret",
+ "access.form.cloud.dns.api.token": "CLOUD_DNS_API_TOKEN",
+ "access.form.cloud.dns.api.token.not.empty": "请输入 CLOUD_DNS_API_TOKEN",
+ "access.form.go.daddy.api.key": "GO_DADDY_API_KEY",
+ "access.form.go.daddy.api.key.not.empty": "请输入 GO_DADDY_API_KEY",
+ "access.form.go.daddy.api.secret": "GO_DADDY_API_SECRET",
+ "access.form.go.daddy.api.secret.not.empty": "请输入 GO_DADDY_API_SECRET",
+ "access.form.namesilo.api.key": "NAMESILO_API_KEY",
+ "access.form.namesilo.api.key.not.empty": "请输入 NAMESILO_API_KEY",
+ "access.form.secret.id": "SecretId",
+ "access.form.secret.id.not.empty": "请输入 SecretId",
+ "access.form.secret.key": "SecretKey",
+ "access.form.secret.key.not.empty": "请输入 SecretKey",
+ "access.form.access.key": "AccessKey",
+ "access.form.access.key.not.empty": "请输入 AccessKey",
+ "access.form.webhook.url": "Webhook URL",
+ "access.form.webhook.url.not.empty": "请输入 Webhook URL",
+ "access.form.ssh.group.label": "授权配置组(用于将一个域名证书部署到多个 ssh 主机)",
+ "access.form.ssh.host": "服务器 Host",
+ "access.form.ssh.host.not.empty": "请输入 Host",
+ "access.form.ssh.port": "SSH 端口",
+ "access.form.ssh.port.not.empty": "请输入 Port",
+ "access.form.ssh.key": "Key(使用证书登录)",
+ "access.form.ssh.key.not.empty": "请输入 Key",
+ "access.form.ssh.key.file.not.empty": "请选择文件",
+ "access.form.ssh.cert.path": "证书上传路径",
+ "access.form.ssh.cert.path.not.empty": "请输入证书上传路径",
+ "access.form.ssh.key.path": "私钥上传路径",
+ "access.form.ssh.key.path.not.empty": "请输入私钥上传路径",
+ "access.form.ssh.command": "Command",
+ "access.form.ssh.command.not.empty": "请输入要执行的命令",
+ "access.form.ding.access.token.placeholder": "加签的签名"
}
\ No newline at end of file
diff --git a/ui/src/pages/access/Access.tsx b/ui/src/pages/access/Access.tsx
index 3f955f2..d48c7ae 100644
--- a/ui/src/pages/access/Access.tsx
+++ b/ui/src/pages/access/Access.tsx
@@ -125,7 +125,7 @@ const Access = () => {
src={accessTypeMap.get(access.configType)?.[1]}
className="w-6"
/>
- {accessTypeMap.get(access.configType)?.[0]}
+ {t(accessTypeMap.get(access.configType)?.[0] || '')}
diff --git a/ui/src/pages/domains/Edit.tsx b/ui/src/pages/domains/Edit.tsx
index 771b96b..530bba5 100644
--- a/ui/src/pages/domains/Edit.tsx
+++ b/ui/src/pages/domains/Edit.tsx
@@ -373,7 +373,7 @@ const Edit = () => {
className="w-6"
src={targetTypeMap.get(key)?.[1]}
/>
-
{targetTypeMap.get(key)?.[0]}
+
{t(targetTypeMap.get(key)?.[0] || '')}
))}
diff --git a/ui/src/pages/setting/Notify.tsx b/ui/src/pages/setting/Notify.tsx
index a727611..bf59754 100644
--- a/ui/src/pages/setting/Notify.tsx
+++ b/ui/src/pages/setting/Notify.tsx
@@ -30,21 +30,21 @@ const Notify = () => {
- 钉钉
+ {t('ding.talk')}
- Telegram
+ {t('telegram')}
- Webhook
+ {t('webhook')}
From 5eba437732f81a5f177f838b0efeee727cc66bda Mon Sep 17 00:00:00 2001
From: elvis liao <1219585136@qq.com>
Date: Fri, 27 Sep 2024 13:41:40 +0800
Subject: [PATCH 6/8] feat: english translation
---
ui/src/i18n/locales/en.json | 400 ++++++++++++++++++------------------
1 file changed, 200 insertions(+), 200 deletions(-)
diff --git a/ui/src/i18n/locales/en.json b/ui/src/i18n/locales/en.json
index a06d2fb..a91ec8d 100644
--- a/ui/src/i18n/locales/en.json
+++ b/ui/src/i18n/locales/en.json
@@ -1,202 +1,202 @@
{
- "username": "username",
- "username.not.empty": "username.not.empty",
- "password": "password",
- "password.not.empty": "password.not.empty",
- "email": "email",
- "logout": "logout",
- "setting": "setting",
- "account": "account",
- "template": "template",
- "save": "save",
- "no.data": "no.data",
- "status": "status",
- "operation": "operation",
- "enable": "enable",
- "disable": "disable",
- "deploy": "deploy",
- "download": "download",
- "delete": "delete",
- "cancel": "cancel",
- "confirm": "confirm",
- "edit": "edit",
- "succeed": "succeed",
- "add": "add",
- "document": "document",
- "variables": "variables",
- "dns": "dns",
- "name": "name",
- "create.time": "create.time",
- "update.time": "update.time",
- "created.in": "created.in",
- "updated.in": "updated.in",
- "basic.setting": "basic.setting",
- "advanced.setting": "advanced.setting",
- "operation.succeed": "operation.succeed",
- "save.succeed": "save.succeed",
- "save.failed": "save.failed",
- "delete.failed": "delete.failed",
- "ding.talk": "ding.talk",
- "telegram": "telegram",
- "webhook": "webhook",
- "tencent": "tencent",
- "tencent.cdn": "tencent.cdn",
- "aliyun": "aliyun",
- "aliyun.cdn": "aliyun.cdn",
- "aliyun.oss": "aliyun.oss",
- "aliyun.dcdn": "aliyun.dcdn",
- "qiniu": "qiniu",
- "qiniu.cdn": "qiniu.cdn",
- "cloudflare": "cloudflare",
- "namesilo": "namesilo",
- "go.daddy": "go.daddy",
- "ssh": "ssh",
- "zod.rule.string.max": "zod.rule.string.max",
- "zod.rule.url": "zod.rule.url",
- "zod.rule.ssh.host": "zod.rule.ssh.host",
- "login.submit": "login.submit",
- "login.username.no.empty.message": "login.username.no.empty.message",
- "login.password.length.message": "login.password.length.message",
- "menu.auth.management": "menu.auth.management",
- "theme.light": "theme.light",
- "theme.dark": "theme.dark",
- "theme.system": "theme.system",
- "dashboard": "dashboard",
- "dashboard.all": "dashboard.all",
- "dashboard.near.expired": "dashboard.near.expired",
- "dashboard.enabled": "dashboard.enabled",
- "dashboard.not.enabled": "dashboard.not.enabled",
- "dashboard.unit": "dashboard.unit",
- "deployment.log.name": "deployment.log.name",
- "deployment.log.empty": "deployment.log.empty",
- "deployment.log.status": "deployment.log.status",
- "deployment.log.stage": "deployment.log.stage",
- "deployment.log.last.execution.time": "deployment.log.last.execution.time",
- "deployment.log.detail.button.text": "deployment.log.detail.button.text",
- "deployment.log.detail": "deployment.log.detail",
- "pagination.next": "pagination.next",
- "pagination.prev": "pagination.prev",
- "domain": "domain",
- "domain.add": "domain.add",
- "domain.delete": "domain.delete",
- "domain.not.empty.verify.message": "domain.not.empty.verify.message",
- "domain.management.name": "domain.management.name",
- "domain.management.start.deploy.succeed.tips": "domain.management.start.deploy.succeed.tips",
- "domain.management.execution.failed": "domain.management.execution.failed",
- "domain.management.execution.failed.tips": "domain.management.execution.failed.tips",
- "domain.management.empty": "domain.management.empty",
- "domain.management.expiry.date": "domain.management.expiry.date",
- "domain.management.expiry.date1": "domain.management.expiry.date1",
- "domain.management.expiry.date2": "domain.management.expiry.date2",
- "domain.management.last.execution.time": "domain.management.last.execution.time",
- "domain.management.last.execution.status": "domain.management.last.execution.status",
- "domain.management.last.execution.stage": "domain.management.last.execution.stage",
- "domain.management.enable": "domain.management.enable",
- "domain.management.start.deploying": "domain.management.start.deploying",
- "domain.management.forced.deployment": "domain.management.forced.deployment",
- "domain.management.delete.confirm": "domain.management.delete.confirm",
- "domain.management.edit.title": "domain.management.edit.title",
- "domain.management.edit.dns.access.label": "domain.management.edit.dns.access.label",
- "domain.management.edit.dns.access.not.empty.message": "domain.management.edit.dns.access.not.empty.message",
- "domain.management.edit.access.label": "domain.management.edit.access.label",
- "domain.management.edit.access.not.empty.message": "domain.management.edit.access.not.empty.message",
- "domain.management.edit.target.type": "domain.management.edit.target.type",
- "domain.management.edit.target.type.not.empty.message": "domain.management.edit.target.type.not.empty.message",
- "domain.management.edit.succeed.tips": "domain.management.edit.succeed.tips",
- "domain.management.edit.target.access": "domain.management.edit.target.access",
- "domain.management.edit.target.access.content.label": "domain.management.edit.target.access.content.label",
- "domain.management.edit.target.access.not.empty.message": "domain.management.edit.target.access.not.empty.message",
- "domain.management.edit.target.access.verify.msg": "domain.management.edit.target.access.verify.msg",
- "domain.management.edit.group.label": "domain.management.edit.group.label",
- "domain.management.edit.group.not.empty.message": "domain.management.edit.group.not.empty.message",
- "domain.management.edit.email.not.empty.message": "domain.management.edit.email.not.empty.message",
- "domain.management.edit.email.description": "domain.management.edit.email.description",
- "domain.management.edit.variables.placeholder": "domain.management.edit.variables.placeholder",
- "domain.management.edit.dns.placeholder": "domain.management.edit.dns.placeholder",
- "domain.management.add.succeed.tips": "domain.management.add.succeed.tips",
- "email.add": "email.add",
- "email.list": "email.list",
- "email.valid.message": "email.valid.message",
- "email.already.exist": "email.already.exist",
- "email.not.empty.message": "email.not.empty.message",
- "setting.notify.menu": "setting.notify.menu",
- "setting.submit": "setting.submit",
- "setting.account.email.valid.message": "setting.account.email.valid.message",
- "setting.account.email.placeholder": "setting.account.email.placeholder",
- "setting.account.email.change.succeed": "setting.account.email.change.succeed",
- "setting.account.email.change.failed": "setting.account.email.change.failed",
- "setting.account.log.back.in": "setting.account.log.back.in",
- "setting.password.length.message": "setting.password.length.message",
- "setting.password.not.match": "setting.password.not.match",
- "setting.password.change.succeed": "setting.password.change.succeed",
- "setting.password.change.failed": "setting.password.change.failed",
- "setting.password.current.password": "setting.password.current.password",
- "setting.password.new.password": "setting.password.new.password",
- "setting.password.confirm.password": "setting.password.confirm.password",
- "setting.notify.template.save.succeed": "setting.notify.template.save.succeed",
- "setting.notify.template.variables.tips.title": "setting.notify.template.variables.tips.title",
- "setting.notify.template.variables.tips.content": "setting.notify.template.variables.tips.content",
- "setting.notify.config.enable": "setting.notify.config.enable",
- "setting.notify.config.save.succeed": "setting.notify.config.save.succeed",
- "setting.notify.config.save.failed": "setting.notify.config.save.failed",
- "setting.notify.config.save.failed.url.not.valid": "setting.notify.config.save.failed.url.not.valid",
- "deploy.progress.check": "deploy.progress.check",
- "deploy.progress.apply": "deploy.progress.apply",
- "deploy.progress.deploy": "deploy.progress.deploy",
- "access.management": "access.management",
- "access.add": "access.add",
- "access.edit": "access.edit",
- "access.all": "access.all",
- "access.list": "access.list",
- "access.type": "access.type",
- "access.type.not.empty": "access.type.not.empty",
- "access.not.empty": "access.not.empty",
- "access.empty": "access.empty",
- "access.group.management": "access.group.management",
- "access.group.add": "access.group.add",
- "access.group.not.empty": "access.group.not.empty",
- "access.group.name": "access.group.name",
- "access.group.name.not.empty": "access.group.name.not.empty",
- "access.group.delete": "access.group.delete",
- "access.group.delete.confirm": "access.group.delete.confirm",
- "access.group.domain.empty": "access.group.domain.empty",
- "access.group.empty": "access.group.empty",
- "access.group.total": "access.group.total",
- "access.form.name.not.empty": "access.form.name.not.empty",
- "access.form.config.field": "access.form.config.field",
- "access.form.access.key.id": "access.form.access.key.id",
- "access.form.access.key.id.not.empty": "access.form.access.key.id.not.empty",
- "access.form.access.key.secret": "access.form.access.key.secret",
- "access.form.access.key.secret.not.empty": "access.form.access.key.secret.not.empty",
- "access.form.cloud.dns.api.token": "access.form.cloud.dns.api.token",
- "access.form.cloud.dns.api.token.not.empty": "access.form.cloud.dns.api.token.not.empty",
- "access.form.go.daddy.api.key": "access.form.go.daddy.api.key",
- "access.form.go.daddy.api.key.not.empty": "access.form.go.daddy.api.key.not.empty",
- "access.form.go.daddy.api.secret": "access.form.go.daddy.api.secret",
- "access.form.go.daddy.api.secret.not.empty": "access.form.go.daddy.api.secret.not.empty",
- "access.form.namesilo.api.key": "access.form.namesilo.api.key",
- "access.form.namesilo.api.key.not.empty": "access.form.namesilo.api.key.not.empty",
- "access.form.secret.id": "access.form.secret.id",
- "access.form.secret.id.not.empty": "access.form.secret.id.not.empty",
- "access.form.secret.key": "access.form.secret.key",
- "access.form.secret.key.not.empty": "access.form.secret.key.not.empty",
- "access.form.access.key": "access.form.access.key",
- "access.form.access.key.not.empty": "access.form.access.key.not.empty",
- "access.form.webhook.url": "access.form.webhook.url",
- "access.form.webhook.url.not.empty": "access.form.webhook.url.not.empty",
- "access.form.ssh.group.label": "access.form.ssh.group.label",
- "access.form.ssh.host": "access.form.ssh.host",
- "access.form.ssh.host.not.empty": "access.form.ssh.host.not.empty",
- "access.form.ssh.port": "access.form.ssh.port",
- "access.form.ssh.port.not.empty": "access.form.ssh.port.not.empty",
- "access.form.ssh.key": "access.form.ssh.key",
- "access.form.ssh.key.not.empty": "access.form.ssh.key.not.empty",
- "access.form.ssh.key.file.not.empty": "access.form.ssh.key.file.not.empty",
- "access.form.ssh.cert.path": "access.form.ssh.cert.path",
- "access.form.ssh.cert.path.not.empty": "access.form.ssh.cert.path.not.empty",
- "access.form.ssh.key.path": "access.form.ssh.key.path",
- "access.form.ssh.key.path.not.empty": "access.form.ssh.key.path.not.empty",
- "access.form.ssh.command": "access.form.ssh.command",
- "access.form.ssh.command.not.empty": "access.form.ssh.command.not.empty",
- "access.form.ding.access.token.placeholder": "access.form.ding.access.token.placeholder"
+ "username": "Username",
+ "username.not.empty": "Please enter username",
+ "password": "Password",
+ "password.not.empty": "Please enter password",
+ "email": "Email",
+ "logout": "Logout",
+ "setting": "Settings",
+ "account": "Account",
+ "template": "Template",
+ "save": "Save",
+ "no.data": "No data available",
+ "status": "Status",
+ "operation": "Operation",
+ "enable": "Enable",
+ "disable": "Disable",
+ "deploy": "Deploy",
+ "download": "Download",
+ "delete": "Delete",
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "edit": "Edit",
+ "succeed": "Successful",
+ "add": "Add",
+ "document": "Document",
+ "variables": "Variables",
+ "dns": "Domain Name Server",
+ "name": "Name",
+ "create.time": "CreateTime",
+ "update.time": "UpdateTime",
+ "created.in": "Created in",
+ "updated.in": "Updated in",
+ "basic.setting": "Basic Settings",
+ "advanced.setting": "Advanced Settings",
+ "operation.succeed": "Operation Successful",
+ "save.succeed": "Save Successful",
+ "save.failed": "Save Failed",
+ "delete.failed": "Delete Failed",
+ "ding.talk": "Ding Talk",
+ "telegram": "Telegram",
+ "webhook": "Webhook",
+ "tencent": "Tencent",
+ "tencent.cdn": "Tencent-CDN",
+ "aliyun": "Alibaba Cloud",
+ "aliyun.cdn": "Alibaba Cloud-CDN",
+ "aliyun.oss": "Alibaba Cloud-OSS",
+ "aliyun.dcdn": "Alibaba Cloud-DCDN",
+ "qiniu": "Qiniu",
+ "qiniu.cdn": "Qiniu-CDN",
+ "cloudflare": "Cloudflare",
+ "namesilo": "Namesilo",
+ "go.daddy": "GoDaddy",
+ "ssh": "SSH Deployment",
+ "zod.rule.string.max": "Please enter no more than {{max}} characters",
+ "zod.rule.url": "Please enter a valid URL",
+ "zod.rule.ssh.host": "Please enter the correct domain name or IP",
+ "login.submit": "Log In",
+ "login.username.no.empty.message": "Please enter a valid email address",
+ "login.password.length.message": "Password should be at least 10 characters",
+ "menu.auth.management": "Authorization Management",
+ "theme.light": "Light",
+ "theme.dark": "Dark",
+ "theme.system": "System",
+ "dashboard": "Dashboard",
+ "dashboard.all": "All",
+ "dashboard.near.expired": "About to Expire",
+ "dashboard.enabled": "Enabled",
+ "dashboard.not.enabled": "Not Enabled",
+ "dashboard.unit": "Unit",
+ "deployment.log.name": "Deployment History",
+ "deployment.log.empty": "You have not created any deployments yet, please add a domain to start deployment!",
+ "deployment.log.status": "Status",
+ "deployment.log.stage": "Stage",
+ "deployment.log.last.execution.time": "Last Execution Time",
+ "deployment.log.detail.button.text": "Log",
+ "deployment.log.detail": "Deployment Details",
+ "pagination.next": "Next",
+ "pagination.prev": "Previous",
+ "domain": "Domain",
+ "domain.add": "Add Domain",
+ "domain.delete": "Delete Domain",
+ "domain.not.empty.verify.message": "Please enter domain",
+ "domain.management.name": "Domain List",
+ "domain.management.start.deploy.succeed.tips": "Deployment initiated, please check the deployment log later.",
+ "domain.management.execution.failed": "Execution Failed",
+ "domain.management.execution.failed.tips": "Execution failed, please check the details in <1>Deployment History1>.",
+ "domain.management.empty": "Please add a domain to start deploying the certificate.",
+ "domain.management.expiry.date": "Validity Period",
+ "domain.management.expiry.date1": "Valid for {{date}} days",
+ "domain.management.expiry.date2": "Expiry on {{date}}",
+ "domain.management.last.execution.time": "Last Execution Time",
+ "domain.management.last.execution.status": "Last Execution Status",
+ "domain.management.last.execution.stage": "Last Execution Stage",
+ "domain.management.enable": "Enable",
+ "domain.management.start.deploying": "Deploy Now",
+ "domain.management.forced.deployment": "Force Deployment",
+ "domain.management.delete.confirm": "Are you sure you want to delete this domain?",
+ "domain.management.edit.title": "Edit Domain",
+ "domain.management.edit.dns.access.label": "DNS Provider Authorization Configuration",
+ "domain.management.edit.dns.access.not.empty.message": "Please select DNS provider authorization configuration",
+ "domain.management.edit.access.label": "Provider Authorization Configuration",
+ "domain.management.edit.access.not.empty.message": "Please select authorization configuration",
+ "domain.management.edit.target.type": "Deployment Service Type",
+ "domain.management.edit.target.type.not.empty.message": "Please select deployment service type",
+ "domain.management.edit.succeed.tips": "Successful domain editing",
+ "domain.management.edit.target.access": "Deployment Service Provider Authorization Configuration",
+ "domain.management.edit.target.access.content.label": "Provider Authorization Configuration",
+ "domain.management.edit.target.access.not.empty.message": "Please select authorization configuration",
+ "domain.management.edit.target.access.verify.msg": "At least one of the deployment authorization and deployment authorization group must be selected",
+ "domain.management.edit.group.label": "Deployment Configuration Group (used to deploy a domain certificate to multiple ssh hosts)",
+ "domain.management.edit.group.not.empty.message": "Please select group",
+ "domain.management.edit.email.not.empty.message": "Please select email",
+ "domain.management.edit.email.description": "(A email is required to apply for a certificate)",
+ "domain.management.edit.variables.placeholder": "It can be used in SSH deployment, like:\nkey=val;\nkey2=val2;",
+ "domain.management.edit.dns.placeholder": "Custom domain name server, separates multiple entries with semicolon, like:\n8.8.8.8;\n8.8.4.4;",
+ "domain.management.add.succeed.tips": "Domain added successfully",
+ "email.add": "Add Email",
+ "email.list": "Email List",
+ "email.valid.message": "Please enter a valid email address",
+ "email.already.exist": "Email already exists",
+ "email.not.empty.message": "Please enter email",
+ "setting.notify.menu": "Notification Push",
+ "setting.submit": "Confirm Changes",
+ "setting.account.email.valid.message": "Please enter a valid email address",
+ "setting.account.email.placeholder": "Please enter email",
+ "setting.account.email.change.succeed": "Account email altered successfully",
+ "setting.account.email.change.failed": "Account email alteration failed",
+ "setting.account.log.back.in": "Please login again",
+ "setting.password.length.message": "Password should be at least 10 characters",
+ "setting.password.not.match": "Passwords do not match",
+ "setting.password.change.succeed": "Password changed successfully",
+ "setting.password.change.failed": "Password change failed",
+ "setting.password.current.password": "Current Password",
+ "setting.password.new.password": "New Password",
+ "setting.password.confirm.password": "Confirm Password",
+ "setting.notify.template.save.succeed": "Notification template saved successfully",
+ "setting.notify.template.variables.tips.title": "Optional variables, COUNT: number of expiring soon",
+ "setting.notify.template.variables.tips.content": "Optional variables, COUNT: number of expiring soon, DOMAINS: Domain list",
+ "setting.notify.config.enable": "Enable",
+ "setting.notify.config.save.succeed": "Configuration saved successfully",
+ "setting.notify.config.save.failed": "Configuration save failed",
+ "setting.notify.config.save.failed.url.not.valid": "Invalid Url format",
+ "deploy.progress.check": "Check",
+ "deploy.progress.apply": "Apply",
+ "deploy.progress.deploy": "Deploy",
+ "access.management": "Authorization Management",
+ "access.add": "Add Authorization",
+ "access.edit": "Edit Authorization",
+ "access.all": "All Authorizations",
+ "access.list": "Authorization List",
+ "access.type": "Provider",
+ "access.type.not.empty": "Please select a provider",
+ "access.not.empty": "Please select a cloud provider",
+ "access.empty": "Please add authorization to start deploying certificate.",
+ "access.group.management": "Authorization Group Management",
+ "access.group.add": "Add Authorization Group",
+ "access.group.not.empty": "Please select a group",
+ "access.group.name": "Group Name",
+ "access.group.name.not.empty": "Please enter group name",
+ "access.group.delete": "Delete Group",
+ "access.group.delete.confirm": "Are you sure you want to delete the deployment authorization group?",
+ "access.group.domain.empty": "Please add a domain to start deploying the certificate.",
+ "access.group.empty": "No deployment authorization configuration yet, please add after starting use.",
+ "access.group.total": "Totally {{total}} deployment authorization configuration",
+ "access.form.name.not.empty": "Please enter authorization name",
+ "access.form.config.field": "Configuration Type",
+ "access.form.access.key.id": "AccessKeyId",
+ "access.form.access.key.id.not.empty": "Please enter AccessKeyId",
+ "access.form.access.key.secret": "AccessKeySecret",
+ "access.form.access.key.secret.not.empty": "Please enter AccessKeySecret",
+ "access.form.cloud.dns.api.token": "CLOUD_DNS_API_TOKEN",
+ "access.form.cloud.dns.api.token.not.empty": "Please enter CLOUD_DNS_API_TOKEN",
+ "access.form.go.daddy.api.key": "GO_DADDY_API_KEY",
+ "access.form.go.daddy.api.key.not.empty": "Please enter GO_DADDY_API_KEY",
+ "access.form.go.daddy.api.secret": "GO_DADDY_API_SECRET",
+ "access.form.go.daddy.api.secret.not.empty": "Please enter GO_DADDY_API_SECRET",
+ "access.form.namesilo.api.key": "NAMESILO_API_KEY",
+ "access.form.namesilo.api.key.not.empty": "Please enter NAMESILO_API_KEY",
+ "access.form.secret.id": "SecretId",
+ "access.form.secret.id.not.empty": "Please enter SecretId",
+ "access.form.secret.key": "SecretKey",
+ "access.form.secret.key.not.empty": "Please enter SecretKey",
+ "access.form.access.key": "AccessKey",
+ "access.form.access.key.not.empty": "Please enter AccessKey",
+ "access.form.webhook.url": "Webhook URL",
+ "access.form.webhook.url.not.empty": "Please enter Webhook URL",
+ "access.form.ssh.group.label": "Authorization Configuration Group (used to deploy a single domain certificate to multiple SSH hosts)",
+ "access.form.ssh.host": "Server Host",
+ "access.form.ssh.host.not.empty": "Please enter Host",
+ "access.form.ssh.port": "SSH Port",
+ "access.form.ssh.port.not.empty": "Please enter Port",
+ "access.form.ssh.key": "Key (Log in using certificate)",
+ "access.form.ssh.key.not.empty": "Please enter Key",
+ "access.form.ssh.key.file.not.empty": "Please select file",
+ "access.form.ssh.cert.path": "Certificate Upload Path",
+ "access.form.ssh.cert.path.not.empty": "Please enter certificate upload path",
+ "access.form.ssh.key.path": "Private Key Upload Path",
+ "access.form.ssh.key.path.not.empty": "Please enter private key upload path",
+ "access.form.ssh.command": "Command",
+ "access.form.ssh.command.not.empty": "Please enter command",
+ "access.form.ding.access.token.placeholder": "Signature for signed addition"
}
\ No newline at end of file
From fcc0dd93fdd50802845e73af07e9468011872273 Mon Sep 17 00:00:00 2001
From: elvis liao <1219585136@qq.com>
Date: Fri, 27 Sep 2024 13:42:58 +0800
Subject: [PATCH 7/8] =?UTF-8?q?refactor(DeployProgress):=20=E9=87=8D?=
=?UTF-8?q?=E6=9E=84=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BF=AE=E5=A4=8D=E6=A0=B7?=
=?UTF-8?q?=E5=BC=8F=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/certimate/DeployProgress.tsx | 162 ++++++------------
1 file changed, 55 insertions(+), 107 deletions(-)
diff --git a/ui/src/components/certimate/DeployProgress.tsx b/ui/src/components/certimate/DeployProgress.tsx
index b96dc2b..caed3f2 100644
--- a/ui/src/components/certimate/DeployProgress.tsx
+++ b/ui/src/components/certimate/DeployProgress.tsx
@@ -1,5 +1,8 @@
import { useTranslation } from "react-i18next";
+import { cn } from "@/lib/utils"
+
+
import { Separator } from "../ui/separator";
type DeployProgressProps = {
@@ -10,116 +13,61 @@ type DeployProgressProps = {
const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
const { t } = useTranslation();
- let rs = <> >;
- if (phase === "check") {
- if (phaseSuccess) {
- rs = (
-
-
- {t('deploy.progress.check')}
-
-
-
- {t('deploy.progress.apply')}
-
-
-
- {t('deploy.progress.deploy')}
-
-
- );
- } else {
- rs = (
-
-
- {t('deploy.progress.check')}
-
-
-
- {t('deploy.progress.apply')}
-
-
-
- {t('deploy.progress.deploy')}
-
-
- );
- }
- }
+ let step = 0;
- if (phase === "apply") {
- if (phaseSuccess) {
- rs = (
-
-
- {t('deploy.progress.check')}
-
-
-
- {t('deploy.progress.apply')}
-
-
-
- {t('deploy.progress.deploy')}
-
-
- );
- } else {
- rs = (
-
-
- {t('deploy.progress.check')}
-
-
-
- {t('deploy.progress.apply')}
-
-
-
- {t('deploy.progress.deploy')}
-
-
- );
- }
- }
-
- if (phase === "deploy") {
- if (phaseSuccess) {
- rs = (
-
-
- {t('deploy.progress.check')}
-
-
-
- {t('deploy.progress.apply')}
-
-
-
- {t('deploy.progress.deploy')}
-
-
- );
- } else {
- rs = (
-
-
- {t('deploy.progress.check')}
-
-
-
- {t('deploy.progress.apply')}
-
-
-
- {t('deploy.progress.deploy')}
-
-
- );
- }
+ if (phase === "check") {
+ step = 1
+ } else if (phase === "apply") {
+ step = 2
+ } else if (phase === "deploy") {
+ step = 3
}
- return rs;
+ return (
+
+
1 ? "text-green-600" : "",
+ )
+ }>
+ {t('deploy.progress.check')}
+
+
1 ? "bg-green-600" : "",
+ )
+ } />
+ 2 ? "text-green-600" : "",
+ )
+ }>
+ {t('deploy.progress.apply')}
+
+ 2 ? "bg-green-600" : "",
+ )
+ } />
+ 3 ? "text-green-600" : "",
+ )
+ }>
+ {t('deploy.progress.deploy')}
+
+
+ )
};
export default DeployProgress;
From 1e2d8fa0277fa1b9b129c9f2156389efe78c7c44 Mon Sep 17 00:00:00 2001
From: elvis liao <1219585136@qq.com>
Date: Fri, 27 Sep 2024 16:31:24 +0800
Subject: [PATCH 8/8] chore: abstract resources configure
---
ui/src/i18n/index.ts | 15 ++-------------
ui/src/i18n/locales/index.ts | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 13 deletions(-)
create mode 100644 ui/src/i18n/locales/index.ts
diff --git a/ui/src/i18n/index.ts b/ui/src/i18n/index.ts
index eaed9c9..60718b0 100644
--- a/ui/src/i18n/index.ts
+++ b/ui/src/i18n/index.ts
@@ -2,24 +2,13 @@ import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
-import zh from './locales/zh.json'
-import en from './locales/en.json'
-
+import resources from './locales'
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
- resources: {
- zh: {
- name: '简体中文',
- translation: zh
- },
- en: {
- name: 'English',
- translation: en
- }
- },
+ resources,
fallbackLng: 'zh',
debug: true,
interpolation: {
diff --git a/ui/src/i18n/locales/index.ts b/ui/src/i18n/locales/index.ts
new file mode 100644
index 0000000..aff6084
--- /dev/null
+++ b/ui/src/i18n/locales/index.ts
@@ -0,0 +1,17 @@
+import { Resource } from 'i18next'
+
+import zh from './zh.json'
+import en from './en.json'
+
+const resources: Resource = {
+ zh: {
+ name: '简体中文',
+ translation: zh
+ },
+ en: {
+ name: 'English',
+ translation: en
+ }
+}
+
+export default resources;
\ No newline at end of file