feat: refine i18n for uc (#4957)

#### What type of PR is this?

/area console
/kind improvement
/milestone 2.11.x

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

完善个人中心相关页面的 i18n。

#### Special notes for your reviewer:

测试各个语言的个人中心相关页面。

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

```release-note
完善个人中心相关页面的 i18n。
```
pull/4941/head
Ryan Wang 2023-11-30 18:56:10 +08:00 committed by GitHub
parent 5208b5c925
commit 5e76da018d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 2179 additions and 1727 deletions

View File

@ -179,7 +179,7 @@ onMounted(() => {
<div class="flex items-center gap-1">
<a
v-tooltip="'个人中心'"
v-tooltip="$t('core.sidebar.operations.profile.tooltip')"
class="group inline-block cursor-pointer rounded-full p-1.5 transition-all hover:bg-gray-100"
href="/uc"
>
@ -188,6 +188,7 @@ onMounted(() => {
/>
</a>
<div
v-tooltip="$t('core.sidebar.operations.logout.tooltip')"
class="group inline-block cursor-pointer rounded-full p-1.5 transition-all hover:bg-gray-100"
@click="handleLogout"
>

View File

@ -161,7 +161,7 @@ const handleResetForm = () => {
]
"
type="text"
label="登录之后默认跳转位置"
:label="$t('core.role.editing_modal.fields.redirect_on_login')"
></FormKit>
</FormKit>
</div>

View File

@ -123,7 +123,7 @@ function handleRouteToUC() {
type="primary"
@click="handleRouteToUC"
>
个人中心
{{ $t("core.user.detail.actions.profile.title") }}
</VButton>
<VDropdown v-if="currentUserHasPermission(['system:users:manage'])">
<VButton type="default">

View File

@ -44,7 +44,7 @@ function handleRouteToNotification(notification: Notification) {
<VCard
:body-class="['h-full', '@container', '!p-0', '!overflow-auto']"
class="h-full"
:title="$t('core.notification.title')"
:title="$t('core.dashboard.widgets.presets.notification.title')"
>
<template #actions>
<div style="padding: 12px 16px">
@ -59,7 +59,7 @@ function handleRouteToNotification(notification: Notification) {
<VLoading v-if="isLoading" />
<VEmpty
v-else-if="!notifications?.length"
:title="$t('core.notification.empty.titles.unread')"
:title="$t('core.dashboard.widgets.presets.notification.empty.title')"
>
<template #actions>
<VButton :loading="isFetching" @click="refetch">

View File

@ -26,7 +26,7 @@ editor:
audio: 音频
table: 表格
no_results: 没有搜索结果
placeholder: "输入 / 以选择输入类型"
placeholder: 输入 / 以选择输入类型
link:
add_link: 添加链接
edit_link: 修改链接

View File

@ -83,7 +83,7 @@ const handleUploadAvatar = () => {
handleCloseCropperModal();
})
.catch(() => {
Toast.error(t("core.user.detail.avatar.toast_upload_failed"));
Toast.error(t("core.components.user_avatar.toast_upload_failed"));
})
.finally(() => {
uploadSaving.value = false;
@ -93,7 +93,7 @@ const handleUploadAvatar = () => {
const handleRemoveCurrentAvatar = () => {
Dialog.warning({
title: t("core.user.detail.avatar.remove.title"),
title: t("core.components.user_avatar.remove.title"),
description: t("core.common.dialog.descriptions.cannot_be_recovered"),
confirmType: "danger",
confirmText: t("core.common.buttons.confirm"),
@ -111,7 +111,7 @@ const handleRemoveCurrentAvatar = () => {
queryClient.invalidateQueries({ queryKey: ["user-detail"] });
})
.catch(() => {
Toast.error(t("core.user.detail.avatar.toast_remove_failed"));
Toast.error(t("core.components.user_avatar.toast_remove_failed"));
});
},
});
@ -174,7 +174,7 @@ const hasAvatar = computed(() => {
<VModal
:visible="visibleCropperModal"
:width="1200"
:title="$t('core.user.detail.avatar.cropper_modal.title')"
:title="$t('core.components.user_avatar.cropper_modal.title')"
mount-to-body
@update:visible="handleCloseCropperModal"
>

View File

@ -64,7 +64,7 @@ const defaultToolbars: ToolbarItem[] = [
onClick: () => {
emit("changeFile");
},
title: t("core.user.detail.avatar.tooltips.upload"),
title: t("core.components.user_avatar.tooltips.upload"),
},
{
name: "zoomIn",
@ -72,7 +72,7 @@ const defaultToolbars: ToolbarItem[] = [
onClick: () => {
cropper.value?.zoom(0.1);
},
title: t("core.user.detail.avatar.tooltips.zoom_in"),
title: t("core.components.user_avatar.tooltips.zoom_in"),
},
{
name: "zoomOut",
@ -80,7 +80,7 @@ const defaultToolbars: ToolbarItem[] = [
onClick: () => {
cropper.value?.zoom(-0.1);
},
title: t("core.user.detail.avatar.tooltips.zoom_out"),
title: t("core.components.user_avatar.tooltips.zoom_out"),
},
{
name: "flipHorizontal",
@ -88,7 +88,7 @@ const defaultToolbars: ToolbarItem[] = [
onClick: () => {
cropper.value?.scaleX(-cropper.value?.getData().scaleX || -1);
},
title: t("core.user.detail.avatar.tooltips.flip_horizontal"),
title: t("core.components.user_avatar.tooltips.flip_horizontal"),
},
{
name: "flipVertical",
@ -96,7 +96,7 @@ const defaultToolbars: ToolbarItem[] = [
onClick: () => {
cropper.value?.scaleY(-cropper.value?.getData().scaleY || -1);
},
title: t("core.user.detail.avatar.tooltips.flip_vertical"),
title: t("core.components.user_avatar.tooltips.flip_vertical"),
},
{
name: "reset",
@ -104,7 +104,7 @@ const defaultToolbars: ToolbarItem[] = [
onClick: () => {
cropper.value?.reset();
},
title: t("core.user.detail.avatar.tooltips.reset"),
title: t("core.components.user_avatar.tooltips.reset"),
},
];
const previewElement = ref<HTMLElement>();

View File

@ -42,7 +42,9 @@ core:
title: Account binding
common:
toast:
mounted: The current login method is not bound to an account, Please bind or sign up a new account first
mounted: >-
The current login method is not bound to an account, Please bind or
sign up a new account first
operations:
login_and_bind:
button: Login and Bind
@ -75,12 +77,21 @@ core:
backup: Backup
operations:
logout:
button: Logout
tooltip: Logout
title: Are you sure you want to log out?
profile:
button: Profile
tooltip: Profile
visit_homepage:
title: Visit homepage
uc_sidebar:
menu:
items:
profile: Profile
notification: Notifications
posts: Posts
operations:
console:
tooltip: Console
dashboard:
title: Dashboard
actions:
@ -104,7 +115,11 @@ core:
title: Recent Posts
visits: "{visits} Visits"
comments: "{comments} Comments"
publishTime: "Publish Time {publishTime}"
publishTime: Publish Time {publishTime}
notification:
title: Notifications
empty:
title: No unread notifications
quicklink:
title: Quick Link
actions:
@ -127,7 +142,9 @@ core:
refresh_search_engine:
title: Refresh Search Engine
dialog_title: Do you want to refresh the search engine index?
dialog_content: This operation will recreate search engine indexes for all published posts.
dialog_content: >-
This operation will recreate search engine indexes for all
published posts.
success_message: Refresh search engine index successfully.
evict_page_cache:
title: Refresh Page Cache
@ -152,10 +169,14 @@ core:
operations:
delete:
title: Are you sure you want to delete this post?
description: This operation will move the post to the recycle bin, and it can be restored from the recycle bin later.
description: >-
This operation will move the post to the recycle bin, and it can be
restored from the recycle bin later.
delete_in_batch:
title: Are you sure you want to delete the selected posts?
description: This operation will move the posts to the recycle bin, and it can be restored from the recycle bin later.
description: >-
This operation will move the posts to the recycle bin, and it can be
restored from the recycle bin later.
filters:
status:
items:
@ -252,7 +273,9 @@ core:
operations:
delete:
title: Are you sure you want to delete this tag?
description: After deleting this tag, the association with the corresponding article will be removed. This operation cannot be undone.
description: >-
After deleting this tag, the association with the corresponding
article will be removed. This operation cannot be undone.
editing_modal:
titles:
update: Update post tag
@ -283,7 +306,9 @@ core:
operations:
delete:
title: Are you sure you want to delete this category?
description: After deleting this category, the association with corresponding articles will be removed. This operation cannot be undone.
description: >-
After deleting this category, the association with corresponding
articles will be removed. This operation cannot be undone.
add_sub_category:
button: Add sub category
editing_modal:
@ -320,10 +345,14 @@ core:
operations:
delete:
title: Are you sure you want to delete this page?
description: This operation will move the page to the recycle bin, and it can be restored from the recycle bin later.
description: >-
This operation will move the page to the recycle bin, and it can be
restored from the recycle bin later.
delete_in_batch:
title: Are you sure you want to delete the selected pages?
description: This operation will move the pages to the recycle bin, and it can be restored from the recycle bin later.
description: >-
This operation will move the pages to the recycle bin, and it can be
restored from the recycle bin later.
filters:
status:
items:
@ -410,16 +439,22 @@ core:
operations:
delete_comment:
title: Are you sure you want to delete this comment?
description: All replies under the comments will be deleted at the same time, and this operation cannot be undone.
description: >-
All replies under the comments will be deleted at the same time, and
this operation cannot be undone.
delete_comment_in_batch:
title: Are you sure you want to delete the selected comments?
description: All replies under the comments will be deleted at the same time, and this operation cannot be undone.
description: >-
All replies under the comments will be deleted at the same time, and
this operation cannot be undone.
approve_comment_in_batch:
button: Approve
title: Are you sure you want to approve the selected comments for review?
approve_applies_in_batch:
button: Approve all replies
title: Are you sure you want to approve all replies to this comment for review?
title: >-
Are you sure you want to approve all replies to this comment for
review?
delete_reply:
title: Are you sure you want to delete this reply?
approve_reply:
@ -468,7 +503,9 @@ core:
storage_policies: Storage Policies
empty:
title: There are no attachments in the current group.
message: The current group has no attachments, you can try refreshing or uploading attachments.
message: >-
The current group has no attachments, you can try refreshing or
uploading attachments.
actions:
upload: Upload Attachment
operations:
@ -484,10 +521,10 @@ core:
filters:
storage_policy:
label: Storage Policy
result: "Storage Policy{storage_policy}"
result: Storage Policy{storage_policy}
owner:
label: Owner
result: "Owner{owner}"
result: Owner{owner}
sort:
items:
create_time_desc: Latest uploaded
@ -531,18 +568,28 @@ core:
delete:
button: And move attachment to ungrouped
title: Are you sure you want to delete this group?
description: The group will be deleted, and the attachments under the group will be moved to ungrouped. This operation cannot be undone.
toast_success: Deletion successful, {total} attachments have been moved to ungrouped
description: >-
The group will be deleted, and the attachments under the group will
be moved to ungrouped. This operation cannot be undone.
toast_success: >-
Deletion successful, {total} attachments have been moved to
ungrouped
delete_with_attachments:
button: Also delete attachments
title: Are you sure you want to delete this group?
description: Deleting the group and all attachments within it, this action cannot be undone.
toast_success: Deletion successful, {total} attachments have been deleted simultaneously
description: >-
Deleting the group and all attachments within it, this action cannot
be undone.
toast_success: >-
Deletion successful, {total} attachments have been deleted
simultaneously
policies_modal:
title: Storage Policies
empty:
title: There is currently no available storage strategy.
message: There are no available storage policies at the moment. You can try refreshing or creating a new policy.
message: >-
There are no available storage policies at the moment. You can try
refreshing or creating a new policy.
operations:
delete:
title: Are you sure you want to delete this policy?
@ -575,7 +622,7 @@ core:
label: Attachments
operations:
select:
result: "({count} items selected)"
result: ({count} items selected)
theme:
title: Themes
common:
@ -587,7 +634,9 @@ core:
management: Themes
empty:
title: There are currently no activated or selected themes.
message: There are currently no activated or selected themes, you can switch themes or install new ones.
message: >-
There are currently no activated or selected themes, you can switch
themes or install new ones.
actions:
switch: Switch Theme
operations:
@ -596,18 +645,24 @@ core:
toast_success: Active theme successful
reset:
title: Are you sure you want to reset all configurations of the theme?
description: This operation will delete the saved configuration and reset it to default settings.
description: >-
This operation will delete the saved configuration and reset it to
default settings.
toast_success: Reset configuration successful
reload:
button: Reload
title: Are you sure you want to reload all configurations of the theme?
description: This operation will only reload the theme configuration and settings form definition, and will not delete any saved configurations.
description: >-
This operation will only reload the theme configuration and settings
form definition, and will not delete any saved configurations.
toast_success: Reload configuration successful
uninstall:
title: Are you sure you want to uninstall this theme?
uninstall_and_delete_config:
button: Uninstall and delete config
title: Are you sure you want to uninstall this theme and its corresponding settings?
title: >-
Are you sure you want to uninstall this theme and its corresponding
settings?
remote_download:
title: Remote download address detected, do you want to download?
description: "Please carefully verify whether this address can be trusted: {url}"
@ -625,7 +680,9 @@ core:
url: Remote URL
empty:
title: There are no installed themes currently.
message: There are currently no installed themes, you can try refreshing or installing a new theme.
message: >-
There are currently no installed themes, you can try refreshing or
installing a new theme.
not_installed_empty:
title: There are no themes currently not installed.
preview_model:
@ -661,10 +718,14 @@ core:
toast_success: Setting successful
delete_menu:
title: Are you sure you want to delete this menu?
description: All menu items under this menu will be deleted at the same time, and this operation cannot be undone.
description: >-
All menu items under this menu will be deleted at the same time, and
this operation cannot be undone.
delete_menu_item:
title: Are you sure you want to delete this menu item?
description: All sub-menu items will be deleted at the same time, and cannot be restored after deletion.
description: >-
All sub-menu items will be deleted at the same time, and cannot be
restored after deletion.
add_sub_menu_item:
button: Add sub menu item
list:
@ -691,7 +752,7 @@ core:
placeholder: Select the parent menu item
ref_kind:
label: Type
placeholder: "Please select {label}"
placeholder: Please select {label}
options:
custom: Custom
post: Post
@ -715,13 +776,17 @@ core:
detail: Detail
empty:
title: There are no installed plugins currently.
message: There are no installed plugins currently, you can try refreshing or installing new plugins.
message: >-
There are no installed plugins currently, you can try refreshing or
installing new plugins.
actions:
install: Install Plugin
operations:
reset:
title: Are you sure you want to reset all configurations of the plugin?
description: This operation will delete the saved configuration and reset it to default settings.
description: >-
This operation will delete the saved configuration and reset it to
default settings.
toast_success: Reset configuration successfully
uninstall:
title: Are you sure you want to uninstall this plugin?
@ -729,13 +794,19 @@ core:
title: Are you sure you want to uninstall these plugin?
uninstall_and_delete_config:
button: Uninstall and delete config
title: Are you sure you want to uninstall this plugin and its corresponding configuration?
title: >-
Are you sure you want to uninstall this plugin and its corresponding
configuration?
uninstall_and_delete_config_in_batch:
button: Uninstall and delete config
title: Are you sure you want to uninstall these plugin and its corresponding configuration?
title: >-
Are you sure you want to uninstall these plugin and its corresponding
configuration?
uninstall_when_enabled:
confirm_text: Stop running and uninstall
description: The current plugin is still in the enabled state and will be uninstalled after it stops running. This operation cannot be undone.
description: >-
The current plugin is still in the enabled state and will be
uninstalled after it stops running. This operation cannot be undone.
change_status_in_batch:
activate_title: Are you sure you want to activate these plugins?
inactivate_title: Are you sure you want to inactivate these plugins?
@ -767,7 +838,9 @@ core:
description: Would you like to activate the currently installed plugin?
existed_during_installation:
title: The plugin already exists.
description: The currently installed plugin already exists, do you want to upgrade?
description: >-
The currently installed plugin already exists, do you want to
upgrade?
detail:
title: Plugin detail
header:
@ -783,8 +856,8 @@ core:
last_starttime: Last Start Time
loader:
toast:
entry_load_failed: "Failed to load plugins entry file"
style_load_failed: "Failed to load plugins stylesheet file"
entry_load_failed: Failed to load plugins entry file
style_load_failed: Failed to load plugins stylesheet file
extension_points:
editor:
providers:
@ -796,7 +869,9 @@ core:
identity_authentication: Identity authentication
empty:
title: There are no users that meet the filtering criteria currently.
message: There are no users that match the filtering criteria at present. You can try refreshing or creating a new user.
message: >-
There are no users that match the filtering criteria at present. You can
try refreshing or creating a new user.
operations:
delete:
title: Are you sure you want to delete this user?
@ -854,19 +929,13 @@ core:
title: User detail
tabs:
detail: Detail
notification-preferences: Notification Preferences
pat: Personal Access Tokens
actions:
update_profile:
title: Update profile
change_password:
title: Change password
operations:
bind:
button: Bind
unbind:
button: Unbind
title: "Are you sure you want to unbind the login method for {display_name}?"
profile:
title: Profile
fields:
display_name: Display name
username: Username
@ -875,21 +944,107 @@ core:
bio: Bio
creation_time: Creation time
identity_authentication: Identity authentication
avatar:
title: Avatar
toast_upload_failed: Failed to upload avatar
toast_remove_failed: Failed to delete avatar
cropper_modal:
title: Crop Avatar
remove:
title: Are you sure you want to delete the avatar?
tooltips:
upload: Upload
zoom_in: Zoom In
zoom_out: Zoom Out
flip_horizontal: Flip Horizontal
flip_vertical: Flip Vertical
reset: Reset
role:
title: Roles
common:
text:
contains_all_permissions: Contains all permissions
contains_n_permissions: Contains {count} permissions
system_reserved: System Reserved
custom: Custom
dependent_on: Dependent on {roles}
provided_by_plugin: Provided by {plugin}
operations:
delete:
title: Are you sure you want to delete this role?
description: >-
After the role is deleted, the associated users will have their role
bindings removed and this operation cannot be undone.
create_based_on_this_role:
button: Create based on this role
detail:
title: Role detail
header:
title: Role information
tabs:
detail: Detail
permissions: Permissions
fields:
display_name: Display name
name: Name
type: Type
creation_time: Creation time
permissions_detail:
system_reserved_alert:
description: >-
The system reserved role does not support modification, it is
recommended to create a new role based on this one.
editing_modal:
titles:
create: Create role
update: Update role
groups:
general: General
permissions: Permissions
fields:
display_name: Display name
redirect_on_login: Default redirect location after logging in
identity_authentication:
title: Identity Authentication
tabs:
detail: Detail
setting: Settings
operations:
enable:
title: Are you sure you want to enable this identity authentication method?
disable:
title: Are you sure you want to disable this identity authentication method?
disable_privileged:
tooltip: The authentication method reserved by the system cannot be disabled
detail:
title: Identity authentication detail
fields:
display_name: Display name
description: Description
website: Website
help_page: Help page
authentication_url: Login URL
uc_profile:
title: Profile
tabs:
detail: Detail
notification-preferences: Notification Preferences
pat: Personal Access Tokens
actions:
update_profile:
title: Update profile
change_password:
title: Change password
detail:
fields:
display_name: Display name
username: Username
email: Email
roles: Roles
bio: Bio
creation_time: Creation time
identity_authentication: Identity authentication
operations:
bind:
button: Bind
unbind:
button: Unbind
title: Are you sure you want to unbind the login method for {display_name}?
email_not_set:
description: >-
Your email address has not been set yet. Click the button below to set
it up.
title: Set up email
email_not_verified:
description: >-
Your email address has not been verified yet, click the button below
to verify it
title: Verify email
pat:
operations:
delete:
@ -911,7 +1066,7 @@ core:
message: You can try refreshing or creating a new personal access token
fields:
expiresAt:
dynamic: "Expires on {expiresAt}"
dynamic: Expires on {expiresAt}
forever: Never expires
status:
normal: Normal
@ -933,67 +1088,54 @@ core:
notification-preferences:
fields:
type: Type
role:
title: Roles
common:
text:
contains_all_permissions: Contains all permissions
contains_n_permissions: Contains {count} permissions
system_reserved: System Reserved
custom: Custom
dependent_on: Dependent on {roles}
provided_by_plugin: Provided by {plugin}
operations:
delete:
title: Are you sure you want to delete this role?
description: After the role is deleted, the associated users will have their role bindings removed and this operation cannot be undone.
create_based_on_this_role:
button: Create based on this role
detail:
title: Role detail
header:
title: Role information
tabs:
detail: Detail
permissions: Permissions
fields:
display_name: Display name
name: Name
type: Type
creation_time: Creation time
permissions_detail:
system_reserved_alert:
description: The system reserved role does not support modification, it is recommended to create a new role based on this one.
editing_modal:
titles:
create: Create role
update: Update role
title: Edit Profile
groups:
general: General
permissions: Permissions
annotations: Annotations
fields:
display_name: Display name
identity_authentication:
title: Identity Authentication
tabs:
detail: Detail
setting: Settings
operations:
enable:
title: Are you sure you want to enable this identity authentication method?
disable:
title: Are you sure you want to disable this identity authentication method?
disable_privileged:
tooltip: The authentication method reserved by the system cannot be disabled
detail:
title: Identity authentication detail
username:
label: Username
validation: Please enter a valid username.
display_name:
label: Display name
email:
label: Email
phone:
label: Phone
avatar:
label: Avatar
bio:
label: Bio
change_password_modal:
title: Change password
fields:
display_name: Display name
description: Description
website: Website
help_page: Help page
authentication_url: Login URL
notification:
new_password:
label: New password
confirm_password:
label: Confirm password
email_verify_modal:
fields:
code:
label: Verification code
email:
label: Email address
new_email:
label: New email address
operations:
send_code:
buttons:
countdown: Resend in {timer} seconds
send: Send the verification code
sending: sending
toast_email_empty: Please enter your email address
toast_success: Verification code sent
verify:
toast_success: Verification successful
titles:
modify: Modify email address
verify: Verify email
uc_notification:
title: Notifications
tabs:
unread: Unread
@ -1005,6 +1147,9 @@ core:
operations:
mark_as_read:
button: Mark as read
delete:
description: Are you sure you want to delete this notification?
title: Delete
setting:
title: Settings
actuator:
@ -1039,7 +1184,10 @@ core:
database: "Database: {database}"
os: "Operating system: {os}"
alert:
external_url_invalid: The external access url detected is inconsistent with the current access url, which may cause some links to fail to redirect properly. Please check the external access url settings.
external_url_invalid: >-
The external access url detected is inconsistent with the current access
url, which may cause some links to fail to redirect properly. Please
check the external access url settings.
backup:
title: Backup and Restore
tabs:
@ -1052,14 +1200,19 @@ core:
create:
button: Create backup
title: Create backup
description: Are you sure you want to create a backup? This operation may last for a long time.
description: >-
Are you sure you want to create a backup? This operation may last for
a long time.
toast_success: Requested to create a backup
delete:
title: Delete the backup
description: Are you sure you want to delete the backup?
restore:
title: Restore successfully
description: After successful restore, you need to restart Halo to load the system resources normally. After clicking OK, we will automatically restart Halo.
description: >-
After successful restore, you need to restart Halo to load the system
resources normally. After clicking OK, we will automatically restart
Halo.
restart:
toast_success: Requested to restart
remote_download:
@ -1077,9 +1230,15 @@ core:
expiresAt: Expires {expiresAt}
restore:
tips:
first: 1. The restore process may last for a long time, please do not refresh the page during this period.
second: 2. During the restore process, although the existing data will not be cleaned up, if there is a conflict, the data will be overwritten.
third: 3. After the restore is completed, you need to restart Halo to load the system resources normally.
first: >-
1. The restore process may last for a long time, please do not refresh
the page during this period.
second: >-
2. During the restore process, although the existing data will not be
cleaned up, if there is a conflict, the data will be overwritten.
third: >-
3. After the restore is completed, you need to restart Halo to load
the system resources normally.
complete: Restore completed, waiting for restart...
start: Start restore
tabs:
@ -1202,7 +1361,7 @@ core:
extensions:
placeholder:
options:
placeholder: "Enter / to select input type."
placeholder: Enter / to select input type.
toolbox:
attachment: Attachment
show_hide_sidebar: Show/Hide Sidebar
@ -1229,18 +1388,35 @@ core:
social_auth_providers:
title: Third-party login
app_download_alert:
description: "Themes and plugins for Halo can be downloaded at the following addresses:"
description: >-
Themes and plugins for Halo can be downloaded at the following
addresses:
sources:
app_store: "Official App Store: {url}"
github: "GitHub: {url}"
user_avatar:
title: Avatar
toast_upload_failed: Failed to upload avatar
toast_remove_failed: Failed to delete avatar
cropper_modal:
title: Crop Avatar
remove:
title: Are you sure you want to delete the avatar?
tooltips:
upload: Upload
zoom_in: Zoom In
zoom_out: Zoom Out
flip_horizontal: Flip Horizontal
flip_vertical: Flip Vertical
reset: Reset
composables:
content_cache:
toast_recovered: Recovered unsaved content from cache
formkit:
category_select:
creation_label: "Create {text} category"
creation_label: Create {text} category
tag_select:
creation_label: "Create {text} tag"
creation_label: Create {text} tag
validation:
trim: Please remove the leading and trailing spaces
common:
@ -1278,9 +1454,11 @@ core:
detail: Detail
select: Select
view_all: View all
verify: Verify
modify: Modify
radio:
"yes": Yes
"no": No
"yes": "Yes"
"no": "No"
select:
public: Public
private: Private
@ -1302,10 +1480,10 @@ core:
copy_success: Copied successfully
operation_failed: Failed to operate
download_failed: Failed to download
save_failed_and_retry: "Failed to save, please retry"
publish_failed_and_retry: "Failed to publish, please retry"
network_error: "Network error, please check your connection"
login_expired: "Login expired, please log in again"
save_failed_and_retry: Failed to save, please retry
publish_failed_and_retry: Failed to publish, please retry
network_error: Network error, please check your connection
login_expired: Login expired, please log in again
forbidden: Access denied
not_found: Resource not found
server_internal_error: Internal server error
@ -1316,7 +1494,9 @@ core:
warning: Warning
descriptions:
cannot_be_recovered: This operation is irreversible.
editor_not_found: No editor found that matches the {raw_type} format. Please check if the editor plugin has been installed.
editor_not_found: >-
No editor found that matches the {raw_type} format. Please check if
the editor plugin has been installed.
filters:
results:
keyword: "Keyword: {keyword}"
@ -1346,3 +1526,15 @@ core:
recovering: Recovering
fields:
post_count: "{count} Posts"
uc_post:
creation_modal:
title: Create post
operations:
cancel_publish:
description: Are you sure you want to cancel publishing?
title: Cancel publish
publish_modal:
title: Publish post
setting_modal:
title: Post settings
title: My posts

File diff suppressed because it is too large Load Diff

View File

@ -75,12 +75,21 @@ core:
backup: 备份
operations:
logout:
button: 退出登录
tooltip: 退出登录
title: 确定要退出登录吗?
profile:
button: 个人资料
tooltip: 个人中心
visit_homepage:
title: 访问首页
uc_sidebar:
menu:
items:
profile: 我的
notification: 消息
posts: 文章
operations:
console:
tooltip: 管理控制台
dashboard:
title: 仪表板
actions:
@ -102,9 +111,13 @@ core:
title: 页面
recent_published:
title: 最近文章
visits: "访问量 {visits}"
comments: "评论 {comments}"
publishTime: "发布日期 {publishTime}"
visits: 访问量 {visits}
comments: 评论 {comments}
publishTime: 发布日期 {publishTime}
notification:
title: 通知
empty:
title: 当前没有未读的消息
quicklink:
title: 快捷访问
actions:
@ -163,19 +176,19 @@ core:
draft: 未发布
visible:
label: 可见性
result: "可见性:{visible}"
result: 可见性:{visible}
items:
public: 公开
private: 私有
category:
label: 分类
result: "分类:{category}"
result: 分类:{category}
tag:
label: 标签
result: "标签:{tag}"
result: 标签:{tag}
author:
label: 作者
result: "作者:{author}"
result: 作者:{author}
sort:
items:
publish_time_desc: 较近发布
@ -185,8 +198,8 @@ core:
list:
fields:
categories: 分类:
visits: "访问量 {visits}"
comments: "评论 {comments}"
visits: 访问量 {visits}
comments: 评论 {comments}
pinned: 已置顶
settings:
title: 文章设置
@ -239,6 +252,18 @@ core:
recovery_in_batch:
title: 确定要恢复选中的文章吗?
description: 该操作会将文章恢复到被删除之前的状态。
uc_post:
title: 我的文章
setting_modal:
title: 文章设置
creation_modal:
title: 创建文章
publish_modal:
title: 发布文章
operations:
cancel_publish:
title: 取消发布
description: 确定要取消发布吗?
post_editor:
title: 文章编辑
untitled: 未命名文章
@ -331,13 +356,13 @@ core:
draft: 未发布
visible:
label: 可见性
result: "可见性:{visible}"
result: 可见性:{visible}
items:
public: 公开
private: 私有
author:
label: 作者
result: "作者:{author}"
result: 作者:{author}
sort:
items:
publish_time_desc: 较近发布
@ -346,8 +371,8 @@ core:
create_time_asc: 较早创建
list:
fields:
visits: "访问量 {visits}"
comments: "评论 {comments}"
visits: 访问量 {visits}
comments: 评论 {comments}
settings:
title: 页面设置
groups:
@ -433,7 +458,7 @@ core:
pending_review: 待审核
owner:
label: 评论者
result: "评论者:{owner}"
result: 评论者:{owner}
sort:
items:
last_reply_time_desc: 较近回复
@ -484,10 +509,10 @@ core:
filters:
storage_policy:
label: 存储策略
result: "存储策略:{storage_policy}"
result: 存储策略:{storage_policy}
owner:
label: 上传者
result: "上传者:{owner}"
result: 上传者:{owner}
sort:
items:
create_time_desc: 较近上传
@ -691,7 +716,7 @@ core:
placeholder: 选择上级菜单项
ref_kind:
label: 类型
placeholder: "请选择{label}"
placeholder: 请选择{label}
options:
custom: 自定义链接
post: 文章
@ -783,8 +808,8 @@ core:
last_starttime: 最近一次启动
loader:
toast:
entry_load_failed: "加载插件入口文件失败"
style_load_failed: "加载插件样式文件失败"
entry_load_failed: 加载插件入口文件失败
style_load_failed: 加载插件样式文件失败
extension_points:
editor:
providers:
@ -854,19 +879,13 @@ core:
title: 用户详情
tabs:
detail: 详情
notification-preferences: 通知配置
pat: 个人令牌
actions:
update_profile:
title: 修改资料
change_password:
title: 修改密码
operations:
bind:
button: 绑定
unbind:
button: 解绑
title: "确定要取消绑定 {display_name} 的登录方式吗?"
profile:
title: 个人中心
fields:
display_name: 显示名称
username: 用户名
@ -875,21 +894,99 @@ core:
bio: 描述
creation_time: 注册时间
identity_authentication: 登录方式
avatar:
title: 头像
toast_upload_failed: 上传头像失败
toast_remove_failed: 删除头像失败
cropper_modal:
title: 裁剪头像
remove:
title: 确定要删除头像吗?
tooltips:
upload: 上传
zoom_in: 放大
zoom_out: 缩小
flip_horizontal: 水平翻转
flip_vertical: 垂直翻转
reset: 重置
role:
title: 角色
common:
text:
contains_all_permissions: 包含所有权限
contains_n_permissions: 包含 {count} 个权限
system_reserved: 系统保留
custom: 自定义
dependent_on: 依赖于 {roles}
provided_by_plugin: 由 {plugin} 提供
operations:
delete:
title: 确定要删除该角色吗?
description: 该角色删除后,相关联的用户将被删除角色绑定,该操作不可恢复。
create_based_on_this_role:
button: 基于此角色创建
detail:
title: 角色详情
header:
title: 角色信息
tabs:
detail: 详情
permissions: 权限设置
fields:
display_name: 名称
name: 别名
type: 类型
creation_time: 创建时间
permissions_detail:
system_reserved_alert:
description: 系统保留的角色不支持修改,推荐基于此角色创建一个新的角色。
editing_modal:
titles:
create: 创建角色
update: 编辑角色
groups:
general: 基本信息
permissions: 权限
fields:
display_name: 名称
redirect_on_login: 登录之后默认跳转位置
identity_authentication:
title: 身份认证
tabs:
detail: 详情
setting: 设置
operations:
enable:
title: 确定要启用该身份认证方式吗?
disable:
title: 确定要停用该身份认证方式吗?
disable_privileged:
tooltip: 系统保留的认证方式,无法禁用
detail:
title: 身份认证详情
fields:
display_name: 名称
description: 描述
website: 网站
help_page: 帮助页面
authentication_url: 登录入口
uc_profile:
title: 我的
tabs:
detail: 详情
notification-preferences: 通知配置
pat: 个人令牌
actions:
update_profile:
title: 修改资料
change_password:
title: 修改密码
detail:
fields:
display_name: 显示名称
username: 用户名
email: 电子邮箱
roles: 角色
bio: 描述
creation_time: 注册时间
identity_authentication: 登录方式
operations:
bind:
button: 绑定
unbind:
button: 解绑
title: 确定要取消绑定 {display_name} 的登录方式吗?
email_not_set:
title: 设置电子邮箱
description: 电子邮箱地址还未设置,点击下方按钮进行设置
email_not_verified:
title: 验证电子邮箱
description: 电子邮箱地址还未验证,点击下方按钮进行验证
pat:
operations:
delete:
@ -933,67 +1030,54 @@ core:
notification-preferences:
fields:
type: 通知类型
role:
title: 角色
common:
text:
contains_all_permissions: 包含所有权限
contains_n_permissions: 包含 {count} 个权限
system_reserved: 系统保留
custom: 自定义
dependent_on: 依赖于 {roles}
provided_by_plugin: 由 {plugin} 提供
operations:
delete:
title: 确定要删除该角色吗?
description: 该角色删除后,相关联的用户将被删除角色绑定,该操作不可恢复。
create_based_on_this_role:
button: 基于此角色创建
detail:
title: 角色详情
header:
title: 角色信息
tabs:
detail: 详情
permissions: 权限设置
fields:
display_name: 名称
name: 别名
type: 类型
creation_time: 创建时间
permissions_detail:
system_reserved_alert:
description: 系统保留的角色不支持修改,推荐基于此角色创建一个新的角色。
editing_modal:
titles:
create: 创建角色
update: 编辑角色
title: 编辑资料
groups:
general: 基本信息
permissions: 权限
general: 常规
annotations: 元数据
fields:
display_name: 名称
identity_authentication:
title: 身份认证
tabs:
detail: 详情
setting: 设置
operations:
enable:
title: 确定要启用该身份认证方式吗?
disable:
title: 确定要停用该身份认证方式吗?
disable_privileged:
tooltip: 系统保留的认证方式,无法禁用
detail:
title: 身份认证详情
username:
label: 用户名
validation: 请输入有效的用户名
display_name:
label: 显示名称
email:
label: 电子邮箱
phone:
label: 手机号
avatar:
label: 头像
bio:
label: 描述
change_password_modal:
title: 密码修改
fields:
display_name: 名称
description: 描述
website: 网站
help_page: 帮助页面
authentication_url: 登录入口
notification:
new_password:
label: 新密码
confirm_password:
label: 确认密码
email_verify_modal:
titles:
modify: 修改电子邮箱
verify: 验证电子邮箱
fields:
new_email:
label: 新电子邮箱
email:
label: 电子邮箱
code:
label: 验证码
operations:
send_code:
buttons:
sending: 发送中
send: 发送验证码
countdown: "{timer} 秒后重发"
toast_success: 验证码已发送
toast_email_empty: 请输入电子邮箱
verify:
toast_success: 验证成功
uc_notification:
title: 消息
tabs:
unread: 未读
@ -1005,6 +1089,9 @@ core:
operations:
mark_as_read:
button: 标记为已读
delete:
title: 删除消息
description: 确定要删除该消息吗?
setting:
title: 设置
actuator:
@ -1202,7 +1289,7 @@ core:
extensions:
placeholder:
options:
placeholder: "输入 / 以选择输入类型"
placeholder: 输入 / 以选择输入类型
toolbox:
attachment: 选择附件
show_hide_sidebar: 显示 / 隐藏侧边栏
@ -1231,8 +1318,23 @@ core:
app_download_alert:
description: Halo 的主题和插件可以在以下地址下载:
sources:
app_store: "官方应用市场:{url}"
github: "GitHub{url}"
app_store: 官方应用市场:{url}
github: GitHub{url}
user_avatar:
title: 头像
toast_upload_failed: 上传头像失败
toast_remove_failed: 删除头像失败
cropper_modal:
title: 裁剪头像
remove:
title: 确定要删除头像吗?
tooltips:
upload: 上传
zoom_in: 放大
zoom_out: 缩小
flip_horizontal: 水平翻转
flip_vertical: 垂直翻转
reset: 重置
composables:
content_cache:
toast_recovered: 已从缓存中恢复未保存的内容
@ -1278,6 +1380,8 @@ core:
detail: 详情
select: 选择
view_all: 查看全部
verify: 验证
modify: 修改
radio:
"yes":
"no":
@ -1319,9 +1423,9 @@ core:
editor_not_found: 未找到符合 {raw_type} 格式的编辑器,请检查是否已安装编辑器插件。
filters:
results:
keyword: "关键词:{keyword}"
sort: "排序:{sort}"
status: "状态:{status}"
keyword: 关键词:{keyword}
sort: 排序:{sort}
status: 状态:{status}
labels:
sort: 排序
status: 状态

View File

@ -75,12 +75,21 @@ core:
backup: 備份
operations:
logout:
button: 登出
tooltip: 登出
title: 確定要登出嗎?
profile:
button: 個人資料
tooltip: 個人中心
visit_homepage:
title: 訪問首頁
uc_sidebar:
menu:
items:
profile: 我的
notification: 消息
posts: 文章
operations:
console:
tooltip: 管理控制台
dashboard:
title: 儀表板
actions:
@ -102,9 +111,13 @@ core:
title: 頁面
recent_published:
title: 最近文章
visits: "訪問量 {visits}"
comments: "留言 {comments}"
publishTime: "發佈日期 {publishTime}"
visits: 訪問量 {visits}
comments: 留言 {comments}
publishTime: 發佈日期 {publishTime}
notification:
title: 通知
empty:
title: 当前没有未读的消息
quicklink:
title: 快捷訪問
actions:
@ -163,19 +176,19 @@ core:
draft: 未發布
visible:
label: 可見性
result: "可見性:{visible}"
result: 可見性:{visible}
items:
public: 公開
private: 私有
category:
label: 分類
result: "分類:{category}"
result: 分類:{category}
tag:
label: 標籤
result: "標籤:{tag}"
result: 標籤:{tag}
author:
label: 作者
result: "作者:{author}"
result: 作者:{author}
sort:
items:
publish_time_desc: 較近發布
@ -185,8 +198,8 @@ core:
list:
fields:
categories: 分類:
visits: "訪問量 {visits}"
comments: "留言 {comments}"
visits: 訪問量 {visits}
comments: 留言 {comments}
pinned: 已置頂
settings:
title: 文章設置
@ -331,13 +344,13 @@ core:
draft: 未發布
visible:
label: 可見性
result: "可見性:{visible}"
result: 可見性:{visible}
items:
public: 公開
private: 私有
author:
label: 作者
result: "作者:{author}"
result: 作者:{author}
sort:
items:
publish_time_desc: 較近發布
@ -346,8 +359,8 @@ core:
create_time_asc: 較早創建
list:
fields:
visits: "訪問量 {visits}"
comments: "留言 {comments}"
visits: 訪問量 {visits}
comments: 留言 {comments}
settings:
title: 頁面設置
groups:
@ -433,7 +446,7 @@ core:
pending_review: 待審核
owner:
label: 留言者
result: "留言者:{owner}"
result: 留言者:{owner}
sort:
items:
last_reply_time_desc: 較近回覆
@ -484,10 +497,10 @@ core:
filters:
storage_policy:
label: 存儲策略
result: "存儲策略:{storage_policy}"
result: 存儲策略:{storage_policy}
owner:
label: 上傳者
result: "上傳者:{owner}"
result: 上傳者:{owner}
sort:
items:
create_time_desc: 較近上傳
@ -691,7 +704,7 @@ core:
placeholder: 選擇上級選單項
ref_kind:
label: 類型
placeholder: "請選擇{label}"
placeholder: 請選擇{label}
options:
custom: 自定義連結
post: 文章
@ -783,8 +796,8 @@ core:
last_starttime: 最近一次啟動
loader:
toast:
entry_load_failed: "讀取插件入口文件失敗"
style_load_failed: "讀取插件樣式文件失敗"
entry_load_failed: 讀取插件入口文件失敗
style_load_failed: 讀取插件樣式文件失敗
extension_points:
editor:
providers:
@ -854,19 +867,13 @@ core:
title: 用戶詳情
tabs:
detail: 詳情
notification-preferences: 通知配置
pat: 個人令牌
actions:
update_profile:
title: 修改資料
change_password:
title: 修改密碼
operations:
bind:
button: 綁定
unbind:
button: 解綁
title: "確定要取消綁定 {display_name} 的登入方式嗎?"
profile:
title: 個人中心
fields:
display_name: 顯示名稱
username: 用戶名
@ -875,64 +882,6 @@ core:
bio: 描述
creation_time: 註冊時間
identity_authentication: 登入方式
avatar:
title: 頭像
toast_upload_failed: 上傳頭像失敗
toast_remove_failed: 刪除頭像失敗
cropper_modal:
title: 裁剪頭像
remove:
title: 確定要刪除頭像嗎?
tooltips:
upload: 上傳
zoom_in: 放大
zoom_out: 縮小
flip_horizontal: 水平翻轉
flip_vertical: 垂直翻轉
reset: 重置
pat:
operations:
delete:
title: 刪除個人令牌
description: 您確定要刪除此個人令牌嗎?
revoke:
button: 撤銷
title: 撤銷個人令牌
description: 您確定要撤銷此個人令牌嗎?
toast_success: 撤銷成功
copy:
title: 請立即複製並保存,令牌僅顯示一次。
restore:
button: 還原
toast_success: 還原成功
list:
empty:
title: 目前尚未建立個人令牌
message: 您可以嘗試重新整理或建立新的個人令牌
fields:
expiresAt:
dynamic: "到期於 {expiresAt}"
forever: 永久有效
status:
normal: 正常
revoked: 已撤銷
expired: 已過期
creation_modal:
title: 建立個人令牌
groups:
general: 基本資訊
permissions: 權限
fields:
name:
label: 名稱
expiresAt:
label: 到期時間
help: 留空代表永不過期
description:
label: 描述
notification-preferences:
fields:
type: 通知類型
role:
title: 角色
common:
@ -973,6 +922,7 @@ core:
permissions: 權限
fields:
display_name: 名稱
redirect_on_login: 登入之後預設跳轉位置
identity_authentication:
title: 身份認證
tabs:
@ -993,7 +943,129 @@ core:
website: 網站
help_page: 幫助頁面
authentication_url: 登入入口
notification:
uc_profile:
title: 我的
tabs:
detail: 詳情
notification-preferences: 通知配置
pat: 個人令牌
actions:
update_profile:
title: 修改資料
change_password:
title: 修改密碼
detail:
fields:
display_name: 顯示名稱
username: 用戶名
email: 電子郵箱
roles: 角色
bio: 描述
creation_time: 註冊時間
identity_authentication: 登入方式
operations:
bind:
button: 綁定
unbind:
button: 解綁
title: 確定要取消綁定 {display_name} 的登入方式嗎?
email_not_set:
description: 電子郵件地址尚未設置,點擊下方按鈕進行設置
title: 設定電子郵件信箱
email_not_verified:
description: 電子郵件地址尚未驗證,點擊下方按鈕進行驗證
title: 驗證電子郵件信箱
pat:
operations:
delete:
title: 刪除個人令牌
description: 您確定要刪除此個人令牌嗎?
revoke:
button: 撤銷
title: 撤銷個人令牌
description: 您確定要撤銷此個人令牌嗎?
toast_success: 撤銷成功
copy:
title: 請立即複製並保存,令牌僅顯示一次。
restore:
button: 還原
toast_success: 還原成功
list:
empty:
title: 目前尚未建立個人令牌
message: 您可以嘗試重新整理或建立新的個人令牌
fields:
expiresAt:
dynamic: 到期於 {expiresAt}
forever: 永久有效
status:
normal: 正常
revoked: 已撤銷
expired: 已過期
creation_modal:
title: 建立個人令牌
groups:
general: 基本資訊
permissions: 權限
fields:
name:
label: 名稱
expiresAt:
label: 到期時間
help: 留空代表永不過期
description:
label: 描述
notification-preferences:
fields:
type: 通知類型
editing_modal:
title: 編輯資料
groups:
general: 常規
annotations: 元數據
fields:
username:
label: 用戶名
validation: 請輸入有效的用戶名
display_name:
label: 顯示名稱
email:
label: 電子郵箱
phone:
label: 手機號
avatar:
label: 頭像
bio:
label: 描述
change_password_modal:
title: 密碼修改
fields:
new_password:
label: 新密碼
confirm_password:
label: 確認密碼
email_verify_modal:
fields:
code:
label: 驗證碼
email:
label: 電子郵件信箱
new_email:
label: 新電子郵件信箱
operations:
send_code:
buttons:
countdown: "{timer} 秒後重發"
send: 發送驗證碼
sending: 發送中
toast_email_empty: 請輸入電子郵件信箱
toast_success: 驗證碼已發送
verify:
toast_success: 驗證成功
titles:
modify: 修改電子郵件信箱
verify: 驗證電子郵件信箱
uc_notification:
title: 訊息
tabs:
unread: 未讀
@ -1005,6 +1077,9 @@ core:
operations:
mark_as_read:
button: 標記為已讀
delete:
description: 確定要刪除該訊息嗎?
title: 刪除訊息
setting:
title: 設置
actuator:
@ -1202,7 +1277,7 @@ core:
extensions:
placeholder:
options:
placeholder: "輸入 / 以選擇輸入類型"
placeholder: 輸入 / 以選擇輸入類型
toolbox:
attachment: 選擇附件
show_hide_sidebar: 顯示 / 隱藏側邊欄
@ -1231,8 +1306,23 @@ core:
app_download_alert:
description: Halo 的主題和插件可以在以下地址下載:
sources:
app_store: "官方應用市場:{url}"
github: "GitHub{url}"
app_store: 官方應用市場:{url}
github: GitHub{url}
user_avatar:
title: 頭像
toast_upload_failed: 上傳頭像失敗
toast_remove_failed: 刪除頭像失敗
cropper_modal:
title: 裁剪頭像
remove:
title: 確定要刪除頭像嗎?
tooltips:
upload: 上傳
zoom_in: 放大
zoom_out: 縮小
flip_horizontal: 水平翻轉
flip_vertical: 垂直翻轉
reset: 重置
composables:
content_cache:
toast_recovered: 已從緩存中恢復未保存的內容
@ -1278,6 +1368,8 @@ core:
detail: 詳情
select: 選擇
view_all: 查看全部
modify: 修改
verify: 驗證
radio:
"yes":
"no":
@ -1319,9 +1411,9 @@ core:
editor_not_found: 未找到符合 {raw_type} 格式的編輯器,請檢查是否已安裝編輯器插件。
filters:
results:
keyword: "關鍵字:{keyword}"
sort: "排序:{sort}"
status: "狀態:{status}"
keyword: 關鍵字:{keyword}
sort: 排序:{sort}
status: 狀態:{status}
labels:
sort: 排序
status: 狀態
@ -1346,3 +1438,15 @@ core:
recovering: 還原中
fields:
post_count: "{count} 篇文章"
uc_post:
creation_modal:
title: 創建文章
operations:
cancel_publish:
description: 確定要取消發布嗎?
title: 取消發布
publish_modal:
title: 發布文章
setting_modal:
title: 文章設定
title: 我的文章

View File

@ -145,7 +145,7 @@ onMounted(() => {
</div>
<div class="flex items-center gap-1">
<a
v-tooltip="'管理控制台'"
v-tooltip="$t('core.uc_sidebar.operations.console.tooltip')"
class="group inline-block cursor-pointer rounded-full p-1.5 transition-all hover:bg-gray-100"
href="/console"
>
@ -154,6 +154,7 @@ onMounted(() => {
/>
</a>
<div
v-tooltip="$t('core.sidebar.operations.logout.tooltip')"
class="group inline-block cursor-pointer rounded-full p-1.5 transition-all hover:bg-gray-100"
@click="handleLogout"
>

View File

@ -419,7 +419,7 @@ useSessionKeepAlive();
<PostCreationModal
v-if="postCreationModal"
title="创建文章"
:title="$t('core.uc_post.creation_modal.title')"
:content="content"
@close="postCreationModal = false"
@success="onCreatePostSuccess"
@ -427,7 +427,7 @@ useSessionKeepAlive();
<PostCreationModal
v-if="postPublishModal"
title="发布文章"
:title="$t('core.uc_post.publish_modal.title')"
:content="content"
publish
@close="postPublishModal = false"

View File

@ -84,7 +84,7 @@ const {
</script>
<template>
<VPageHeader title="我的文章">
<VPageHeader :title="$t('core.uc_post.title')">
<template #icon>
<IconBookRead class="mr-2 self-center" />
</template>
@ -121,21 +121,13 @@ const {
value: undefined,
},
{
label: '已发布',
label: $t('core.post.filters.status.items.published'),
value: 'PUBLISHED',
},
{
label: '待审核',
value: 'PENDING_APPROVAL',
},
{
label: '未发布',
label: $t('core.post.filters.status.items.draft'),
value: 'DRAFT',
},
{
label: '发布失败',
value: 'FAILED',
},
]"
/>
<div

View File

@ -67,8 +67,10 @@ async function handlePublish() {
function handleUnpublish() {
Dialog.warning({
title: "取消发布",
description: "确定要取消发布吗?",
title: t("core.uc_post.operations.cancel_publish.title"),
description: t("core.uc_post.operations.cancel_publish.description"),
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
async onConfirm() {
await apiClient.uc.post.unpublishMyPost({
name: props.post.post.metadata.name,
@ -216,17 +218,17 @@ function handleUnpublish() {
})
"
>
编辑
{{ $t("core.common.buttons.edit") }}
</VDropdownItem>
<HasPermission :permissions="['uc:posts:publish']">
<VDropdownItem
v-if="post.post.metadata.labels?.[postLabels.PUBLISHED] === 'false'"
@click="handlePublish"
>
发布
{{ $t("core.common.buttons.publish") }}
</VDropdownItem>
<VDropdownItem v-else type="danger" @click="handleUnpublish">
取消发布
{{ $t("core.common.buttons.cancel_publish") }}
</VDropdownItem>
</HasPermission>
</template>

View File

@ -88,7 +88,7 @@ function onSubmit(data: PostFormState) {
<VModal
v-if="shouldRender"
v-model:visible="visible"
title="文章设置"
:title="$t('core.uc_post.setting_modal.title')"
:width="700"
centered
@close="onClose"

View File

@ -16,11 +16,11 @@ export default definePlugin({
name: "Posts",
component: PostList,
meta: {
title: "core.post.title",
title: "core.uc_post.title",
searchable: true,
permissions: ["uc:posts:manage"],
menu: {
name: "core.sidebar.menu.items.posts",
name: "core.uc_sidebar.menu.items.posts",
group: "content",
icon: markRaw(IconBookRead),
priority: 0,
@ -33,7 +33,7 @@ export default definePlugin({
name: "PostEditor",
component: PostEditor,
meta: {
title: "文章编辑",
title: "core.post_editor.title",
searchable: true,
permissions: ["uc:posts:manage"],
},

View File

@ -56,7 +56,7 @@ const selectedNotification = computed(() => {
</script>
<template>
<VPageHeader :title="$t('core.notification.title')">
<VPageHeader :title="$t('core.uc_notification.title')">
<template #icon>
<IconNotificationBadgeLine class="mr-2 self-center" />
</template>
@ -80,8 +80,8 @@ const selectedNotification = computed(() => {
v-model:active-id="activeTab"
class="sticky top-0 z-10 !rounded-none"
:items="[
{ id: 'unread', label: $t('core.notification.tabs.unread') },
{ id: 'read', label: $t('core.notification.tabs.read') },
{ id: 'unread', label: $t('core.uc_notification.tabs.unread') },
{ id: 'read', label: $t('core.uc_notification.tabs.read') },
]"
type="outline"
@change="selectedNotificationName = undefined"
@ -95,8 +95,8 @@ const selectedNotification = computed(() => {
<VEmpty
:title="`${
activeTab === 'unread'
? $t('core.notification.empty.titles.unread')
: $t('core.notification.empty.titles.read')
? $t('core.uc_notification.empty.titles.unread')
: $t('core.uc_notification.empty.titles.read')
}`"
>
<template #actions>

View File

@ -7,8 +7,10 @@ import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { Dialog, Toast, VStatusDot } from "@halo-dev/components";
import { watch } from "vue";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
const queryClient = useQueryClient();
const { t } = useI18n();
const props = withDefaults(
defineProps<{
@ -43,8 +45,8 @@ const { mutate: handleMarkAsRead } = useMutation({
function handleDelete() {
Dialog.warning({
title: "删除消息",
description: "确定要删除该消息吗?",
title: t("core.uc_notification.operations.delete.title"),
description: t("core.uc_notification.operations.delete.description"),
async onConfirm() {
await apiClient.notification.deleteSpecifiedNotification({
name: props.notification.metadata.name,
@ -53,7 +55,7 @@ function handleDelete() {
await queryClient.invalidateQueries({ queryKey: ["user-notifications"] });
Toast.success("删除成功");
Toast.success(t("core.common.toast.delete_success"));
},
});
}
@ -109,7 +111,7 @@ watch(
class="text-sm text-gray-600 hover:text-gray-900"
@click.stop="handleMarkAsRead({ refetch: true })"
>
{{ $t("core.notification.operations.mark_as_read.button") }}
{{ $t("core.uc_notification.operations.mark_as_read.button") }}
</span>
<span
class="text-sm text-red-600 hover:text-red-700"

View File

@ -15,10 +15,10 @@ export default definePlugin({
name: "Notifications",
component: Notifications,
meta: {
title: "消息",
title: "core.uc_notification.title",
searchable: true,
menu: {
name: "消息",
name: "core.uc_sidebar.menu.items.notification",
group: "dashboard",
icon: markRaw(IconNotificationBadgeLine),
priority: 1,

View File

@ -63,19 +63,19 @@ provide<Ref<DetailedUser | undefined>>("user", user);
const tabs: UserTab[] = [
{
id: "detail",
label: t("core.user.detail.tabs.detail"),
label: t("core.uc_profile.tabs.detail"),
component: markRaw(DetailTab),
priority: 10,
},
{
id: "notification-preferences",
label: t("core.user.detail.tabs.notification-preferences"),
label: t("core.uc_profile.tabs.notification-preferences"),
component: markRaw(NotificationPreferences),
priority: 20,
},
{
id: "pat",
label: t("core.user.detail.tabs.pat"),
label: t("core.uc_profile.tabs.pat"),
component: markRaw(PersonalAccessTokensTab),
priority: 30,
},
@ -122,10 +122,10 @@ const activeTab = useRouteQuery<string>("tab", tabs[0].id, {
</VButton>
<template #popper>
<VDropdownItem @click="editingModal = true">
{{ $t("core.user.detail.actions.update_profile.title") }}
{{ $t("core.uc_profile.actions.update_profile.title") }}
</VDropdownItem>
<VDropdownItem @click="passwordChangeModal = true">
{{ $t("core.user.detail.actions.change_password.title") }}
{{ $t("core.uc_profile.actions.change_password.title") }}
</VDropdownItem>
</template>
</VDropdown>

View File

@ -8,8 +8,10 @@ import { apiClient } from "@/utils/api-client";
import { useUserStore } from "@/stores/user";
import { useIntervalFn } from "@vueuse/shared";
import { computed } from "vue";
import { useI18n } from "vue-i18n";
const queryClient = useQueryClient();
const { t } = useI18n();
const { currentUser, fetchCurrentUser } = useUserStore();
@ -58,7 +60,11 @@ const { mutate: sendVerifyCode, isLoading: isSending } = useMutation({
mutationKey: ["send-verify-code"],
mutationFn: async () => {
if (!email.value) {
Toast.error("请输入电子邮箱");
Toast.error(
t(
"core.uc_profile.email_verify_modal.operations.send_code.toast_email_empty"
)
);
throw new Error("email is empty");
}
return await apiClient.user.sendEmailVerificationCode({
@ -68,7 +74,9 @@ const { mutate: sendVerifyCode, isLoading: isSending } = useMutation({
});
},
onSuccess() {
Toast.success("验证码已发送");
Toast.success(
t("core.uc_profile.email_verify_modal.operations.send_code.toast_success")
);
timer.value = 60;
resume();
},
@ -76,9 +84,16 @@ const { mutate: sendVerifyCode, isLoading: isSending } = useMutation({
const sendVerifyCodeButtonText = computed(() => {
if (isSending.value) {
return "发送中";
return t(
"core.uc_profile.email_verify_modal.operations.send_code.buttons.sending"
);
}
return isActive.value ? `${timer.value} 秒后重发` : "发送验证码";
return isActive.value
? t(
"core.uc_profile.email_verify_modal.operations.send_code.buttons.countdown",
{ timer: timer.value }
)
: t("core.uc_profile.email_verify_modal.operations.send_code.buttons.send");
});
const { mutate: verifyEmail, isLoading: isVerifying } = useMutation({
@ -91,7 +106,9 @@ const { mutate: verifyEmail, isLoading: isVerifying } = useMutation({
});
},
onSuccess() {
Toast.success("验证成功");
Toast.success(
t("core.uc_profile.email_verify_modal.operations.verify.toast_success")
);
queryClient.invalidateQueries({ queryKey: ["user-detail"] });
fetchCurrentUser();
onClose();
@ -107,7 +124,11 @@ function handleVerify(data: { code: string }) {
<VModal
v-if="shouldRender"
v-model:visible="visible"
:title="currentUser?.spec.emailVerified ? '修改电子邮箱' : '绑定电子邮箱'"
:title="
currentUser?.spec.emailVerified
? $t('core.uc_profile.email_verify_modal.titles.modify')
: $t('core.uc_profile.email_verify_modal.titles.verify')
"
@close="onClose"
>
<FormKit
@ -119,11 +140,20 @@ function handleVerify(data: { code: string }) {
<FormKit
v-model="email"
type="email"
:label="currentUser?.spec.emailVerified ? '新电子邮箱' : '电子邮箱'"
:label="
currentUser?.spec.emailVerified
? $t('core.uc_profile.email_verify_modal.fields.new_email.label')
: $t('core.uc_profile.email_verify_modal.fields.email.label')
"
name="email"
validation="required|email"
></FormKit>
<FormKit type="number" name="code" label="验证码" validation="required">
<FormKit
type="number"
name="code"
:label="$t('core.uc_profile.email_verify_modal.fields.code.label')"
validation="required"
>
<template #suffix>
<VButton
:loading="isSending"
@ -143,9 +173,11 @@ function handleVerify(data: { code: string }) {
type="secondary"
@click="$formkit.submit('email-verify-form')"
>
验证
{{ $t("core.common.buttons.verify") }}
</VButton>
<VButton @click="emit('close')">
{{ $t("core.common.buttons.close_and_shortcut") }}
</VButton>
<VButton @click="emit('close')"></VButton>
</VSpace>
</template>
</VModal>

View File

@ -85,7 +85,7 @@ const handleChangePassword = async () => {
<VModal
:visible="visible"
:width="500"
:title="$t('core.user.change_password_modal.title')"
:title="$t('core.uc_profile.change_password_modal.title')"
@update:visible="onVisibleChange"
>
<FormKit
@ -99,7 +99,9 @@ const handleChangePassword = async () => {
>
<FormKit
id="passwordInput"
:label="$t('core.user.change_password_modal.fields.new_password.label')"
:label="
$t('core.uc_profile.change_password_modal.fields.new_password.label')
"
name="password"
type="password"
validation="required:trim|length:5,100|matches:/^\S.*\S$/"
@ -109,7 +111,9 @@ const handleChangePassword = async () => {
></FormKit>
<FormKit
:label="
$t('core.user.change_password_modal.fields.confirm_password.label')
$t(
'core.uc_profile.change_password_modal.fields.confirm_password.label'
)
"
name="password_confirm"
type="password"

View File

@ -76,7 +76,7 @@ const {
setTimeout(() => {
Dialog.info({
title: t("core.user.pat.operations.copy.title"),
title: t("core.uc_profile.pat.operations.copy.title"),
description: data.metadata.annotations?.[patAnnotations.ACCESS_TOKEN],
confirmType: "secondary",
confirmText: t("core.common.buttons.copy"),
@ -102,7 +102,7 @@ const { copy } = useClipboard({
<VModal
v-model:visible="visible"
:width="700"
:title="$t('core.user.pat.creation_modal.title')"
:title="$t('core.uc_profile.pat.creation_modal.title')"
@close="emit('close')"
>
<div>
@ -110,7 +110,7 @@ const { copy } = useClipboard({
<div class="md:col-span-1">
<div class="sticky top-0">
<span class="text-base font-medium text-gray-900">
{{ $t("core.user.pat.creation_modal.groups.general") }}
{{ $t("core.uc_profile.pat.creation_modal.groups.general") }}
</span>
</div>
</div>
@ -126,19 +126,27 @@ const { copy } = useClipboard({
validation="required"
type="text"
name="name"
:label="$t('core.user.pat.creation_modal.fields.name.label')"
:label="
$t('core.uc_profile.pat.creation_modal.fields.name.label')
"
></FormKit>
<FormKit
type="datetime-local"
name="expiresAt"
:label="$t('core.user.pat.creation_modal.fields.expiresAt.label')"
:help="$t('core.user.pat.creation_modal.fields.expiresAt.help')"
:label="
$t('core.uc_profile.pat.creation_modal.fields.expiresAt.label')
"
:help="
$t('core.uc_profile.pat.creation_modal.fields.expiresAt.help')
"
></FormKit>
<FormKit
type="textarea"
name="description"
:label="
$t('core.user.pat.creation_modal.fields.description.label')
$t(
'core.uc_profile.pat.creation_modal.fields.description.label'
)
"
></FormKit>
</FormKit>
@ -154,7 +162,7 @@ const { copy } = useClipboard({
<div class="md:col-span-1">
<div class="sticky top-0">
<span class="text-base font-medium text-gray-900">
{{ $t("core.user.pat.creation_modal.groups.permissions") }}
{{ $t("core.uc_profile.pat.creation_modal.groups.permissions") }}
</span>
</div>
</div>

View File

@ -27,8 +27,8 @@ const { t } = useI18n();
function handleDelete() {
Dialog.warning({
title: t("core.user.pat.operations.delete.title"),
description: t("core.user.pat.operations.delete.description"),
title: t("core.uc_profile.pat.operations.delete.title"),
description: t("core.uc_profile.pat.operations.delete.description"),
async onConfirm() {
await apiClient.pat.deletePat({
name: props.token.metadata.name,
@ -42,14 +42,14 @@ function handleDelete() {
function handleRevoke() {
Dialog.warning({
title: t("core.user.pat.operations.revoke.title"),
description: t("core.user.pat.operations.revoke.description"),
title: t("core.uc_profile.pat.operations.revoke.title"),
description: t("core.uc_profile.pat.operations.revoke.description"),
async onConfirm() {
await apiClient.pat.revokePat({
name: props.token.metadata.name,
});
Toast.success(t("core.user.pat.operations.revoke.toast_success"));
Toast.success(t("core.uc_profile.pat.operations.revoke.toast_success"));
queryClient.invalidateQueries({ queryKey: ["personal-access-tokens"] });
},
});
@ -58,19 +58,19 @@ function handleRevoke() {
async function handleRestore() {
await apiClient.pat.restorePat({ name: props.token.metadata.name });
Toast.success(t("core.user.pat.operations.restore.toast_success"));
Toast.success(t("core.uc_profile.pat.operations.restore.toast_success"));
queryClient.invalidateQueries({ queryKey: ["personal-access-tokens"] });
}
const statusText = computed(() => {
const { expiresAt } = props.token.spec || {};
if (expiresAt && new Date(expiresAt) < new Date()) {
return t("core.user.pat.list.fields.status.expired");
return t("core.uc_profile.pat.list.fields.status.expired");
}
return t(
props.token.spec?.revoked
? "core.user.pat.list.fields.status.revoked"
: "core.user.pat.list.fields.status.normal"
? "core.uc_profile.pat.list.fields.status.revoked"
: "core.uc_profile.pat.list.fields.status.normal"
);
});
@ -109,13 +109,13 @@ const statusTheme = computed(() => {
v-tooltip="formatDatetime(token.spec.expiresAt)"
>
{{
$t("core.user.pat.list.fields.expiresAt.dynamic", {
$t("core.uc_profile.pat.list.fields.expiresAt.dynamic", {
expiresAt: relativeTimeTo(token.spec?.expiresAt),
})
}}
</span>
<span v-else>
{{ $t("core.user.pat.list.fields.expiresAt.forever") }}
{{ $t("core.uc_profile.pat.list.fields.expiresAt.forever") }}
</span>
</div>
</template>
@ -135,10 +135,10 @@ const statusTheme = computed(() => {
type="danger"
@click="handleRevoke"
>
{{ $t("core.user.pat.operations.revoke.button") }}
{{ $t("core.uc_profile.pat.operations.revoke.button") }}
</VDropdownItem>
<VDropdownItem v-else @click="handleRestore">
{{ $t("core.user.pat.operations.restore.button") }}
{{ $t("core.uc_profile.pat.operations.restore.button") }}
</VDropdownItem>
<VDropdownDivider />
<VDropdownItem type="danger" @click="handleDelete">

View File

@ -114,7 +114,7 @@ async function onEmailVerifyModalClose() {
</script>
<template>
<VModal
:title="$t('core.user.editing_modal.titles.update')"
:title="$t('core.uc_profile.editing_modal.title')"
:visible="visible"
:width="700"
@update:visible="onVisibleChange"
@ -131,7 +131,7 @@ async function onEmailVerifyModalClose() {
<div class="md:col-span-1">
<div class="sticky top-0">
<span class="text-base font-medium text-gray-900">
{{ $t("core.user.editing_modal.groups.general") }}
{{ $t("core.uc_profile.editing_modal.groups.general") }}
</span>
</div>
</div>
@ -140,21 +140,23 @@ async function onEmailVerifyModalClose() {
id="userNameInput"
v-model="formState.metadata.name"
:disabled="true"
:label="$t('core.user.editing_modal.fields.username.label')"
:label="$t('core.uc_profile.editing_modal.fields.username.label')"
type="text"
name="name"
></FormKit>
<FormKit
id="displayNameInput"
v-model="formState.spec.displayName"
:label="$t('core.user.editing_modal.fields.display_name.label')"
:label="
$t('core.uc_profile.editing_modal.fields.display_name.label')
"
type="text"
name="displayName"
validation="required|length:0,50"
></FormKit>
<FormKit
v-model="formState.spec.email"
:label="$t('core.user.editing_modal.fields.email.label')"
:label="$t('core.uc_profile.editing_modal.fields.email.label')"
type="email"
name="email"
readonly
@ -165,20 +167,20 @@ async function onEmailVerifyModalClose() {
class="rounded-none border-y-0 border-l border-r-0"
@click="emailVerifyModal = true"
>
修改
{{ $t("core.common.buttons.modify") }}
</VButton>
</template>
</FormKit>
<FormKit
v-model="formState.spec.phone"
:label="$t('core.user.editing_modal.fields.phone.label')"
:label="$t('core.uc_profile.editing_modal.fields.phone.label')"
type="text"
name="phone"
validation="length:0,20"
></FormKit>
<FormKit
v-model="formState.spec.bio"
:label="$t('core.user.editing_modal.fields.bio.label')"
:label="$t('core.uc_profile.editing_modal.fields.bio.label')"
type="textarea"
name="bio"
validation="length:0,2048"

View File

@ -17,10 +17,10 @@ export default definePlugin({
name: "Profile",
component: Profile,
meta: {
title: "个人资料",
title: "core.uc_profile.title",
searchable: true,
menu: {
name: "我的",
name: "core.uc_sidebar.menu.items.profile",
group: "dashboard",
icon: markRaw(IconAccountCircleLine),
priority: 0,

View File

@ -40,7 +40,7 @@ const availableAuthProviders = computed(() => {
const handleUnbindAuth = (authProvider: ListedAuthProvider) => {
Dialog.warning({
title: t("core.user.detail.operations.unbind.title", {
title: t("core.uc_profile.detail.operations.unbind.title", {
display_name: authProvider.displayName,
}),
confirmText: t("core.common.buttons.confirm"),
@ -74,29 +74,33 @@ const emailVerifyModal = ref(false);
<div class="border-t border-gray-100">
<VDescription>
<VDescriptionItem
:label="$t('core.user.detail.fields.display_name')"
:label="$t('core.uc_profile.detail.fields.display_name')"
:content="user?.user.spec.displayName"
class="!px-2"
/>
<VDescriptionItem
:label="$t('core.user.detail.fields.username')"
:label="$t('core.uc_profile.detail.fields.username')"
:content="user?.user.metadata.name"
class="!px-2"
/>
<VDescriptionItem
:label="$t('core.user.detail.fields.email')"
:label="$t('core.uc_profile.detail.fields.email')"
class="!px-2"
>
<div v-if="user" class="w-full xl:w-1/2">
<VAlert
v-if="!user.user.spec.email"
title="设置电子邮箱"
description="电子邮箱地址还未设置,点击下方按钮进行设置"
:title="$t('core.uc_profile.detail.email_not_set.title')"
:description="
$t('core.uc_profile.detail.email_not_set.description')
"
type="warning"
:closable="false"
>
<template #actions>
<VButton size="sm" @click="emailVerifyModal = true">设置</VButton>
<VButton size="sm" @click="emailVerifyModal = true">
{{ $t("core.common.buttons.setting") }}
</VButton>
</template>
</VAlert>
@ -104,14 +108,16 @@ const emailVerifyModal = ref(false);
<span>{{ user.user.spec.email }}</span>
<div v-if="!user.user.spec.emailVerified" class="mt-3">
<VAlert
title="验证电子邮箱"
description="电子邮箱地址还未验证,点击下方按钮进行验证"
:title="$t('core.uc_profile.detail.email_not_verified.title')"
:description="
$t('core.uc_profile.detail.email_not_verified.description')
"
type="warning"
:closable="false"
>
<template #actions>
<VButton size="sm" @click="emailVerifyModal = true">
验证
{{ $t("core.common.buttons.verify") }}
</VButton>
</template>
</VAlert>
@ -120,7 +126,7 @@ const emailVerifyModal = ref(false);
</div>
</VDescriptionItem>
<VDescriptionItem
:label="$t('core.user.detail.fields.roles')"
:label="$t('core.uc_profile.detail.fields.roles')"
class="!px-2"
>
<VTag v-for="role in user?.roles" :key="role.metadata.name">
@ -134,18 +140,18 @@ const emailVerifyModal = ref(false);
</VTag>
</VDescriptionItem>
<VDescriptionItem
:label="$t('core.user.detail.fields.bio')"
:label="$t('core.uc_profile.detail.fields.bio')"
:content="user?.user.spec?.bio || $t('core.common.text.none')"
class="!px-2"
/>
<VDescriptionItem
:label="$t('core.user.detail.fields.creation_time')"
:label="$t('core.uc_profile.detail.fields.creation_time')"
:content="formatDatetime(user?.user.metadata?.creationTimestamp)"
class="!px-2"
/>
<VDescriptionItem
v-if="!isFetching && availableAuthProviders?.length"
:label="$t('core.user.detail.fields.identity_authentication')"
:label="$t('core.uc_profile.detail.fields.identity_authentication')"
class="!px-2"
>
<ul class="space-y-2">
@ -171,7 +177,7 @@ const emailVerifyModal = ref(false);
size="sm"
@click="handleUnbindAuth(authProvider)"
>
{{ $t("core.user.detail.operations.unbind.button") }}
{{ $t("core.uc_profile.detail.operations.unbind.button") }}
</VButton>
<VButton
v-else
@ -179,7 +185,7 @@ const emailVerifyModal = ref(false);
type="secondary"
@click="handleBindAuth(authProvider)"
>
{{ $t("core.user.detail.operations.bind.button") }}
{{ $t("core.uc_profile.detail.operations.unbind.button") }}
</VButton>
</div>
</div>

View File

@ -101,7 +101,7 @@ const {
class="px-4 py-3 text-left text-sm font-semibold text-gray-900 sm:w-96"
scope="col"
>
{{ $t("core.user.notification-preferences.fields.type") }}
{{ $t("core.uc_profile.notification-preferences.fields.type") }}
</th>
<th
v-for="notifier in data?.notifiers"

View File

@ -65,8 +65,8 @@ function onCreationModalClose() {
<Transition v-else-if="!pats?.length" appear name="fade">
<VEmpty
:message="$t('core.user.pat.list.empty.message')"
:title="$t('core.user.pat.list.empty.title')"
:message="$t('core.uc_profile.pat.list.empty.message')"
:title="$t('core.uc_profile.pat.list.empty.title')"
>
<template #actions>
<VSpace>