feat: refine 2fa-related i18n (#6228)

#### What type of PR is this?

/area ui
/kind feature
/milestone 2.17.x

#### What this PR does / why we need it:

补充 2FA 相关的 i18n 定义。

#### Does this PR introduce a user-facing change?

```release-note
None
```
pull/6211/head
Ryan Wang 2024-07-01 14:41:16 +08:00 committed by GitHub
parent 2c454ccb28
commit f3f48e2753
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 285 additions and 39 deletions

View File

@ -1,15 +1,24 @@
<script lang="ts" setup>
import { setFocus } from "@/formkit/utils/focus";
import { submitForm } from "@formkit/core";
import { Toast, VButton } from "@halo-dev/components";
import axios from "axios";
import qs from "qs";
import { onMounted, ref } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const emit = defineEmits<{
(event: "succeed"): void;
}>();
const loading = ref(false);
async function onSubmit({ code }: { code: string }) {
try {
loading.value = true;
const _csrf = document.cookie
.split("; ")
.find((row) => row.startsWith("XSRF-TOKEN"))
@ -36,9 +45,15 @@ async function onSubmit({ code }: { code: string }) {
emit("succeed");
} catch (error) {
Toast.error("验证失败");
Toast.error(t("core.common.toast.validation_failed"));
} finally {
loading.value = false;
}
}
onMounted(() => {
setFocus("code");
});
</script>
<template>
@ -54,19 +69,25 @@ async function onSubmit({ code }: { code: string }) {
@keyup.enter="submitForm('mfa-form')"
>
<FormKit
id="code"
:classes="{
outer: '!py-0',
}"
name="code"
placeholder="请输入两步验证码"
validation-label="两步验证码"
:autofocus="true"
:placeholder="$t('core.login.2fa.fields.code.placeholder')"
:validation-label="$t('core.login.2fa.fields.code.label')"
type="text"
validation="required"
>
</FormKit>
</FormKit>
<VButton class="mt-8" block type="secondary" @click="submitForm('mfa-form')">
验证
<VButton
:loading="loading"
class="mt-8"
block
type="secondary"
@click="submitForm('mfa-form')"
>
{{ $t("core.common.buttons.verify") }}
</VButton>
</template>

View File

@ -25,6 +25,11 @@ core:
button: Login
modal:
title: Re-login
2fa:
fields:
code:
placeholder: Two-step verification code
label: Two-step verification code
signup:
title: Sign up
fields:
@ -1161,6 +1166,7 @@ core:
detail: Detail
notification-preferences: Notification Preferences
pat: Personal Access Tokens
2fa: 2FA
devices: Devices
actions:
update_profile:
@ -1194,6 +1200,49 @@ core:
title: Verify email
email_verified:
tooltip: Verified
2fa:
operations:
enable:
button: Enable 2FA
title: Enable 2FA
disable:
title: Disable 2FA
disable_totp:
title: Disable TOTP
password_validation_form:
fields:
password:
label: Password
help: Login password of the current account
methods:
title: Two-factor methods
totp:
title: TOTP
description: Configure two-step verification with TOTP application
fields:
status:
configured: Configured
not_configured: Not configured
operations:
reconfigure:
button: Reconfigure
configure:
button: Configure
title: TOTP configuration
fields:
code:
label: Verification code
help: >-
6-digit verification code obtained from the validator
application
password:
label: Password
help: Login password of the current account
qrcode:
label: "Use the validator application to scan the QR code below:"
manual:
label: "If you can't scan the QR code, click to view the alternative steps."
help: "Manually configure the validator application with the following code:"
pat:
operations:
delete:
@ -1707,6 +1756,8 @@ core:
access: Access
schedule_publish: Schedule publish
revoke: Revoke
disable: Disable
enable: Enable
radio:
"yes": "Yes"
"no": "No"
@ -1739,6 +1790,8 @@ core:
not_found: Resource not found
server_internal_error: Internal server error
unknown_error: Unknown error
disable_success: Disabled successfully
enable_success: Enabled successfully
dialog:
titles:
tip: Tip

View File

@ -25,6 +25,11 @@ core:
button: 登录
modal:
title: 重新登录
2fa:
fields:
code:
placeholder: 请输入两步验证码
label: 两步验证码
signup:
title: 注册
fields:
@ -1086,6 +1091,7 @@ core:
detail: 详情
notification-preferences: 通知配置
pat: 个人令牌
2fa: 两步验证
devices: 登录设备
actions:
update_profile:
@ -1115,6 +1121,47 @@ core:
description: 电子邮箱地址还未验证,点击下方按钮进行验证
email_verified:
tooltip: 已验证
2fa:
operations:
enable:
button: 启用两步验证
title: 启用两步验证
disable:
title: 禁用两步验证
disable_totp:
title: 停用 TOTP
password_validation_form:
fields:
password:
label: 验证密码
help: 当前账号的登录密码
methods:
title: 验证方式
totp:
title: TOTP
description: 使用 TOTP 应用程序配置两步验证
fields:
status:
configured: 已配置
not_configured: 未配置
operations:
reconfigure:
button: 重新配置
configure:
button: 配置
title: TOTP 配置
fields:
code:
label: 验证码
help: 从验证器应用获得的 6 位验证码
password:
label: 验证密码
help: 当前账号的登录密码
qrcode:
label: 使用验证器应用扫描下方二维码:
manual:
label: 如果无法扫描二维码,点击查看代替步骤
help: 使用以下代码手动配置验证器应用:
pat:
operations:
delete:
@ -1624,6 +1671,8 @@ core:
access: 访问
schedule_publish: 定时发布
revoke: 撤销
disable: 禁用
enable: 启用
radio:
"yes":
"no":
@ -1656,6 +1705,8 @@ core:
not_found: 资源不存在
server_internal_error: 服务器内部错误
unknown_error: 未知错误
disable_success: 禁用成功
enable_success: 啟用成功
dialog:
titles:
tip: 提示

View File

@ -25,6 +25,11 @@ core:
button: 登入
modal:
title: 重新登入
2fa:
fields:
code:
placeholder: 請輸入兩步驟驗證碼
label: 兩步驟驗證碼
signup:
title: 註冊
fields:
@ -1066,6 +1071,7 @@ core:
detail: 詳情
notification-preferences: 通知配置
pat: 個人令牌
2fa: 两步验证
devices: 登錄設備
actions:
update_profile:
@ -1095,6 +1101,47 @@ core:
title: 驗證電子郵件信箱
email_verified:
tooltip: 已驗證
2fa:
operations:
enable:
button: 启用两步验证
title: 启用两步验证
disable:
title: 禁用两步验证
disable_totp:
title: 停用 TOTP
password_validation_form:
fields:
password:
label: 验证密碼
help: 目前帳號的登入密碼
methods:
title: 驗證方法
totp:
title: TOTP
description: 使用 TOTP 應用程式設定兩步驟驗證
fields:
status:
configured: 已配置
not_configured: 未配置
operations:
reconfigure:
button: 重新配置
configure:
button: 配置
title: TOTP 配置
fields:
code:
label: 驗證碼
help: 從驗證器應用程式取得的 6 位驗證碼
password:
label: 验证密碼
help: 目前帳號的登入密碼
qrcode:
label: 使用驗證器應用程式掃描下面的二維碼:
manual:
label: 如果您無法掃描二維碼,請按一下查看替代步驟。
help: 使用以下程式碼手動配置驗證器應用程式:
pat:
operations:
delete:
@ -1581,6 +1628,8 @@ core:
access: 訪問
schedule_publish: 定時發佈
revoke: 撤銷
disable: 禁用
enable: 启用
radio:
"yes":
"no":
@ -1613,6 +1662,8 @@ core:
not_found: 資源不存在
server_internal_error: 伺服器內部錯誤
unknown_error: 未知錯誤
disable_success: 禁用成功
enable_success: 啟用成功
dialog:
titles:
tip: 提示

View File

@ -70,7 +70,7 @@ const tabs = ref<UserProfileTab[]>([
},
{
id: "2fa",
label: "两步验证",
label: t("core.uc_profile.tabs.2fa"),
component: markRaw(TwoFactor),
priority: 40,
},

View File

@ -51,7 +51,9 @@ const totpDeletionModalVisible = ref(false);
:checked="settings?.enabled"
@change="onEnabledChange"
/>
<span class="text-sm font-medium text-gray-700">启用两步验证</span>
<span class="text-sm font-medium text-gray-700">
{{ $t("core.uc_profile.2fa.operations.enable.button") }}
</span>
</label>
</div>
@ -63,7 +65,9 @@ const totpDeletionModalVisible = ref(false);
role="list"
>
<li class="bg-gray-50 px-4 py-3">
<span class="text-sm font-semibold text-gray-900">验证方式</span>
<span class="text-sm font-semibold text-gray-900">
{{ $t("core.uc_profile.2fa.methods.title") }}
</span>
</li>
<li>
<VEntity>
@ -74,20 +78,36 @@ const totpDeletionModalVisible = ref(false);
</template>
</VEntityField>
<VEntityField
title="TOTP"
description="使用 TOTP 应用程序配置两步验证"
:title="$t('core.uc_profile.2fa.methods.totp.title')"
:description="$t('core.uc_profile.2fa.methods.totp.description')"
/>
</template>
<template #end>
<StatusDotField
:state="settings?.totpConfigured ? 'success' : 'default'"
:text="settings?.totpConfigured ? '已配置' : '未配置'"
:text="
settings?.totpConfigured
? $t(
'core.uc_profile.2fa.methods.totp.fields.status.configured'
)
: $t(
'core.uc_profile.2fa.methods.totp.fields.status.not_configured'
)
"
></StatusDotField>
<VEntityField>
<template #description>
<VSpace>
<VButton size="sm" @click="totpConfigureModalVisible = true">
{{ settings?.totpConfigured ? "重新配置" : "配置" }}
{{
settings?.totpConfigured
? $t(
"core.uc_profile.2fa.methods.totp.operations.reconfigure.button"
)
: $t(
"core.uc_profile.2fa.methods.totp.operations.configure.button"
)
}}
</VButton>
<VButton
v-if="settings?.totpConfigured"
@ -95,7 +115,7 @@ const totpDeletionModalVisible = ref(false);
type="danger"
@click="totpDeletionModalVisible = true"
>
停用
{{ $t("core.common.buttons.disable") }}
</VButton>
</VSpace>
</template>

View File

@ -17,10 +17,14 @@ function onSubmit({ password }: { password: string }) {
>
<FormKit
type="password"
label="验证密码"
:label="
$t('core.uc_profile.2fa.password_validation_form.fields.password.label')
"
validation="required"
name="password"
help="当前账号的登录密码"
:help="
$t('core.uc_profile.2fa.password_validation_form.fields.password.help')
"
autocomplete="current-password"
></FormKit>
</FormKit>

View File

@ -5,8 +5,10 @@ import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import { useQRCode } from "@vueuse/integrations/useQRCode";
import { computed, ref } from "vue";
import { useI18n } from "vue-i18n";
const queryClient = useQueryClient();
const { t } = useI18n();
const emit = defineEmits<{
(event: "close"): void;
@ -32,7 +34,7 @@ const { mutate, isLoading } = useMutation({
});
},
onSuccess() {
Toast.success("配置成功");
Toast.success(t("core.common.toast.save_success"));
queryClient.invalidateQueries({ queryKey: ["two-factor-settings"] });
modal.value?.close();
},
@ -48,20 +50,34 @@ function onSubmit(data: TotpRequest) {
ref="modal"
:width="500"
:centered="false"
title="TOTP 配置"
:title="$t('core.uc_profile.2fa.methods.totp.operations.configure.title')"
@close="emit('close')"
>
<div>
<div class="mb-4 space-y-3 border-b border-gray-100 pb-4 text-gray-900">
<div class="text-sm font-semibold">使用验证器应用扫描下方二维码</div>
<div class="text-sm font-semibold">
{{
$t(
"core.uc_profile.2fa.methods.totp.operations.configure.fields.qrcode.label"
)
}}
</div>
<img :src="qrcode" class="rounded-base border border-gray-100" />
<details>
<summary class="cursor-pointer select-none text-sm text-gray-800">
如果无法扫描二维码点击查看代替步骤
{{
$t(
"core.uc_profile.2fa.methods.totp.operations.configure.fields.manual.label"
)
}}
</summary>
<div class="mt-3 rounded-base border border-gray-100 p-2">
<span class="text-sm text-gray-600">
使用以下代码手动配置验证器应用
{{
$t(
"core.uc_profile.2fa.methods.totp.operations.configure.fields.manual.help"
)
}}
</span>
<div class="mt-2">
<code
@ -77,16 +93,32 @@ function onSubmit(data: TotpRequest) {
<FormKit
type="number"
name="code"
label="验证码"
:label="
$t(
'core.uc_profile.2fa.methods.totp.operations.configure.fields.code.label'
)
"
validation="required"
help="从验证器应用获得的 6 位验证码"
:help="
$t(
'core.uc_profile.2fa.methods.totp.operations.configure.fields.code.help'
)
"
></FormKit>
<FormKit
type="password"
label="验证密码"
:label="
$t(
'core.uc_profile.2fa.methods.totp.operations.configure.fields.password.label'
)
"
validation="required"
name="password"
help="当前账号的登录密码"
:help="
$t(
'core.uc_profile.2fa.methods.totp.operations.configure.fields.password.help'
)
"
autocomplete="current-password"
></FormKit>
<FormKit
@ -103,9 +135,11 @@ function onSubmit(data: TotpRequest) {
type="secondary"
@click="$formkit.submit('totp-form')"
>
完成
{{ $t("core.common.buttons.save") }}
</VButton>
<VButton @click="modal?.close()">
{{ $t("core.common.buttons.close") }}
</VButton>
<VButton @click="modal?.close()"></VButton>
</VSpace>
</template>
</VModal>

View File

@ -3,9 +3,11 @@ import { ucApiClient } from "@halo-dev/api-client";
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import PasswordValidationForm from "./PasswordValidationForm.vue";
const queryClient = useQueryClient();
const { t } = useI18n();
const emit = defineEmits<{
(event: "close"): void;
@ -23,7 +25,7 @@ const { mutate, isLoading } = useMutation({
});
},
onSuccess() {
Toast.success("停用成功");
Toast.success(t("core.common.toast.disable_success"));
queryClient.invalidateQueries({ queryKey: ["two-factor-settings"] });
modal.value?.close();
},
@ -39,7 +41,7 @@ function onSubmit(password: string) {
ref="modal"
:width="500"
:centered="false"
title="停用 TOTP"
:title="$t('core.uc_profile.2fa.operations.disable_totp.title')"
@close="emit('close')"
>
<PasswordValidationForm @submit="onSubmit" />
@ -50,9 +52,11 @@ function onSubmit(password: string) {
type="danger"
@click="$formkit.submit('password-validation-form')"
>
停用
{{ $t("core.common.buttons.disable") }}
</VButton>
<VButton @click="modal?.close()">
{{ $t("core.common.buttons.close") }}
</VButton>
<VButton @click="modal?.close()"></VButton>
</VSpace>
</template>
</VModal>

View File

@ -3,9 +3,11 @@ import { ucApiClient } from "@halo-dev/api-client";
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import PasswordValidationForm from "./PasswordValidationForm.vue";
const queryClient = useQueryClient();
const { t } = useI18n();
const emit = defineEmits<{
(event: "close"): void;
@ -23,7 +25,7 @@ const { mutate, isLoading } = useMutation({
});
},
onSuccess() {
Toast.success("停用成功");
Toast.success(t("core.common.toast.disable_success"));
queryClient.invalidateQueries({ queryKey: ["two-factor-settings"] });
modal.value?.close();
},
@ -39,7 +41,7 @@ function onSubmit(password: string) {
ref="modal"
:width="500"
:centered="false"
title="停用两步验证"
:title="$t('core.uc_profile.2fa.operations.disable.title')"
@close="emit('close')"
>
<PasswordValidationForm @submit="onSubmit" />
@ -50,9 +52,11 @@ function onSubmit(password: string) {
type="danger"
@click="$formkit.submit('password-validation-form')"
>
停用
{{ $t("core.common.buttons.disable") }}
</VButton>
<VButton @click="modal?.close()">
{{ $t("core.common.buttons.close") }}
</VButton>
<VButton @click="modal?.close()"></VButton>
</VSpace>
</template>
</VModal>

View File

@ -3,9 +3,11 @@ import { ucApiClient } from "@halo-dev/api-client";
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import PasswordValidationForm from "./PasswordValidationForm.vue";
const queryClient = useQueryClient();
const { t } = useI18n();
const emit = defineEmits<{
(event: "close"): void;
@ -23,7 +25,7 @@ const { mutate, isLoading } = useMutation({
});
},
onSuccess() {
Toast.success("启用成功");
Toast.success(t("core.common.toast.enable_success"));
queryClient.invalidateQueries({ queryKey: ["two-factor-settings"] });
modal.value?.close();
},
@ -39,7 +41,7 @@ function onSubmit(password: string) {
ref="modal"
:width="500"
:centered="false"
title="启用两步验证"
:title="$t('core.uc_profile.2fa.operations.enable.title')"
@close="emit('close')"
>
<PasswordValidationForm @submit="onSubmit" />
@ -50,9 +52,11 @@ function onSubmit(password: string) {
type="secondary"
@click="$formkit.submit('password-validation-form')"
>
启用
{{ $t("core.common.buttons.enable") }}
</VButton>
<VButton @click="modal?.close()">
{{ $t("core.common.buttons.close") }}
</VButton>
<VButton @click="modal?.close()"></VButton>
</VSpace>
</template>
</VModal>