🌐 feat(i18n): add internationalization support for TwoFASetting component

- Add comprehensive i18n support to TwoFASetting.js component
- Add all required English translations to en.json for 2FA settings
- Update component to accept t function as prop and use translation keys
- Fix prop passing in PersonalSetting.js to provide t function
- Maintain all existing UI improvements and functionality
- Support both Chinese and English interfaces for:
  * Main 2FA settings card with status indicators
  * Setup modal with guided steps (QR scan, backup codes, verification)
  * Disable 2FA modal with impact warnings and confirmation
  * Regenerate backup codes modal with success states
  * All buttons, placeholders, messages, and notifications
- Follow project i18n conventions using t('key') pattern
- Ensure seamless language switching for enhanced user experience

This enables the 2FA settings to be fully localized while preserving
the modern UI design and improved user workflow from previous updates.
This commit is contained in:
t0ng7u
2025-08-16 19:22:14 +08:00
parent aa1f5c6e4e
commit 9934cdc5bd
3 changed files with 119 additions and 64 deletions

View File

@@ -1043,7 +1043,7 @@ const PersonalSetting = () => {
</Card>
{/* 两步验证设置 */}
<TwoFASetting />
<TwoFASetting t={t} />
{/* 危险区域 */}
<Card

View File

@@ -30,7 +30,7 @@ import { QRCodeSVG } from 'qrcode.react';
const { Text, Paragraph } = Typography;
const TwoFASetting = () => {
const TwoFASetting = ({ t }) => {
const [loading, setLoading] = useState(false);
const [status, setStatus] = useState({
enabled: false,
@@ -59,7 +59,7 @@ const TwoFASetting = () => {
setStatus(res.data.data);
}
} catch (error) {
showError('获取2FA状态失败');
showError(t('获取2FA状态失败'));
}
};
@@ -80,7 +80,7 @@ const TwoFASetting = () => {
showError(res.data.message);
}
} catch (error) {
showError('设置2FA失败');
showError(t('设置2FA失败'));
} finally {
setLoading(false);
}
@@ -89,7 +89,7 @@ const TwoFASetting = () => {
// 启用2FA
const handleEnable2FA = async () => {
if (!verificationCode) {
showWarning('请输入验证码');
showWarning(t('请输入验证码'));
return;
}
@@ -99,7 +99,7 @@ const TwoFASetting = () => {
code: verificationCode
});
if (res.data.success) {
showSuccess('两步验证启用成功!');
showSuccess(t('两步验证启用成功!'));
setEnableModalVisible(false);
setSetupModalVisible(false);
setVerificationCode('');
@@ -109,7 +109,7 @@ const TwoFASetting = () => {
showError(res.data.message);
}
} catch (error) {
showError('启用2FA失败');
showError(t('启用2FA失败'));
} finally {
setLoading(false);
}
@@ -118,12 +118,12 @@ const TwoFASetting = () => {
// 禁用2FA
const handleDisable2FA = async () => {
if (!verificationCode) {
showWarning('请输入验证码或备用码');
showWarning(t('请输入验证码或备用码'));
return;
}
if (!confirmDisable) {
showWarning('请确认您已了解禁用两步验证的后果');
showWarning(t('请确认您已了解禁用两步验证的后果'));
return;
}
@@ -133,7 +133,7 @@ const TwoFASetting = () => {
code: verificationCode
});
if (res.data.success) {
showSuccess('两步验证已禁用');
showSuccess(t('两步验证已禁用'));
setDisableModalVisible(false);
setVerificationCode('');
setConfirmDisable(false);
@@ -142,7 +142,7 @@ const TwoFASetting = () => {
showError(res.data.message);
}
} catch (error) {
showError('禁用2FA失败');
showError(t('禁用2FA失败'));
} finally {
setLoading(false);
}
@@ -151,7 +151,7 @@ const TwoFASetting = () => {
// 重新生成备用码
const handleRegenerateBackupCodes = async () => {
if (!verificationCode) {
showWarning('请输入验证码');
showWarning(t('请输入验证码'));
return;
}
@@ -162,31 +162,31 @@ const TwoFASetting = () => {
});
if (res.data.success) {
setBackupCodes(res.data.data.backup_codes);
showSuccess('备用码重新生成成功');
showSuccess(t('备用码重新生成成功'));
setVerificationCode('');
fetchStatus();
} else {
showError(res.data.message);
}
} catch (error) {
showError('重新生成备用码失败');
showError(t('重新生成备用码失败'));
} finally {
setLoading(false);
}
};
// 通用复制函数
const copyTextToClipboard = (text, successMessage = '已复制到剪贴板') => {
const copyTextToClipboard = (text, successMessage = t('已复制到剪贴板')) => {
navigator.clipboard.writeText(text).then(() => {
showSuccess(successMessage);
}).catch(() => {
showError('复制失败,请手动复制');
showError(t('复制失败,请手动复制'));
});
};
const copyBackupCodes = () => {
const codesText = backupCodes.join('\n');
copyTextToClipboard(codesText, '备用码已复制到剪贴板');
copyTextToClipboard(codesText, t('备用码已复制到剪贴板'));
};
// 备用码展示组件
@@ -229,7 +229,7 @@ const TwoFASetting = () => {
onClick={onCopy}
className="!rounded-lg !bg-slate-600 hover:!bg-slate-700 w-full"
>
复制所有代码
{t('复制所有代码')}
</Button>
</div>
</Card>
@@ -245,7 +245,7 @@ const TwoFASetting = () => {
onClick={() => setCurrentStep(currentStep - 1)}
className="!rounded-lg"
>
上一步
{t('上一步')}
</Button>
)}
{currentStep < 2 ? (
@@ -255,7 +255,7 @@ const TwoFASetting = () => {
onClick={() => setCurrentStep(currentStep + 1)}
className="!rounded-lg !bg-slate-600 hover:!bg-slate-700"
>
下一步
{t('下一步')}
</Button>
) : (
<Button
@@ -264,14 +264,14 @@ const TwoFASetting = () => {
loading={loading}
onClick={() => {
if (!verificationCode) {
showWarning('请输入验证码');
showWarning(t('请输入验证码'));
return;
}
handleEnable2FA();
}}
className="!rounded-lg !bg-slate-600 hover:!bg-slate-700"
>
完成设置并启用两步验证
{t('完成设置并启用两步验证')}
</Button>
)}
</>
@@ -290,7 +290,7 @@ const TwoFASetting = () => {
}}
className="!rounded-lg"
>
取消
{t('取消')}
</Button>
<Button
type="danger"
@@ -300,7 +300,7 @@ const TwoFASetting = () => {
onClick={handleDisable2FA}
className="!rounded-lg !bg-slate-500 hover:!bg-slate-600"
>
确认禁用
{t('确认禁用')}
</Button>
</>
);
@@ -320,7 +320,7 @@ const TwoFASetting = () => {
}}
className="!rounded-lg !bg-slate-600 hover:!bg-slate-700"
>
完成
{t('完成')}
</Button>
);
}
@@ -335,7 +335,7 @@ const TwoFASetting = () => {
}}
className="!rounded-lg"
>
取消
{t('取消')}
</Button>
<Button
type="primary"
@@ -345,7 +345,7 @@ const TwoFASetting = () => {
onClick={handleRegenerateBackupCodes}
className="!rounded-lg !bg-slate-600 hover:!bg-slate-700"
>
生成新的备用码
{t('生成新的备用码')}
</Button>
</>
);
@@ -366,23 +366,23 @@ const TwoFASetting = () => {
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<Typography.Title heading={6} className="mb-0">
两步验证设置
{t('两步验证设置')}
</Typography.Title>
{status.enabled ? (
<Tag color="green" shape="circle" size="small">已启用</Tag>
<Tag color="green" shape="circle" size="small">{t('已启用')}</Tag>
) : (
<Tag color="red" shape="circle" size="small">未启用</Tag>
<Tag color="red" shape="circle" size="small">{t('未启用')}</Tag>
)}
{status.locked && (
<Tag color="orange" shape="circle" size="small">账户已锁定</Tag>
<Tag color="orange" shape="circle" size="small">{t('账户已锁定')}</Tag>
)}
</div>
<Typography.Text type="tertiary" className="text-sm">
两步验证2FA为您的账户提供额外的安全保护启用后登录时需要输入密码和验证器应用生成的验证码
{t('两步验证2FA为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。')}
</Typography.Text>
{status.enabled && (
<div className="mt-2">
<Text size="small" type="secondary">剩余备用码{status.backup_codes_remaining || 0} </Text>
<Text size="small" type="secondary">{t('剩余备用码:')}{status.backup_codes_remaining || 0}{t('个')}</Text>
</div>
)}
</div>
@@ -398,7 +398,7 @@ const TwoFASetting = () => {
className="!rounded-lg !bg-slate-600 hover:!bg-slate-700"
icon={<IconShield />}
>
启用验证
{t('启用验证')}
</Button>
) : (
<div className="flex flex-col space-y-2">
@@ -410,7 +410,7 @@ const TwoFASetting = () => {
className="!rounded-lg !bg-slate-500 hover:!bg-slate-600"
icon={<IconAlertTriangle />}
>
禁用两步验证
{t('禁用两步验证')}
</Button>
<Button
type="primary"
@@ -420,7 +420,7 @@ const TwoFASetting = () => {
className="!rounded-lg"
icon={<IconRefresh />}
>
重新生成备用码
{t('重新生成备用码')}
</Button>
</div>
)}
@@ -433,7 +433,7 @@ const TwoFASetting = () => {
title={
<div className="flex items-center">
<IconShield className="mr-2 text-slate-600" />
设置两步验证
{t('设置两步验证')}
</div>
}
visible={setupModalVisible}
@@ -451,9 +451,9 @@ const TwoFASetting = () => {
<div className="space-y-6">
{/* 步骤进度 */}
<Steps type="basic" size="small" current={currentStep}>
<Steps.Step title="扫描二维码" description="使用认证器应用扫描二维码" />
<Steps.Step title="保存备用码" description="保存备用码以备不时之需" />
<Steps.Step title="验证设置" description="输入验证码完成设置" />
<Steps.Step title={t('扫描二维码')} description={t('使用认证器应用扫描二维码')} />
<Steps.Step title={t('保存备用码')} description={t('保存备用码以备不时之需')} />
<Steps.Step title={t('验证设置')} description={t('输入验证码完成设置')} />
</Steps>
{/* 步骤内容 */}
@@ -461,7 +461,7 @@ const TwoFASetting = () => {
{currentStep === 0 && (
<div>
<Paragraph className="text-gray-600 dark:text-gray-300 mb-4">
使用认证器应用 Google AuthenticatorMicrosoft Authenticator扫描下方二维码
{t('使用认证器应用(如 Google AuthenticatorMicrosoft Authenticator扫描下方二维码:')}
</Paragraph>
<div className="flex justify-center mb-4">
<div className="bg-white p-4 rounded-lg shadow-sm">
@@ -470,7 +470,7 @@ const TwoFASetting = () => {
</div>
<div className="bg-blue-50 dark:bg-blue-900 rounded-lg p-3">
<Text className="text-blue-800 dark:text-blue-200 text-sm">
或手动输入密钥<Text code copyable className="ml-2">{setupData.secret}</Text>
{t('或手动输入密钥:')}<Text code copyable className="ml-2">{setupData.secret}</Text>
</Text>
</div>
</div>
@@ -481,10 +481,10 @@ const TwoFASetting = () => {
{/* 备用码展示 */}
<BackupCodesDisplay
codes={setupData.backup_codes}
title="备用恢复代码"
title={t('备用恢复代码')}
onCopy={() => {
const codesText = setupData.backup_codes.join('\n');
copyTextToClipboard(codesText, '备用码已复制到剪贴板');
copyTextToClipboard(codesText, t('备用码已复制到剪贴板'));
}}
/>
</div>
@@ -492,7 +492,7 @@ const TwoFASetting = () => {
{currentStep === 2 && (
<Input
placeholder="输入认证器应用显示的6位数字验证码"
placeholder={t('输入认证器应用显示的6位数字验证码')}
value={verificationCode}
onChange={setVerificationCode}
size="large"
@@ -510,7 +510,7 @@ const TwoFASetting = () => {
title={
<div className="flex items-center">
<IconAlertTriangle className="mr-2 text-red-500" />
禁用两步验证
{t('禁用两步验证')}
</div>
}
visible={disableModalVisible}
@@ -528,7 +528,7 @@ const TwoFASetting = () => {
<div className="rounded-xl">
<Banner
type="warning"
description="警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!"
description={t('警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!')}
className="!rounded-lg"
/>
</div>
@@ -537,24 +537,24 @@ const TwoFASetting = () => {
<div className="space-y-4">
<div>
<Text strong className="block mb-2 text-slate-700 dark:text-slate-200">
禁用后的影响
{t('禁用后的影响:')}
</Text>
<ul className="space-y-2 text-sm text-slate-600 dark:text-slate-300">
<li className="flex items-start gap-2">
<Badge dot type='warning' />
降低您账户的安全性
{t('降低您账户的安全性')}
</li>
<li className="flex items-start gap-2">
<Badge dot type='warning' />
需要重新完整设置才能再次启用
{t('需要重新完整设置才能再次启用')}
</li>
<li className="flex items-start gap-2">
<Badge dot type='danger' />
永久删除您的两步验证设置
{t('永久删除您的两步验证设置')}
</li>
<li className="flex items-start gap-2">
<Badge dot type='danger' />
永久删除所有备用码包括未使用的
{t('永久删除所有备用码包括未使用的)')}
</li>
</ul>
</div>
@@ -564,10 +564,10 @@ const TwoFASetting = () => {
<div className="space-y-4">
<div>
<Text strong className="block mb-2 text-slate-700 dark:text-slate-200">
验证身份
{t('验证身份')}
</Text>
<Input
placeholder="请输入认证器验证码或备用码"
placeholder={t('请输入认证器验证码或备用码')}
value={verificationCode}
onChange={setVerificationCode}
size="large"
@@ -581,7 +581,7 @@ const TwoFASetting = () => {
onChange={(e) => setConfirmDisable(e.target.checked)}
className="text-sm"
>
我已了解禁用两步验证将永久删除所有相关设置和备用码此操作不可撤销
{t('我已了解禁用两步验证将永久删除所有相关设置和备用码此操作不可撤销')}
</Checkbox>
</div>
</div>
@@ -594,7 +594,7 @@ const TwoFASetting = () => {
title={
<div className="flex items-center">
<IconRefresh className="mr-2 text-slate-600" />
重新生成备用码
{t('重新生成备用码')}
</div>
}
visible={backupModalVisible}
@@ -614,7 +614,7 @@ const TwoFASetting = () => {
<div className="rounded-xl">
<Banner
type="warning"
description="重新生成备用码将使现有的备用码失效,请确保您已保存了当前的备用码。"
description={t('重新生成备用码将使现有的备用码失效,请确保您已保存了当前的备用码。')}
className="!rounded-lg"
/>
</div>
@@ -623,10 +623,10 @@ const TwoFASetting = () => {
<div className="space-y-4">
<div>
<Text strong className="block mb-2 text-slate-700 dark:text-slate-200">
验证身份
{t('验证身份')}
</Text>
<Input
placeholder="请输入认证器验证码"
placeholder={t('请输入认证器验证码')}
value={verificationCode}
onChange={setVerificationCode}
size="large"
@@ -642,17 +642,17 @@ const TwoFASetting = () => {
<div className="flex items-center justify-center gap-2">
<Badge dot type='success' />
<Text strong className="text-lg text-slate-700 dark:text-slate-200">
新的备用码已生成
{t('新的备用码已生成')}
</Text>
</div>
<Text className="text-slate-500 dark:text-slate-400 text-sm">
旧的备用码已失效请保存新的备用码
{t('旧的备用码已失效请保存新的备用码')}
</Text>
{/* 备用码展示 */}
<BackupCodesDisplay
codes={backupCodes}
title="新的备用恢复代码"
title={t('新的备用恢复代码')}
onCopy={copyBackupCodes}
/>
</Space>

View File

@@ -697,7 +697,7 @@
"令牌分组": "Token grouping",
"隐": "hidden",
"本站当前已启用模型": "The model is currently enabled on this site",
"个": "indivual",
"个": " indivual",
"倍率是本站的计算方式,不同模型有着不同的倍率,并非官方价格的多少倍,请务必知晓。": "The magnification is the calculation method of this website. Different models have different magnifications, which are not multiples of the official price. Please be sure to know.",
"所有各厂聊天模型请统一使用OpenAI方式请求支持OpenAI官方库<br/>Claude()Claude官方格式请求": "Please use the OpenAI method to request all chat models from each factory, and support the OpenAI official library<br/>Claude()Claude official format request",
"分组说明": "Group description",
@@ -1918,5 +1918,60 @@
"后缀名称匹配": "Suffix name matching",
"包含名称匹配": "Contains name matching",
"展开更多": "Expand more",
"已切换至最优倍率视图,每个模型使用其最低倍率分组": "Switched to the optimal ratio view, each model uses its lowest ratio group"
"已切换至最优倍率视图,每个模型使用其最低倍率分组": "Switched to the optimal ratio view, each model uses its lowest ratio group",
"两步验证设置": "Two-factor authentication settings",
"两步验证2FA为您的账户提供额外的安全保护。启用后登录时需要输入密码和验证器应用生成的验证码。": "Two-factor authentication (2FA) provides additional security protection for your account. After enabling, you need to enter your password and the verification code generated by the authenticator application when logging in.",
"启用两步验证": "Enable two-factor authentication",
"禁用两步验证": "Disable two-factor authentication",
"启用两步验证后,登录时需要输入密码和验证器应用生成的验证码": "After enabling two-factor authentication, you need to enter your password and the verification code generated by the authenticator application when logging in",
"禁用两步验证后,登录时只需要输入密码": "After disabling two-factor authentication, you only need to enter your password when logging in",
"验证身份": "Verify identity",
"为了保护您的账户安全,请输入认证器验证码来确认身份": "To protect your account security, please enter the authenticator verification code to confirm your identity",
"输入认证器应用显示的6位数字验证码": "Enter the 6-digit verification code displayed on the authenticator application",
"新的备用恢复代码": "New backup recovery code",
"新的备用码已生成": "New backup code has been generated",
"我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销": "I have understood that disabling two-factor authentication will permanently delete all related settings and backup codes, this operation cannot be undone",
"账户已锁定": "Account locked",
"剩余备用码:": "Remaining backup codes: ",
"启用验证": "Enable Authentication",
"重新生成备用码": "Regenerate backup codes",
"设置两步验证": "Set up two-factor authentication",
"扫描二维码": "Scan QR code",
"使用认证器应用扫描二维码": "Scan QR code with authenticator app",
"保存备用码": "Save backup codes",
"保存备用码以备不时之需": "Save backup codes for emergencies",
"验证设置": "Verify setup",
"输入验证码完成设置": "Enter verification code to complete setup",
"使用认证器应用(如 Google Authenticator、Microsoft Authenticator扫描下方二维码": "Use an authenticator app (such as Google Authenticator, Microsoft Authenticator) to scan the QR code below:",
"或手动输入密钥:": "Or manually enter the secret:",
"备用恢复代码": "Backup recovery codes",
"复制所有代码": "Copy all codes",
"上一步": "Previous",
"下一步": "Next",
"完成设置并启用两步验证": "Complete setup and enable two-factor authentication",
"确认禁用": "Confirm disable",
"完成": "Complete",
"生成新的备用码": "Generate new backup codes",
"警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Warning: Disabling two-factor authentication will permanently delete your verification settings and all backup codes. This action is irreversible!",
"禁用后的影响:": "Impact after disabling:",
"降低您账户的安全性": "Reduce your account security",
"需要重新完整设置才能再次启用": "Need to set up again to re-enable",
"永久删除您的两步验证设置": "Permanently delete your two-factor authentication settings",
"永久删除所有备用码(包括未使用的)": "Permanently delete all backup codes (including unused ones)",
"请输入认证器验证码或备用码": "Please enter authenticator verification code or backup code",
"重新生成备用码将使现有的备用码失效,请确保您已保存了当前的备用码。": "Regenerating backup codes will invalidate existing backup codes. Please ensure you have saved the current backup codes.",
"请输入认证器验证码": "Please enter authenticator verification code",
"旧的备用码已失效,请保存新的备用码": "Old backup codes have been invalidated, please save the new backup codes",
"获取2FA状态失败": "Failed to get 2FA status",
"设置2FA失败": "Failed to set up 2FA",
"请输入验证码": "Please enter verification code",
"两步验证启用成功!": "Two-factor authentication enabled successfully!",
"启用2FA失败": "Failed to enable 2FA",
"请输入验证码或备用码": "Please enter verification code or backup code",
"请确认您已了解禁用两步验证的后果": "Please confirm that you understand the consequences of disabling two-factor authentication",
"两步验证已禁用": "Two-factor authentication has been disabled",
"禁用2FA失败": "Failed to disable 2FA",
"备用码重新生成成功": "Backup codes regenerated successfully",
"重新生成备用码失败": "Failed to regenerate backup codes",
"备用码已复制到剪贴板": "Backup codes copied to clipboard"
}