From 660311a7ec8140c3d3289ca730374ec02b66fa41 Mon Sep 17 00:00:00 2001 From: ray_wuhao <443547225@qq.com> Date: Mon, 24 Apr 2023 13:57:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=9B=BD=E9=99=85=E5=8C=96?= =?UTF-8?q?=E5=8C=85=E7=AE=A1=E7=90=86=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 - .vscode/extensions.json | 2 +- .vscode/settings.json | 11 ++ CHANGELOG.md | 1 + auto-imports.d.ts | 11 -- locales/en-US.json | 48 ------- locales/system-one/en-US.json | 41 ------ locales/system-one/zh-CN.json | 41 ------ locales/system-two/en-US.json | 41 ------ locales/system-two/zh-CN.json | 41 ------ locales/zh-CN.json | 48 ------- src/components/RayGlobalProvider/index.tsx | 2 +- src/components/RayIcon/index.tsx | 22 +++- src/language/index.ts | 117 ------------------ .../components/ThemeSwitch/index.tsx | 4 +- .../Components/SettingDrawer/index.tsx | 10 +- src/layout/components/SiderBar/index.tsx | 26 ++-- src/locales/helper.ts | 32 +++++ src/locales/index.ts | 90 ++++++++++++++ src/locales/lang/en-US.ts | 11 ++ .../lang/en-US/headerSettingOptions.json | 10 ++ src/locales/lang/en-US/headerTooltip.json | 9 ++ src/locales/lang/en-US/menu.json | 17 +++ src/locales/lang/en-US/setting.json | 10 ++ src/locales/lang/en-US/views/login/index.json | 10 ++ src/locales/lang/zh-CN.ts | 11 ++ .../lang/zh-CN/headerSettingOptions.json | 10 ++ src/locales/lang/zh-CN/headerTooltip.json | 9 ++ src/locales/lang/zh-CN/menu.json | 17 +++ src/locales/lang/zh-CN/setting.json | 10 ++ src/locales/lang/zh-CN/views/login/index.json | 10 ++ src/{language => locales}/language.ts | 0 src/{language => locales}/useI18n.ts | 25 ++-- src/main.ts | 12 +- src/store/modules/menu/index.ts | 6 +- src/store/modules/setting.ts | 8 +- src/views/login/components/Signin/index.tsx | 16 +-- src/views/login/index.tsx | 13 +- tsconfig.json | 2 +- 39 files changed, 349 insertions(+), 458 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 locales/en-US.json delete mode 100644 locales/system-one/en-US.json delete mode 100644 locales/system-one/zh-CN.json delete mode 100644 locales/system-two/en-US.json delete mode 100644 locales/system-two/zh-CN.json delete mode 100644 locales/zh-CN.json delete mode 100644 src/language/index.ts create mode 100644 src/locales/helper.ts create mode 100644 src/locales/index.ts create mode 100644 src/locales/lang/en-US.ts create mode 100644 src/locales/lang/en-US/headerSettingOptions.json create mode 100644 src/locales/lang/en-US/headerTooltip.json create mode 100644 src/locales/lang/en-US/menu.json create mode 100644 src/locales/lang/en-US/setting.json create mode 100644 src/locales/lang/en-US/views/login/index.json create mode 100644 src/locales/lang/zh-CN.ts create mode 100644 src/locales/lang/zh-CN/headerSettingOptions.json create mode 100644 src/locales/lang/zh-CN/headerTooltip.json create mode 100644 src/locales/lang/zh-CN/menu.json create mode 100644 src/locales/lang/zh-CN/setting.json create mode 100644 src/locales/lang/zh-CN/views/login/index.json rename src/{language => locales}/language.ts (100%) rename src/{language => locales}/useI18n.ts (67%) diff --git a/.gitignore b/.gitignore index e1775de0..ec7bcf40 100644 --- a/.gitignore +++ b/.gitignore @@ -16,9 +16,6 @@ dist/ visualizer.* # Editor directories and files -.vscode -.vscode/* -!.vscode/extensions.json .idea .DS_Store *.suo diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a7cea0b0..62a1b221 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["Vue.volar"] + "recommendations": ["vue.volar", "lokalise.i18n-ally"] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..11990044 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "i18n-ally.localesPaths": ["src/locales/lang"], + "i18n-ally.keystyle": "nested", + "i18n-ally.sortKeys": true, + "i18n-ally.namespace": true, + "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}", + "i18n-ally.enabledParsers": ["json"], + "i18n-ally.sourceLanguage": "en", + "i18n-ally.displayLanguage": "zh-CN", + "i18n-ally.enabledFrameworks": ["vue", "react"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dfaa33f..647de306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - 手动补充 AppRouteRecordRaw、AppRouteMeta 类型 - 重新拆分 Layout 入口文件 - 重新指定组件暴露方法、属性 +- 修改国际化管理方式,现在支持自动合并管理与结合 i18n-ally 使用。并且支持 unplugin-vue-i18n 构建,提高性能 ## 3.1.7 diff --git a/auto-imports.d.ts b/auto-imports.d.ts index a0e4a019..58e06e8c 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -50,8 +50,6 @@ declare global { const nextTick: typeof import('vue')['nextTick'] const onActivated: typeof import('vue')['onActivated'] const onBeforeMount: typeof import('vue')['onBeforeMount'] - const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] - const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] @@ -83,7 +81,6 @@ declare global { const refThrottled: typeof import('@vueuse/core')['refThrottled'] const refWithControl: typeof import('@vueuse/core')['refWithControl'] const resolveComponent: typeof import('vue')['resolveComponent'] - const resolveDirective: typeof import('vue')['resolveDirective'] const resolveRef: typeof import('@vueuse/core')['resolveRef'] const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] const setActivePinia: typeof import('pinia')['setActivePinia'] @@ -115,12 +112,10 @@ declare global { const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] - const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] const useArraySome: typeof import('@vueuse/core')['useArraySome'] - const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] const useAttrs: typeof import('vue')['useAttrs'] @@ -132,7 +127,6 @@ declare global { const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] const useCached: typeof import('@vueuse/core')['useCached'] const useClipboard: typeof import('@vueuse/core')['useClipboard'] - const useCloned: typeof import('@vueuse/core')['useCloned'] const useColorMode: typeof import('@vueuse/core')['useColorMode'] const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] const useCounter: typeof import('@vueuse/core')['useCounter'] @@ -183,7 +177,6 @@ declare global { const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] - const useLink: typeof import('vue-router')['useLink'] const useLoadingBar: typeof import('naive-ui')['useLoadingBar'] const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] @@ -209,14 +202,11 @@ declare global { const useParallax: typeof import('@vueuse/core')['useParallax'] const usePermission: typeof import('@vueuse/core')['usePermission'] const usePointer: typeof import('@vueuse/core')['usePointer'] - const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] - const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] - const usePrevious: typeof import('@vueuse/core')['usePrevious'] const useRafFn: typeof import('@vueuse/core')['useRafFn'] const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] @@ -230,7 +220,6 @@ declare global { const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] const useShare: typeof import('@vueuse/core')['useShare'] const useSlots: typeof import('vue')['useSlots'] - const useSorted: typeof import('@vueuse/core')['useSorted'] const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] const useStepper: typeof import('@vueuse/core')['useStepper'] diff --git a/locales/en-US.json b/locales/en-US.json deleted file mode 100644 index af08fd55..00000000 --- a/locales/en-US.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "GlobalMenuOptions": { - "Dashboard": "Home", - "Rely": "Rely", - "RelyAbout": "Rely About", - "Error": "Error Page", - "Echart": "Chart", - "scrollReveal": "Scroll Reveal", - "Axios": "Axios Request", - "Table": "Table", - "MultiMenu": "MultiMenu", - "Doc": "Doc", - "DocLocal": "Doc (China)", - "Office": "Office", - "Office_Document": "Document", - "Office_Presentation": "Presentation", - "Office_Spreadsheet": "Spreadsheet" - }, - "LayoutHeaderTooltipOptions": { - "Reload": "Reload Current Page", - "Lock": "Lock", - "Setting": "Setting", - "Github": "Github", - "FullScreen": "Full Screen", - "CancelFullScreen": "Cancel Full Screen", - "Search": "Search" - }, - "LayoutHeaderSettingOptions": { - "Title": "Configuration", - "ThemeOptions": { - "Title": "Theme", - "Dark": "Dark", - "Light": "Light", - "PrimaryColorConfig": "Primary Color" - }, - "InterfaceDisplay": "Interface Display" - }, - "LoginModule": { - "Register": "Register", - "Signin": "Signin", - "QRCodeSignin": "QRCode Signin", - "NamePlaceholder": "please enter user name", - "PasswordPlaceholder": "please enter password", - "Login": "Login", - "Name": "User Name", - "Password": "User Password" - } -} diff --git a/locales/system-one/en-US.json b/locales/system-one/en-US.json deleted file mode 100644 index a0dc36f8..00000000 --- a/locales/system-one/en-US.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "GlobalMenuOptions": { - "Dashboard": "Home", - "Rely": "Rely", - "RelyAbout": "Rely About", - "Error": "Error Page", - "Echart": "Chart", - "scrollReveal": "Scroll Reveal", - "Axios": "Axios Request", - "Table": "Table", - "MultiMenu": "MultiMenu", - "Doc": "Doc", - "DocLocal": "Doc (China)" - }, - "LayoutHeaderTooltipOptions": { - "Reload": "Reload Current Page", - "Lock": "Lock", - "Setting": "Setting", - "Github": "Github", - "FullScreen": "Full Screen", - "CancelFullScreen": "Cancel Full Screen" - }, - "LayoutHeaderSettingOptions": { - "Title": "Configuration", - "ThemeOptions": { - "Title": "Theme", - "Dark": "Dark", - "Light": "Light", - "PrimaryColorConfig": "Primary Color" - } - }, - "LoginModule": { - "Register": "Register", - "Signin": "Signin", - "NamePlaceholder": "please enter user name", - "PasswordPlaceholder": "please enter password", - "Login": "Login", - "Name": "User Name", - "Password": "User Password" - } -} diff --git a/locales/system-one/zh-CN.json b/locales/system-one/zh-CN.json deleted file mode 100644 index 645bec79..00000000 --- a/locales/system-one/zh-CN.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "GlobalMenuOptions": { - "Dashboard": "首页", - "Rely": "依赖项", - "RelyAbout": "关于", - "Error": "错误页", - "Echart": "可视化", - "scrollReveal": "滚动动画", - "Axios": "请求", - "Table": "表格", - "MultiMenu": "多级菜单", - "Doc": "文档", - "DocLocal": "文档 (国内地址)" - }, - "LayoutHeaderTooltipOptions": { - "Reload": "刷新当前页面", - "Lock": "锁屏", - "Setting": "设置", - "Github": "Github", - "FullScreen": "全屏", - "CancelFullScreen": "退出全屏" - }, - "LayoutHeaderSettingOptions": { - "Title": "项目配置", - "ThemeOptions": { - "Title": "主题", - "Dark": "暗色", - "Light": "明亮", - "PrimaryColorConfig": "主题色" - } - }, - "LoginModule": { - "Register": "注册", - "Signin": "登陆", - "NamePlaceholder": "请输入用户名", - "PasswordPlaceholder": "请输入密码", - "Login": "登 陆", - "Name": "用户名", - "Password": "密码" - } -} diff --git a/locales/system-two/en-US.json b/locales/system-two/en-US.json deleted file mode 100644 index a0dc36f8..00000000 --- a/locales/system-two/en-US.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "GlobalMenuOptions": { - "Dashboard": "Home", - "Rely": "Rely", - "RelyAbout": "Rely About", - "Error": "Error Page", - "Echart": "Chart", - "scrollReveal": "Scroll Reveal", - "Axios": "Axios Request", - "Table": "Table", - "MultiMenu": "MultiMenu", - "Doc": "Doc", - "DocLocal": "Doc (China)" - }, - "LayoutHeaderTooltipOptions": { - "Reload": "Reload Current Page", - "Lock": "Lock", - "Setting": "Setting", - "Github": "Github", - "FullScreen": "Full Screen", - "CancelFullScreen": "Cancel Full Screen" - }, - "LayoutHeaderSettingOptions": { - "Title": "Configuration", - "ThemeOptions": { - "Title": "Theme", - "Dark": "Dark", - "Light": "Light", - "PrimaryColorConfig": "Primary Color" - } - }, - "LoginModule": { - "Register": "Register", - "Signin": "Signin", - "NamePlaceholder": "please enter user name", - "PasswordPlaceholder": "please enter password", - "Login": "Login", - "Name": "User Name", - "Password": "User Password" - } -} diff --git a/locales/system-two/zh-CN.json b/locales/system-two/zh-CN.json deleted file mode 100644 index 645bec79..00000000 --- a/locales/system-two/zh-CN.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "GlobalMenuOptions": { - "Dashboard": "首页", - "Rely": "依赖项", - "RelyAbout": "关于", - "Error": "错误页", - "Echart": "可视化", - "scrollReveal": "滚动动画", - "Axios": "请求", - "Table": "表格", - "MultiMenu": "多级菜单", - "Doc": "文档", - "DocLocal": "文档 (国内地址)" - }, - "LayoutHeaderTooltipOptions": { - "Reload": "刷新当前页面", - "Lock": "锁屏", - "Setting": "设置", - "Github": "Github", - "FullScreen": "全屏", - "CancelFullScreen": "退出全屏" - }, - "LayoutHeaderSettingOptions": { - "Title": "项目配置", - "ThemeOptions": { - "Title": "主题", - "Dark": "暗色", - "Light": "明亮", - "PrimaryColorConfig": "主题色" - } - }, - "LoginModule": { - "Register": "注册", - "Signin": "登陆", - "NamePlaceholder": "请输入用户名", - "PasswordPlaceholder": "请输入密码", - "Login": "登 陆", - "Name": "用户名", - "Password": "密码" - } -} diff --git a/locales/zh-CN.json b/locales/zh-CN.json deleted file mode 100644 index 1a77f172..00000000 --- a/locales/zh-CN.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "GlobalMenuOptions": { - "Dashboard": "首页", - "Rely": "依赖项", - "RelyAbout": "关于", - "Error": "错误页", - "Echart": "可视化", - "scrollReveal": "滚动动画", - "Axios": "请求", - "Table": "表格", - "MultiMenu": "多级菜单", - "Doc": "文档", - "DocLocal": "文档 (国内地址)", - "Office": "办公", - "Office_Document": "文档", - "Office_Presentation": "演示", - "Office_Spreadsheet": "表格" - }, - "LayoutHeaderTooltipOptions": { - "Reload": "刷新当前页面", - "Lock": "锁屏", - "Setting": "设置", - "Github": "Github", - "FullScreen": "全屏", - "CancelFullScreen": "退出全屏", - "Search": "搜索" - }, - "LayoutHeaderSettingOptions": { - "Title": "项目配置", - "ThemeOptions": { - "Title": "主题", - "Dark": "暗色", - "Light": "明亮", - "PrimaryColorConfig": "主题色" - }, - "InterfaceDisplay": "界面显示" - }, - "LoginModule": { - "Register": "注册", - "Signin": "登陆", - "QRCodeSignin": "扫码登陆", - "NamePlaceholder": "请输入用户名", - "PasswordPlaceholder": "请输入密码", - "Login": "登 陆", - "Name": "用户名", - "Password": "密码" - } -} diff --git a/src/components/RayGlobalProvider/index.tsx b/src/components/RayGlobalProvider/index.tsx index a11585c3..42eb7bc3 100644 --- a/src/components/RayGlobalProvider/index.tsx +++ b/src/components/RayGlobalProvider/index.tsx @@ -10,7 +10,7 @@ import { } from 'naive-ui' import { useSetting } from '@/store' -import { naiveLocales } from '@/language/index' +import { naiveLocales } from '@/locales/index' const GlobalProvider = defineComponent({ name: 'GlobalProvider', diff --git a/src/components/RayIcon/index.tsx b/src/components/RayIcon/index.tsx index 42f8423b..f1491ec2 100644 --- a/src/components/RayIcon/index.tsx +++ b/src/components/RayIcon/index.tsx @@ -54,7 +54,10 @@ const RayIcon = defineComponent({ default: false, }, }, - setup(props) { + emits: ['click'], + setup(props, ctx) { + const emit = ctx.emit + const modelColor = computed(() => props.color) const symbolId = computed(() => `#${props.prefix}-${props.name}`) const cssVars = computed(() => { @@ -75,17 +78,28 @@ const RayIcon = defineComponent({ return cssVar }) + const handleClick = () => { + emit('click') + } + return { modelColor, symbolId, cssVars, + handleClick, } }, render() { return ( - - - + + + ) diff --git a/src/language/index.ts b/src/language/index.ts deleted file mode 100644 index a2b1a30c..00000000 --- a/src/language/index.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** - * - * @author Ray - * - * @date 2022-12-08 - * - * @workspace ray-template - * - * @remark 今天也是元气满满撸代码的一天 - */ - -/** - * - * 注册 `vue-i18n` - * - * 预设 `localeLanguage` 作为缓存 `key` - * - * 预设中文作为基础语言 - * - * `naive ui` 语言包切换 - * - * 注意: - * - 因使用 `i18n` 提供虚拟文件注入缘故, 每次修改 `locales` 中的文件后, 需要重启项目 - * - 建议按照主流约定语言包命名 - */ - -import { createI18n } from 'vue-i18n' - -import { naiveLocales } from './language' -import { getCache } from '@use-utils/cache' -import { forIn, merge } from 'lodash-es' - -export { naiveLocales, localOptions } from './language' - -import type { App } from 'vue' -import type { I18n } from 'vue-i18n' - -export let i18n: I18n - -/** - * - * @returns 整合后的语言包 - * - * @remark 自动归并 locales 下所有 json 语言包, 需要注意语言包名称问题(必须统一) - * @remark 注意 key 的重复问题, 如果需要多模块区分语言包, 则需要保证 key 的唯一性, 否则会被覆盖 - * @remark 使用该方法会导致子包中的路径不能使用 i18n ally 语法提示 - */ -export const getMatchLanguageModule = () => { - const msg = {} - const reg = /([^\\/]+)\.json$/i - - try { - const modules = import.meta.glob('../../locales/**', { - eager: true, - as: 'raw', - }) - - const moduleKeys = Object.keys(modules) - - moduleKeys.forEach((curr) => { - const k = curr.match(reg)?.[1] as string // 当前语言包类型(zh-CN, en-US...) - const content = JSON.parse(modules[curr]) // 当前语言包内容 - - msg[k] = merge({}, msg[k]) - - forIn(content, (value, ckey) => { - msg[k][ckey] = merge(msg[k][ckey], value) - }) - }) - } catch (e) { - console.error(e) - } - - return msg -} - -/** - * - * @returns 获取当前环境默认语言 - * - * @remak 未避免出现加载语言错误问题, 故而在 `main.ts` 注册时, 应优先加载 `i18n` 避免出现该问题 - */ -export const getDefaultLocal = () => { - const catchLanguage = getCache('localeLanguage', 'localStorage') - - const locale: string = catchLanguage !== 'no' ? catchLanguage : 'zh-CN' - - return locale -} - -export const setupI18n = (app: App) => { - const locale = getDefaultLocal() - - const i18nInstance = createI18n({ - locale, - allowComposition: true, - messages: getMatchLanguageModule(), - legacy: false, - sync: true, - }) - - i18n = i18nInstance - - app.use(i18nInstance) -} - -/** - * - * @returns 当前环境语言组件库语言包 - * - * @remark 获取默认语言包 - */ -export const getDefaultNaiveLocal = () => { - const local = getDefaultLocal() - - return naiveLocales(local) -} diff --git a/src/layout/components/SiderBar/Components/SettingDrawer/components/ThemeSwitch/index.tsx b/src/layout/components/SiderBar/Components/SettingDrawer/components/ThemeSwitch/index.tsx index d61f6a39..1948e118 100644 --- a/src/layout/components/SiderBar/Components/SettingDrawer/components/ThemeSwitch/index.tsx +++ b/src/layout/components/SiderBar/Components/SettingDrawer/components/ThemeSwitch/index.tsx @@ -90,8 +90,8 @@ const ThemeSwitch = defineComponent({ ), default: () => this.themeValue - ? t('LayoutHeaderSettingOptions.ThemeOptions.Dark') - : t('LayoutHeaderSettingOptions.ThemeOptions.Light'), + ? t('headerSettingOptions.ThemeOptions.Dark') + : t('headerSettingOptions.ThemeOptions.Light'), }} diff --git a/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx b/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx index 8e5fb03e..f78a1306 100644 --- a/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx +++ b/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx @@ -13,7 +13,7 @@ import ThemeSwitch from '@/layout/components/SiderBar/components/SettingDrawer/c import { useSwatchesColorOptions } from './hook' import { useSetting } from '@/store' -import { useI18n } from '@/language/useI18n' +import { useI18n } from '@/locales/useI18n' import type { PropType } from 'vue' @@ -75,14 +75,14 @@ const SettingDrawer = defineComponent({ placement={this.placement} width={this.width} > - + - {t('LayoutHeaderSettingOptions.ThemeOptions.Title')} + {t('headerSettingOptions.ThemeOptions.Title')} - {t('LayoutHeaderSettingOptions.ThemeOptions.PrimaryColorConfig')} + {t('headerSettingOptions.ThemeOptions.PrimaryColorConfig')} - {t('LayoutHeaderSettingOptions.InterfaceDisplay')} + {t('headerSettingOptions.InterfaceDisplay')} diff --git a/src/layout/components/SiderBar/index.tsx b/src/layout/components/SiderBar/index.tsx index f5f8ce17..b33747e0 100644 --- a/src/layout/components/SiderBar/index.tsx +++ b/src/layout/components/SiderBar/index.tsx @@ -20,11 +20,11 @@ import GlobalSeach from './components/GlobalSeach/index' import { useSetting } from '@/store' import { useSignin } from '@/store' -import { localOptions } from '@/language/index' +import { localOptions } from '@/locales/index' import { useAvatarOptions } from './hook' import { getCache } from '@/utils/cache' import screenfull from 'screenfull' -import { useI18n } from '@/language/useI18n' +import { useI18n } from '@/locales/useI18n' import type { IconEventMapOptions, IconEventMap } from './type' @@ -57,43 +57,43 @@ const SiderBar = defineComponent({ * * 顶部左边操作栏 */ - const leftIconOptions = [ + const leftIconOptions = computed(() => [ { name: 'reload', size: 18, - tooltip: 'LayoutHeaderTooltipOptions.Reload', + tooltip: t('headerTooltip.Reload'), }, - ] + ]) /** * * 顶部右边提示框操作栏 */ - const rightTooltipIconOptions = [ + const rightTooltipIconOptions = computed(() => [ { name: 'search', size: 18, - tooltip: 'LayoutHeaderTooltipOptions.Search', + tooltip: t('headerTooltip.Search'), eventKey: 'search', }, { name: 'fullscreen', size: 18, - tooltip: 'LayoutHeaderTooltipOptions.FullScreen', + tooltip: t('headerTooltip.FullScreen'), eventKey: 'screen', }, { name: 'github', size: 18, - tooltip: 'LayoutHeaderTooltipOptions.Github', + tooltip: t('headerTooltip.Github'), eventKey: 'github', }, { name: 'setting', size: 18, - tooltip: 'LayoutHeaderTooltipOptions.Setting', + tooltip: t('headerTooltip.Setting'), eventKey: 'setting', }, - ] + ]) const iconEventMap: IconEventMapOptions = { reload: () => { changeSwitcher(false, 'reloadRouteSwitch') @@ -178,7 +178,7 @@ const SiderBar = defineComponent({ onClick={this.handleIconClick.bind(this, curr.name)} /> ), - default: () => this.t(curr.tooltip), + default: () => curr.tooltip, }} ))} @@ -192,7 +192,7 @@ const SiderBar = defineComponent({ {this.rightTooltipIconOptions.map((curr) => ( ))} diff --git a/src/locales/helper.ts b/src/locales/helper.ts new file mode 100644 index 00000000..47eafaf3 --- /dev/null +++ b/src/locales/helper.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { set } from 'lodash-es' + +import type { Recordable } from '@/types/type-utils' + +export const mergeMessage = (langs: Record, prefix = 'lang') => { + const langsGather: Recordable = {} + + Object.keys(langs).forEach((key) => { + const langFileModule = langs[key].default + + let fileName = key.replace(`./${prefix}/`, '').replace(/^\.\//, '') + const lastIndex = fileName.lastIndexOf('.') + + fileName = fileName.substring(0, lastIndex) + + const keyList = fileName.split('/') + const moduleName = keyList.shift() + const objKey = keyList.join('.') + + if (moduleName) { + if (objKey) { + set(langsGather, moduleName, langsGather[moduleName] || {}) + set(langsGather[moduleName], objKey, langFileModule) + } else { + set(langsGather, moduleName, langFileModule || {}) + } + } + }) + + return langsGather +} diff --git a/src/locales/index.ts b/src/locales/index.ts new file mode 100644 index 00000000..81071f9f --- /dev/null +++ b/src/locales/index.ts @@ -0,0 +1,90 @@ +/** + * + * @author Ray + * + * @date 2022-12-08 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +/** + * + * 注册 `vue-i18n` + * + * 预设 `localeLanguage` 作为缓存 `key` + * + * 预设中文作为基础语言 + * + * `naive ui` 语言包切换 + * + * 注意: + * - 因使用 `i18n` 提供虚拟文件注入缘故, 每次修改 `locales` 中的文件后, 需要重启项目 + * - 建议按照主流约定语言包命名 + */ + +import { createI18n } from 'vue-i18n' +import { localOptions } from './language' + +import { getCache } from '@use-utils/cache' + +export { naiveLocales, localOptions } from './language' + +import type { App } from 'vue' +import type { I18n } from 'vue-i18n' + +export let i18n: I18n + +/** + * + * @returns 获取当前环境默认语言 + * + * @remak 未避免出现加载语言错误问题, 故而在 `main.ts` 注册时, 应优先加载 `i18n` 避免出现该问题 + */ +export const getDefaultLocal = () => { + const catchLanguage = getCache('localeLanguage', 'localStorage') + + const locale: string = catchLanguage !== 'no' ? catchLanguage : 'zh-CN' + + return locale +} + +/** 获取所有语言 */ +const getAppLocales = async () => { + const message = {} + + for (const curr of localOptions) { + const msg = await import(`./lang/${curr.key}.ts`) + + message[curr.key] = msg.default?.message ?? {} + } + + return message +} + +/** 创建 i18n 实例 */ +const createI18nOptions = async () => { + const locale = getDefaultLocal() + const message = await getAppLocales() + + const i18nInstance = createI18n({ + legacy: false, + locale, + messages: message, + sync: true, + missingWarn: false, + silentFallbackWarn: true, + }) + + return i18nInstance +} + +/** 注册 i18n */ +export const setupI18n = async (app: App) => { + const i18nInstance = await createI18nOptions() + + i18n = i18nInstance + + app.use(i18nInstance) +} diff --git a/src/locales/lang/en-US.ts b/src/locales/lang/en-US.ts new file mode 100644 index 00000000..380ff207 --- /dev/null +++ b/src/locales/lang/en-US.ts @@ -0,0 +1,11 @@ +import { mergeMessage } from '@/locales/helper' + +const modules = import.meta.glob('./en-US/**/*.json', { + eager: true, +}) + +export default { + message: { + ...mergeMessage(modules, 'en-US'), + }, +} diff --git a/src/locales/lang/en-US/headerSettingOptions.json b/src/locales/lang/en-US/headerSettingOptions.json new file mode 100644 index 00000000..bf66ab47 --- /dev/null +++ b/src/locales/lang/en-US/headerSettingOptions.json @@ -0,0 +1,10 @@ +{ + "Title": "Configuration", + "ThemeOptions": { + "Title": "Theme", + "Dark": "Dark", + "Light": "Light", + "PrimaryColorConfig": "Primary Color" + }, + "InterfaceDisplay": "Display" +} diff --git a/src/locales/lang/en-US/headerTooltip.json b/src/locales/lang/en-US/headerTooltip.json new file mode 100644 index 00000000..819b1056 --- /dev/null +++ b/src/locales/lang/en-US/headerTooltip.json @@ -0,0 +1,9 @@ +{ + "Reload": "Reload Current Page", + "Lock": "Lock", + "Setting": "Setting", + "Github": "Github", + "FullScreen": "Full Screen", + "CancelFullScreen": "Cancel Full Screen", + "Search": "Search" +} diff --git a/src/locales/lang/en-US/menu.json b/src/locales/lang/en-US/menu.json new file mode 100644 index 00000000..5e735abe --- /dev/null +++ b/src/locales/lang/en-US/menu.json @@ -0,0 +1,17 @@ +{ + "Dashboard": "Home", + "Rely": "Rely", + "RelyAbout": "Rely About", + "Error": "Error Page", + "Echart": "Chart", + "scrollReveal": "Scroll Reveal", + "Axios": "Axios Request", + "Table": "Table", + "MultiMenu": "MultiMenu", + "Doc": "Doc", + "DocLocal": "Doc (China)", + "Office": "Office", + "Office_Document": "Document", + "Office_Presentation": "Presentation", + "Office_Spreadsheet": "Spreadsheet" +} diff --git a/src/locales/lang/en-US/setting.json b/src/locales/lang/en-US/setting.json new file mode 100644 index 00000000..e00157a4 --- /dev/null +++ b/src/locales/lang/en-US/setting.json @@ -0,0 +1,10 @@ +{ + "Title": "Configuration", + "ThemeOptions": { + "Title": "Theme", + "Dark": "Dark", + "Light": "Light", + "PrimaryColorConfig": "Primary Color" + }, + "InterfaceDisplay": "Interface Display" +} diff --git a/src/locales/lang/en-US/views/login/index.json b/src/locales/lang/en-US/views/login/index.json new file mode 100644 index 00000000..efeece16 --- /dev/null +++ b/src/locales/lang/en-US/views/login/index.json @@ -0,0 +1,10 @@ +{ + "Register": "Register", + "Signin": "Signin", + "QRCodeSignin": "QRCode Signin", + "NamePlaceholder": "please enter user name", + "PasswordPlaceholder": "please enter password", + "Login": "Login", + "Name": "User Name", + "Password": "User Password" +} diff --git a/src/locales/lang/zh-CN.ts b/src/locales/lang/zh-CN.ts new file mode 100644 index 00000000..c25dd991 --- /dev/null +++ b/src/locales/lang/zh-CN.ts @@ -0,0 +1,11 @@ +import { mergeMessage } from '@/locales/helper' + +const modules = import.meta.glob('./zh-CN/**/*.json', { + eager: true, +}) + +export default { + message: { + ...mergeMessage(modules, 'zh-CN'), + }, +} diff --git a/src/locales/lang/zh-CN/headerSettingOptions.json b/src/locales/lang/zh-CN/headerSettingOptions.json new file mode 100644 index 00000000..1b8bd34c --- /dev/null +++ b/src/locales/lang/zh-CN/headerSettingOptions.json @@ -0,0 +1,10 @@ +{ + "Title": "项目配置", + "ThemeOptions": { + "Title": "主题", + "Dark": "暗色", + "Light": "明亮", + "PrimaryColorConfig": "主题色" + }, + "InterfaceDisplay": "界面显示" +} diff --git a/src/locales/lang/zh-CN/headerTooltip.json b/src/locales/lang/zh-CN/headerTooltip.json new file mode 100644 index 00000000..9cfd9a50 --- /dev/null +++ b/src/locales/lang/zh-CN/headerTooltip.json @@ -0,0 +1,9 @@ +{ + "Reload": "刷新当前页面", + "Lock": "锁屏", + "Setting": "设置", + "Github": "Github", + "FullScreen": "全屏", + "CancelFullScreen": "退出全屏", + "Search": "搜索" +} diff --git a/src/locales/lang/zh-CN/menu.json b/src/locales/lang/zh-CN/menu.json new file mode 100644 index 00000000..7aad141f --- /dev/null +++ b/src/locales/lang/zh-CN/menu.json @@ -0,0 +1,17 @@ +{ + "Dashboard": "首页", + "Rely": "依赖项", + "RelyAbout": "关于", + "Error": "错误页", + "Echart": "可视化", + "scrollReveal": "滚动动画", + "Axios": "请求", + "Table": "表格", + "MultiMenu": "多级菜单", + "Doc": "文档", + "DocLocal": "文档 (国内地址)", + "Office": "办公", + "Office_Document": "文档", + "Office_Presentation": "演示", + "Office_Spreadsheet": "表格" +} diff --git a/src/locales/lang/zh-CN/setting.json b/src/locales/lang/zh-CN/setting.json new file mode 100644 index 00000000..1b8bd34c --- /dev/null +++ b/src/locales/lang/zh-CN/setting.json @@ -0,0 +1,10 @@ +{ + "Title": "项目配置", + "ThemeOptions": { + "Title": "主题", + "Dark": "暗色", + "Light": "明亮", + "PrimaryColorConfig": "主题色" + }, + "InterfaceDisplay": "界面显示" +} diff --git a/src/locales/lang/zh-CN/views/login/index.json b/src/locales/lang/zh-CN/views/login/index.json new file mode 100644 index 00000000..a99252d3 --- /dev/null +++ b/src/locales/lang/zh-CN/views/login/index.json @@ -0,0 +1,10 @@ +{ + "Register": "注册", + "Signin": "登陆", + "QRCodeSignin": "扫码登陆", + "NamePlaceholder": "请输入用户名", + "PasswordPlaceholder": "请输入密码", + "Login": "登 陆", + "Name": "用户名", + "Password": "密码" +} diff --git a/src/language/language.ts b/src/locales/language.ts similarity index 100% rename from src/language/language.ts rename to src/locales/language.ts diff --git a/src/language/useI18n.ts b/src/locales/useI18n.ts similarity index 67% rename from src/language/useI18n.ts rename to src/locales/useI18n.ts index bdd8ace9..bebf9600 100644 --- a/src/language/useI18n.ts +++ b/src/locales/useI18n.ts @@ -1,5 +1,8 @@ import { i18n } from './index' +import type { WritableComputedRef } from 'vue' +import type { useI18n as _useI18n } from 'vue-i18n' + const getI18nKey = (namespace: string | undefined, key: string) => { if (!namespace) { return key @@ -13,17 +16,7 @@ const getI18nKey = (namespace: string | undefined, key: string) => { } export const useI18n = (namespace?: string) => { - const normalFunc = { - t: (key: string) => { - return getI18nKey(namespace, key) - }, - } - - if (!i18n) { - return normalFunc - } - - const { t, ...methods } = i18n.global + const { t, locale, ...methods } = i18n.global const overridesTFunc = (key: string, ...args: any[]) => { if (!key) { @@ -38,11 +31,15 @@ export const useI18n = (namespace?: string) => { return (t as any)(getI18nKey(namespace, key), ...args) } + const overrideLocaleFunc = (lang: string) => { + const localeRef = locale as WritableComputedRef + + localeRef.value = lang + } + return { ...methods, t: overridesTFunc, + locale: overrideLocaleFunc, } } - -/** 配合 i18n ally 插件提示使用 */ -export const t = (key: string) => key diff --git a/src/main.ts b/src/main.ts index 1e74c2d6..e3e708eb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,16 +15,16 @@ import { permissionRouter, } from './router/index' import { setupStore } from './store/index' -import { setupI18n } from './language/index' +import { setupI18n } from './locales/index' /** * * 普通应用注册方法 */ -const setupTemplate = () => { +const setupTemplate = async () => { const app = createApp(App) - setupI18n(app) + await setupI18n(app) setupStore(app) @@ -42,13 +42,13 @@ const setupTemplate = () => { * 作为 `wujie-micro` 子应用注册应用方法 * 注意: 此处的 `instance` 名称不可以写为 `app` */ -const setupWujieTemplate = () => { +const setupWujieTemplate = async () => { let instance: AppType - window.__WUJIE_MOUNT = () => { + window.__WUJIE_MOUNT = async () => { instance = createApp(App) - setupI18n(instance) + await setupI18n(instance) setupStore(instance) diff --git a/src/store/modules/menu/index.ts b/src/store/modules/menu/index.ts index 0a3a5b24..fc8b6788 100644 --- a/src/store/modules/menu/index.ts +++ b/src/store/modules/menu/index.ts @@ -28,7 +28,7 @@ import RayIcon from '@/components/RayIcon/index' import { getCache, setCache } from '@/utils/cache' import { validRole } from '@/router/basic' import { parse, matchMenuOption, updateDocumentTitle } from './helper' -import { useI18n } from '@/language/useI18n' +import { useI18n } from '@/locales/useI18n' import type { MenuOption } from 'naive-ui' import type { RouteMeta } from 'vue-router' @@ -173,9 +173,7 @@ export const useMenu = defineStore( /** 设置 label, i18nKey 优先级最高 */ const label = computed(() => - meta?.i18nKey - ? t(`GlobalMenuOptions.${meta!.i18nKey}`) - : meta?.noLocalTitle, + meta?.i18nKey ? t(`menu.${meta!.i18nKey}`) : meta?.noLocalTitle, ) /** 拼装菜单项 */ const route = { diff --git a/src/store/modules/setting.ts b/src/store/modules/setting.ts index b417608a..4c267a6f 100644 --- a/src/store/modules/setting.ts +++ b/src/store/modules/setting.ts @@ -1,7 +1,8 @@ -import { getDefaultLocal } from '@/language/index' +import { getDefaultLocal } from '@/locales/index' import { setCache } from '@use-utils/cache' import { set } from 'lodash-es' import { addClass, removeClass, colorToRgba } from '@/utils/element' +import { useI18n } from '@/locales/useI18n' import type { ConditionalPick } from '@/types/type-utils' import type { GlobalThemeOverrides } from 'naive-ui' @@ -24,7 +25,7 @@ export const useSetting = defineStore( const { appPrimaryColor: { primaryColor }, } = __APP_CFG__ // 默认主题色 - const { locale } = useI18n() + const { t, locale } = useI18n() const settingState = reactive({ drawerPlacement: 'right' as NaiveDrawerPlacement, @@ -45,7 +46,8 @@ export const useSetting = defineStore( /** 修改当前语言 */ const updateLocale = (key: string) => { - locale.value = key + locale(key) + settingState.localeLanguage = key setCache('localeLanguage', key, 'localStorage') diff --git a/src/views/login/components/Signin/index.tsx b/src/views/login/components/Signin/index.tsx index 9e8d5ef6..48f36c6d 100644 --- a/src/views/login/components/Signin/index.tsx +++ b/src/views/login/components/Signin/index.tsx @@ -3,7 +3,7 @@ import { NForm, NFormItem, NInput, NButton, NSpace, NDivider } from 'naive-ui' import { setCache } from '@/utils/cache' import { useSpin } from '@/spin' import { useSignin } from '@/store' -import { useI18n } from '@/language/useI18n' +import { useI18n } from '@/locales/useI18n' import type { FormInst } from 'naive-ui' @@ -31,12 +31,12 @@ const Signin = defineComponent({ const rules = { name: { required: true, - message: t('LoginModule.NamePlaceholder'), + message: t('views.login.index.NamePlaceholder'), trigger: ['blur', 'input'], }, pwd: { required: true, - message: t('LoginModule.PasswordPlaceholder'), + message: t('views.login.index.PasswordPlaceholder'), trigger: ['blur', 'input'], }, } @@ -78,17 +78,17 @@ const Signin = defineComponent({ return ( - + - + - {t('LoginModule.Login')} + {t('views.login.index.Login')} ) diff --git a/src/views/login/index.tsx b/src/views/login/index.tsx index 06ed4013..a96fe238 100644 --- a/src/views/login/index.tsx +++ b/src/views/login/index.tsx @@ -20,8 +20,8 @@ import RayLink from '@/components/RayLink/index' import ThemeSwitch from '@/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index' import { useSetting } from '@/store' -import { localOptions } from '@/language/index' -import { useI18n } from '@/language/useI18n' +import { localOptions } from '@/locales/index' +import { useI18n } from '@/locales/useI18n' const Login = defineComponent({ name: 'Login', @@ -122,17 +122,20 @@ const Login = defineComponent({ {{ default: () => ( <> - + diff --git a/tsconfig.json b/tsconfig.json index aac89f85..5bc2f6b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "resolveJsonModule": true, "isolatedModules": true, "esModuleInterop": true, - "lib": ["ESNext", "DOM", "es5", "es6", "dom.iterable"], + "lib": ["ESNext", "DOM", "es5", "es6", "dom.iterable", "es2022"], "skipLibCheck": true, "baseUrl": "./", "paths": {