🌐 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:
@@ -1043,7 +1043,7 @@ const PersonalSetting = () => {
|
||||
</Card>
|
||||
|
||||
{/* 两步验证设置 */}
|
||||
<TwoFASetting />
|
||||
<TwoFASetting t={t} />
|
||||
|
||||
{/* 危险区域 */}
|
||||
<Card
|
||||
|
||||
@@ -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 Authenticator、Microsoft Authenticator)扫描下方二维码:
|
||||
{t('使用认证器应用(如 Google Authenticator、Microsoft 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>
|
||||
|
||||
Reference in New Issue
Block a user