perf: 优化中英文翻译与切换

This commit is contained in:
xiaojunnuo
2025-06-28 23:57:01 +08:00
parent 082f47663d
commit acaa8b1731
43 changed files with 4121 additions and 4175 deletions
@@ -1,15 +1,15 @@
<script setup lang="ts">
import type { VbenFormSchema } from '/@/vben/form-ui';
import type { VbenFormSchema } from "/@/vben/form-ui";
import { computed, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { computed, reactive } from "vue";
import { useRouter } from "vue-router";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import { useVbenForm } from '/@/vben/form-ui';
import { VbenButton } from '/@/vben/shadcn-ui';
import { useVbenForm } from "/@/vben/form-ui";
import { VbenButton } from "/@/vben/shadcn-ui";
import Title from './auth-title.vue';
import Title from "./auth-title.vue";
interface Props {
formSchema: VbenFormSchema[];
@@ -36,15 +36,15 @@ interface Props {
}
defineOptions({
name: 'ForgetPassword',
name: "ForgetPassword",
});
const props = withDefaults(defineProps<Props>(), {
loading: false,
loginPath: '/auth/login',
submitButtonText: '',
subTitle: '',
title: '',
loginPath: "/auth/login",
submitButtonText: "",
subTitle: "",
title: "",
});
const emit = defineEmits<{
@@ -59,7 +59,7 @@ const [Form, formApi] = useVbenForm(
},
schema: computed(() => props.formSchema),
showDefaultActions: false,
}),
})
);
const router = useRouter();
@@ -68,7 +68,7 @@ async function handleSubmit() {
const { valid } = await formApi.validate();
const values = await formApi.getValues();
if (valid) {
emit('submit', values);
emit("submit", values);
}
}
@@ -84,12 +84,10 @@ defineExpose({
<template>
<div>
<Title>
<slot name="title">
{{ title || $t('authentication.forgetPassword') }} 🤦🏻
</slot>
<slot name="title"> {{ title || $t("authentication.forgetPassword") }} 🤦🏻 </slot>
<template #desc>
<slot name="subTitle">
{{ subTitle || $t('authentication.forgetPasswordSubtitle') }}
{{ subTitle || $t("authentication.forgetPasswordSubtitle") }}
</slot>
</template>
</Title>
@@ -105,11 +103,11 @@ defineExpose({
@click="handleSubmit"
>
<slot name="submitButtonText">
{{ submitButtonText || $t('authentication.sendResetLink') }}
{{ submitButtonText || $t("authentication.sendResetLink") }}
</slot>
</VbenButton>
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
{{ $t('common.back') }}
{{ $t("common.back") }}
</VbenButton>
</div>
</div>
@@ -1,11 +1,11 @@
export { default as Breadcrumb } from './breadcrumb.vue';
export * from './check-updates';
export { default as AuthenticationColorToggle } from './color-toggle.vue';
export * from './global-search';
export { default as LanguageToggle } from './language-toggle.vue';
export { default as AuthenticationLayoutToggle } from './layout-toggle.vue';
export * from './lock-screen';
export * from './notification';
export * from './preferences';
export * from './theme-toggle';
export * from './user-dropdown';
export { default as Breadcrumb } from "./breadcrumb.vue";
export * from "./check-updates";
export { default as AuthenticationColorToggle } from "./color-toggle.vue";
export * from "./global-search";
export { default as LanguageToggle } from "./language-toggle.vue";
export { default as AuthenticationLayoutToggle } from "./layout-toggle.vue";
export * from "./lock-screen";
export * from "./notification";
export * from "./preferences";
export * from "./theme-toggle";
export * from "./user-dropdown";
@@ -12,25 +12,25 @@ import { preferences, updatePreferences, usePreferences } from "/@/vben/preferen
import { VbenDropdownRadioMenu, VbenIconButton } from "/@/vben//shadcn-ui";
defineOptions({
name: "AuthenticationLayoutToggle"
name: "AuthenticationLayoutToggle",
});
const menus = computed((): VbenDropdownMenuItem[] => [
{
icon: PanelLeft,
label: $t("authentication.layout.alignLeft"),
value: "panel-left"
value: "panel-left",
},
{
icon: InspectionPanel,
label: $t("authentication.layout.center"),
value: "panel-center"
value: "panel-center",
},
{
icon: PanelRight,
label: $t("authentication.layout.alignRight"),
value: "panel-right"
}
value: "panel-right",
},
]);
const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences();
@@ -38,8 +38,8 @@ const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences();
function handleUpdate(value: string) {
updatePreferences({
app: {
authPageLayout: value as AuthPageLayoutType
}
authPageLayout: value as AuthPageLayoutType,
},
});
}
</script>
@@ -1,71 +1,69 @@
<script setup lang="ts">
import type { SelectOption } from '/@/vben/types';
import type { SelectOption } from "/@/vben/types";
import { computed } from 'vue';
import { computed } from "vue";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import SelectItem from '../select-item.vue';
import SwitchItem from '../switch-item.vue';
import SelectItem from "../select-item.vue";
import SwitchItem from "../switch-item.vue";
defineOptions({
name: 'PreferenceInterfaceControl',
name: "PreferenceInterfaceControl",
});
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch');
const widgetFullscreen = defineModel<boolean>('widgetFullscreen');
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle');
const widgetNotification = defineModel<boolean>('widgetNotification');
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
const appPreferencesButtonPosition = defineModel<string>(
'appPreferencesButtonPosition',
);
const widgetRefresh = defineModel<boolean>('widgetRefresh');
const widgetGlobalSearch = defineModel<boolean>("widgetGlobalSearch");
const widgetFullscreen = defineModel<boolean>("widgetFullscreen");
const widgetLanguageToggle = defineModel<boolean>("widgetLanguageToggle");
const widgetNotification = defineModel<boolean>("widgetNotification");
const widgetThemeToggle = defineModel<boolean>("widgetThemeToggle");
const widgetSidebarToggle = defineModel<boolean>("widgetSidebarToggle");
const widgetLockScreen = defineModel<boolean>("widgetLockScreen");
const appPreferencesButtonPosition = defineModel<string>("appPreferencesButtonPosition");
const widgetRefresh = defineModel<boolean>("widgetRefresh");
const positionItems = computed((): SelectOption[] => [
{
label: $t('preferences.position.auto'),
value: 'auto',
label: $t("preferences.position.auto"),
value: "auto",
},
{
label: $t('preferences.position.header'),
value: 'header',
label: $t("preferences.position.header"),
value: "header",
},
{
label: $t('preferences.position.fixed'),
value: 'fixed',
label: $t("preferences.position.fixed"),
value: "fixed",
},
]);
</script>
<template>
<SwitchItem v-model="widgetGlobalSearch">
{{ $t('preferences.widget.globalSearch') }}
{{ $t("preferences.widget.globalSearch") }}
</SwitchItem>
<SwitchItem v-model="widgetThemeToggle">
{{ $t('preferences.widget.themeToggle') }}
{{ $t("preferences.widget.themeToggle") }}
</SwitchItem>
<SwitchItem v-model="widgetLanguageToggle">
{{ $t('preferences.widget.languageToggle') }}
{{ $t("preferences.widget.languageToggle") }}
</SwitchItem>
<SwitchItem v-model="widgetFullscreen">
{{ $t('preferences.widget.fullscreen') }}
{{ $t("preferences.widget.fullscreen") }}
</SwitchItem>
<SwitchItem v-model="widgetNotification">
{{ $t('preferences.widget.notification') }}
{{ $t("preferences.widget.notification") }}
</SwitchItem>
<SwitchItem v-model="widgetLockScreen">
{{ $t('preferences.widget.lockScreen') }}
{{ $t("preferences.widget.lockScreen") }}
</SwitchItem>
<SwitchItem v-model="widgetSidebarToggle">
{{ $t('preferences.widget.sidebarToggle') }}
{{ $t("preferences.widget.sidebarToggle") }}
</SwitchItem>
<SwitchItem v-model="widgetRefresh">
{{ $t('preferences.widget.refresh') }}
{{ $t("preferences.widget.refresh") }}
</SwitchItem>
<SelectItem v-model="appPreferencesButtonPosition" :items="positionItems">
{{ $t('preferences.position.title') }}
{{ $t("preferences.position.title") }}
</SelectItem>
</template>
@@ -108,7 +108,7 @@ const defaultPreferences: Preferences = {
widget: {
fullscreen: true,
globalSearch: true,
languageToggle: false,
languageToggle: true,
lockScreen: true,
notification: false,
refresh: true,
@@ -1,35 +1,23 @@
import type { Preferences } from './types';
import type { Preferences } from "./types";
import { preferencesManager } from './preferences';
import { preferencesManager } from "./preferences";
// 偏好设置(带有层级关系)
const preferences: Preferences =
preferencesManager.getPreferences.apply(preferencesManager);
const preferences: Preferences = preferencesManager.getPreferences.apply(preferencesManager);
// 更新偏好设置
const updatePreferences =
preferencesManager.updatePreferences.bind(preferencesManager);
const updatePreferences = preferencesManager.updatePreferences.bind(preferencesManager);
// 重置偏好设置
const resetPreferences =
preferencesManager.resetPreferences.bind(preferencesManager);
const resetPreferences = preferencesManager.resetPreferences.bind(preferencesManager);
const clearPreferencesCache =
preferencesManager.clearCache.bind(preferencesManager);
const clearPreferencesCache = preferencesManager.clearCache.bind(preferencesManager);
// 初始化偏好设置
const initPreferences =
preferencesManager.initPreferences.bind(preferencesManager);
const initPreferences = preferencesManager.initPreferences.bind(preferencesManager);
export {
clearPreferencesCache,
initPreferences,
preferences,
preferencesManager,
resetPreferences,
updatePreferences,
};
export { clearPreferencesCache, initPreferences, preferences, preferencesManager, resetPreferences, updatePreferences };
export * from './constants';
export type * from './types';
export * from './use-preferences';
export * from "./constants";
export type * from "./types";
export * from "./use-preferences";
@@ -1,22 +1,18 @@
import type { DeepPartial } from '/@/vben/typings';
import type { DeepPartial } from "/@/vben/typings";
import type { InitialOptions, Preferences } from './types';
import type { InitialOptions, Preferences } from "./types";
import { markRaw, reactive, readonly, watch } from 'vue';
import { markRaw, reactive, readonly, watch } from "vue";
import { StorageManager } from '/@/vben/shared/cache';
import { isMacOs, merge } from '/@/vben/shared/utils';
import { StorageManager } from "/@/vben/shared/cache";
import { isMacOs, merge } from "/@/vben/shared/utils";
import {
breakpointsTailwind,
useBreakpoints,
useDebounceFn,
} from '@vueuse/core';
import { breakpointsTailwind, useBreakpoints, useDebounceFn } from "@vueuse/core";
import { defaultPreferences } from './config';
import { updateCSSVariables } from './update-css-variables';
import { defaultPreferences } from "./config";
import { updateCSSVariables } from "./update-css-variables";
const STORAGE_KEY = 'preferences';
const STORAGE_KEY = "preferences";
const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`;
const STORAGE_KEY_THEME = `${STORAGE_KEY}-theme`;
@@ -33,14 +29,11 @@ class PreferenceManager {
this.cache = new StorageManager();
// 避免频繁的操作缓存
this.savePreferences = useDebounceFn(
(preference: Preferences) => this._savePreferences(preference),
150,
);
this.savePreferences = useDebounceFn((preference: Preferences) => this._savePreferences(preference), 150);
}
clearCache() {
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach((key) => {
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach(key => {
this.cache?.removeItem(key);
});
}
@@ -73,7 +66,7 @@ class PreferenceManager {
{},
// overrides,
this.loadCachedPreferences() || {},
this.initialPreferences,
this.initialPreferences
);
// 更新偏好设置
@@ -103,7 +96,7 @@ class PreferenceManager {
// 保存重置后的偏好设置
this.savePreferences(this.state);
// 从存储中移除偏好设置项
[STORAGE_KEY, STORAGE_KEY_THEME, STORAGE_KEY_LOCALE].forEach((key) => {
[STORAGE_KEY, STORAGE_KEY_THEME, STORAGE_KEY_LOCALE].forEach(key => {
this.cache?.removeItem(key);
});
this.updatePreferences(this.state);
@@ -145,17 +138,14 @@ class PreferenceManager {
updateCSSVariables(this.state);
}
if (
Reflect.has(appUpdates, 'colorGrayMode') ||
Reflect.has(appUpdates, 'colorWeakMode')
) {
if (Reflect.has(appUpdates, "colorGrayMode") || Reflect.has(appUpdates, "colorWeakMode")) {
this.updateColorMode(this.state);
}
}
private initPlatform() {
const dom = document.documentElement;
dom.dataset.platform = isMacOs() ? 'macOs' : 'window';
dom.dataset.platform = isMacOs() ? "macOs" : "window";
}
/**
@@ -183,25 +173,23 @@ class PreferenceManager {
// 监听断点,判断是否移动端
const breakpoints = useBreakpoints(breakpointsTailwind);
const isMobile = breakpoints.smaller('md');
const isMobile = breakpoints.smaller("md");
watch(
() => isMobile.value,
(val) => {
val => {
this.updatePreferences({
app: { isMobile: val },
});
},
{ immediate: true },
{ immediate: true }
);
// 监听系统主题偏好设置变化
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', ({ matches: isDark }) => {
this.updatePreferences({
theme: { mode: isDark ? 'dark' : 'light' },
});
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", ({ matches: isDark }) => {
this.updatePreferences({
theme: { mode: isDark ? "dark" : "light" },
});
});
}
/**
@@ -212,14 +200,10 @@ class PreferenceManager {
if (preference.app) {
const { colorGrayMode, colorWeakMode } = preference.app;
const dom = document.documentElement;
const COLOR_WEAK = 'invert-mode';
const COLOR_GRAY = 'grayscale-mode';
colorWeakMode
? dom.classList.add(COLOR_WEAK)
: dom.classList.remove(COLOR_WEAK);
colorGrayMode
? dom.classList.add(COLOR_GRAY)
: dom.classList.remove(COLOR_GRAY);
const COLOR_WEAK = "invert-mode";
const COLOR_GRAY = "grayscale-mode";
colorWeakMode ? dom.classList.add(COLOR_WEAK) : dom.classList.remove(COLOR_WEAK);
colorGrayMode ? dom.classList.add(COLOR_GRAY) : dom.classList.remove(COLOR_GRAY);
}
}
}
@@ -1,9 +1,9 @@
import type { Preferences } from './types';
import type { Preferences } from "./types";
import { generatorColorVariables } from '/@/vben/shared/color';
import { updateCSSVariables as executeUpdateCSSVariables } from '/@/vben/shared/utils';
import { generatorColorVariables } from "/@/vben/shared/color";
import { updateCSSVariables as executeUpdateCSSVariables } from "/@/vben/shared/utils";
import { BUILT_IN_THEME_PRESETS } from './constants';
import { BUILT_IN_THEME_PRESETS } from "./constants";
/**
* 更新主题的 CSS 变量以及其他 CSS 变量
@@ -21,13 +21,13 @@ function updateCSSVariables(preferences: Preferences) {
const { builtinType, mode, radius } = theme;
// html 设置 dark 类
if (Reflect.has(theme, 'mode')) {
if (Reflect.has(theme, "mode")) {
const dark = isDarkTheme(mode);
root.classList.toggle('dark', dark);
root.classList.toggle("dark", dark);
}
// html 设置 data-theme=[builtinType]
if (Reflect.has(theme, 'builtinType')) {
if (Reflect.has(theme, "builtinType")) {
const rootTheme = root.dataset.theme;
if (rootTheme !== builtinType) {
root.dataset.theme = builtinType;
@@ -35,36 +35,26 @@ function updateCSSVariables(preferences: Preferences) {
}
// 获取当前的内置主题
const currentBuiltType = [...BUILT_IN_THEME_PRESETS].find(
(item) => item.type === builtinType,
);
const currentBuiltType = [...BUILT_IN_THEME_PRESETS].find(item => item.type === builtinType);
let builtinTypeColorPrimary: string | undefined = '';
let builtinTypeColorPrimary: string | undefined = "";
if (currentBuiltType) {
const isDark = isDarkTheme(preferences.theme.mode);
// 设置不同主题的主要颜色
const color = isDark
? currentBuiltType.darkPrimaryColor || currentBuiltType.primaryColor
: currentBuiltType.primaryColor;
const color = isDark ? currentBuiltType.darkPrimaryColor || currentBuiltType.primaryColor : currentBuiltType.primaryColor;
builtinTypeColorPrimary = color || currentBuiltType.color;
}
// 如果内置主题颜色和自定义颜色都不存在,则不更新主题颜色
if (
builtinTypeColorPrimary ||
Reflect.has(theme, 'colorPrimary') ||
Reflect.has(theme, 'colorDestructive') ||
Reflect.has(theme, 'colorSuccess') ||
Reflect.has(theme, 'colorWarning')
) {
if (builtinTypeColorPrimary || Reflect.has(theme, "colorPrimary") || Reflect.has(theme, "colorDestructive") || Reflect.has(theme, "colorSuccess") || Reflect.has(theme, "colorWarning")) {
// preferences.theme.colorPrimary = builtinTypeColorPrimary || colorPrimary;
updateMainColorVariables(preferences);
}
// 更新圆角
if (Reflect.has(theme, 'radius')) {
document.documentElement.style.setProperty('--radius', `${radius}rem`);
if (Reflect.has(theme, "radius")) {
document.documentElement.style.setProperty("--radius", `${radius}rem`);
}
}
@@ -76,22 +66,21 @@ function updateMainColorVariables(preference: Preferences) {
if (!preference.theme) {
return;
}
const { colorDestructive, colorPrimary, colorSuccess, colorWarning } =
preference.theme;
const { colorDestructive, colorPrimary, colorSuccess, colorWarning } = preference.theme;
const colorVariables = generatorColorVariables([
{ color: colorPrimary, name: 'primary' },
{ alias: 'warning', color: colorWarning, name: 'yellow' },
{ alias: 'success', color: colorSuccess, name: 'green' },
{ alias: 'destructive', color: colorDestructive, name: 'red' },
{ color: colorPrimary, name: "primary" },
{ alias: "warning", color: colorWarning, name: "yellow" },
{ alias: "success", color: colorSuccess, name: "green" },
{ alias: "destructive", color: colorDestructive, name: "red" },
]);
// 要设置的 CSS 变量映射
const colorMappings = {
'--green-500': '--success',
'--primary-500': '--primary',
'--red-500': '--destructive',
'--yellow-500': '--warning',
"--green-500": "--success",
"--primary-500": "--primary",
"--red-500": "--destructive",
"--yellow-500": "--warning",
};
// 统一处理颜色变量的更新
@@ -106,9 +95,9 @@ function updateMainColorVariables(preference: Preferences) {
}
function isDarkTheme(theme: string) {
let dark = theme === 'dark';
if (theme === 'auto') {
dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
let dark = theme === "dark";
if (theme === "auto") {
dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
}
return dark;
}
@@ -166,7 +166,7 @@ function usePreferences() {
if (!enablePreferences) {
return {
fixed: false,
header: false
header: false,
};
}
@@ -182,7 +182,7 @@ function usePreferences() {
if (preferencesButtonPosition !== "auto") {
return {
fixed: preferencesButtonPosition === "fixed",
header: isHeaderPosition
header: isHeaderPosition,
};
}
@@ -191,7 +191,7 @@ function usePreferences() {
return {
fixed,
header: !fixed
header: !fixed,
};
});
@@ -219,7 +219,7 @@ function usePreferences() {
locale,
preferencesButtonPosition,
sidebarCollapsed,
theme
theme,
};
}