mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat(i18n): add internationalization support for 2FA setup and error messages
This commit is contained in:
parent
2bced3e9b6
commit
6df680e9da
@ -26,6 +26,7 @@ import { authClient } from "@/lib/auth-client";
|
|||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { Fingerprint, QrCode } from "lucide-react";
|
import { Fingerprint, QrCode } from "lucide-react";
|
||||||
|
import { useTranslation } from "next-i18next";
|
||||||
import QRCode from "qrcode";
|
import QRCode from "qrcode";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
@ -55,6 +56,7 @@ type PinForm = z.infer<typeof PinSchema>;
|
|||||||
|
|
||||||
export const Enable2FA = () => {
|
export const Enable2FA = () => {
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
|
const { t } = useTranslation("settings");
|
||||||
const [data, setData] = useState<TwoFactorSetupData | null>(null);
|
const [data, setData] = useState<TwoFactorSetupData | null>(null);
|
||||||
const [backupCodes, setBackupCodes] = useState<string[]>([]);
|
const [backupCodes, setBackupCodes] = useState<string[]>([]);
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
@ -86,16 +88,18 @@ export const Enable2FA = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setStep("verify");
|
setStep("verify");
|
||||||
toast.success("Scan the QR code with your authenticator app");
|
toast.success(t("settings.2fa.scanQrCode"));
|
||||||
} else {
|
} else {
|
||||||
throw new Error("No TOTP URI received from server");
|
throw new Error("No TOTP URI received from server");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(
|
toast.error(
|
||||||
error instanceof Error ? error.message : "Error setting up 2FA",
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: t("settings.2fa.errorSettingUp")
|
||||||
);
|
);
|
||||||
passwordForm.setError("password", {
|
passwordForm.setError("password", {
|
||||||
message: "Error verifying password",
|
message: t("settings.2fa.errorVerifyingPassword"),
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsPasswordLoading(false);
|
setIsPasswordLoading(false);
|
||||||
@ -111,9 +115,9 @@ export const Enable2FA = () => {
|
|||||||
if (result.error) {
|
if (result.error) {
|
||||||
if (result.error.code === "INVALID_TWO_FACTOR_AUTHENTICATION") {
|
if (result.error.code === "INVALID_TWO_FACTOR_AUTHENTICATION") {
|
||||||
pinForm.setError("pin", {
|
pinForm.setError("pin", {
|
||||||
message: "Invalid code. Please try again.",
|
message: t("settings.2fa.invalidCode"),
|
||||||
});
|
});
|
||||||
toast.error("Invalid verification code");
|
toast.error(t("settings.2fa.invalidVerificationCode"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,14 +128,14 @@ export const Enable2FA = () => {
|
|||||||
throw new Error("No response received from server");
|
throw new Error("No response received from server");
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success("2FA configured successfully");
|
toast.success(t("settings.2fa.success"));
|
||||||
utils.user.get.invalidate();
|
utils.user.get.invalidate();
|
||||||
setIsDialogOpen(false);
|
setIsDialogOpen(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
error.message === "Failed to fetch"
|
error.message === "Failed to fetch"
|
||||||
? "Connection error. Please check your internet connection."
|
? t("settings.2fa.connectionError")
|
||||||
: error.message;
|
: error.message;
|
||||||
|
|
||||||
pinForm.setError("pin", {
|
pinForm.setError("pin", {
|
||||||
@ -140,9 +144,9 @@ export const Enable2FA = () => {
|
|||||||
toast.error(errorMessage);
|
toast.error(errorMessage);
|
||||||
} else {
|
} else {
|
||||||
pinForm.setError("pin", {
|
pinForm.setError("pin", {
|
||||||
message: "Error verifying code",
|
message: t("settings.2fa.errorVerifyingCode"),
|
||||||
});
|
});
|
||||||
toast.error("Error verifying 2FA code");
|
toast.error(t("settings.2fa.errorVerifying2faCode"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -176,16 +180,16 @@ export const Enable2FA = () => {
|
|||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="ghost">
|
<Button variant="ghost">
|
||||||
<Fingerprint className="size-4 text-muted-foreground" />
|
<Fingerprint className="size-4 text-muted-foreground" />
|
||||||
Enable 2FA
|
{t("settings.2fa.enable2fa")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-xl">
|
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>2FA Setup</DialogTitle>
|
<DialogTitle>{t("settings.2fa.title")}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{step === "password"
|
{step === "password"
|
||||||
? "Enter your password to begin 2FA setup"
|
? t("settings.2fa.enterPassword")
|
||||||
: "Scan the QR code and verify with your authenticator app"}
|
: t("settings.2fa.scanQrCodeAndVerify")}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
@ -201,16 +205,18 @@ export const Enable2FA = () => {
|
|||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Password</FormLabel>
|
<FormLabel>
|
||||||
|
{t("settings.2fa.password")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Enter your password"
|
placeholder={t("settings.2fa.enterPasswordPlaceholder")}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Enter your password to enable 2FA
|
{t("settings.2fa.enterPasswordDescription")}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@ -221,7 +227,7 @@ export const Enable2FA = () => {
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
isLoading={isPasswordLoading}
|
isLoading={isPasswordLoading}
|
||||||
>
|
>
|
||||||
Continue
|
{t("settings.2fa.continue")}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
@ -238,16 +244,16 @@ export const Enable2FA = () => {
|
|||||||
<div className="flex flex-col items-center gap-4 p-6 border rounded-lg">
|
<div className="flex flex-col items-center gap-4 p-6 border rounded-lg">
|
||||||
<QrCode className="size-5 text-muted-foreground" />
|
<QrCode className="size-5 text-muted-foreground" />
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
Scan this QR code with your authenticator app
|
{t("settings.2fa.scanQrCode")}
|
||||||
</span>
|
</span>
|
||||||
<img
|
<img
|
||||||
src={data.qrCodeUrl}
|
src={data.qrCodeUrl}
|
||||||
alt="2FA QR Code"
|
alt={t("settings.2fa.qrCodeAlt")}
|
||||||
className="rounded-lg w-48 h-48"
|
className="rounded-lg w-48 h-48"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-2 text-center">
|
<div className="flex flex-col gap-2 text-center">
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
Can't scan the QR code?
|
{t("settings.2fa.cantScanQrCode")}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs font-mono bg-muted p-2 rounded">
|
<span className="text-xs font-mono bg-muted p-2 rounded">
|
||||||
{data.secret}
|
{data.secret}
|
||||||
@ -257,7 +263,9 @@ export const Enable2FA = () => {
|
|||||||
|
|
||||||
{backupCodes && backupCodes.length > 0 && (
|
{backupCodes && backupCodes.length > 0 && (
|
||||||
<div className="w-full space-y-3 border rounded-lg p-4">
|
<div className="w-full space-y-3 border rounded-lg p-4">
|
||||||
<h4 className="font-medium">Backup Codes</h4>
|
<h4 className="font-medium">
|
||||||
|
{t("settings.2fa.backupCodes")}
|
||||||
|
</h4>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
{backupCodes.map((code, index) => (
|
{backupCodes.map((code, index) => (
|
||||||
<code
|
<code
|
||||||
@ -269,9 +277,7 @@ export const Enable2FA = () => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Save these backup codes in a secure place. You can use
|
{t("settings.2fa.saveBackupCodes")}
|
||||||
them to access your account if you lose access to your
|
|
||||||
authenticator device.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -288,7 +294,9 @@ export const Enable2FA = () => {
|
|||||||
name="pin"
|
name="pin"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-col justify-center items-center">
|
<FormItem className="flex flex-col justify-center items-center">
|
||||||
<FormLabel>Verification Code</FormLabel>
|
<FormLabel>
|
||||||
|
{t("settings.2fa.verificationCode")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<InputOTP maxLength={6} {...field}>
|
<InputOTP maxLength={6} {...field}>
|
||||||
<InputOTPGroup>
|
<InputOTPGroup>
|
||||||
@ -302,7 +310,7 @@ export const Enable2FA = () => {
|
|||||||
</InputOTP>
|
</InputOTP>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Enter the 6-digit code from your authenticator app
|
{t("settings.2fa.enterVerificationCode")}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@ -314,7 +322,7 @@ export const Enable2FA = () => {
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
isLoading={isPasswordLoading}
|
isLoading={isPasswordLoading}
|
||||||
>
|
>
|
||||||
Enable 2FA
|
{t("settings.2fa.enable2fa")}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -43,6 +43,30 @@
|
|||||||
"settings.profile.password": "Password",
|
"settings.profile.password": "Password",
|
||||||
"settings.profile.avatar": "Avatar",
|
"settings.profile.avatar": "Avatar",
|
||||||
|
|
||||||
|
"settings.2fa.enable2fa": "Enable 2FA",
|
||||||
|
"settings.2fa.title": "2FA Setup",
|
||||||
|
"settings.2fa.enterPassword": "Enter your password to begin 2FA setup",
|
||||||
|
"settings.2fa.scanQrCodeAndVerify": "Scan the QR code and verify with your authenticator app",
|
||||||
|
"settings.2fa.password": "Password",
|
||||||
|
"settings.2fa.enterPasswordPlaceholder": "Enter your password",
|
||||||
|
"settings.2fa.enterPasswordDescription": "Enter your password to enable 2FA",
|
||||||
|
"settings.2fa.continue": "Continue",
|
||||||
|
"settings.2fa.scanQrCode": "Scan this QR code with your authenticator app",
|
||||||
|
"settings.2fa.qrCodeAlt": "2FA QR Code",
|
||||||
|
"settings.2fa.cantScanQrCode": "Can't scan the QR code?",
|
||||||
|
"settings.2fa.backupCodes": "Backup Codes",
|
||||||
|
"settings.2fa.saveBackupCodes": "Save these backup codes in a secure place. You can use them to access your account if you lose access to your authenticator device.",
|
||||||
|
"settings.2fa.verificationCode": "Verification Code",
|
||||||
|
"settings.2fa.enterVerificationCode": "Enter the 6-digit code from your authenticator app",
|
||||||
|
"settings.2fa.errorSettingUp": "Error setting up 2FA",
|
||||||
|
"settings.2fa.errorVerifyingPassword": "Error verifying password",
|
||||||
|
"settings.2fa.invalidCode": "Invalid code. Please try again.",
|
||||||
|
"settings.2fa.invalidVerificationCode": "Invalid verification code",
|
||||||
|
"settings.2fa.success": "2FA configured successfully",
|
||||||
|
"settings.2fa.connectionError": "Connection error. Please check your internet connection.",
|
||||||
|
"settings.2fa.errorVerifyingCode": "Error verifying code",
|
||||||
|
"settings.2fa.errorVerifying2faCode": "Error verifying 2FA code",
|
||||||
|
|
||||||
"settings.appearance.title": "Appearance",
|
"settings.appearance.title": "Appearance",
|
||||||
"settings.appearance.description": "Customize the theme of your dashboard.",
|
"settings.appearance.description": "Customize the theme of your dashboard.",
|
||||||
"settings.appearance.theme": "Theme",
|
"settings.appearance.theme": "Theme",
|
||||||
|
@ -43,6 +43,30 @@
|
|||||||
"settings.profile.password": "密码",
|
"settings.profile.password": "密码",
|
||||||
"settings.profile.avatar": "头像",
|
"settings.profile.avatar": "头像",
|
||||||
|
|
||||||
|
"settings.2fa.enable2fa": "启用 2FA",
|
||||||
|
"settings.2fa.title": "2FA 设置",
|
||||||
|
"settings.2fa.enterPassword": "输入您的密码以开始 2FA 设置",
|
||||||
|
"settings.2fa.scanQrCodeAndVerify": "扫描二维码并使用您的身份验证器应用程序进行验证",
|
||||||
|
"settings.2fa.password": "密码",
|
||||||
|
"settings.2fa.enterPasswordPlaceholder": "输入您的密码",
|
||||||
|
"settings.2fa.enterPasswordDescription": "输入您的密码以启用 2FA",
|
||||||
|
"settings.2fa.continue": "继续",
|
||||||
|
"settings.2fa.scanQrCode": "使用您的身份验证器应用程序扫描此二维码",
|
||||||
|
"settings.2fa.qrCodeAlt": "2FA 二维码",
|
||||||
|
"settings.2fa.cantScanQrCode": "无法扫描二维码?",
|
||||||
|
"settings.2fa.backupCodes": "备份代码",
|
||||||
|
"settings.2fa.saveBackupCodes": "将这些备份代码保存在安全的地方。如果您丢失了身份验证设备,可以使用它们访问您的帐户。",
|
||||||
|
"settings.2fa.verificationCode": "验证码",
|
||||||
|
"settings.2fa.enterVerificationCode": "输入您的身份验证器应用程序中的 6 位数代码",
|
||||||
|
"settings.2fa.errorSettingUp": "设置 2FA 时出错",
|
||||||
|
"settings.2fa.errorVerifyingPassword": "验证密码时出错",
|
||||||
|
"settings.2fa.invalidCode": "无效的代码。请再试一次。",
|
||||||
|
"settings.2fa.invalidVerificationCode": "无效的验证码",
|
||||||
|
"settings.2fa.success": "2FA 配置成功",
|
||||||
|
"settings.2fa.connectionError": "连接错误。请检查您的互联网连接。",
|
||||||
|
"settings.2fa.errorVerifyingCode": "验证代码时出错",
|
||||||
|
"settings.2fa.errorVerifying2faCode": "验证 2FA 代码时出错",
|
||||||
|
|
||||||
"settings.appearance.title": "外观",
|
"settings.appearance.title": "外观",
|
||||||
"settings.appearance.description": "自定义面板主题",
|
"settings.appearance.description": "自定义面板主题",
|
||||||
"settings.appearance.theme": "主题",
|
"settings.appearance.theme": "主题",
|
||||||
|
Loading…
Reference in New Issue
Block a user