From 3a0dbc26d1462332d5f80714533ecf7d38e4b46f Mon Sep 17 00:00:00 2001 From: JiPai Date: Sat, 8 Mar 2025 16:14:13 +0800 Subject: [PATCH] feat(i18n): add date-fns locale support --- .../dashboard/settings/api/add-api-key.tsx | 87 +++++++++---------- .../dashboard/settings/api/show-api-keys.tsx | 39 +++++---- apps/dokploy/lib/languages.ts | 71 ++++++++++----- apps/dokploy/public/locales/en/settings.json | 71 ++++++++++++++- .../public/locales/zh-Hans/settings.json | 71 ++++++++++++++- 5 files changed, 257 insertions(+), 82 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx b/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx index 131d7ddf..7f8a1750 100644 --- a/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx +++ b/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx @@ -33,6 +33,7 @@ import { import { Switch } from "@/components/ui/switch"; import copy from "copy-to-clipboard"; import { CodeEditor } from "@/components/shared/code-editor"; +import { useTranslation } from "next-i18next"; const formSchema = z.object({ name: z.string().min(1, "Name is required"), @@ -79,6 +80,7 @@ const REFILL_INTERVAL_OPTIONS = [ ]; export const AddApiKey = () => { + const { t } = useTranslation('settings'); const [open, setOpen] = useState(false); const [showSuccessModal, setShowSuccessModal] = useState(false); const [newApiKey, setNewApiKey] = useState(""); @@ -95,7 +97,7 @@ export const AddApiKey = () => { void refetch(); }, onError: () => { - toast.error("Failed to generate API key"); + toast.error(t("settings.api.errorGeneratingApiKey")); }, }); @@ -140,14 +142,13 @@ export const AddApiKey = () => { <> - + - Generate API Key + {t("settings.api.generateApiKey")} - Create a new API key for accessing the API. You can set an - expiration date and a custom prefix for better organization. + {t("settings.api.createNewApiKeyDescription")}
@@ -157,9 +158,9 @@ export const AddApiKey = () => { name="name" render={({ field }) => ( - Name + {t("settings.api.name")} - + @@ -170,9 +171,9 @@ export const AddApiKey = () => { name="prefix" render={({ field }) => ( - Prefix + {t("settings.api.prefix")} - + @@ -183,7 +184,7 @@ export const AddApiKey = () => { name="expiresIn" render={({ field }) => ( - Expiration + {t("settings.api.expiration")} - + @@ -234,16 +235,16 @@ export const AddApiKey = () => { {/* Rate Limiting Section */}
-

Rate Limiting

+

{t("settings.api.rateLimiting")}

(
- Enable Rate Limiting + {t("settings.api.enableRateLimiting")} - Limit the number of requests within a time window + {t("settings.api.limitRequestsDescription")}
@@ -263,7 +264,7 @@ export const AddApiKey = () => { name="rateLimitTimeWindow" render={({ field }) => ( - Time Window + {t("settings.api.timeWindow")} - The duration in which requests are counted + {t("settings.api.timeWindowDescription")} @@ -298,11 +299,11 @@ export const AddApiKey = () => { name="rateLimitMax" render={({ field }) => ( - Maximum Requests + {t("settings.api.maxRequests")} field.onChange( @@ -314,8 +315,7 @@ export const AddApiKey = () => { /> - Maximum number of requests allowed within the time - window + {t("settings.api.maxRequestsDescription")} @@ -327,17 +327,17 @@ export const AddApiKey = () => { {/* Request Limiting Section */}
-

Request Limiting

+

{t("settings.api.requestLimiting")}

( - Total Request Limit + {t("settings.api.totalRequestLimit")} field.onChange( @@ -349,8 +349,7 @@ export const AddApiKey = () => { /> - Total number of requests allowed (leave empty for - unlimited) + {t("settings.api.totalRequestLimitDescription")} @@ -362,11 +361,11 @@ export const AddApiKey = () => { name="refillAmount" render={({ field }) => ( - Refill Amount + {t("settings.api.refillAmount")} field.onChange( @@ -378,7 +377,7 @@ export const AddApiKey = () => { /> - Number of requests to add on each refill + {t("settings.api.refillAmountDescription")} @@ -390,7 +389,7 @@ export const AddApiKey = () => { name="refillInterval" render={({ field }) => ( - Refill Interval + {t("settings.api.refillInterval")} - How often to refill the request limit + {t("settings.api.refillIntervalDescription")} @@ -425,9 +424,9 @@ export const AddApiKey = () => { variant="outline" onClick={() => setOpen(false)} > - Cancel + {t("settings.api.cancel")} - +
@@ -437,9 +436,9 @@ export const AddApiKey = () => { - API Key Generated Successfully + {t("settings.api.apiKeyGeneratedSuccessfully")} - Please copy your API key now. You won't be able to see it again! + {t("settings.api.copyApiKeyNow")}
@@ -453,16 +452,16 @@ export const AddApiKey = () => {
diff --git a/apps/dokploy/components/dashboard/settings/api/show-api-keys.tsx b/apps/dokploy/components/dashboard/settings/api/show-api-keys.tsx index 6744f1de..bb8e57ea 100644 --- a/apps/dokploy/components/dashboard/settings/api/show-api-keys.tsx +++ b/apps/dokploy/components/dashboard/settings/api/show-api-keys.tsx @@ -14,8 +14,11 @@ import { formatDistanceToNow } from "date-fns"; import { DialogAction } from "@/components/shared/dialog-action"; import { AddApiKey } from "./add-api-key"; import { Badge } from "@/components/ui/badge"; +import { useTranslation } from "next-i18next"; +import { getDateFnsLocaleByCode } from "@/lib/languages"; export const ShowApiKeys = () => { + const { t, i18n } = useTranslation("settings"); const { data, refetch } = api.user.get.useQuery(); const { mutateAsync: deleteApiKey, isLoading: isLoadingDelete } = api.user.deleteApiKey.useMutation(); @@ -28,22 +31,24 @@ export const ShowApiKeys = () => {
- API/CLI Keys + {t("settings.api.apiCliKeys")} - Generate and manage API keys to access the API/CLI + {t("settings.api.generateAndManageKeys")}
- Swagger API: + {t("settings.api.swaggerApi")} - View + + {t("settings.api.view")} +
@@ -62,9 +67,11 @@ export const ShowApiKeys = () => {
- Created{" "} - {formatDistanceToNow(new Date(apiKey.createdAt))}{" "} - ago + {t("settings.api.created")}{" "} + {formatDistanceToNow(new Date(apiKey.createdAt), { + locale: getDateFnsLocaleByCode(i18n.language), + })}{" "} + {t("settings.api.ago")} {apiKey.prefix && ( { className="flex items-center gap-1" > - Expires in{" "} - {formatDistanceToNow( - new Date(apiKey.expiresAt), - )}{" "} + {t("settings.api.expiresIn")}{" "} + {formatDistanceToNow(new Date(apiKey.expiresAt), { + locale: getDateFnsLocaleByCode(i18n.language), + })}{" "} )}
{ try { @@ -99,12 +106,12 @@ export const ShowApiKeys = () => { apiKeyId: apiKey.id, }); await refetch(); - toast.success("API key deleted successfully"); + toast.success(t("settings.api.apiKeyDeleted")); } catch (error) { toast.error( error instanceof Error ? error.message - : "Error deleting API key", + : t("settings.api.errorDeletingApiKey"), ); } }} @@ -124,7 +131,7 @@ export const ShowApiKeys = () => {
- No API keys found + {t("settings.api.noApiKeysFound")}
)} diff --git a/apps/dokploy/lib/languages.ts b/apps/dokploy/lib/languages.ts index a19c9589..571eb103 100644 --- a/apps/dokploy/lib/languages.ts +++ b/apps/dokploy/lib/languages.ts @@ -1,25 +1,56 @@ +import { + enUS, + zhCN, + zhHK, + pl, + uk, + ru, + fr, + de, + tr, + ko, + ptBR, + it, + ja, + es, + az, + id, + kk, + faIR, + nb, +} from "date-fns/locale"; + export const Languages = { - english: { code: "en", name: "English" }, - polish: { code: "pl", name: "Polski" }, - ukrainian: { code: "uk", name: "Українська" }, - russian: { code: "ru", name: "Русский" }, - french: { code: "fr", name: "Français" }, - german: { code: "de", name: "Deutsch" }, - chineseTraditional: { code: "zh-Hant", name: "繁體中文" }, - chineseSimplified: { code: "zh-Hans", name: "简体中文" }, - turkish: { code: "tr", name: "Türkçe" }, - kazakh: { code: "kz", name: "Қазақ" }, - persian: { code: "fa", name: "فارسی" }, - korean: { code: "ko", name: "한국어" }, - portuguese: { code: "pt-br", name: "Português" }, - italian: { code: "it", name: "Italiano" }, - japanese: { code: "ja", name: "日本語" }, - spanish: { code: "es", name: "Español" }, - norwegian: { code: "no", name: "Norsk" }, - azerbaijani: { code: "az", name: "Azərbaycan" }, - indonesian: { code: "id", name: "Bahasa Indonesia" }, - malayalam: { code: "ml", name: "മലയാളം" }, + english: { code: "en", name: "English", dateFnsLocale: enUS }, + polish: { code: "pl", name: "Polski", dateFnsLocale: pl }, + ukrainian: { code: "uk", name: "Українська", dateFnsLocale: uk }, + russian: { code: "ru", name: "Русский", dateFnsLocale: ru }, + french: { code: "fr", name: "Français", dateFnsLocale: fr }, + german: { code: "de", name: "Deutsch", dateFnsLocale: de }, + chineseTraditional: { + code: "zh-Hant", + name: "繁體中文", + dateFnsLocale: zhHK, + }, + chineseSimplified: { code: "zh-Hans", name: "简体中文", dateFnsLocale: zhCN }, + turkish: { code: "tr", name: "Türkçe", dateFnsLocale: tr }, + kazakh: { code: "kz", name: "Қазақ", dateFnsLocale: kk }, + persian: { code: "fa", name: "فارسی", dateFnsLocale: faIR }, + korean: { code: "ko", name: "한국어", dateFnsLocale: ko }, + portuguese: { code: "pt-br", name: "Português", dateFnsLocale: ptBR }, + italian: { code: "it", name: "Italiano", dateFnsLocale: it }, + japanese: { code: "ja", name: "日本語", dateFnsLocale: ja }, + spanish: { code: "es", name: "Español", dateFnsLocale: es }, + norwegian: { code: "no", name: "Norsk", dateFnsLocale: nb }, + azerbaijani: { code: "az", name: "Azərbaycan", dateFnsLocale: az }, + indonesian: { code: "id", name: "Bahasa Indonesia", dateFnsLocale: id }, + malayalam: { code: "ml", name: "മലയാളം", dateFnsLocale: enUS }, }; +export function getDateFnsLocaleByCode(code: LanguageCode) { + const language = Object.values(Languages).find((lang) => lang.code === code); + return language ? language.dateFnsLocale : enUS; +} + export type Language = keyof typeof Languages; export type LanguageCode = (typeof Languages)[keyof typeof Languages]["code"]; diff --git a/apps/dokploy/public/locales/en/settings.json b/apps/dokploy/public/locales/en/settings.json index ee7961ee..28ef1a51 100644 --- a/apps/dokploy/public/locales/en/settings.json +++ b/apps/dokploy/public/locales/en/settings.json @@ -80,5 +80,74 @@ "settings.terminal.connectionSettings": "Connection settings", "settings.terminal.ipAddress": "IP Address", "settings.terminal.port": "Port", - "settings.terminal.username": "Username" + "settings.terminal.username": "Username", + + "settings.api.apiCliKeys": "API/CLI Keys", + "settings.api.generateAndManageKeys": "Generate and manage API keys to access the API/CLI", + "settings.api.swaggerApi": "Swagger API:", + "settings.api.view": "View", + "settings.api.created": "Created", + "settings.api.ago": "ago", + "settings.api.expiresIn": "Expires in", + "settings.api.deleteApiKey": "Delete API Key", + "settings.api.deleteApiKeyDescription": "Are you sure you want to delete this API key? This action cannot be undone.", + "settings.api.apiKeyDeleted": "API key deleted successfully", + "settings.api.errorDeletingApiKey": "Error deleting API key", + "settings.api.noApiKeysFound": "No API keys found", + "settings.api.errorGeneratingApiKey": "Failed to generate API key", + "settings.api.generateNewKey": "Generate New Key", + "settings.api.generateApiKey": "Generate API Key", + "settings.api.createNewApiKeyDescription": "Create a new API key for accessing the API. You can set an expiration date and a custom prefix for better organization.", + "settings.api.name": "Name", + "settings.api.namePlaceholder": "My API Key", + "settings.api.prefix": "Prefix", + "settings.api.prefixPlaceholder": "my_app", + "settings.api.expiration": "Expiration", + "settings.api.selectExpirationTime": "Select expiration time", + "settings.api.expirationOptions.Never": "Never", + "settings.api.expirationOptions.1 day": "1 day", + "settings.api.expirationOptions.7 days": "7 days", + "settings.api.expirationOptions.30 days": "30 days", + "settings.api.expirationOptions.90 days": "90 days", + "settings.api.expirationOptions.1 year": "1 year", + "settings.api.organization": "Organization", + "settings.api.selectOrganization": "Select organization", + "settings.api.rateLimiting": "Rate Limiting", + "settings.api.enableRateLimiting": "Enable Rate Limiting", + "settings.api.limitRequestsDescription": "Limit the number of requests within a time window", + "settings.api.timeWindow": "Time Window", + "settings.api.selectTimeWindow": "Select time window", + "settings.api.timeWindowOptions.1 minute": "1 minute", + "settings.api.timeWindowOptions.5 minutes": "5 minutes", + "settings.api.timeWindowOptions.15 minutes": "15 minutes", + "settings.api.timeWindowOptions.30 minutes": "30 minutes", + "settings.api.timeWindowOptions.1 hour": "1 hour", + "settings.api.timeWindowOptions.1 day": "1 day", + "settings.api.timeWindowDescription": "The duration in which requests are counted", + "settings.api.maxRequests": "Maximum Requests", + "settings.api.maxRequestsPlaceholder": "100", + "settings.api.maxRequestsDescription": "Maximum number of requests allowed within the time window", + "settings.api.requestLimiting": "Request Limiting", + "settings.api.totalRequestLimit": "Total Request Limit", + "settings.api.totalRequestLimitPlaceholder": "Leave empty for unlimited", + "settings.api.totalRequestLimitDescription": "Total number of requests allowed (leave empty for unlimited)", + "settings.api.refillAmount": "Refill Amount", + "settings.api.refillAmountPlaceholder": "Amount to refill", + "settings.api.refillAmountDescription": "Number of requests to add on each refill", + "settings.api.refillInterval": "Refill Interval", + "settings.api.selectRefillInterval": "Select refill interval", + "settings.api.refillIntervalOptions.1 hour": "1 hour", + "settings.api.refillIntervalOptions.6 hours": "6 hours", + "settings.api.refillIntervalOptions.12 hours": "12 hours", + "settings.api.refillIntervalOptions.1 day": "1 day", + "settings.api.refillIntervalOptions.7 days": "7 days", + "settings.api.refillIntervalOptions.30 days": "30 days", + "settings.api.refillIntervalDescription": "How often to refill the request limit", + "settings.api.cancel": "Cancel", + "settings.api.generate": "Generate", + "settings.api.apiKeyGeneratedSuccessfully": "API Key Generated Successfully", + "settings.api.copyApiKeyNow": "Please copy your API key now. You won't be able to see it again!", + "settings.api.apiKeyCopied": "API key copied to clipboard", + "settings.api.copyToClipboard": "Copy to Clipboard", + "settings.api.close": "Close" } diff --git a/apps/dokploy/public/locales/zh-Hans/settings.json b/apps/dokploy/public/locales/zh-Hans/settings.json index c16365e4..964e97e6 100644 --- a/apps/dokploy/public/locales/zh-Hans/settings.json +++ b/apps/dokploy/public/locales/zh-Hans/settings.json @@ -80,5 +80,74 @@ "settings.terminal.connectionSettings": "终端设置", "settings.terminal.ipAddress": "IP", "settings.terminal.port": "端口", - "settings.terminal.username": "用户名" + "settings.terminal.username": "用户名", + + "settings.api.apiCliKeys": "API/CLI 密钥", + "settings.api.generateAndManageKeys": "生成和管理 API 密钥以访问 API/CLI", + "settings.api.swaggerApi": "Swagger API:", + "settings.api.view": "查看", + "settings.api.created": "创建于", + "settings.api.ago": "前", + "settings.api.expiresIn": "过期于", + "settings.api.deleteApiKey": "删除 API 密钥", + "settings.api.deleteApiKeyDescription": "您确定要删除此 API 密钥吗?此操作无法撤销。", + "settings.api.apiKeyDeleted": "API 密钥删除成功", + "settings.api.errorDeletingApiKey": "删除 API 密钥时出错", + "settings.api.noApiKeysFound": "未找到 API 密钥", + "settings.api.errorGeneratingApiKey": "生成 API 密钥失败", + "settings.api.generateNewKey": "生成新密钥", + "settings.api.generateApiKey": "生成 API 密钥", + "settings.api.createNewApiKeyDescription": "创建一个新的 API 密钥以访问 API。您可以设置过期日期和自定义前缀以便更好地组织。", + "settings.api.name": "名称", + "settings.api.namePlaceholder": "我的 API 密钥", + "settings.api.prefix": "前缀", + "settings.api.prefixPlaceholder": "我的应用", + "settings.api.expiration": "过期时间", + "settings.api.selectExpirationTime": "选择过期时间", + "settings.api.expirationOptions.Never": "从不过期", + "settings.api.expirationOptions.1 day": "1 天", + "settings.api.expirationOptions.7 days": "7 天", + "settings.api.expirationOptions.30 days": "30 天", + "settings.api.expirationOptions.90 days": "90 天", + "settings.api.expirationOptions.1 year": "1 年", + "settings.api.organization": "组织", + "settings.api.selectOrganization": "选择组织", + "settings.api.rateLimiting": "速率限制", + "settings.api.enableRateLimiting": "启用速率限制", + "settings.api.limitRequestsDescription": "限制在时间窗口内的请求数量", + "settings.api.timeWindow": "时间窗口", + "settings.api.selectTimeWindow": "选择时间窗口", + "settings.api.timeWindowOptions.1 minute": "1 分钟", + "settings.api.timeWindowOptions.5 minutes": "5 分钟", + "settings.api.timeWindowOptions.15 minutes": "15 分钟", + "settings.api.timeWindowOptions.30 minutes": "30 分钟", + "settings.api.timeWindowOptions.1 hour": "1 小时", + "settings.api.timeWindowOptions.1 day": "1 天", + "settings.api.timeWindowDescription": "请求计数的持续时间", + "settings.api.maxRequests": "最大请求数", + "settings.api.maxRequestsPlaceholder": "100", + "settings.api.maxRequestsDescription": "时间窗口内允许的最大请求数", + "settings.api.requestLimiting": "请求限制", + "settings.api.totalRequestLimit": "总请求限制", + "settings.api.totalRequestLimitPlaceholder": "留空表示无限制", + "settings.api.totalRequestLimitDescription": "允许的总请求数(留空表示无限制)", + "settings.api.refillAmount": "补充数量", + "settings.api.refillAmountPlaceholder": "补充数量", + "settings.api.refillAmountDescription": "每次补充时添加的请求数量", + "settings.api.refillInterval": "补充间隔", + "settings.api.selectRefillInterval": "选择补充间隔", + "settings.api.refillIntervalOptions.1 hour": "1 小时", + "settings.api.refillIntervalOptions.6 hours": "6 小时", + "settings.api.refillIntervalOptions.12 hours": "12 小时", + "settings.api.refillIntervalOptions.1 day": "1 天", + "settings.api.refillIntervalOptions.7 days": "7 天", + "settings.api.refillIntervalOptions.30 days": "30 天", + "settings.api.refillIntervalDescription": "请求限制的补充频率", + "settings.api.cancel": "取消", + "settings.api.generate": "生成", + "settings.api.apiKeyGeneratedSuccessfully": "API 密钥生成成功", + "settings.api.copyApiKeyNow": "请立即复制您的 API 密钥。您将无法再次查看它!", + "settings.api.apiKeyCopied": "API 密钥已复制到剪贴板", + "settings.api.copyToClipboard": "复制到剪贴板", + "settings.api.close": "关闭" }