Browse Source

refactor: reconstruct language files into multi-file structures (#4683)

* refactor: reconstruct language files into multi-file structures

* chore: typo
Vben 5 months ago
parent
commit
0df8c5c02c
93 changed files with 1634 additions and 1441 deletions
  1. 5 3
      .vscode/settings.json
  2. 8 8
      apps/backend-mock/utils/mock-data.ts
  3. 1 1
      apps/web-antd/src/adapter/component/index.ts
  4. 2 2
      apps/web-antd/src/adapter/form.ts
  5. 2 2
      apps/web-antd/src/layouts/basic.vue
  6. 10 4
      apps/web-antd/src/locales/index.ts
  7. 0 8
      apps/web-antd/src/locales/langs/en-US.json
  8. 12 0
      apps/web-antd/src/locales/langs/en-US/demos.json
  9. 14 0
      apps/web-antd/src/locales/langs/en-US/page.json
  10. 0 8
      apps/web-antd/src/locales/langs/zh-CN.json
  11. 12 0
      apps/web-antd/src/locales/langs/zh-CN/demos.json
  12. 14 0
      apps/web-antd/src/locales/langs/zh-CN/page.json
  13. 5 5
      apps/web-antd/src/router/routes/core.ts
  14. 2 2
      apps/web-antd/src/router/routes/modules/demos.ts
  15. 5 5
      apps/web-antd/src/router/routes/modules/vben.ts
  16. 1 1
      apps/web-ele/src/adapter/component/index.ts
  17. 2 2
      apps/web-ele/src/adapter/form.ts
  18. 2 2
      apps/web-ele/src/layouts/basic.vue
  19. 10 4
      apps/web-ele/src/locales/index.ts
  20. 0 8
      apps/web-ele/src/locales/langs/en-US.json
  21. 12 0
      apps/web-ele/src/locales/langs/en-US/demos.json
  22. 14 0
      apps/web-ele/src/locales/langs/en-US/page.json
  23. 0 8
      apps/web-ele/src/locales/langs/zh-CN.json
  24. 12 0
      apps/web-ele/src/locales/langs/zh-CN/demos.json
  25. 14 0
      apps/web-ele/src/locales/langs/zh-CN/page.json
  26. 5 5
      apps/web-ele/src/router/routes/core.ts
  27. 2 2
      apps/web-ele/src/router/routes/modules/demos.ts
  28. 5 5
      apps/web-ele/src/router/routes/modules/vben.ts
  29. 1 1
      apps/web-naive/src/adapter/component/index.ts
  30. 2 2
      apps/web-naive/src/adapter/form.ts
  31. 2 2
      apps/web-naive/src/layouts/basic.vue
  32. 10 3
      apps/web-naive/src/locales/index.ts
  33. 0 9
      apps/web-naive/src/locales/langs/en-US.json
  34. 13 0
      apps/web-naive/src/locales/langs/en-US/demos.json
  35. 14 0
      apps/web-naive/src/locales/langs/en-US/page.json
  36. 0 9
      apps/web-naive/src/locales/langs/zh-CN.json
  37. 13 0
      apps/web-naive/src/locales/langs/zh-CN/demos.json
  38. 14 0
      apps/web-naive/src/locales/langs/zh-CN/page.json
  39. 5 5
      apps/web-naive/src/router/routes/core.ts
  40. 3 3
      apps/web-naive/src/router/routes/modules/demos.ts
  41. 5 5
      apps/web-naive/src/router/routes/modules/vben.ts
  42. 1 1
      docs/src/_env/adapter/component.ts
  43. 2 2
      docs/src/_env/adapter/form.ts
  44. 1 1
      docs/src/components/common-ui/vben-drawer.md
  45. 3 3
      docs/src/components/common-ui/vben-form.md
  46. 1 1
      docs/src/components/common-ui/vben-modal.md
  47. 9 9
      docs/src/en/guide/essentials/route.md
  48. 1 1
      docs/src/en/guide/in-depth/locale.md
  49. 9 9
      docs/src/guide/essentials/route.md
  50. 1 1
      docs/src/guide/in-depth/locale.md
  51. 1 1
      package.json
  52. 0 1
      packages/@core/ui-kit/tabs-ui/package.json
  53. 9 9
      packages/effects/common-ui/src/ui/fallback/fallback.vue
  54. 2 2
      packages/effects/layouts/src/widgets/check-updates/check-updates.vue
  55. 5 5
      packages/effects/layouts/src/widgets/global-search/global-search.vue
  56. 3 3
      packages/effects/layouts/src/widgets/global-search/search-panel.vue
  57. 4 4
      packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue
  58. 4 4
      packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue
  59. 4 4
      packages/effects/layouts/src/widgets/notification/notification.vue
  60. 1 1
      packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue
  61. 2 2
      packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue
  62. 8 8
      packages/effects/request/src/request-client/preset-interceptors.ts
  63. 54 4
      packages/locales/src/i18n.ts
  64. 15 2
      packages/locales/src/index.ts
  65. 0 339
      packages/locales/src/langs/en-US.json
  66. 56 0
      packages/locales/src/langs/en-US/authentication.json
  67. 13 0
      packages/locales/src/langs/en-US/common.json
  68. 169 0
      packages/locales/src/langs/en-US/preferences.json
  69. 77 0
      packages/locales/src/langs/en-US/ui.json
  70. 0 339
      packages/locales/src/langs/zh-CN.json
  71. 56 0
      packages/locales/src/langs/zh-CN/authentication.json
  72. 13 0
      packages/locales/src/langs/zh-CN/common.json
  73. 169 0
      packages/locales/src/langs/zh-CN/preferences.json
  74. 77 0
      packages/locales/src/langs/zh-CN/ui.json
  75. 1 1
      playground/src/adapter/component/index.ts
  76. 2 2
      playground/src/adapter/form.ts
  77. 2 2
      playground/src/layouts/basic.vue
  78. 10 4
      playground/src/locales/index.ts
  79. 0 125
      playground/src/locales/langs/en-US.json
  80. 69 0
      playground/src/locales/langs/en-US/demos.json
  81. 60 0
      playground/src/locales/langs/en-US/examples.json
  82. 14 0
      playground/src/locales/langs/en-US/page.json
  83. 0 125
      playground/src/locales/langs/zh-CN.json
  84. 69 0
      playground/src/locales/langs/zh-CN/demos.json
  85. 60 0
      playground/src/locales/langs/zh-CN/examples.json
  86. 14 0
      playground/src/locales/langs/zh-CN/page.json
  87. 5 5
      playground/src/router/routes/core.ts
  88. 42 42
      playground/src/router/routes/modules/demos.ts
  89. 26 26
      playground/src/router/routes/modules/examples.ts
  90. 6 6
      playground/src/router/routes/modules/vben.ts
  91. 20 23
      playground/src/views/examples/captcha/point-selection-captcha.vue
  92. 203 192
      pnpm-lock.yaml
  93. 8 8
      pnpm-workspace.yaml

+ 5 - 3
.vscode/settings.json

@@ -197,11 +197,14 @@
     "playground/src/locales/langs",
     "apps/*/src/locales/langs"
   ],
-  "i18n-ally.pathMatcher": "{locale}.json",
-  "i18n-ally.enabledParsers": ["json", "ts", "js", "yaml"],
+  "i18n-ally.pathMatcher": "{locale}/{namespace}.{ext}",
+  "i18n-ally.enabledParsers": ["json"],
   "i18n-ally.sourceLanguage": "en",
   "i18n-ally.displayLanguage": "zh-CN",
   "i18n-ally.enabledFrameworks": ["vue", "react"],
+  "i18n-ally.keystyle": "nested",
+  "i18n-ally.sortKeys": true,
+  "i18n-ally.namespace": true,
 
   // 控制相关文件嵌套展示
   "explorer.fileNesting.enabled": true,
@@ -216,7 +219,6 @@
     "tailwind.config.mjs": "postcss.*"
   },
   "commentTranslate.hover.enabled": false,
-  "i18n-ally.keystyle": "nested",
   "commentTranslate.multiLineMerge": true,
   "vue.server.hybridMode": true,
   "typescript.tsdk": "node_modules/typescript/lib"

+ 8 - 8
apps/backend-mock/utils/mock-data.ts

@@ -86,7 +86,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
       component: '/demos/access/admin-visible',
       meta: {
         icon: 'mdi:button-cursor',
-        title: 'page.demos.access.adminVisible',
+        title: 'demos.access.adminVisible',
       },
       name: 'AccessAdminVisibleDemo',
       path: '/demos/access/admin-visible',
@@ -95,7 +95,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
       component: '/demos/access/super-visible',
       meta: {
         icon: 'mdi:button-cursor',
-        title: 'page.demos.access.superVisible',
+        title: 'demos.access.superVisible',
       },
       name: 'AccessSuperVisibleDemo',
       path: '/demos/access/super-visible',
@@ -104,7 +104,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
       component: '/demos/access/user-visible',
       meta: {
         icon: 'mdi:button-cursor',
-        title: 'page.demos.access.userVisible',
+        title: 'demos.access.userVisible',
       },
       name: 'AccessUserVisibleDemo',
       path: '/demos/access/user-visible',
@@ -118,7 +118,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
         icon: 'ic:baseline-view-in-ar',
         keepAlive: true,
         order: 1000,
-        title: 'page.demos.title',
+        title: 'demos.title',
       },
       name: 'Demos',
       path: '/demos',
@@ -129,7 +129,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
           path: '/demosaccess',
           meta: {
             icon: 'mdi:cloud-key-outline',
-            title: 'page.demos.access.backendPermissions',
+            title: 'demos.access.backendPermissions',
           },
           redirect: '/demos/access/page-control',
           children: [
@@ -139,7 +139,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
               component: '/demos/access/index',
               meta: {
                 icon: 'mdi:page-previous-outline',
-                title: 'page.demos.access.pageAccess',
+                title: 'demos.access.pageAccess',
               },
             },
             {
@@ -148,7 +148,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
               component: '/demos/access/button-control',
               meta: {
                 icon: 'mdi:button-cursor',
-                title: 'page.demos.access.buttonControl',
+                title: 'demos.access.buttonControl',
               },
             },
             {
@@ -159,7 +159,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
                 authority: ['no-body'],
                 icon: 'mdi:button-cursor',
                 menuVisibleWithForbidden: true,
-                title: 'page.demos.access.menuVisible403',
+                title: 'demos.access.menuVisible403',
               },
             },
             roleWithMenus[role],

+ 1 - 1
apps/web-antd/src/adapter/component/index.ts

@@ -41,7 +41,7 @@ const withDefaultPlaceholder = <T extends Component>(
   type: 'input' | 'select',
 ) => {
   return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
     return h(component, { ...props, ...attrs, placeholder }, slots);
   };
 };

+ 2 - 2
apps/web-antd/src/adapter/form.ts

@@ -25,14 +25,14 @@ setupVbenForm<ComponentType>({
     // 输入项目必填国际化适配
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     // 选择项目必填国际化适配
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },

+ 2 - 2
apps/web-antd/src/layouts/basic.vue

@@ -68,7 +68,7 @@ const menus = computed(() => [
       });
     },
     icon: BookOpenText,
-    text: $t('widgets.document'),
+    text: $t('ui.widgets.document'),
   },
   {
     handler: () => {
@@ -86,7 +86,7 @@ const menus = computed(() => [
       });
     },
     icon: CircleHelp,
-    text: $t('widgets.qa'),
+    text: $t('ui.widgets.qa'),
   },
 ]);
 

+ 10 - 4
apps/web-antd/src/locales/index.ts

@@ -4,7 +4,11 @@ import type { Locale } from 'ant-design-vue/es/locale';
 import type { App } from 'vue';
 import { ref } from 'vue';
 
-import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import {
+  $t,
+  setupI18n as coreSetup,
+  loadLocalesMapFromDir,
+} from '@vben/locales';
 import { preferences } from '@vben/preferences';
 
 import antdEnLocale from 'ant-design-vue/es/locale/en_US';
@@ -13,10 +17,12 @@ import dayjs from 'dayjs';
 
 const antdLocale = ref<Locale>(antdDefaultLocale);
 
-const modules = import.meta.glob('./langs/*.json');
-
-const localesMap = loadLocalesMap(modules);
+const modules = import.meta.glob('./langs/**/*.json');
 
+const localesMap = loadLocalesMapFromDir(
+  /\.\/langs\/([^/]+)\/(.*)\.json$/,
+  modules,
+);
 /**
  * 加载应用特有的语言包
  * 这里也可以改造为从服务端获取翻译数据

+ 0 - 8
apps/web-antd/src/locales/langs/en-US.json

@@ -1,8 +0,0 @@
-{
-  "page": {
-    "demos": {
-      "title": "Demos",
-      "antd": "Ant Design Vue"
-    }
-  }
-}

+ 12 - 0
apps/web-antd/src/locales/langs/en-US/demos.json

@@ -0,0 +1,12 @@
+{
+  "title": "Demos",
+  "antd": "Ant Design Vue",
+  "vben": {
+    "title": "Project",
+    "about": "About",
+    "document": "Document",
+    "antdv": "Ant Design Vue Version",
+    "naive-ui": "Naive UI Version",
+    "element-plus": "Element Plus Version"
+  }
+}

+ 14 - 0
apps/web-antd/src/locales/langs/en-US/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "Login",
+    "register": "Register",
+    "codeLogin": "Code Login",
+    "qrcodeLogin": "Qr Code Login",
+    "forgetPassword": "Forget Password"
+  },
+  "dashboard": {
+    "title": "Dashboard",
+    "analytics": "Analytics",
+    "workspace": "Workspace"
+  }
+}

+ 0 - 8
apps/web-antd/src/locales/langs/zh-CN.json

@@ -1,8 +0,0 @@
-{
-  "page": {
-    "demos": {
-      "title": "演示",
-      "antd": "Ant Design Vue"
-    }
-  }
-}

+ 12 - 0
apps/web-antd/src/locales/langs/zh-CN/demos.json

@@ -0,0 +1,12 @@
+{
+  "title": "演示",
+  "antd": "Ant Design Vue",
+  "vben": {
+    "title": "项目",
+    "about": "关于",
+    "document": "文档",
+    "antdv": "Ant Design Vue 版本",
+    "naive-ui": "Naive UI 版本",
+    "element-plus": "Element Plus 版本"
+  }
+}

+ 14 - 0
apps/web-antd/src/locales/langs/zh-CN/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "登陆",
+    "register": "注册",
+    "codeLogin": "验证码登陆",
+    "qrcodeLogin": "二维码登陆",
+    "forgetPassword": "忘记密码"
+  },
+  "dashboard": {
+    "title": "概览",
+    "analytics": "分析页",
+    "workspace": "工作台"
+  }
+}

+ 5 - 5
apps/web-antd/src/router/routes/core.ts

@@ -43,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'login',
         component: Login,
         meta: {
-          title: $t('page.core.login'),
+          title: $t('page.auth.login'),
         },
       },
       {
@@ -51,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'code-login',
         component: () => import('#/views/_core/authentication/code-login.vue'),
         meta: {
-          title: $t('page.core.codeLogin'),
+          title: $t('page.auth.codeLogin'),
         },
       },
       {
@@ -60,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/qrcode-login.vue'),
         meta: {
-          title: $t('page.core.qrcodeLogin'),
+          title: $t('page.auth.qrcodeLogin'),
         },
       },
       {
@@ -69,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/forget-password.vue'),
         meta: {
-          title: $t('page.core.forgetPassword'),
+          title: $t('page.auth.forgetPassword'),
         },
       },
       {
@@ -77,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'register',
         component: () => import('#/views/_core/authentication/register.vue'),
         meta: {
-          title: $t('page.core.register'),
+          title: $t('page.auth.register'),
         },
       },
     ],

+ 2 - 2
apps/web-antd/src/router/routes/modules/demos.ts

@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
     children: [
       {
         meta: {
-          title: $t('page.demos.antd'),
+          title: $t('demos.antd'),
         },
         name: 'AntDesignDemos',
         path: '/demos/ant-design',

+ 5 - 5
apps/web-antd/src/router/routes/modules/vben.ts

@@ -18,7 +18,7 @@ const routes: RouteRecordRaw[] = [
       badgeType: 'dot',
       icon: VBEN_LOGO_URL,
       order: 9999,
-      title: $t('page.vben.title'),
+      title: $t('demos.vben.title'),
     },
     name: 'VbenProject',
     path: '/vben-admin',
@@ -29,7 +29,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/_core/about/index.vue'),
         meta: {
           icon: 'lucide:copyright',
-          title: $t('page.vben.about'),
+          title: $t('demos.vben.about'),
         },
       },
       {
@@ -39,7 +39,7 @@ const routes: RouteRecordRaw[] = [
         meta: {
           icon: 'lucide:book-open-text',
           link: VBEN_DOC_URL,
-          title: $t('page.vben.document'),
+          title: $t('demos.vben.document'),
         },
       },
       {
@@ -60,7 +60,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:naiveui',
           link: VBEN_NAIVE_PREVIEW_URL,
-          title: $t('page.vben.naive-ui'),
+          title: $t('demos.vben.naive-ui'),
         },
       },
       {
@@ -71,7 +71,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:element',
           link: VBEN_ELE_PREVIEW_URL,
-          title: $t('page.vben.element-plus'),
+          title: $t('demos.vben.element-plus'),
         },
       },
     ],

+ 1 - 1
apps/web-ele/src/adapter/component/index.ts

@@ -33,7 +33,7 @@ const withDefaultPlaceholder = <T extends Component>(
   type: 'input' | 'select',
 ) => {
   return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
     return h(component, { ...props, ...attrs, placeholder }, slots);
   };
 };

+ 2 - 2
apps/web-ele/src/adapter/form.ts

@@ -17,13 +17,13 @@ setupVbenForm<ComponentType>({
   defineRules: {
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },

+ 2 - 2
apps/web-ele/src/layouts/basic.vue

@@ -68,7 +68,7 @@ const menus = computed(() => [
       });
     },
     icon: BookOpenText,
-    text: $t('widgets.document'),
+    text: $t('ui.widgets.document'),
   },
   {
     handler: () => {
@@ -86,7 +86,7 @@ const menus = computed(() => [
       });
     },
     icon: CircleHelp,
-    text: $t('widgets.qa'),
+    text: $t('ui.widgets.qa'),
   },
 ]);
 

+ 10 - 4
apps/web-ele/src/locales/index.ts

@@ -4,7 +4,11 @@ import type { Language } from 'element-plus/es/locale';
 import type { App } from 'vue';
 import { ref } from 'vue';
 
-import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import {
+  $t,
+  setupI18n as coreSetup,
+  loadLocalesMapFromDir,
+} from '@vben/locales';
 import { preferences } from '@vben/preferences';
 
 import dayjs from 'dayjs';
@@ -13,10 +17,12 @@ import defaultLocale from 'element-plus/es/locale/lang/zh-cn';
 
 const elementLocale = ref<Language>(defaultLocale);
 
-const modules = import.meta.glob('./langs/*.json');
-
-const localesMap = loadLocalesMap(modules);
+const modules = import.meta.glob('./langs/**/*.json');
 
+const localesMap = loadLocalesMapFromDir(
+  /\.\/langs\/([^/]+)\/(.*)\.json$/,
+  modules,
+);
 /**
  * 加载应用特有的语言包
  * 这里也可以改造为从服务端获取翻译数据

+ 0 - 8
apps/web-ele/src/locales/langs/en-US.json

@@ -1,8 +0,0 @@
-{
-  "page": {
-    "demos": {
-      "title": "Demos",
-      "element-plus": "Element Plus"
-    }
-  }
-}

+ 12 - 0
apps/web-ele/src/locales/langs/en-US/demos.json

@@ -0,0 +1,12 @@
+{
+  "title": "Demos",
+  "elementPlus": "Element Plus",
+  "vben": {
+    "title": "Project",
+    "about": "About",
+    "document": "Document",
+    "antdv": "Ant Design Vue Version",
+    "naive-ui": "Naive UI Version",
+    "element-plus": "Element Plus Version"
+  }
+}

+ 14 - 0
apps/web-ele/src/locales/langs/en-US/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "Login",
+    "register": "Register",
+    "codeLogin": "Code Login",
+    "qrcodeLogin": "Qr Code Login",
+    "forgetPassword": "Forget Password"
+  },
+  "dashboard": {
+    "title": "Dashboard",
+    "analytics": "Analytics",
+    "workspace": "Workspace"
+  }
+}

+ 0 - 8
apps/web-ele/src/locales/langs/zh-CN.json

@@ -1,8 +0,0 @@
-{
-  "page": {
-    "demos": {
-      "title": "演示",
-      "element-plus": "Element Plus"
-    }
-  }
-}

+ 12 - 0
apps/web-ele/src/locales/langs/zh-CN/demos.json

@@ -0,0 +1,12 @@
+{
+  "title": "演示",
+  "elementPlus": "Element Plus",
+  "vben": {
+    "title": "项目",
+    "about": "关于",
+    "document": "文档",
+    "antdv": "Ant Design Vue 版本",
+    "naive-ui": "Naive UI 版本",
+    "element-plus": "Element Plus 版本"
+  }
+}

+ 14 - 0
apps/web-ele/src/locales/langs/zh-CN/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "登陆",
+    "register": "注册",
+    "codeLogin": "验证码登陆",
+    "qrcodeLogin": "二维码登陆",
+    "forgetPassword": "忘记密码"
+  },
+  "dashboard": {
+    "title": "概览",
+    "analytics": "分析页",
+    "workspace": "工作台"
+  }
+}

+ 5 - 5
apps/web-ele/src/router/routes/core.ts

@@ -43,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'login',
         component: Login,
         meta: {
-          title: $t('page.core.login'),
+          title: $t('page.auth.login'),
         },
       },
       {
@@ -51,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'code-login',
         component: () => import('#/views/_core/authentication/code-login.vue'),
         meta: {
-          title: $t('page.core.codeLogin'),
+          title: $t('page.auth.codeLogin'),
         },
       },
       {
@@ -60,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/qrcode-login.vue'),
         meta: {
-          title: $t('page.core.qrcodeLogin'),
+          title: $t('page.auth.qrcodeLogin'),
         },
       },
       {
@@ -69,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/forget-password.vue'),
         meta: {
-          title: $t('page.core.forgetPassword'),
+          title: $t('page.auth.forgetPassword'),
         },
       },
       {
@@ -77,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'register',
         component: () => import('#/views/_core/authentication/register.vue'),
         meta: {
-          title: $t('page.core.register'),
+          title: $t('page.auth.register'),
         },
       },
     ],

+ 2 - 2
apps/web-ele/src/router/routes/modules/demos.ts

@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
     children: [
       {
         meta: {
-          title: $t('page.demos.element-plus'),
+          title: $t('demos.elementPlus'),
         },
         name: 'NaiveDemos',
         path: '/demos/element',

+ 5 - 5
apps/web-ele/src/router/routes/modules/vben.ts

@@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
       badgeType: 'dot',
       icon: VBEN_LOGO_URL,
       order: 9999,
-      title: $t('page.vben.title'),
+      title: $t('demos.vben.title'),
     },
     name: 'VbenProject',
     path: '/vben-admin',
@@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/_core/about/index.vue'),
         meta: {
           icon: 'lucide:copyright',
-          title: $t('page.vben.about'),
+          title: $t('demos.vben.about'),
         },
       },
       {
@@ -40,7 +40,7 @@ const routes: RouteRecordRaw[] = [
         meta: {
           icon: 'lucide:book-open-text',
           link: VBEN_DOC_URL,
-          title: $t('page.vben.document'),
+          title: $t('demos.vben.document'),
         },
       },
       {
@@ -61,7 +61,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:naiveui',
           link: VBEN_NAIVE_PREVIEW_URL,
-          title: $t('page.vben.naive-ui'),
+          title: $t('demos.vben.naive-ui'),
         },
       },
       {
@@ -72,7 +72,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: SvgAntdvLogoIcon,
           link: VBEN_ANT_PREVIEW_URL,
-          title: $t('page.vben.antdv'),
+          title: $t('demos.vben.antdv'),
         },
       },
     ],

+ 1 - 1
apps/web-naive/src/adapter/component/index.ts

@@ -35,7 +35,7 @@ const withDefaultPlaceholder = <T extends Component>(
   type: 'input' | 'select',
 ) => {
   return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
     return h(component, { ...props, ...attrs, placeholder }, slots);
   };
 };

+ 2 - 2
apps/web-naive/src/adapter/form.ts

@@ -24,13 +24,13 @@ setupVbenForm<ComponentType>({
   defineRules: {
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },

+ 2 - 2
apps/web-naive/src/layouts/basic.vue

@@ -68,7 +68,7 @@ const menus = computed(() => [
       });
     },
     icon: BookOpenText,
-    text: $t('widgets.document'),
+    text: $t('ui.widgets.document'),
   },
   {
     handler: () => {
@@ -86,7 +86,7 @@ const menus = computed(() => [
       });
     },
     icon: CircleHelp,
-    text: $t('widgets.qa'),
+    text: $t('ui.widgets.qa'),
   },
 ]);
 

+ 10 - 3
apps/web-naive/src/locales/index.ts

@@ -2,12 +2,19 @@ import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
 
 import type { App } from 'vue';
 
-import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import {
+  $t,
+  setupI18n as coreSetup,
+  loadLocalesMapFromDir,
+} from '@vben/locales';
 import { preferences } from '@vben/preferences';
 
-const modules = import.meta.glob('./langs/*.json');
+const modules = import.meta.glob('./langs/**/*.json');
 
-const localesMap = loadLocalesMap(modules);
+const localesMap = loadLocalesMapFromDir(
+  /\.\/langs\/([^/]+)\/(.*)\.json$/,
+  modules,
+);
 
 /**
  * 加载应用特有的语言包

+ 0 - 9
apps/web-naive/src/locales/langs/en-US.json

@@ -1,9 +0,0 @@
-{
-  "page": {
-    "demos": {
-      "title": "Demos",
-      "naive": "Naive UI",
-      "table": "Table"
-    }
-  }
-}

+ 13 - 0
apps/web-naive/src/locales/langs/en-US/demos.json

@@ -0,0 +1,13 @@
+{
+  "title": "Demos",
+  "naive": "Naive UI",
+  "table": "Table",
+  "vben": {
+    "title": "Project",
+    "about": "About",
+    "document": "Document",
+    "antdv": "Ant Design Vue Version",
+    "naive-ui": "Naive UI Version",
+    "element-plus": "Element Plus Version"
+  }
+}

+ 14 - 0
apps/web-naive/src/locales/langs/en-US/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "Login",
+    "register": "Register",
+    "codeLogin": "Code Login",
+    "qrcodeLogin": "Qr Code Login",
+    "forgetPassword": "Forget Password"
+  },
+  "dashboard": {
+    "title": "Dashboard",
+    "analytics": "Analytics",
+    "workspace": "Workspace"
+  }
+}

+ 0 - 9
apps/web-naive/src/locales/langs/zh-CN.json

@@ -1,9 +0,0 @@
-{
-  "page": {
-    "demos": {
-      "title": "演示",
-      "naive": "Naive UI",
-      "table": "Table"
-    }
-  }
-}

+ 13 - 0
apps/web-naive/src/locales/langs/zh-CN/demos.json

@@ -0,0 +1,13 @@
+{
+  "title": "演示",
+  "naive": "Naive UI",
+  "table": "Table",
+  "vben": {
+    "title": "项目",
+    "about": "关于",
+    "document": "文档",
+    "antdv": "Ant Design Vue 版本",
+    "naive-ui": "Naive UI 版本",
+    "element-plus": "Element Plus 版本"
+  }
+}

+ 14 - 0
apps/web-naive/src/locales/langs/zh-CN/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "登陆",
+    "register": "注册",
+    "codeLogin": "验证码登陆",
+    "qrcodeLogin": "二维码登陆",
+    "forgetPassword": "忘记密码"
+  },
+  "dashboard": {
+    "title": "概览",
+    "analytics": "分析页",
+    "workspace": "工作台"
+  }
+}

+ 5 - 5
apps/web-naive/src/router/routes/core.ts

@@ -43,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'login',
         component: Login,
         meta: {
-          title: $t('page.core.login'),
+          title: $t('page.auth.login'),
         },
       },
       {
@@ -51,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'code-login',
         component: () => import('#/views/_core/authentication/code-login.vue'),
         meta: {
-          title: $t('page.core.codeLogin'),
+          title: $t('page.auth.codeLogin'),
         },
       },
       {
@@ -60,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/qrcode-login.vue'),
         meta: {
-          title: $t('page.core.qrcodeLogin'),
+          title: $t('page.auth.qrcodeLogin'),
         },
       },
       {
@@ -69,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/forget-password.vue'),
         meta: {
-          title: $t('page.core.forgetPassword'),
+          title: $t('page.auth.forgetPassword'),
         },
       },
       {
@@ -77,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'register',
         component: () => import('#/views/_core/authentication/register.vue'),
         meta: {
-          title: $t('page.core.register'),
+          title: $t('page.auth.register'),
         },
       },
     ],

+ 3 - 3
apps/web-naive/src/router/routes/modules/demos.ts

@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
     children: [
       {
         meta: {
-          title: $t('page.demos.naive'),
+          title: $t('demos.naive'),
         },
         name: 'NaiveDemos',
         path: '/demos/naive',
@@ -25,7 +25,7 @@ const routes: RouteRecordRaw[] = [
       },
       {
         meta: {
-          title: $t('page.demos.table'),
+          title: $t('demos.table'),
         },
         name: 'Table',
         path: '/demos/table',

+ 5 - 5
apps/web-naive/src/router/routes/modules/vben.ts

@@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
       badgeType: 'dot',
       icon: VBEN_LOGO_URL,
       order: 9999,
-      title: $t('page.vben.title'),
+      title: $t('demos.vben.title'),
     },
     name: 'VbenProject',
     path: '/vben-admin',
@@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/_core/about/index.vue'),
         meta: {
           icon: 'lucide:copyright',
-          title: $t('page.vben.about'),
+          title: $t('demos.vben.about'),
         },
       },
       {
@@ -40,7 +40,7 @@ const routes: RouteRecordRaw[] = [
         meta: {
           icon: 'lucide:book-open-text',
           link: VBEN_DOC_URL,
-          title: $t('page.vben.document'),
+          title: $t('demos.vben.document'),
         },
       },
       {
@@ -61,7 +61,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: SvgAntdvLogoIcon,
           link: VBEN_ANT_PREVIEW_URL,
-          title: $t('page.vben.antdv'),
+          title: $t('demos.vben.antdv'),
         },
       },
       {
@@ -72,7 +72,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:element',
           link: VBEN_ELE_PREVIEW_URL,
-          title: $t('page.vben.element-plus'),
+          title: $t('demos.vben.element-plus'),
         },
       },
     ],

+ 1 - 1
docs/src/_env/adapter/component.ts

@@ -41,7 +41,7 @@ const withDefaultPlaceholder = <T extends Component>(
   type: 'input' | 'select',
 ) => {
   return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
     return h(component, { ...props, ...attrs, placeholder }, slots);
   };
 };

+ 2 - 2
docs/src/_env/adapter/form.ts

@@ -28,13 +28,13 @@ setupVbenForm<ComponentType>({
   defineRules: {
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },

+ 1 - 1
docs/src/components/common-ui/vben-drawer.md

@@ -53,7 +53,7 @@ Drawer 内的内容一般业务中,会比较复杂,所以我们可以将 dra
 ::: info 注意
 
 - `VbenDrawer` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenDrawer参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
-- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenDrawer`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onComfirm`,那么以内部的 `onComfirm` 为准。`onOpenChange`事件除外,内外都会触发。
+- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenDrawer`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onConfirm`,那么以内部的 `onConfirm` 为准。`onOpenChange`事件除外,内外都会触发。
 
 :::
 

+ 3 - 3
docs/src/components/common-ui/vben-form.md

@@ -51,14 +51,14 @@ setupVbenForm<ComponentType>({
     // 输入项目必填国际化适配
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     // 选择项目必填国际化适配
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },
@@ -120,7 +120,7 @@ const withDefaultPlaceholder = <T extends Component>(
   type: 'input' | 'select',
 ) => {
   return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
     return h(component, { ...props, ...attrs, placeholder }, slots);
   };
 };

+ 1 - 1
docs/src/components/common-ui/vben-modal.md

@@ -59,7 +59,7 @@ Modal 内的内容一般业务中,会比较复杂,所以我们可以将 moda
 ::: info 注意
 
 - `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
-- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onComfirm`,那么以内部的 `onComfirm` 为准。`onOpenChange`事件除外,内外都会触发。
+- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onConfirm`,那么以内部的 `onConfirm` 为准。`onOpenChange`事件除外,内外都会触发。
 
 :::
 

+ 9 - 9
docs/src/en/guide/essentials/route.md

@@ -106,7 +106,7 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
@@ -116,7 +116,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'ic:round-menu',
-          title: $t('page.demos.nested.title'),
+          title: $t('demos.nested.title'),
         },
         name: 'NestedDemos',
         path: '/demos/nested',
@@ -129,7 +129,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu1'),
+              title: $t('demos.nested.menu1'),
             },
           },
           {
@@ -138,7 +138,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu2'),
+              title: $t('demos.nested.menu2'),
             },
             redirect: '/demos/nested/menu2/menu2-1',
             children: [
@@ -149,7 +149,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu2_1'),
+                  title: $t('demos.nested.menu2_1'),
                 },
               },
             ],
@@ -159,7 +159,7 @@ const routes: RouteRecordRaw[] = [
             path: '/demos/nested/menu3',
             meta: {
               icon: 'ic:round-menu',
-              title: $t('page.demos.nested.menu3'),
+              title: $t('demos.nested.menu3'),
             },
             redirect: '/demos/nested/menu3/menu3-1',
             children: [
@@ -170,7 +170,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu3_1'),
+                  title: $t('demos.nested.menu3_1'),
                 },
               },
               {
@@ -178,7 +178,7 @@ const routes: RouteRecordRaw[] = [
                 path: 'menu3-2',
                 meta: {
                   icon: 'ic:round-menu',
-                  title: $t('page.demos.nested.menu3_2'),
+                  title: $t('demos.nested.menu3_2'),
                 },
                 redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
                 children: [
@@ -190,7 +190,7 @@ const routes: RouteRecordRaw[] = [
                     meta: {
                       icon: 'ic:round-menu',
                       keepAlive: true,
-                      title: $t('page.demos.nested.menu3_2_1'),
+                      title: $t('demos.nested.menu3_2_1'),
                     },
                   },
                 ],

+ 1 - 1
docs/src/en/guide/in-depth/locale.md

@@ -58,7 +58,7 @@ updateLocale('en-US');
 
 To add new translation texts, simply find `src/locales/langs/` in the corresponding application and add the texts accordingly, for example:
 
-**src/locales/langs/zh-CN.ts**
+**src/locales/langs/zh-CN/\*.json**
 
 ````ts
 ```json

+ 9 - 9
docs/src/guide/essentials/route.md

@@ -105,7 +105,7 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
@@ -115,7 +115,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'ic:round-menu',
-          title: $t('page.demos.nested.title'),
+          title: $t('demos.nested.title'),
         },
         name: 'NestedDemos',
         path: '/demos/nested',
@@ -128,7 +128,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu1'),
+              title: $t('demos.nested.menu1'),
             },
           },
           {
@@ -137,7 +137,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu2'),
+              title: $t('demos.nested.menu2'),
             },
             redirect: '/demos/nested/menu2/menu2-1',
             children: [
@@ -148,7 +148,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu2_1'),
+                  title: $t('demos.nested.menu2_1'),
                 },
               },
             ],
@@ -158,7 +158,7 @@ const routes: RouteRecordRaw[] = [
             path: '/demos/nested/menu3',
             meta: {
               icon: 'ic:round-menu',
-              title: $t('page.demos.nested.menu3'),
+              title: $t('demos.nested.menu3'),
             },
             redirect: '/demos/nested/menu3/menu3-1',
             children: [
@@ -169,7 +169,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu3_1'),
+                  title: $t('demos.nested.menu3_1'),
                 },
               },
               {
@@ -177,7 +177,7 @@ const routes: RouteRecordRaw[] = [
                 path: 'menu3-2',
                 meta: {
                   icon: 'ic:round-menu',
-                  title: $t('page.demos.nested.menu3_2'),
+                  title: $t('demos.nested.menu3_2'),
                 },
                 redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
                 children: [
@@ -189,7 +189,7 @@ const routes: RouteRecordRaw[] = [
                     meta: {
                       icon: 'ic:round-menu',
                       keepAlive: true,
-                      title: $t('page.demos.nested.menu3_2_1'),
+                      title: $t('demos.nested.menu3_2_1'),
                     },
                   },
                 ],

+ 1 - 1
docs/src/guide/in-depth/locale.md

@@ -58,7 +58,7 @@ updateLocale('en-US');
 
 新增翻译文本,只需要在对应的应用内,找到 `src/locales/langs/`,新增对应的文本即可,例:
 
-**src/locales/langs/zh-CN.ts**
+**src/locales/langs/zh-CN/\*.json**
 
 ````ts
 ```json

+ 1 - 1
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "vben-admin-pro",
+  "name": "vben-admin-monorepo",
   "version": "5.4.1",
   "private": true,
   "keywords": [

+ 0 - 1
packages/@core/ui-kit/tabs-ui/package.json

@@ -40,7 +40,6 @@
     "@vben-core/composables": "workspace:*",
     "@vben-core/icons": "workspace:*",
     "@vben-core/shadcn-ui": "workspace:*",
-    "@vben-core/shared": "workspace:*",
     "@vben-core/typings": "workspace:*",
     "@vueuse/core": "catalog:",
     "vue": "catalog:"

+ 9 - 9
packages/effects/common-ui/src/ui/fallback/fallback.vue

@@ -40,19 +40,19 @@ const titleText = computed(() => {
 
   switch (props.status) {
     case '403': {
-      return $t('fallback.forbidden');
+      return $t('ui.fallback.forbidden');
     }
     case '404': {
-      return $t('fallback.pageNotFound');
+      return $t('ui.fallback.pageNotFound');
     }
     case '500': {
-      return $t('fallback.internalError');
+      return $t('ui.fallback.internalError');
     }
     case 'coming-soon': {
-      return $t('fallback.comingSoon');
+      return $t('ui.fallback.comingSoon');
     }
     case 'offline': {
-      return $t('fallback.offlineError');
+      return $t('ui.fallback.offlineError');
     }
     default: {
       return '';
@@ -66,16 +66,16 @@ const descText = computed(() => {
   }
   switch (props.status) {
     case '403': {
-      return $t('fallback.forbiddenDesc');
+      return $t('ui.fallback.forbiddenDesc');
     }
     case '404': {
-      return $t('fallback.pageNotFoundDesc');
+      return $t('ui.fallback.pageNotFoundDesc');
     }
     case '500': {
-      return $t('fallback.internalErrorDesc');
+      return $t('ui.fallback.internalErrorDesc');
     }
     case 'offline': {
-      return $t('fallback.offlineErrorDesc');
+      return $t('ui.fallback.offlineErrorDesc');
     }
     default: {
       return '';

+ 2 - 2
packages/effects/layouts/src/widgets/check-updates/check-updates.vue

@@ -123,12 +123,12 @@ onUnmounted(() => {
     :cancel-text="$t('common.cancel')"
     :confirm-text="$t('common.refresh')"
     :fullscreen-button="false"
-    :title="$t('widgets.checkUpdatesTitle')"
+    :title="$t('ui.widgets.checkUpdatesTitle')"
     centered
     content-class="px-8 min-h-10"
     footer-class="border-none mb-3 mr-3"
     header-class="border-none"
   >
-    {{ $t('widgets.checkUpdatesDescription') }}
+    {{ $t('ui.widgets.checkUpdatesDescription') }}
   </UpdateNoticeModal>
 </template>

+ 5 - 5
packages/effects/layouts/src/widgets/global-search/global-search.vue

@@ -102,7 +102,7 @@ onMounted(() => {
           <input
             ref="searchInputRef"
             v-model="keyword"
-            :placeholder="$t('widgets.search.searchNavigate')"
+            :placeholder="$t('ui.widgets.search.searchNavigate')"
             class="ring-none placeholder:text-muted-foreground w-[80%] rounded-md border border-none bg-transparent p-2 pl-0 text-sm font-normal outline-none ring-0 ring-offset-transparent focus-visible:ring-transparent"
           />
         </div>
@@ -113,16 +113,16 @@ onMounted(() => {
         <div class="flex w-full justify-start text-xs">
           <div class="mr-2 flex items-center">
             <CornerDownLeft class="mr-1 size-3" />
-            {{ $t('widgets.search.select') }}
+            {{ $t('ui.widgets.search.select') }}
           </div>
           <div class="mr-2 flex items-center">
             <ArrowUp class="mr-1 size-3" />
             <ArrowDown class="mr-1 size-3" />
-            {{ $t('widgets.search.navigate') }}
+            {{ $t('ui.widgets.search.navigate') }}
           </div>
           <div class="flex items-center">
             <MdiKeyboardEsc class="mr-1 size-3" />
-            {{ $t('widgets.search.close') }}
+            {{ $t('ui.widgets.search.close') }}
           </div>
         </div>
       </template>
@@ -137,7 +137,7 @@ onMounted(() => {
       <span
         class="text-muted-foreground group-hover:text-foreground hidden text-xs duration-300 md:block"
       >
-        {{ $t('widgets.search.title') }}
+        {{ $t('ui.widgets.search.title') }}
       </span>
       <span
         v-if="enableShortcutKey"

+ 3 - 3
packages/effects/layouts/src/widgets/global-search/search-panel.vue

@@ -230,7 +230,7 @@ onMounted(() => {
       >
         <SearchX class="mx-auto mt-4 size-12" />
         <p class="mb-10 mt-6 text-xs">
-          {{ $t('widgets.search.noResults') }}
+          {{ $t('ui.widgets.search.noResults') }}
           <span class="text-foreground text-sm font-medium">
             "{{ keyword }}"
           </span>
@@ -242,7 +242,7 @@ onMounted(() => {
         class="text-muted-foreground text-center"
       >
         <p class="my-10 text-xs">
-          {{ $t('widgets.search.noRecent') }}
+          {{ $t('ui.widgets.search.noRecent') }}
         </p>
       </div>
 
@@ -251,7 +251,7 @@ onMounted(() => {
           v-if="searchHistory.length > 0 && !keyword"
           class="text-muted-foreground mb-2 text-xs"
         >
-          {{ $t('widgets.search.recent') }}
+          {{ $t('ui.widgets.search.recent') }}
         </li>
         <li
           v-for="(item, index) in uniqueByField(searchResults, 'path')"

+ 4 - 4
packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue

@@ -42,14 +42,14 @@ const [Form, { resetForm, validate }] = useVbenForm(
       {
         component: 'VbenInputPassword' as const,
         componentProps: {
-          placeholder: $t('widgets.lockScreen.placeholder'),
+          placeholder: $t('ui.widgets.lockScreen.placeholder'),
         },
         fieldName: 'lockScreenPassword',
         formFieldProps: { validateOnBlur: false },
         label: $t('authentication.password'),
         rules: z
           .string()
-          .min(1, { message: $t('widgets.lockScreen.placeholder') }),
+          .min(1, { message: $t('ui.widgets.lockScreen.placeholder') }),
       },
     ]),
     showDefaultActions: false,
@@ -79,7 +79,7 @@ async function handleSubmit() {
   <Modal
     :footer="false"
     :fullscreen-button="false"
-    :title="$t('widgets.lockScreen.title')"
+    :title="$t('ui.widgets.lockScreen.title')"
   >
     <div
       class="mb-10 flex w-full flex-col items-center px-10"
@@ -98,7 +98,7 @@ async function handleSubmit() {
         </div>
         <Form />
         <VbenButton class="mt-1 w-full" @click="handleSubmit">
-          {{ $t('widgets.lockScreen.screenButton') }}
+          {{ $t('ui.widgets.lockScreen.screenButton') }}
         </VbenButton>
       </div>
     </div>

+ 4 - 4
packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue

@@ -46,7 +46,7 @@ const [Form, { form, validate }] = useVbenForm(
       {
         component: 'VbenInputPassword' as const,
         componentProps: {
-          placeholder: $t('widgets.lockScreen.placeholder'),
+          placeholder: $t('ui.widgets.lockScreen.placeholder'),
         },
         fieldName: 'password',
         label: $t('authentication.password'),
@@ -90,7 +90,7 @@ useScrollLock();
           <LockKeyhole
             class="size-5 transition-all duration-300 group-hover:scale-125"
           />
-          <span>{{ $t('widgets.lockScreen.unlock') }}</span>
+          <span>{{ $t('ui.widgets.lockScreen.unlock') }}</span>
         </div>
         <div class="flex h-full justify-center px-[10%]">
           <div
@@ -123,14 +123,14 @@ useScrollLock();
             <Form />
           </div>
           <VbenButton class="enter-x w-full" @click="handleSubmit">
-            {{ $t('widgets.lockScreen.entry') }}
+            {{ $t('ui.widgets.lockScreen.entry') }}
           </VbenButton>
           <VbenButton
             class="enter-x my-2 w-full"
             variant="ghost"
             @click="$emit('toLogin')"
           >
-            {{ $t('widgets.lockScreen.backToLogin') }}
+            {{ $t('ui.widgets.lockScreen.backToLogin') }}
           </VbenButton>
           <VbenButton
             class="enter-x mr-2 w-full"

+ 4 - 4
packages/effects/layouts/src/widgets/notification/notification.vue

@@ -79,10 +79,10 @@ function handleClick(item: NotificationItem) {
 
     <div class="relative">
       <div class="flex items-center justify-between p-4 py-3">
-        <div class="text-foreground">{{ $t('widgets.notifications') }}</div>
+        <div class="text-foreground">{{ $t('ui.widgets.notifications') }}</div>
         <VbenIconButton
           :disabled="notifications.length <= 0"
-          :tooltip="$t('widgets.markAllAsRead')"
+          :tooltip="$t('ui.widgets.markAllAsRead')"
           @click="handleMakeAll"
         >
           <MailCheck class="size-4" />
@@ -138,10 +138,10 @@ function handleClick(item: NotificationItem) {
           variant="ghost"
           @click="handleClear"
         >
-          {{ $t('widgets.clearNotifications') }}
+          {{ $t('ui.widgets.clearNotifications') }}
         </VbenButton>
         <VbenButton size="sm" @click="handleViewAll">
-          {{ $t('widgets.viewAll') }}
+          {{ $t('ui.widgets.viewAll') }}
         </VbenButton>
       </div>
     </div>

+ 1 - 1
packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue

@@ -44,7 +44,7 @@ const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
     <template #shortcut> {{ altView }} , </template>
   </SwitchItem> -->
   <SwitchItem v-model="shortcutKeysLockScreen" :disabled="!shortcutKeysEnable">
-    {{ $t('widgets.lockScreen.title') }}
+    {{ $t('ui.widgets.lockScreen.title') }}
     <template #shortcut> {{ altView }} L </template>
   </SwitchItem>
 </template>

+ 2 - 2
packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue

@@ -152,7 +152,7 @@ if (enableShortcutKey.value) {
     footer-class="border-none mb-3 mr-3"
     header-class="border-none"
   >
-    {{ $t('widgets.logoutTip') }}
+    {{ $t('ui.widgets.logoutTip') }}
   </LogoutModal>
 
   <DropdownMenu>
@@ -206,7 +206,7 @@ if (enableShortcutKey.value) {
         @click="handleOpenLock"
       >
         <LockKeyhole class="mr-2 size-4" />
-        {{ $t('widgets.lockScreen.title') }}
+        {{ $t('ui.widgets.lockScreen.title') }}
         <DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
           {{ altView }} L
         </DropdownMenuShortcut>

+ 8 - 8
packages/effects/request/src/request-client/preset-interceptors.ts

@@ -82,9 +82,9 @@ export const errorMessageResponseInterceptor = (
       const err: string = error?.toString?.() ?? '';
       let errMsg = '';
       if (err?.includes('Network Error')) {
-        errMsg = $t('fallback.http.networkError');
+        errMsg = $t('ui.fallback.http.networkError');
       } else if (error?.message?.includes?.('timeout')) {
-        errMsg = $t('fallback.http.requestTimeout');
+        errMsg = $t('ui.fallback.http.requestTimeout');
       }
       if (errMsg) {
         makeErrorMessage?.(errMsg, error);
@@ -96,27 +96,27 @@ export const errorMessageResponseInterceptor = (
 
       switch (status) {
         case 400: {
-          errorMessage = $t('fallback.http.badRequest');
+          errorMessage = $t('ui.fallback.http.badRequest');
           break;
         }
         case 401: {
-          errorMessage = $t('fallback.http.unauthorized');
+          errorMessage = $t('ui.fallback.http.unauthorized');
           break;
         }
         case 403: {
-          errorMessage = $t('fallback.http.forbidden');
+          errorMessage = $t('ui.fallback.http.forbidden');
           break;
         }
         case 404: {
-          errorMessage = $t('fallback.http.notFound');
+          errorMessage = $t('ui.fallback.http.notFound');
           break;
         }
         case 408: {
-          errorMessage = $t('fallback.http.requestTimeout');
+          errorMessage = $t('ui.fallback.http.requestTimeout');
           break;
         }
         default: {
-          errorMessage = $t('fallback.http.internalServerError');
+          errorMessage = $t('ui.fallback.http.internalServerError');
         }
       }
       makeErrorMessage?.(errorMessage, error);

+ 54 - 4
packages/locales/src/i18n.ts

@@ -19,12 +19,14 @@ const i18n = createI18n({
   messages: {},
 });
 
-const modules = import.meta.glob('./langs/*.json');
+const modules = import.meta.glob('./langs/**/*.json');
 
 const { setSimpleLocale } = useSimpleLocale();
 
-const localesMap = loadLocalesMap(modules);
-
+const localesMap = loadLocalesMapFromDir(
+  /\.\/langs\/([^/]+)\/(.*)\.json$/,
+  modules,
+);
 let loadMessages: LoadMessageFn;
 
 /**
@@ -40,6 +42,48 @@ function loadLocalesMap(modules: Record<string, () => Promise<unknown>>) {
       localesMap[key] = loadLocale as ImportLocaleFn;
     }
   }
+  return localesMap;
+}
+
+/**
+ * Load locale modules with directory structure
+ * @param regexp - Regular expression to match language and file names
+ * @param modules - The modules object containing paths and import functions
+ * @returns A map of locales to their corresponding import functions
+ */
+function loadLocalesMapFromDir(
+  regexp: RegExp,
+  modules: Record<string, () => Promise<unknown>>,
+): Record<Locale, ImportLocaleFn> {
+  const localesRaw: Record<Locale, Record<string, () => Promise<unknown>>> = {};
+  const localesMap: Record<Locale, ImportLocaleFn> = {};
+
+  // Iterate over the modules to extract language and file names
+  for (const path in modules) {
+    const match = path.match(regexp);
+    if (match) {
+      const [_, locale, fileName] = match;
+      if (locale && fileName) {
+        if (!localesRaw[locale]) {
+          localesRaw[locale] = {};
+        }
+        if (modules[path]) {
+          localesRaw[locale][fileName] = modules[path];
+        }
+      }
+    }
+  }
+
+  // Convert raw locale data into async import functions
+  for (const [locale, files] of Object.entries(localesRaw)) {
+    localesMap[locale] = async () => {
+      const messages: Record<string, any> = {};
+      for (const [fileName, importFn] of Object.entries(files)) {
+        messages[fileName] = ((await importFn()) as any)?.default;
+      }
+      return { default: messages };
+    };
+  }
 
   return localesMap;
 }
@@ -93,4 +137,10 @@ async function loadLocaleMessages(lang: SupportedLanguagesType) {
   return setI18nLanguage(lang);
 }
 
-export { i18n, loadLocaleMessages, loadLocalesMap, setupI18n };
+export {
+  i18n,
+  loadLocaleMessages,
+  loadLocalesMap,
+  loadLocalesMapFromDir,
+  setupI18n,
+};

+ 15 - 2
packages/locales/src/index.ts

@@ -1,8 +1,21 @@
-import { i18n, loadLocaleMessages, loadLocalesMap, setupI18n } from './i18n';
+import {
+  i18n,
+  loadLocaleMessages,
+  loadLocalesMap,
+  loadLocalesMapFromDir,
+  setupI18n,
+} from './i18n';
 
 const $t = i18n.global.t;
 
-export { $t, i18n, loadLocaleMessages, loadLocalesMap, setupI18n };
+export {
+  $t,
+  i18n,
+  loadLocaleMessages,
+  loadLocalesMap,
+  loadLocalesMapFromDir,
+  setupI18n,
+};
 export {
   type ImportLocaleFn,
   type LocaleSetupOptions,

+ 0 - 339
packages/locales/src/langs/en-US.json

@@ -1,339 +0,0 @@
-{
-  "page": {
-    "core": {
-      "login": "Login",
-      "register": "Register",
-      "codeLogin": "Code Login",
-      "qrcodeLogin": "Qr Code Login",
-      "forgetPassword": "Forget Password"
-    },
-    "dashboard": {
-      "title": "Dashboard",
-      "analytics": "Analytics",
-      "workspace": "Workspace"
-    },
-    "vben": {
-      "title": "Project",
-      "about": "About",
-      "document": "Document",
-      "antdv": "Ant Design Vue Version",
-      "naive-ui": "Naive UI Version",
-      "element-plus": "Element Plus Version"
-    }
-  },
-  "common": {
-    "back": "Back",
-    "backToHome": "Back To Home",
-    "login": "Login",
-    "logout": "Logout",
-    "prompt": "Prompt",
-    "cancel": "Cancel",
-    "confirm": "Comfirm",
-    "noData": "No Data",
-    "refresh": "Refresh",
-    "loadingMenu": "Loading Menu",
-    "query": "Search"
-  },
-  "fallback": {
-    "pageNotFound": "Oops! Page Not Found",
-    "pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.",
-    "forbidden": "Oops! Access Denied",
-    "forbiddenDesc": "Sorry, but you don't have permission to access this page.",
-    "internalError": "Oops! Something Went Wrong",
-    "internalErrorDesc": "Sorry, but the server encountered an error.",
-    "offline": "Offline Page",
-    "offlineError": "Oops! Network Error",
-    "offlineErrorDesc": "Sorry, can't connect to the internet. Check your connection.",
-    "comingSoon": "Coming Soon",
-    "http": {
-      "requestTimeout": "The request timed out. Please try again later.",
-      "networkError": "A network error occurred. Please check your internet connection and try again.",
-      "badRequest": "Bad Request. Please check your input and try again.",
-      "unauthorized": "Unauthorized. Please log in to continue.",
-      "forbidden": "Forbidden. You do not have permission to access this resource.",
-      "notFound": "Not Found. The requested resource could not be found.",
-      "internalServerError": "Internal Server Error. Something went wrong on our end. Please try again later."
-    }
-  },
-  "formRules": {
-    "required": "Please enter {0}",
-    "selectRequired": "Please select {0}"
-  },
-  "placeholder": {
-    "input": "Please enter",
-    "select": "Please select"
-  },
-  "widgets": {
-    "document": "Document",
-    "qa": "Q&A",
-    "setting": "Settings",
-    "logoutTip": "Do you want to logout?",
-    "viewAll": "View All Messages",
-    "notifications": "Notifications",
-    "markAllAsRead": "Make All as Read",
-    "clearNotifications": "Clear",
-    "checkUpdatesTitle": "New Version Available",
-    "checkUpdatesDescription": "Click to refresh and get the latest version",
-    "search": {
-      "title": "Search",
-      "searchNavigate": "Search Navigation",
-      "select": "Select",
-      "navigate": "Navigate",
-      "close": "Close",
-      "noResults": "No Search Results Found",
-      "noRecent": "No Search History",
-      "recent": "Search History"
-    },
-    "lockScreen": {
-      "title": "Lock Screen",
-      "screenButton": "Locking",
-      "password": "Password",
-      "placeholder": "Please enter password",
-      "unlock": "Click to unlock",
-      "errorPasswordTip": "Password error, please re-enter",
-      "backToLogin": "Back to login",
-      "entry": "Enter the system"
-    }
-  },
-  "authentication": {
-    "welcomeBack": "Welcome Back",
-    "pageTitle": "Plug-and-play Admin system",
-    "pageDesc": "Efficient, versatile frontend template",
-    "loginSuccess": "Login Successful",
-    "loginSuccessDesc": "Welcome Back",
-    "loginSubtitle": "Enter your account details to manage your projects",
-    "selectAccount": "Quick Select Account",
-    "username": "Username",
-    "password": "Password",
-    "usernameTip": "Please enter username",
-    "passwordErrorTip": "Password is incorrect",
-    "passwordTip": "Please enter password",
-    "verifyRequiredTip": "Please complete the verification first",
-    "rememberMe": "Remember Me",
-    "createAnAccount": "Create an Account",
-    "createAccount": "Create Account",
-    "alreadyHaveAccount": "Already have an account?",
-    "accountTip": "Don't have an account?",
-    "signUp": "Sign Up",
-    "signUpSubtitle": "Make managing your applications simple and fun",
-    "confirmPassword": "Comfirm Password",
-    "confirmPasswordTip": "The passwords do not match",
-    "agree": "I agree to",
-    "privacyPolicy": "Privacy-policy",
-    "terms": "Terms",
-    "agreeTip": "Please agree to the Privacy Policy and Terms",
-    "goToLogin": "Login instead",
-    "passwordStrength": "Use 8 or more characters with a mix of letters, numbers & symbols",
-    "forgetPassword": "Forget Password?",
-    "forgetPasswordSubtitle": "Enter your email and we'll send you instructions to reset your password",
-    "emailTip": "Please enter email",
-    "emailValidErrorTip": "The email format you entered is incorrect",
-    "sendResetLink": "Send Reset Link",
-    "email": "Email",
-    "qrcodeSubtitle": "Scan the QR code with your phone to login",
-    "qrcodePrompt": "Click 'Confirm' after scanning to complete login",
-    "qrcodeLogin": "QR Code Login",
-    "codeSubtitle": "Enter your phone number to start managing your project",
-    "code": "Security code",
-    "codeTip": "Security code is required",
-    "mobile": "Mobile",
-    "mobileLogin": "Mobile Login",
-    "mobileTip": "Please enter mobile number",
-    "mobileErrortip": "The phone number format is incorrect",
-    "sendCode": "Get Security code",
-    "sendText": "Resend in {0}s",
-    "thirdPartyLogin": "Or continue with",
-    "loginAgainTitle": "Please Log In Again",
-    "loginAgainSubTitle": "Your login session has expired. Please log in again to continue.",
-    "layout": {
-      "center": "Align Center",
-      "alignLeft": "Align Left",
-      "alignRight": "Align Right"
-    }
-  },
-  "preferences": {
-    "title": "Preferences",
-    "subtitle": "Customize Preferences & Preview in Real Time",
-    "resetTip": "Data has changed, click to reset",
-    "resetTitle": "Reset Preferences",
-    "resetSuccess": "Preferences reset successfully",
-    "appearance": "Appearance",
-    "layout": "Layout",
-    "content": "Content",
-    "other": "Other",
-    "wide": "Wide",
-    "compact": "Fixed",
-    "followSystem": "Follow System",
-    "vertical": "Vertical",
-    "verticalTip": "Side vertical menu mode",
-    "horizontal": "Horizontal",
-    "horizontalTip": "Horizontal menu mode, all menus displayed at the top",
-    "twoColumn": "Two Column",
-    "twoColumnTip": "Vertical Two Column Menu Mode",
-    "mixedMenu": "Mixed Menu",
-    "mixedMenuTip": "Vertical & Horizontal Menu Co-exists",
-    "fullContent": "Full Content",
-    "fullContentTip": "Only display content body, hide all menus",
-    "normal": "Normal",
-    "plain": "Plain",
-    "rounded": "Rounded",
-    "copyPreferences": "Copy Preferences",
-    "copyPreferencesSuccessTitle": "Copy successful",
-    "copyPreferencesSuccess": "Copy successful, please override in `src/preferences.ts` under app",
-    "clearAndLogout": "Clear Cache & Logout",
-    "mode": "Mode",
-    "general": "General",
-    "language": "Language",
-    "dynamicTitle": "Dynamic Title",
-    "watermark": "Watermark",
-    "checkUpdates": "Periodic update check",
-    "position": {
-      "title": "Preferences Postion",
-      "header": "Header",
-      "auto": "Auto",
-      "fixed": "Fixed"
-    },
-    "sidebar": {
-      "title": "Sidebar",
-      "width": "Width",
-      "visible": "Show Sidebar",
-      "collapsed": "Collpase Menu",
-      "collapsedShowTitle": "Show Menu Title"
-    },
-    "tabbar": {
-      "title": "Tabbar",
-      "enable": "Enable Tab Bar",
-      "icon": "Show Tabbar Icon",
-      "showMore": "Show More Button",
-      "showMaximize": "Show Maximize Button",
-      "persist": "Persist Tabs",
-      "draggable": "Enable Draggable Sort",
-      "styleType": {
-        "title": "Tabs Style",
-        "chrome": "Chrome",
-        "card": "Card",
-        "plain": "Plain",
-        "brisk": "Brisk"
-      },
-      "contextMenu": {
-        "reload": "Reload",
-        "close": "Close",
-        "pin": "Pin",
-        "unpin": "Unpin",
-        "closeLeft": "Close Left Tabs",
-        "closeRight": "Close Right Tabs",
-        "closeOther": "Close Other Tabs",
-        "closeAll": "Close All Tabs",
-        "openInNewWindow": "Open in New Window",
-        "maximize": "Maximize",
-        "restoreMaximize": "Restore"
-      }
-    },
-    "navigationMenu": {
-      "title": "Navigation Menu",
-      "style": "Navigation Menu Style",
-      "accordion": "Sidebar Accordion Menu",
-      "split": "Navigation Menu Separation",
-      "splitTip": "When enabled, the sidebar displays the top bar's submenu"
-    },
-    "breadcrumb": {
-      "title": "Breadcrumb",
-      "home": "Show Home Button",
-      "enable": "Enable Breadcrumb",
-      "icon": "Show Breadcrumb Icon",
-      "background": "background",
-      "style": "Breadcrumb Style",
-      "hideOnlyOne": "Hidden when only one"
-    },
-    "animation": {
-      "title": "Animation",
-      "loading": "Page Loading",
-      "transition": "Page Transition",
-      "progress": "Page Progress"
-    },
-    "theme": {
-      "title": "Theme",
-      "radius": "Radius",
-      "light": "Light",
-      "dark": "Dark",
-      "darkSidebar": "Semi Dark Sidebar",
-      "darkHeader": "Semi Dark Header",
-      "weakMode": "Weak Mode",
-      "grayMode": "Gray Mode",
-      "builtin": {
-        "title": "Built-in",
-        "default": "Default",
-        "violet": "Violet",
-        "pink": "Pink",
-        "rose": "Rose",
-        "skyBlue": "Sky Blue",
-        "deepBlue": "Deep Blue",
-        "green": "Green",
-        "deepGreen": "Deep Green",
-        "orange": "Orange",
-        "yellow": "Yellow",
-        "zinc": "Zinc",
-        "neutral": "Neutral",
-        "slate": "Slate",
-        "gray": "Gray",
-        "custom": "Custom"
-      }
-    },
-    "header": {
-      "title": "Header",
-      "visible": "Show Header",
-      "modeStatic": "Static",
-      "modeFixed": "Fixed",
-      "modeAuto": "Auto hide & Show",
-      "modeAutoScroll": "Scroll to Hide & Show"
-    },
-    "footer": {
-      "title": "Footer",
-      "visible": "Show Footer",
-      "fixed": "Fixed at Bottom"
-    },
-    "copyright": {
-      "title": "Copyright",
-      "enable": "Enable Copyright",
-      "companyName": "Company Name",
-      "companySiteLink": "Company Site Link",
-      "date": "Date",
-      "icp": "ICP License Number",
-      "icpLink": "ICP Site Link"
-    },
-    "shortcutKeys": {
-      "title": "Shortcut Keys",
-      "global": "Global",
-      "search": "Global Search",
-      "logout": "Logout",
-      "preferences": "Preferences"
-    },
-    "widget": {
-      "title": "Widget",
-      "globalSearch": "Enable Global Search",
-      "fullscreen": "Enable Fullscreen",
-      "themeToggle": "Enable Theme Toggle",
-      "languageToggle": "Enable Language Toggle",
-      "notification": "Enable Notification",
-      "sidebarToggle": "Enable Sidebar Toggle",
-      "lockScreen": "Enable Lock Screen",
-      "refresh": "Enable Refresh"
-    }
-  },
-  "ui": {
-    "captcha": {
-      "title": "Please complete the security verification",
-      "sliderSuccessText": "Passed",
-      "sliderDefaultText": "Slider and drag",
-      "alt": "Supports img tag src attribute value",
-      "sliderRotateDefaultTip": "Click picture to refresh",
-      "sliderRotateFailTip": "Validation failed",
-      "sliderRotateSuccessTip": "Validation successful, time {0} seconds",
-      "refreshAriaLabel": "Refresh captcha",
-      "confirmAriaLabel": "Confirm selection",
-      "confirm": "Confirm",
-      "pointAriaLabel": "Click point",
-      "clickInOrder": "Please click in order"
-    }
-  }
-}

+ 56 - 0
packages/locales/src/langs/en-US/authentication.json

@@ -0,0 +1,56 @@
+{
+  "welcomeBack": "Welcome Back",
+  "pageTitle": "Plug-and-play Admin system",
+  "pageDesc": "Efficient, versatile frontend template",
+  "loginSuccess": "Login Successful",
+  "loginSuccessDesc": "Welcome Back",
+  "loginSubtitle": "Enter your account details to manage your projects",
+  "selectAccount": "Quick Select Account",
+  "username": "Username",
+  "password": "Password",
+  "usernameTip": "Please enter username",
+  "passwordErrorTip": "Password is incorrect",
+  "passwordTip": "Please enter password",
+  "verifyRequiredTip": "Please complete the verification first",
+  "rememberMe": "Remember Me",
+  "createAnAccount": "Create an Account",
+  "createAccount": "Create Account",
+  "alreadyHaveAccount": "Already have an account?",
+  "accountTip": "Don't have an account?",
+  "signUp": "Sign Up",
+  "signUpSubtitle": "Make managing your applications simple and fun",
+  "confirmPassword": "Confirm Password",
+  "confirmPasswordTip": "The passwords do not match",
+  "agree": "I agree to",
+  "privacyPolicy": "Privacy-policy",
+  "terms": "Terms",
+  "agreeTip": "Please agree to the Privacy Policy and Terms",
+  "goToLogin": "Login instead",
+  "passwordStrength": "Use 8 or more characters with a mix of letters, numbers & symbols",
+  "forgetPassword": "Forget Password?",
+  "forgetPasswordSubtitle": "Enter your email and we'll send you instructions to reset your password",
+  "emailTip": "Please enter email",
+  "emailValidErrorTip": "The email format you entered is incorrect",
+  "sendResetLink": "Send Reset Link",
+  "email": "Email",
+  "qrcodeSubtitle": "Scan the QR code with your phone to login",
+  "qrcodePrompt": "Click 'Confirm' after scanning to complete login",
+  "qrcodeLogin": "QR Code Login",
+  "codeSubtitle": "Enter your phone number to start managing your project",
+  "code": "Security code",
+  "codeTip": "Security code is required",
+  "mobile": "Mobile",
+  "mobileLogin": "Mobile Login",
+  "mobileTip": "Please enter mobile number",
+  "mobileErrortip": "The phone number format is incorrect",
+  "sendCode": "Get Security code",
+  "sendText": "Resend in {0}s",
+  "thirdPartyLogin": "Or continue with",
+  "loginAgainTitle": "Please Log In Again",
+  "loginAgainSubTitle": "Your login session has expired. Please log in again to continue.",
+  "layout": {
+    "center": "Align Center",
+    "alignLeft": "Align Left",
+    "alignRight": "Align Right"
+  }
+}

+ 13 - 0
packages/locales/src/langs/en-US/common.json

@@ -0,0 +1,13 @@
+{
+  "back": "Back",
+  "backToHome": "Back To Home",
+  "login": "Login",
+  "logout": "Logout",
+  "prompt": "Prompt",
+  "cancel": "Cancel",
+  "confirm": "Confirm",
+  "noData": "No Data",
+  "refresh": "Refresh",
+  "loadingMenu": "Loading Menu",
+  "query": "Search"
+}

+ 169 - 0
packages/locales/src/langs/en-US/preferences.json

@@ -0,0 +1,169 @@
+{
+  "title": "Preferences",
+  "subtitle": "Customize Preferences & Preview in Real Time",
+  "resetTip": "Data has changed, click to reset",
+  "resetTitle": "Reset Preferences",
+  "resetSuccess": "Preferences reset successfully",
+  "appearance": "Appearance",
+  "layout": "Layout",
+  "content": "Content",
+  "other": "Other",
+  "wide": "Wide",
+  "compact": "Fixed",
+  "followSystem": "Follow System",
+  "vertical": "Vertical",
+  "verticalTip": "Side vertical menu mode",
+  "horizontal": "Horizontal",
+  "horizontalTip": "Horizontal menu mode, all menus displayed at the top",
+  "twoColumn": "Two Column",
+  "twoColumnTip": "Vertical Two Column Menu Mode",
+  "mixedMenu": "Mixed Menu",
+  "mixedMenuTip": "Vertical & Horizontal Menu Co-exists",
+  "fullContent": "Full Content",
+  "fullContentTip": "Only display content body, hide all menus",
+  "normal": "Normal",
+  "plain": "Plain",
+  "rounded": "Rounded",
+  "copyPreferences": "Copy Preferences",
+  "copyPreferencesSuccessTitle": "Copy successful",
+  "copyPreferencesSuccess": "Copy successful, please override in `src/preferences.ts` under app",
+  "clearAndLogout": "Clear Cache & Logout",
+  "mode": "Mode",
+  "general": "General",
+  "language": "Language",
+  "dynamicTitle": "Dynamic Title",
+  "watermark": "Watermark",
+  "checkUpdates": "Periodic update check",
+  "position": {
+    "title": "Preferences Postion",
+    "header": "Header",
+    "auto": "Auto",
+    "fixed": "Fixed"
+  },
+  "sidebar": {
+    "title": "Sidebar",
+    "width": "Width",
+    "visible": "Show Sidebar",
+    "collapsed": "Collpase Menu",
+    "collapsedShowTitle": "Show Menu Title"
+  },
+  "tabbar": {
+    "title": "Tabbar",
+    "enable": "Enable Tab Bar",
+    "icon": "Show Tabbar Icon",
+    "showMore": "Show More Button",
+    "showMaximize": "Show Maximize Button",
+    "persist": "Persist Tabs",
+    "draggable": "Enable Draggable Sort",
+    "styleType": {
+      "title": "Tabs Style",
+      "chrome": "Chrome",
+      "card": "Card",
+      "plain": "Plain",
+      "brisk": "Brisk"
+    },
+    "contextMenu": {
+      "reload": "Reload",
+      "close": "Close",
+      "pin": "Pin",
+      "unpin": "Unpin",
+      "closeLeft": "Close Left Tabs",
+      "closeRight": "Close Right Tabs",
+      "closeOther": "Close Other Tabs",
+      "closeAll": "Close All Tabs",
+      "openInNewWindow": "Open in New Window",
+      "maximize": "Maximize",
+      "restoreMaximize": "Restore"
+    }
+  },
+  "navigationMenu": {
+    "title": "Navigation Menu",
+    "style": "Navigation Menu Style",
+    "accordion": "Sidebar Accordion Menu",
+    "split": "Navigation Menu Separation",
+    "splitTip": "When enabled, the sidebar displays the top bar's submenu"
+  },
+  "breadcrumb": {
+    "title": "Breadcrumb",
+    "home": "Show Home Button",
+    "enable": "Enable Breadcrumb",
+    "icon": "Show Breadcrumb Icon",
+    "background": "background",
+    "style": "Breadcrumb Style",
+    "hideOnlyOne": "Hidden when only one"
+  },
+  "animation": {
+    "title": "Animation",
+    "loading": "Page Loading",
+    "transition": "Page Transition",
+    "progress": "Page Progress"
+  },
+  "theme": {
+    "title": "Theme",
+    "radius": "Radius",
+    "light": "Light",
+    "dark": "Dark",
+    "darkSidebar": "Semi Dark Sidebar",
+    "darkHeader": "Semi Dark Header",
+    "weakMode": "Weak Mode",
+    "grayMode": "Gray Mode",
+    "builtin": {
+      "title": "Built-in",
+      "default": "Default",
+      "violet": "Violet",
+      "pink": "Pink",
+      "rose": "Rose",
+      "skyBlue": "Sky Blue",
+      "deepBlue": "Deep Blue",
+      "green": "Green",
+      "deepGreen": "Deep Green",
+      "orange": "Orange",
+      "yellow": "Yellow",
+      "zinc": "Zinc",
+      "neutral": "Neutral",
+      "slate": "Slate",
+      "gray": "Gray",
+      "custom": "Custom"
+    }
+  },
+  "header": {
+    "title": "Header",
+    "visible": "Show Header",
+    "modeStatic": "Static",
+    "modeFixed": "Fixed",
+    "modeAuto": "Auto hide & Show",
+    "modeAutoScroll": "Scroll to Hide & Show"
+  },
+  "footer": {
+    "title": "Footer",
+    "visible": "Show Footer",
+    "fixed": "Fixed at Bottom"
+  },
+  "copyright": {
+    "title": "Copyright",
+    "enable": "Enable Copyright",
+    "companyName": "Company Name",
+    "companySiteLink": "Company Site Link",
+    "date": "Date",
+    "icp": "ICP License Number",
+    "icpLink": "ICP Site Link"
+  },
+  "shortcutKeys": {
+    "title": "Shortcut Keys",
+    "global": "Global",
+    "search": "Global Search",
+    "logout": "Logout",
+    "preferences": "Preferences"
+  },
+  "widget": {
+    "title": "Widget",
+    "globalSearch": "Enable Global Search",
+    "fullscreen": "Enable Fullscreen",
+    "themeToggle": "Enable Theme Toggle",
+    "languageToggle": "Enable Language Toggle",
+    "notification": "Enable Notification",
+    "sidebarToggle": "Enable Sidebar Toggle",
+    "lockScreen": "Enable Lock Screen",
+    "refresh": "Enable Refresh"
+  }
+}

+ 77 - 0
packages/locales/src/langs/en-US/ui.json

@@ -0,0 +1,77 @@
+{
+  "formRules": {
+    "required": "Please enter {0}",
+    "selectRequired": "Please select {0}"
+  },
+  "placeholder": {
+    "input": "Please enter",
+    "select": "Please select"
+  },
+  "captcha": {
+    "title": "Please complete the security verification",
+    "sliderSuccessText": "Passed",
+    "sliderDefaultText": "Slider and drag",
+    "alt": "Supports img tag src attribute value",
+    "sliderRotateDefaultTip": "Click picture to refresh",
+    "sliderRotateFailTip": "Validation failed",
+    "sliderRotateSuccessTip": "Validation successful, time {0} seconds",
+    "refreshAriaLabel": "Refresh captcha",
+    "confirmAriaLabel": "Confirm selection",
+    "confirm": "Confirm",
+    "pointAriaLabel": "Click point",
+    "clickInOrder": "Please click in order"
+  },
+  "fallback": {
+    "pageNotFound": "Oops! Page Not Found",
+    "pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.",
+    "forbidden": "Oops! Access Denied",
+    "forbiddenDesc": "Sorry, but you don't have permission to access this page.",
+    "internalError": "Oops! Something Went Wrong",
+    "internalErrorDesc": "Sorry, but the server encountered an error.",
+    "offline": "Offline Page",
+    "offlineError": "Oops! Network Error",
+    "offlineErrorDesc": "Sorry, can't connect to the internet. Check your connection.",
+    "comingSoon": "Coming Soon",
+    "http": {
+      "requestTimeout": "The request timed out. Please try again later.",
+      "networkError": "A network error occurred. Please check your internet connection and try again.",
+      "badRequest": "Bad Request. Please check your input and try again.",
+      "unauthorized": "Unauthorized. Please log in to continue.",
+      "forbidden": "Forbidden. You do not have permission to access this resource.",
+      "notFound": "Not Found. The requested resource could not be found.",
+      "internalServerError": "Internal Server Error. Something went wrong on our end. Please try again later."
+    }
+  },
+  "widgets": {
+    "document": "Document",
+    "qa": "Q&A",
+    "setting": "Settings",
+    "logoutTip": "Do you want to logout?",
+    "viewAll": "View All Messages",
+    "notifications": "Notifications",
+    "markAllAsRead": "Make All as Read",
+    "clearNotifications": "Clear",
+    "checkUpdatesTitle": "New Version Available",
+    "checkUpdatesDescription": "Click to refresh and get the latest version",
+    "search": {
+      "title": "Search",
+      "searchNavigate": "Search Navigation",
+      "select": "Select",
+      "navigate": "Navigate",
+      "close": "Close",
+      "noResults": "No Search Results Found",
+      "noRecent": "No Search History",
+      "recent": "Search History"
+    },
+    "lockScreen": {
+      "title": "Lock Screen",
+      "screenButton": "Locking",
+      "password": "Password",
+      "placeholder": "Please enter password",
+      "unlock": "Click to unlock",
+      "errorPasswordTip": "Password error, please re-enter",
+      "backToLogin": "Back to login",
+      "entry": "Enter the system"
+    }
+  }
+}

+ 0 - 339
packages/locales/src/langs/zh-CN.json

@@ -1,339 +0,0 @@
-{
-  "page": {
-    "core": {
-      "login": "登陆",
-      "register": "注册",
-      "codeLogin": "验证码登陆",
-      "qrcodeLogin": "二维码登陆",
-      "forgetPassword": "忘记密码"
-    },
-    "dashboard": {
-      "title": "概览",
-      "analytics": "分析页",
-      "workspace": "工作台"
-    },
-    "vben": {
-      "title": "项目",
-      "about": "关于",
-      "document": "文档",
-      "antdv": "Ant Design Vue 版本",
-      "naive-ui": "Naive UI 版本",
-      "element-plus": "Element Plus 版本"
-    }
-  },
-  "common": {
-    "back": "返回",
-    "backToHome": "返回首页",
-    "login": "登录",
-    "logout": "退出登录",
-    "prompt": "提示",
-    "cancel": "取消",
-    "confirm": "确认",
-    "noData": "暂无数据",
-    "refresh": "刷新",
-    "loadingMenu": "加载菜单中",
-    "query": "查询"
-  },
-  "fallback": {
-    "pageNotFound": "哎呀!未找到页面",
-    "pageNotFoundDesc": "抱歉,我们无法找到您要找的页面。",
-    "forbidden": "哎呀!访问被拒绝",
-    "forbiddenDesc": "抱歉,您没有权限访问此页面。",
-    "internalError": "哎呀!出错了",
-    "internalErrorDesc": "抱歉,服务器遇到错误。",
-    "offline": "离线页面",
-    "offlineError": "哎呀!网络错误",
-    "offlineErrorDesc": "抱歉,无法连接到互联网,请检查您的网络连接并重试。",
-    "comingSoon": "即将推出",
-    "http": {
-      "requestTimeout": "请求超时,请稍后再试。",
-      "networkError": "网络异常,请检查您的网络连接后重试。",
-      "badRequest": "请求错误。请检查您的输入并重试。",
-      "unauthorized": "登录认证过期,请重新登录后继续。",
-      "forbidden": "禁止访问, 您没有权限访问此资源。",
-      "notFound": "未找到, 请求的资源不存在。",
-      "internalServerError": "内部服务器错误,请稍后再试。"
-    }
-  },
-  "formRules": {
-    "required": "请输入{0}",
-    "selectRequired": "请选择{0}"
-  },
-  "placeholder": {
-    "input": "请输入",
-    "select": "请选择"
-  },
-  "widgets": {
-    "document": "文档",
-    "qa": "问题 & 帮助",
-    "setting": "设置",
-    "logoutTip": "是否退出登录?",
-    "viewAll": "查看所有消息",
-    "notifications": "通知",
-    "markAllAsRead": "全部标记为已读",
-    "clearNotifications": "清空",
-    "checkUpdatesTitle": "新版本可用",
-    "checkUpdatesDescription": "点击刷新以获取最新版本",
-    "search": {
-      "title": "搜索",
-      "searchNavigate": "搜索导航菜单",
-      "select": "选择",
-      "navigate": "导航",
-      "close": "关闭",
-      "noResults": "未找到搜索结果",
-      "noRecent": "没有搜索历史",
-      "recent": "搜索历史"
-    },
-    "lockScreen": {
-      "title": "锁定屏幕",
-      "screenButton": "锁定",
-      "password": "密码",
-      "placeholder": "请输入锁屏密码",
-      "unlock": "点击解锁",
-      "errorPasswordTip": "密码错误,请重新输入",
-      "backToLogin": "返回登录",
-      "entry": "进入系统"
-    }
-  },
-  "authentication": {
-    "welcomeBack": "欢迎回来",
-    "pageTitle": "开箱即用的大型中后台管理系统",
-    "pageDesc": "工程化、高性能、跨组件库的前端模版",
-    "loginSuccess": "登录成功",
-    "loginSuccessDesc": "欢迎回来",
-    "loginSubtitle": "请输入您的帐户信息以开始管理您的项目",
-    "selectAccount": "快速选择账号",
-    "username": "账号",
-    "password": "密码",
-    "usernameTip": "请输入用户名",
-    "passwordTip": "请输入密码",
-    "verifyRequiredTip": "请先完成验证",
-    "passwordErrorTip": "密码错误",
-    "rememberMe": "记住账号",
-    "createAnAccount": "创建一个账号",
-    "createAccount": "创建账号",
-    "alreadyHaveAccount": "已经有账号了?",
-    "accountTip": "还没有账号?",
-    "signUp": "注册",
-    "signUpSubtitle": "让您的应用程序管理变得简单而有趣",
-    "confirmPassword": "确认密码",
-    "confirmPasswordTip": "两次输入的密码不一致",
-    "agree": "我同意",
-    "privacyPolicy": "隐私政策",
-    "terms": "条款",
-    "agreeTip": "请同意隐私政策和条款",
-    "goToLogin": "去登录",
-    "passwordStrength": "使用 8 个或更多字符,混合字母、数字和符号",
-    "forgetPassword": "忘记密码?",
-    "forgetPasswordSubtitle": "输入您的电子邮件,我们将向您发送重置密码的连接",
-    "emailTip": "请输入邮箱",
-    "emailValidErrorTip": "你输入的邮箱格式不正确",
-    "sendResetLink": "发送重置链接",
-    "email": "邮箱",
-    "qrcodeSubtitle": "请用手机扫描二维码登录",
-    "qrcodePrompt": "扫码后点击 '确认',即可完成登录",
-    "qrcodeLogin": "扫码登录",
-    "codeSubtitle": "请输入您的手机号码以开始管理您的项目",
-    "code": "验证码",
-    "codeTip": "请输入验证码",
-    "mobile": "手机号码",
-    "mobileTip": "请输入手机号",
-    "mobileErrortip": "手机号码格式错误",
-    "mobileLogin": "手机号登录",
-    "sendCode": "获取验证码",
-    "sendText": "{0}秒后重新获取",
-    "thirdPartyLogin": "其他登录方式",
-    "loginAgainTitle": "重新登录",
-    "loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。",
-    "layout": {
-      "center": "居中",
-      "alignLeft": "居左",
-      "alignRight": "居右"
-    }
-  },
-  "preferences": {
-    "title": "偏好设置",
-    "subtitle": "自定义偏好设置 & 实时预览",
-    "resetTitle": "重置偏好设置",
-    "resetTip": "数据有变化,点击可进行重置",
-    "resetSuccess": "重置偏好设置成功",
-    "appearance": "外观",
-    "layout": "布局",
-    "content": "内容",
-    "other": "其它",
-    "wide": "流式",
-    "compact": "定宽",
-    "followSystem": "跟随系统",
-    "vertical": "垂直",
-    "verticalTip": "侧边垂直菜单模式",
-    "horizontal": "水平",
-    "horizontalTip": "水平菜单模式,菜单全部显示在顶部",
-    "twoColumn": "双列菜单",
-    "twoColumnTip": "垂直双列菜单模式",
-    "mixedMenu": "混合菜单",
-    "mixedMenuTip": "垂直水平菜单共存",
-    "fullContent": "内容全屏",
-    "fullContentTip": "不显示任何菜单,只显示内容主体",
-    "normal": "常规",
-    "plain": "朴素",
-    "rounded": "圆润",
-    "copyPreferences": "复制偏好设置",
-    "copyPreferencesSuccessTitle": "复制成功",
-    "copyPreferencesSuccess": "复制成功,请在 app 下的 `src/preferences.ts`内进行覆盖",
-    "clearAndLogout": "清空缓存 & 退出登录",
-    "mode": "模式",
-    "general": "通用",
-    "language": "语言",
-    "dynamicTitle": "动态标题",
-    "watermark": "水印",
-    "checkUpdates": "定时检查更新",
-    "position": {
-      "title": "偏好设置位置",
-      "header": "顶栏",
-      "auto": "自动",
-      "fixed": "固定"
-    },
-    "sidebar": {
-      "title": "侧边栏",
-      "width": "宽度",
-      "visible": "显示侧边栏",
-      "collapsed": "折叠菜单",
-      "collapsedShowTitle": "折叠显示菜单名"
-    },
-    "tabbar": {
-      "title": "标签栏",
-      "enable": "启用标签栏",
-      "icon": "显示标签栏图标",
-      "showMore": "显示更多按钮",
-      "showMaximize": "显示最大化按钮",
-      "persist": "持久化标签页",
-      "draggable": "启动拖拽排序",
-      "styleType": {
-        "title": "标签页风格",
-        "chrome": "谷歌",
-        "card": "卡片",
-        "plain": "朴素",
-        "brisk": "轻快"
-      },
-      "contextMenu": {
-        "reload": "重新加载",
-        "close": "关闭",
-        "pin": "固定",
-        "unpin": "取消固定",
-        "closeLeft": "关闭左侧标签页",
-        "closeRight": "关闭右侧标签页",
-        "closeOther": "关闭其它标签页",
-        "closeAll": "关闭全部标签页",
-        "openInNewWindow": "在新窗口打开",
-        "maximize": "最大化",
-        "restoreMaximize": "还原"
-      }
-    },
-    "navigationMenu": {
-      "title": "导航菜单",
-      "style": "导航菜单风格",
-      "accordion": "侧边导航菜单手风琴模式",
-      "split": "导航菜单分离",
-      "splitTip": "开启时,侧边栏显示顶栏对应菜单的子菜单"
-    },
-    "breadcrumb": {
-      "title": "面包屑导航",
-      "enable": "开启面包屑导航",
-      "icon": "显示面包屑图标",
-      "home": "显示首页按钮",
-      "style": "面包屑风格",
-      "hideOnlyOne": "仅有一个时隐藏",
-      "background": "背景"
-    },
-    "animation": {
-      "title": "动画",
-      "loading": "页面切换 Loading",
-      "transition": "页面切换动画",
-      "progress": "页面切换进度条"
-    },
-    "theme": {
-      "title": "主题",
-      "radius": "圆角",
-      "light": "浅色",
-      "dark": "深色",
-      "darkSidebar": "深色侧边栏",
-      "darkHeader": "深色顶栏",
-      "weakMode": "色弱模式",
-      "grayMode": "灰色模式",
-      "builtin": {
-        "title": "内置主题",
-        "default": "默认",
-        "violet": "紫罗兰",
-        "pink": "樱花粉",
-        "rose": "玫瑰红",
-        "skyBlue": "天蓝色",
-        "deepBlue": "深蓝色",
-        "green": "浅绿色",
-        "deepGreen": "深绿色",
-        "orange": "橙黄色",
-        "yellow": "柠檬黄",
-        "zinc": "锌色灰",
-        "neutral": "中性色",
-        "slate": "石板灰",
-        "gray": "中灰色",
-        "custom": "自定义"
-      }
-    },
-    "header": {
-      "title": "顶栏",
-      "modeStatic": "静止",
-      "modeFixed": "固定",
-      "modeAuto": "自动隐藏和显示",
-      "modeAutoScroll": "滚动隐藏和显示",
-      "visible": "显示顶栏"
-    },
-    "footer": {
-      "title": "底栏",
-      "visible": "显示底栏",
-      "fixed": "固定在底部"
-    },
-    "copyright": {
-      "title": "版权",
-      "enable": "启用版权",
-      "companyName": "公司名",
-      "companySiteLink": "公司主页",
-      "date": "日期",
-      "icp": "ICP 备案号",
-      "icpLink": "ICP 网站链接"
-    },
-    "shortcutKeys": {
-      "title": "快捷键",
-      "global": "全局",
-      "search": "全局搜索",
-      "logout": "退出登录",
-      "preferences": "偏好设置"
-    },
-    "widget": {
-      "title": "小部件",
-      "globalSearch": "启用全局搜索",
-      "fullscreen": "启用全屏",
-      "themeToggle": "启用主题切换",
-      "languageToggle": "启用语言切换",
-      "notification": "启用通知",
-      "sidebarToggle": "启用侧边栏切换",
-      "lockScreen": "启用锁屏",
-      "refresh": "启用刷新"
-    }
-  },
-  "ui": {
-    "captcha": {
-      "title": "请完成安全验证",
-      "sliderSuccessText": "验证通过",
-      "sliderDefaultText": "请按住滑块拖动",
-      "sliderRotateDefaultTip": "点击图片可刷新",
-      "sliderRotateFailTip": "验证失败",
-      "sliderRotateSuccessTip": "验证成功,耗时{0}秒",
-      "alt": "支持img标签src属性值",
-      "refreshAriaLabel": "刷新验证码",
-      "confirmAriaLabel": "确认选择",
-      "confirm": "确认",
-      "pointAriaLabel": "点击点",
-      "clickInOrder": "请依次点击"
-    }
-  }
-}

+ 56 - 0
packages/locales/src/langs/zh-CN/authentication.json

@@ -0,0 +1,56 @@
+{
+  "welcomeBack": "欢迎回来",
+  "pageTitle": "开箱即用的大型中后台管理系统",
+  "pageDesc": "工程化、高性能、跨组件库的前端模版",
+  "loginSuccess": "登录成功",
+  "loginSuccessDesc": "欢迎回来",
+  "loginSubtitle": "请输入您的帐户信息以开始管理您的项目",
+  "selectAccount": "快速选择账号",
+  "username": "账号",
+  "password": "密码",
+  "usernameTip": "请输入用户名",
+  "passwordTip": "请输入密码",
+  "verifyRequiredTip": "请先完成验证",
+  "passwordErrorTip": "密码错误",
+  "rememberMe": "记住账号",
+  "createAnAccount": "创建一个账号",
+  "createAccount": "创建账号",
+  "alreadyHaveAccount": "已经有账号了?",
+  "accountTip": "还没有账号?",
+  "signUp": "注册",
+  "signUpSubtitle": "让您的应用程序管理变得简单而有趣",
+  "confirmPassword": "确认密码",
+  "confirmPasswordTip": "两次输入的密码不一致",
+  "agree": "我同意",
+  "privacyPolicy": "隐私政策",
+  "terms": "条款",
+  "agreeTip": "请同意隐私政策和条款",
+  "goToLogin": "去登录",
+  "passwordStrength": "使用 8 个或更多字符,混合字母、数字和符号",
+  "forgetPassword": "忘记密码?",
+  "forgetPasswordSubtitle": "输入您的电子邮件,我们将向您发送重置密码的连接",
+  "emailTip": "请输入邮箱",
+  "emailValidErrorTip": "你输入的邮箱格式不正确",
+  "sendResetLink": "发送重置链接",
+  "email": "邮箱",
+  "qrcodeSubtitle": "请用手机扫描二维码登录",
+  "qrcodePrompt": "扫码后点击 '确认',即可完成登录",
+  "qrcodeLogin": "扫码登录",
+  "codeSubtitle": "请输入您的手机号码以开始管理您的项目",
+  "code": "验证码",
+  "codeTip": "请输入验证码",
+  "mobile": "手机号码",
+  "mobileTip": "请输入手机号",
+  "mobileErrortip": "手机号码格式错误",
+  "mobileLogin": "手机号登录",
+  "sendCode": "获取验证码",
+  "sendText": "{0}秒后重新获取",
+  "thirdPartyLogin": "其他登录方式",
+  "loginAgainTitle": "重新登录",
+  "loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。",
+  "layout": {
+    "center": "居中",
+    "alignLeft": "居左",
+    "alignRight": "居右"
+  }
+}

+ 13 - 0
packages/locales/src/langs/zh-CN/common.json

@@ -0,0 +1,13 @@
+{
+  "back": "返回",
+  "backToHome": "返回首页",
+  "login": "登录",
+  "logout": "退出登录",
+  "prompt": "提示",
+  "cancel": "取消",
+  "confirm": "确认",
+  "noData": "暂无数据",
+  "refresh": "刷新",
+  "loadingMenu": "加载菜单中",
+  "query": "查询"
+}

+ 169 - 0
packages/locales/src/langs/zh-CN/preferences.json

@@ -0,0 +1,169 @@
+{
+  "title": "偏好设置",
+  "subtitle": "自定义偏好设置 & 实时预览",
+  "resetTitle": "重置偏好设置",
+  "resetTip": "数据有变化,点击可进行重置",
+  "resetSuccess": "重置偏好设置成功",
+  "appearance": "外观",
+  "layout": "布局",
+  "content": "内容",
+  "other": "其它",
+  "wide": "流式",
+  "compact": "定宽",
+  "followSystem": "跟随系统",
+  "vertical": "垂直",
+  "verticalTip": "侧边垂直菜单模式",
+  "horizontal": "水平",
+  "horizontalTip": "水平菜单模式,菜单全部显示在顶部",
+  "twoColumn": "双列菜单",
+  "twoColumnTip": "垂直双列菜单模式",
+  "mixedMenu": "混合菜单",
+  "mixedMenuTip": "垂直水平菜单共存",
+  "fullContent": "内容全屏",
+  "fullContentTip": "不显示任何菜单,只显示内容主体",
+  "normal": "常规",
+  "plain": "朴素",
+  "rounded": "圆润",
+  "copyPreferences": "复制偏好设置",
+  "copyPreferencesSuccessTitle": "复制成功",
+  "copyPreferencesSuccess": "复制成功,请在 app 下的 `src/preferences.ts`内进行覆盖",
+  "clearAndLogout": "清空缓存 & 退出登录",
+  "mode": "模式",
+  "general": "通用",
+  "language": "语言",
+  "dynamicTitle": "动态标题",
+  "watermark": "水印",
+  "checkUpdates": "定时检查更新",
+  "position": {
+    "title": "偏好设置位置",
+    "header": "顶栏",
+    "auto": "自动",
+    "fixed": "固定"
+  },
+  "sidebar": {
+    "title": "侧边栏",
+    "width": "宽度",
+    "visible": "显示侧边栏",
+    "collapsed": "折叠菜单",
+    "collapsedShowTitle": "折叠显示菜单名"
+  },
+  "tabbar": {
+    "title": "标签栏",
+    "enable": "启用标签栏",
+    "icon": "显示标签栏图标",
+    "showMore": "显示更多按钮",
+    "showMaximize": "显示最大化按钮",
+    "persist": "持久化标签页",
+    "draggable": "启动拖拽排序",
+    "styleType": {
+      "title": "标签页风格",
+      "chrome": "谷歌",
+      "card": "卡片",
+      "plain": "朴素",
+      "brisk": "轻快"
+    },
+    "contextMenu": {
+      "reload": "重新加载",
+      "close": "关闭",
+      "pin": "固定",
+      "unpin": "取消固定",
+      "closeLeft": "关闭左侧标签页",
+      "closeRight": "关闭右侧标签页",
+      "closeOther": "关闭其它标签页",
+      "closeAll": "关闭全部标签页",
+      "openInNewWindow": "在新窗口打开",
+      "maximize": "最大化",
+      "restoreMaximize": "还原"
+    }
+  },
+  "navigationMenu": {
+    "title": "导航菜单",
+    "style": "导航菜单风格",
+    "accordion": "侧边导航菜单手风琴模式",
+    "split": "导航菜单分离",
+    "splitTip": "开启时,侧边栏显示顶栏对应菜单的子菜单"
+  },
+  "breadcrumb": {
+    "title": "面包屑导航",
+    "enable": "开启面包屑导航",
+    "icon": "显示面包屑图标",
+    "home": "显示首页按钮",
+    "style": "面包屑风格",
+    "hideOnlyOne": "仅有一个时隐藏",
+    "background": "背景"
+  },
+  "animation": {
+    "title": "动画",
+    "loading": "页面切换 Loading",
+    "transition": "页面切换动画",
+    "progress": "页面切换进度条"
+  },
+  "theme": {
+    "title": "主题",
+    "radius": "圆角",
+    "light": "浅色",
+    "dark": "深色",
+    "darkSidebar": "深色侧边栏",
+    "darkHeader": "深色顶栏",
+    "weakMode": "色弱模式",
+    "grayMode": "灰色模式",
+    "builtin": {
+      "title": "内置主题",
+      "default": "默认",
+      "violet": "紫罗兰",
+      "pink": "樱花粉",
+      "rose": "玫瑰红",
+      "skyBlue": "天蓝色",
+      "deepBlue": "深蓝色",
+      "green": "浅绿色",
+      "deepGreen": "深绿色",
+      "orange": "橙黄色",
+      "yellow": "柠檬黄",
+      "zinc": "锌色灰",
+      "neutral": "中性色",
+      "slate": "石板灰",
+      "gray": "中灰色",
+      "custom": "自定义"
+    }
+  },
+  "header": {
+    "title": "顶栏",
+    "modeStatic": "静止",
+    "modeFixed": "固定",
+    "modeAuto": "自动隐藏和显示",
+    "modeAutoScroll": "滚动隐藏和显示",
+    "visible": "显示顶栏"
+  },
+  "footer": {
+    "title": "底栏",
+    "visible": "显示底栏",
+    "fixed": "固定在底部"
+  },
+  "copyright": {
+    "title": "版权",
+    "enable": "启用版权",
+    "companyName": "公司名",
+    "companySiteLink": "公司主页",
+    "date": "日期",
+    "icp": "ICP 备案号",
+    "icpLink": "ICP 网站链接"
+  },
+  "shortcutKeys": {
+    "title": "快捷键",
+    "global": "全局",
+    "search": "全局搜索",
+    "logout": "退出登录",
+    "preferences": "偏好设置"
+  },
+  "widget": {
+    "title": "小部件",
+    "globalSearch": "启用全局搜索",
+    "fullscreen": "启用全屏",
+    "themeToggle": "启用主题切换",
+    "languageToggle": "启用语言切换",
+    "notification": "启用通知",
+    "sidebarToggle": "启用侧边栏切换",
+    "lockScreen": "启用锁屏",
+    "refresh": "启用刷新"
+  }
+}

+ 77 - 0
packages/locales/src/langs/zh-CN/ui.json

@@ -0,0 +1,77 @@
+{
+  "formRules": {
+    "required": "请输入{0}",
+    "selectRequired": "请选择{0}"
+  },
+  "placeholder": {
+    "input": "请输入",
+    "select": "请选择"
+  },
+  "captcha": {
+    "title": "请完成安全验证",
+    "sliderSuccessText": "验证通过",
+    "sliderDefaultText": "请按住滑块拖动",
+    "sliderRotateDefaultTip": "点击图片可刷新",
+    "sliderRotateFailTip": "验证失败",
+    "sliderRotateSuccessTip": "验证成功,耗时{0}秒",
+    "alt": "支持img标签src属性值",
+    "refreshAriaLabel": "刷新验证码",
+    "confirmAriaLabel": "确认选择",
+    "confirm": "确认",
+    "pointAriaLabel": "点击点",
+    "clickInOrder": "请依次点击"
+  },
+  "fallback": {
+    "pageNotFound": "哎呀!未找到页面",
+    "pageNotFoundDesc": "抱歉,我们无法找到您要找的页面。",
+    "forbidden": "哎呀!访问被拒绝",
+    "forbiddenDesc": "抱歉,您没有权限访问此页面。",
+    "internalError": "哎呀!出错了",
+    "internalErrorDesc": "抱歉,服务器遇到错误。",
+    "offline": "离线页面",
+    "offlineError": "哎呀!网络错误",
+    "offlineErrorDesc": "抱歉,无法连接到互联网,请检查您的网络连接并重试。",
+    "comingSoon": "即将推出",
+    "http": {
+      "requestTimeout": "请求超时,请稍后再试。",
+      "networkError": "网络异常,请检查您的网络连接后重试。",
+      "badRequest": "请求错误。请检查您的输入并重试。",
+      "unauthorized": "登录认证过期,请重新登录后继续。",
+      "forbidden": "禁止访问, 您没有权限访问此资源。",
+      "notFound": "未找到, 请求的资源不存在。",
+      "internalServerError": "内部服务器错误,请稍后再试。"
+    }
+  },
+  "widgets": {
+    "document": "文档",
+    "qa": "问题 & 帮助",
+    "setting": "设置",
+    "logoutTip": "是否退出登录?",
+    "viewAll": "查看所有消息",
+    "notifications": "通知",
+    "markAllAsRead": "全部标记为已读",
+    "clearNotifications": "清空",
+    "checkUpdatesTitle": "新版本可用",
+    "checkUpdatesDescription": "点击刷新以获取最新版本",
+    "search": {
+      "title": "搜索",
+      "searchNavigate": "搜索导航菜单",
+      "select": "选择",
+      "navigate": "导航",
+      "close": "关闭",
+      "noResults": "未找到搜索结果",
+      "noRecent": "没有搜索历史",
+      "recent": "搜索历史"
+    },
+    "lockScreen": {
+      "title": "锁定屏幕",
+      "screenButton": "锁定",
+      "password": "密码",
+      "placeholder": "请输入锁屏密码",
+      "unlock": "点击解锁",
+      "errorPasswordTip": "密码错误,请重新输入",
+      "backToLogin": "返回登录",
+      "entry": "进入系统"
+    }
+  }
+}

+ 1 - 1
playground/src/adapter/component/index.ts

@@ -41,7 +41,7 @@ const withDefaultPlaceholder = <T extends Component>(
   type: 'input' | 'select',
 ) => {
   return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
     return h(component, { ...props, ...attrs, placeholder }, slots);
   };
 };

+ 2 - 2
playground/src/adapter/form.ts

@@ -24,14 +24,14 @@ setupVbenForm<ComponentType>({
     // 输入项目必填国际化适配
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     // 选择项目必填国际化适配
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },

+ 2 - 2
playground/src/layouts/basic.vue

@@ -68,7 +68,7 @@ const menus = computed(() => [
       });
     },
     icon: BookOpenText,
-    text: $t('widgets.document'),
+    text: $t('ui.widgets.document'),
   },
   {
     handler: () => {
@@ -86,7 +86,7 @@ const menus = computed(() => [
       });
     },
     icon: CircleHelp,
-    text: $t('widgets.qa'),
+    text: $t('ui.widgets.qa'),
   },
 ]);
 

+ 10 - 4
playground/src/locales/index.ts

@@ -4,7 +4,11 @@ import type { Locale } from 'ant-design-vue/es/locale';
 import type { App } from 'vue';
 import { ref } from 'vue';
 
-import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import {
+  $t,
+  setupI18n as coreSetup,
+  loadLocalesMapFromDir,
+} from '@vben/locales';
 import { preferences } from '@vben/preferences';
 
 import antdEnLocale from 'ant-design-vue/es/locale/en_US';
@@ -13,10 +17,12 @@ import dayjs from 'dayjs';
 
 const antdLocale = ref<Locale>(antdDefaultLocale);
 
-const modules = import.meta.glob('./langs/*.json');
-
-const localesMap = loadLocalesMap(modules);
+const modules = import.meta.glob('./langs/**/*.json');
 
+const localesMap = loadLocalesMapFromDir(
+  /\.\/langs\/([^/]+)\/(.*)\.json$/,
+  modules,
+);
 /**
  * 加载应用特有的语言包
  * 这里也可以改造为从服务端获取翻译数据

+ 0 - 125
playground/src/locales/langs/en-US.json

@@ -1,125 +0,0 @@
-{
-  "page": {
-    "demos": {
-      "title": "Demos",
-      "access": {
-        "frontendPermissions": "Frontend Permissions",
-        "backendPermissions": "Backend Permissions",
-        "pageAccess": "Page Access",
-        "buttonControl": "Button Control",
-        "menuVisible403": "Menu Visible(403)",
-        "superVisible": "Visible to Super",
-        "adminVisible": "Visible to Admin",
-        "userVisible": "Visible to User"
-      },
-      "nested": {
-        "title": "Nested Menu",
-        "menu1": "Menu 1",
-        "menu2": "Menu 2",
-        "menu2_1": "Menu 2-1",
-        "menu3": "Menu 3",
-        "menu3_1": "Menu 3-1",
-        "menu3_2": "Menu 3-2",
-        "menu3_2_1": "Menu 3-2-1"
-      },
-      "outside": {
-        "title": "External Pages",
-        "embedded": "Embedded",
-        "externalLink": "External Link"
-      },
-      "badge": {
-        "title": "Menu Badge",
-        "dot": "Dot Badge",
-        "text": "Text Badge",
-        "color": "Badge Color"
-      },
-      "activeIcon": {
-        "title": "Active Menu Icon",
-        "children": "Children Active Icon"
-      },
-      "fallback": {
-        "title": "Fallback Page"
-      },
-      "features": {
-        "title": "Features",
-        "hideChildrenInMenu": "Hide Menu Children",
-        "loginExpired": "Login Expired",
-        "icons": "Icons",
-        "watermark": "Watermark",
-        "tabs": "Tabs",
-        "tabDetail": "Tab Detail Page",
-        "fullScreen": {
-          "title": "FullScreen"
-        },
-        "clipboard": "Clipboard"
-      },
-      "breadcrumb": {
-        "navigation": "Breadcrumb Navigation",
-        "lateral": "Lateral Mode",
-        "lateralDetail": "Lateral Mode Detail",
-        "level": "Level Mode",
-        "levelDetail": "Level Mode Detail"
-      }
-    },
-    "examples": {
-      "title": "Examples",
-      "modal": {
-        "title": "Modal"
-      },
-      "drawer": {
-        "title": "Drawer"
-      },
-      "ellipsis": {
-        "title": "EllipsisText"
-      },
-      "form": {
-        "title": "Form",
-        "basic": "Basic Form",
-        "query": "Query Form",
-        "rules": "Form Rules",
-        "dynamic": "Dynamic Form",
-        "custom": "Custom Component",
-        "api": "Api",
-        "merge": "Merge Form"
-      },
-      "vxeTable": {
-        "title": "Vxe Table",
-        "basic": "Basic Table",
-        "remote": "Remote Load",
-        "tree": "Tree Table",
-        "fixed": "Fixed Header/Column",
-        "virtual": "Virtual Scroll",
-        "editCell": "Edit Cell",
-        "editRow": "Edit Row",
-        "custom-cell": "Custom Cell",
-        "form": "Form Table"
-      },
-      "captcha": {
-        "title": "Captcha",
-        "pointSelection": "Point Selection Captcha",
-        "sliderCaptcha": "Slider Captcha",
-        "sliderRotateCaptcha": "Rotate Captcha",
-        "captchaCardTitle": "Please complete the security verification",
-        "pageDescription": "Verify user identity by clicking on specific locations in the image.",
-        "pageTitle": "Captcha Component Example",
-        "basic": "Basic Usage",
-        "titlePlaceholder": "Captcha Title Text",
-        "captchaImageUrlPlaceholder": "Captcha Image (supports img tag src attribute value)",
-        "hintImage": "Hint Image",
-        "hintText": "Hint Text",
-        "hintImagePlaceholder": "Hint Image (supports img tag src attribute value)",
-        "hintTextPlaceholder": "Hint Text",
-        "showConfirm": "Show Confirm",
-        "hideConfirm": "Hide Confirm",
-        "widthPlaceholder": "Captcha Image Width Default 300px",
-        "heightPlaceholder": "Captcha Image Height Default 220px",
-        "paddingXPlaceholder": "Horizontal Padding Default 12px",
-        "paddingYPlaceholder": "Vertical Padding Default 16px",
-        "index": "Index:",
-        "timestamp": "Timestamp:",
-        "x": "x:",
-        "y": "y:"
-      }
-    }
-  }
-}

+ 69 - 0
playground/src/locales/langs/en-US/demos.json

@@ -0,0 +1,69 @@
+{
+  "title": "Demos",
+  "access": {
+    "frontendPermissions": "Frontend Permissions",
+    "backendPermissions": "Backend Permissions",
+    "pageAccess": "Page Access",
+    "buttonControl": "Button Control",
+    "menuVisible403": "Menu Visible(403)",
+    "superVisible": "Visible to Super",
+    "adminVisible": "Visible to Admin",
+    "userVisible": "Visible to User"
+  },
+  "nested": {
+    "title": "Nested Menu",
+    "menu1": "Menu 1",
+    "menu2": "Menu 2",
+    "menu2_1": "Menu 2-1",
+    "menu3": "Menu 3",
+    "menu3_1": "Menu 3-1",
+    "menu3_2": "Menu 3-2",
+    "menu3_2_1": "Menu 3-2-1"
+  },
+  "outside": {
+    "title": "External Pages",
+    "embedded": "Embedded",
+    "externalLink": "External Link"
+  },
+  "badge": {
+    "title": "Menu Badge",
+    "dot": "Dot Badge",
+    "text": "Text Badge",
+    "color": "Badge Color"
+  },
+  "activeIcon": {
+    "title": "Active Menu Icon",
+    "children": "Children Active Icon"
+  },
+  "fallback": {
+    "title": "Fallback Page"
+  },
+  "features": {
+    "title": "Features",
+    "hideChildrenInMenu": "Hide Menu Children",
+    "loginExpired": "Login Expired",
+    "icons": "Icons",
+    "watermark": "Watermark",
+    "tabs": "Tabs",
+    "tabDetail": "Tab Detail Page",
+    "fullScreen": {
+      "title": "FullScreen"
+    },
+    "clipboard": "Clipboard"
+  },
+  "breadcrumb": {
+    "navigation": "Breadcrumb Navigation",
+    "lateral": "Lateral Mode",
+    "lateralDetail": "Lateral Mode Detail",
+    "level": "Level Mode",
+    "levelDetail": "Level Mode Detail"
+  },
+  "vben": {
+    "title": "Project",
+    "about": "About",
+    "document": "Document",
+    "antdv": "Ant Design Vue Version",
+    "naive-ui": "Naive UI Version",
+    "element-plus": "Element Plus Version"
+  }
+}

+ 60 - 0
playground/src/locales/langs/en-US/examples.json

@@ -0,0 +1,60 @@
+{
+  "title": "Examples",
+  "modal": {
+    "title": "Modal"
+  },
+  "drawer": {
+    "title": "Drawer"
+  },
+  "ellipsis": {
+    "title": "EllipsisText"
+  },
+  "form": {
+    "title": "Form",
+    "basic": "Basic Form",
+    "query": "Query Form",
+    "rules": "Form Rules",
+    "dynamic": "Dynamic Form",
+    "custom": "Custom Component",
+    "api": "Api",
+    "merge": "Merge Form"
+  },
+  "vxeTable": {
+    "title": "Vxe Table",
+    "basic": "Basic Table",
+    "remote": "Remote Load",
+    "tree": "Tree Table",
+    "fixed": "Fixed Header/Column",
+    "virtual": "Virtual Scroll",
+    "editCell": "Edit Cell",
+    "editRow": "Edit Row",
+    "custom-cell": "Custom Cell",
+    "form": "Form Table"
+  },
+  "captcha": {
+    "title": "Captcha",
+    "pointSelection": "Point Selection Captcha",
+    "sliderCaptcha": "Slider Captcha",
+    "sliderRotateCaptcha": "Rotate Captcha",
+    "captchaCardTitle": "Please complete the security verification",
+    "pageDescription": "Verify user identity by clicking on specific locations in the image.",
+    "pageTitle": "Captcha Component Example",
+    "basic": "Basic Usage",
+    "titlePlaceholder": "Captcha Title Text",
+    "captchaImageUrlPlaceholder": "Captcha Image (supports img tag src attribute value)",
+    "hintImage": "Hint Image",
+    "hintText": "Hint Text",
+    "hintImagePlaceholder": "Hint Image (supports img tag src attribute value)",
+    "hintTextPlaceholder": "Hint Text",
+    "showConfirm": "Show Confirm",
+    "hideConfirm": "Hide Confirm",
+    "widthPlaceholder": "Captcha Image Width Default 300px",
+    "heightPlaceholder": "Captcha Image Height Default 220px",
+    "paddingXPlaceholder": "Horizontal Padding Default 12px",
+    "paddingYPlaceholder": "Vertical Padding Default 16px",
+    "index": "Index:",
+    "timestamp": "Timestamp:",
+    "x": "x:",
+    "y": "y:"
+  }
+}

+ 14 - 0
playground/src/locales/langs/en-US/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "Login",
+    "register": "Register",
+    "codeLogin": "Code Login",
+    "qrcodeLogin": "Qr Code Login",
+    "forgetPassword": "Forget Password"
+  },
+  "dashboard": {
+    "title": "Dashboard",
+    "analytics": "Analytics",
+    "workspace": "Workspace"
+  }
+}

+ 0 - 125
playground/src/locales/langs/zh-CN.json

@@ -1,125 +0,0 @@
-{
-  "page": {
-    "demos": {
-      "title": "演示",
-      "access": {
-        "frontendPermissions": "前端权限",
-        "backendPermissions": "后端权限",
-        "pageAccess": "页面访问",
-        "buttonControl": "按钮控制",
-        "menuVisible403": "菜单可见(403)",
-        "superVisible": "Super 可见",
-        "adminVisible": "Admin 可见",
-        "userVisible": "User 可见"
-      },
-      "nested": {
-        "title": "嵌套菜单",
-        "menu1": "菜单 1",
-        "menu2": "菜单 2",
-        "menu2_1": "菜单 2-1",
-        "menu3": "菜单 3",
-        "menu3_1": "菜单 3-1",
-        "menu3_2": "菜单 3-2",
-        "menu3_2_1": "菜单 3-2-1"
-      },
-      "outside": {
-        "title": "外部页面",
-        "embedded": "内嵌",
-        "externalLink": "外链"
-      },
-      "badge": {
-        "title": "菜单徽标",
-        "dot": "点徽标",
-        "text": "文本徽标",
-        "color": "徽标颜色"
-      },
-      "activeIcon": {
-        "title": "菜单激活图标",
-        "children": "子级激活图标"
-      },
-      "fallback": {
-        "title": "缺省页"
-      },
-      "features": {
-        "title": "功能",
-        "hideChildrenInMenu": "隐藏子菜单",
-        "loginExpired": "登录过期",
-        "icons": "图标",
-        "watermark": "水印",
-        "tabs": "标签页",
-        "tabDetail": "标签详情页",
-        "fullScreen": {
-          "title": "全屏"
-        },
-        "clipboard": "剪贴板"
-      },
-      "breadcrumb": {
-        "navigation": "面包屑导航",
-        "lateral": "平级模式",
-        "level": "层级模式",
-        "levelDetail": "层级模式详情",
-        "lateralDetail": "平级模式详情"
-      }
-    },
-    "examples": {
-      "title": "示例",
-      "modal": {
-        "title": "弹窗"
-      },
-      "drawer": {
-        "title": "抽屉"
-      },
-      "ellipsis": {
-        "title": "文本省略"
-      },
-      "form": {
-        "title": "表单",
-        "basic": "基础表单",
-        "query": "查询表单",
-        "rules": "表单校验",
-        "dynamic": "动态表单",
-        "custom": "自定义组件",
-        "api": "Api",
-        "merge": "合并表单"
-      },
-      "vxeTable": {
-        "title": "Vxe 表格",
-        "basic": "基础表格",
-        "remote": "远程加载",
-        "tree": "树形表格",
-        "fixed": "固定表头/列",
-        "virtual": "虚拟滚动",
-        "editCell": "单元格编辑",
-        "editRow": "行编辑",
-        "custom-cell": "自定义单元格",
-        "form": "搜索表单"
-      },
-      "captcha": {
-        "title": "验证码",
-        "pointSelection": "点选验证",
-        "sliderCaptcha": "滑块验证",
-        "sliderRotateCaptcha": "旋转验证",
-        "captchaCardTitle": "请完成安全验证",
-        "pageDescription": "通过点击图片中的特定位置来验证用户身份。",
-        "pageTitle": "验证码组件示例",
-        "basic": "基本使用",
-        "titlePlaceholder": "验证码标题文案",
-        "captchaImageUrlPlaceholder": "验证码图片(支持img标签src属性值)",
-        "hintImage": "提示图片",
-        "hintText": "提示文本",
-        "hintImagePlaceholder": "提示图片(支持img标签src属性值)",
-        "hintTextPlaceholder": "提示文本",
-        "showConfirm": "展示确认",
-        "hideConfirm": "隐藏确认",
-        "widthPlaceholder": "验证码图片宽度 默认300px",
-        "heightPlaceholder": "验证码图片高度 默认220px",
-        "paddingXPlaceholder": "水平内边距 默认12px",
-        "paddingYPlaceholder": "垂直内边距 默认16px",
-        "index": "索引:",
-        "timestamp": "时间戳:",
-        "x": "x:",
-        "y": "y:"
-      }
-    }
-  }
-}

+ 69 - 0
playground/src/locales/langs/zh-CN/demos.json

@@ -0,0 +1,69 @@
+{
+  "title": "演示",
+  "access": {
+    "frontendPermissions": "前端权限",
+    "backendPermissions": "后端权限",
+    "pageAccess": "页面访问",
+    "buttonControl": "按钮控制",
+    "menuVisible403": "菜单可见(403)",
+    "superVisible": "Super 可见",
+    "adminVisible": "Admin 可见",
+    "userVisible": "User 可见"
+  },
+  "nested": {
+    "title": "嵌套菜单",
+    "menu1": "菜单 1",
+    "menu2": "菜单 2",
+    "menu2_1": "菜单 2-1",
+    "menu3": "菜单 3",
+    "menu3_1": "菜单 3-1",
+    "menu3_2": "菜单 3-2",
+    "menu3_2_1": "菜单 3-2-1"
+  },
+  "outside": {
+    "title": "外部页面",
+    "embedded": "内嵌",
+    "externalLink": "外链"
+  },
+  "badge": {
+    "title": "菜单徽标",
+    "dot": "点徽标",
+    "text": "文本徽标",
+    "color": "徽标颜色"
+  },
+  "activeIcon": {
+    "title": "菜单激活图标",
+    "children": "子级激活图标"
+  },
+  "fallback": {
+    "title": "缺省页"
+  },
+  "features": {
+    "title": "功能",
+    "hideChildrenInMenu": "隐藏子菜单",
+    "loginExpired": "登录过期",
+    "icons": "图标",
+    "watermark": "水印",
+    "tabs": "标签页",
+    "tabDetail": "标签详情页",
+    "fullScreen": {
+      "title": "全屏"
+    },
+    "clipboard": "剪贴板"
+  },
+  "breadcrumb": {
+    "navigation": "面包屑导航",
+    "lateral": "平级模式",
+    "level": "层级模式",
+    "levelDetail": "层级模式详情",
+    "lateralDetail": "平级模式详情"
+  },
+  "vben": {
+    "title": "项目",
+    "about": "关于",
+    "document": "文档",
+    "antdv": "Ant Design Vue 版本",
+    "naive-ui": "Naive UI 版本",
+    "element-plus": "Element Plus 版本"
+  }
+}

+ 60 - 0
playground/src/locales/langs/zh-CN/examples.json

@@ -0,0 +1,60 @@
+{
+  "title": "示例",
+  "modal": {
+    "title": "弹窗"
+  },
+  "drawer": {
+    "title": "抽屉"
+  },
+  "ellipsis": {
+    "title": "文本省略"
+  },
+  "form": {
+    "title": "表单",
+    "basic": "基础表单",
+    "query": "查询表单",
+    "rules": "表单校验",
+    "dynamic": "动态表单",
+    "custom": "自定义组件",
+    "api": "Api",
+    "merge": "合并表单"
+  },
+  "vxeTable": {
+    "title": "Vxe 表格",
+    "basic": "基础表格",
+    "remote": "远程加载",
+    "tree": "树形表格",
+    "fixed": "固定表头/列",
+    "virtual": "虚拟滚动",
+    "editCell": "单元格编辑",
+    "editRow": "行编辑",
+    "custom-cell": "自定义单元格",
+    "form": "搜索表单"
+  },
+  "captcha": {
+    "title": "验证码",
+    "pointSelection": "点选验证",
+    "sliderCaptcha": "滑块验证",
+    "sliderRotateCaptcha": "旋转验证",
+    "captchaCardTitle": "请完成安全验证",
+    "pageDescription": "通过点击图片中的特定位置来验证用户身份。",
+    "pageTitle": "验证码组件示例",
+    "basic": "基本使用",
+    "titlePlaceholder": "验证码标题文案",
+    "captchaImageUrlPlaceholder": "验证码图片(支持img标签src属性值)",
+    "hintImage": "提示图片",
+    "hintText": "提示文本",
+    "hintImagePlaceholder": "提示图片(支持img标签src属性值)",
+    "hintTextPlaceholder": "提示文本",
+    "showConfirm": "展示确认",
+    "hideConfirm": "隐藏确认",
+    "widthPlaceholder": "验证码图片宽度 默认300px",
+    "heightPlaceholder": "验证码图片高度 默认220px",
+    "paddingXPlaceholder": "水平内边距 默认12px",
+    "paddingYPlaceholder": "垂直内边距 默认16px",
+    "index": "索引:",
+    "timestamp": "时间戳:",
+    "x": "x:",
+    "y": "y:"
+  }
+}

+ 14 - 0
playground/src/locales/langs/zh-CN/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "登陆",
+    "register": "注册",
+    "codeLogin": "验证码登陆",
+    "qrcodeLogin": "二维码登陆",
+    "forgetPassword": "忘记密码"
+  },
+  "dashboard": {
+    "title": "概览",
+    "analytics": "分析页",
+    "workspace": "工作台"
+  }
+}

+ 5 - 5
playground/src/router/routes/core.ts

@@ -43,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'login',
         component: Login,
         meta: {
-          title: $t('page.core.login'),
+          title: $t('page.auth.login'),
         },
       },
       {
@@ -51,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'code-login',
         component: () => import('#/views/_core/authentication/code-login.vue'),
         meta: {
-          title: $t('page.core.codeLogin'),
+          title: $t('page.auth.codeLogin'),
         },
       },
       {
@@ -60,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/qrcode-login.vue'),
         meta: {
-          title: $t('page.core.qrcodeLogin'),
+          title: $t('page.auth.qrcodeLogin'),
         },
       },
       {
@@ -69,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/forget-password.vue'),
         meta: {
-          title: $t('page.core.forgetPassword'),
+          title: $t('page.auth.forgetPassword'),
         },
       },
       {
@@ -77,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'register',
         component: () => import('#/views/_core/authentication/register.vue'),
         meta: {
-          title: $t('page.core.register'),
+          title: $t('page.auth.register'),
         },
       },
     ],

+ 42 - 42
playground/src/router/routes/modules/demos.ts

@@ -10,7 +10,7 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
@@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'mdi:shield-key-outline',
-          title: $t('page.demos.access.frontendPermissions'),
+          title: $t('demos.access.frontendPermissions'),
         },
         name: 'AccessDemos',
         path: '/demos/access',
@@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('#/views/demos/access/index.vue'),
             meta: {
               icon: 'mdi:page-previous-outline',
-              title: $t('page.demos.access.pageAccess'),
+              title: $t('demos.access.pageAccess'),
             },
           },
           {
@@ -39,7 +39,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('#/views/demos/access/button-control.vue'),
             meta: {
               icon: 'mdi:button-cursor',
-              title: $t('page.demos.access.buttonControl'),
+              title: $t('demos.access.buttonControl'),
             },
           },
           {
@@ -51,7 +51,7 @@ const routes: RouteRecordRaw[] = [
               authority: ['no-body'],
               icon: 'mdi:button-cursor',
               menuVisibleWithForbidden: true,
-              title: $t('page.demos.access.menuVisible403'),
+              title: $t('demos.access.menuVisible403'),
             },
           },
           {
@@ -61,7 +61,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               authority: ['super'],
               icon: 'mdi:button-cursor',
-              title: $t('page.demos.access.superVisible'),
+              title: $t('demos.access.superVisible'),
             },
           },
           {
@@ -71,7 +71,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               authority: ['admin'],
               icon: 'mdi:button-cursor',
-              title: $t('page.demos.access.adminVisible'),
+              title: $t('demos.access.adminVisible'),
             },
           },
           {
@@ -81,7 +81,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               authority: ['user'],
               icon: 'mdi:button-cursor',
-              title: $t('page.demos.access.userVisible'),
+              title: $t('demos.access.userVisible'),
             },
           },
         ],
@@ -90,7 +90,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'mdi:feature-highlight',
-          title: $t('page.demos.features.title'),
+          title: $t('demos.features.title'),
         },
         name: 'FeaturesDemos',
         path: '/demos/features',
@@ -102,7 +102,7 @@ const routes: RouteRecordRaw[] = [
               import('#/views/demos/features/login-expired/index.vue'),
             meta: {
               icon: 'mdi:encryption-expiration',
-              title: $t('page.demos.features.loginExpired'),
+              title: $t('demos.features.loginExpired'),
             },
           },
           {
@@ -111,7 +111,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('#/views/demos/features/icons/index.vue'),
             meta: {
               icon: 'lucide:annoyed',
-              title: $t('page.demos.features.icons'),
+              title: $t('demos.features.icons'),
             },
           },
           {
@@ -121,7 +121,7 @@ const routes: RouteRecordRaw[] = [
               import('#/views/demos/features/watermark/index.vue'),
             meta: {
               icon: 'lucide:tags',
-              title: $t('page.demos.features.watermark'),
+              title: $t('demos.features.watermark'),
             },
           },
           {
@@ -130,7 +130,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('#/views/demos/features/tabs/index.vue'),
             meta: {
               icon: 'lucide:app-window',
-              title: $t('page.demos.features.tabs'),
+              title: $t('demos.features.tabs'),
             },
           },
           {
@@ -142,7 +142,7 @@ const routes: RouteRecordRaw[] = [
               activePath: '/demos/features/tabs',
               hideInMenu: true,
               maxNumOfOpenTab: 3,
-              title: $t('page.demos.features.tabDetail'),
+              title: $t('demos.features.tabDetail'),
             },
           },
           {
@@ -153,7 +153,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               hideChildrenInMenu: true,
               icon: 'ic:round-menu',
-              title: $t('page.demos.features.hideChildrenInMenu'),
+              title: $t('demos.features.hideChildrenInMenu'),
             },
             children: [
               {
@@ -163,7 +163,7 @@ const routes: RouteRecordRaw[] = [
                   import(
                     '#/views/demos/features/hide-menu-children/children.vue'
                   ),
-                meta: { title: $t('page.demos.features.hideChildrenInMenu') },
+                meta: { title: $t('demos.features.hideChildrenInMenu') },
               },
             ],
           },
@@ -174,7 +174,7 @@ const routes: RouteRecordRaw[] = [
               import('#/views/demos/features/full-screen/index.vue'),
             meta: {
               icon: 'lucide:fullscreen',
-              title: $t('page.demos.features.fullScreen.title'),
+              title: $t('demos.features.fullScreen.title'),
             },
           },
           {
@@ -184,7 +184,7 @@ const routes: RouteRecordRaw[] = [
               import('#/views/demos/features/clipboard/index.vue'),
             meta: {
               icon: 'lucide:copy',
-              title: $t('page.demos.features.clipboard'),
+              title: $t('demos.features.clipboard'),
             },
           },
           {
@@ -205,7 +205,7 @@ const routes: RouteRecordRaw[] = [
         path: '/demos/breadcrumb',
         meta: {
           icon: 'lucide:navigation',
-          title: $t('page.demos.breadcrumb.navigation'),
+          title: $t('demos.breadcrumb.navigation'),
         },
         children: [
           {
@@ -214,7 +214,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('#/views/demos/breadcrumb/lateral.vue'),
             meta: {
               icon: 'lucide:navigation',
-              title: $t('page.demos.breadcrumb.lateral'),
+              title: $t('demos.breadcrumb.lateral'),
             },
           },
           {
@@ -225,7 +225,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               activePath: '/demos/breadcrumb/lateral',
               hideInMenu: true,
-              title: $t('page.demos.breadcrumb.lateralDetail'),
+              title: $t('demos.breadcrumb.lateralDetail'),
             },
           },
           {
@@ -233,7 +233,7 @@ const routes: RouteRecordRaw[] = [
             path: '/demos/breadcrumb/level',
             meta: {
               icon: 'lucide:navigation',
-              title: $t('page.demos.breadcrumb.level'),
+              title: $t('demos.breadcrumb.level'),
             },
             children: [
               {
@@ -242,7 +242,7 @@ const routes: RouteRecordRaw[] = [
                 component: () =>
                   import('#/views/demos/breadcrumb/level-detail.vue'),
                 meta: {
-                  title: $t('page.demos.breadcrumb.levelDetail'),
+                  title: $t('demos.breadcrumb.levelDetail'),
                 },
               },
             ],
@@ -253,7 +253,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'mdi:lightbulb-error-outline',
-          title: $t('page.demos.fallback.title'),
+          title: $t('demos.fallback.title'),
         },
         name: 'FallbackDemos',
         path: '/demos/fallback',
@@ -292,7 +292,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('#/views/_core/fallback/offline.vue'),
             meta: {
               icon: 'mdi:offline',
-              title: $t('fallback.offline'),
+              title: $t('ui.fallback.offline'),
             },
           },
         ],
@@ -303,7 +303,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           badgeVariants: 'destructive',
           icon: 'lucide:circle-dot',
-          title: $t('page.demos.badge.title'),
+          title: $t('demos.badge.title'),
         },
         name: 'BadgeDemos',
         path: '/demos/badge',
@@ -315,7 +315,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               badgeType: 'dot',
               icon: 'lucide:square-dot',
-              title: $t('page.demos.badge.dot'),
+              title: $t('demos.badge.dot'),
             },
           },
           {
@@ -325,7 +325,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               badge: '10',
               icon: 'lucide:square-dot',
-              title: $t('page.demos.badge.text'),
+              title: $t('demos.badge.text'),
             },
           },
           {
@@ -336,7 +336,7 @@ const routes: RouteRecordRaw[] = [
               badge: 'Hot',
               badgeVariants: 'destructive',
               icon: 'lucide:square-dot',
-              title: $t('page.demos.badge.color'),
+              title: $t('demos.badge.color'),
             },
           },
         ],
@@ -346,7 +346,7 @@ const routes: RouteRecordRaw[] = [
         meta: {
           activeIcon: 'fluent-emoji:radioactive',
           icon: 'bi:radioactive',
-          title: $t('page.demos.activeIcon.title'),
+          title: $t('demos.activeIcon.title'),
         },
         name: 'ActiveIconDemos',
         path: '/demos/active-icon',
@@ -358,7 +358,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               activeIcon: 'fluent-emoji:radioactive',
               icon: 'bi:radioactive',
-              title: $t('page.demos.activeIcon.children'),
+              title: $t('demos.activeIcon.children'),
             },
           },
         ],
@@ -367,7 +367,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'ic:round-settings-input-composite',
-          title: $t('page.demos.outside.title'),
+          title: $t('demos.outside.title'),
         },
         name: 'OutsideDemos',
         path: '/demos/outside',
@@ -377,7 +377,7 @@ const routes: RouteRecordRaw[] = [
             path: '/demos/outside/iframe',
             meta: {
               icon: 'mdi:newspaper-variant-outline',
-              title: $t('page.demos.outside.embedded'),
+              title: $t('demos.outside.embedded'),
             },
             children: [
               {
@@ -409,7 +409,7 @@ const routes: RouteRecordRaw[] = [
             path: '/demos/outside/external-link',
             meta: {
               icon: 'mdi:newspaper-variant-multiple-outline',
-              title: $t('page.demos.outside.externalLink'),
+              title: $t('demos.outside.externalLink'),
             },
             children: [
               {
@@ -440,7 +440,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'ic:round-menu',
-          title: $t('page.demos.nested.title'),
+          title: $t('demos.nested.title'),
         },
         name: 'NestedDemos',
         path: '/demos/nested',
@@ -452,7 +452,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu1'),
+              title: $t('demos.nested.menu1'),
             },
           },
           {
@@ -461,7 +461,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu2'),
+              title: $t('demos.nested.menu2'),
             },
             children: [
               {
@@ -471,7 +471,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu2_1'),
+                  title: $t('demos.nested.menu2_1'),
                 },
               },
             ],
@@ -481,7 +481,7 @@ const routes: RouteRecordRaw[] = [
             path: '/demos/nested/menu3',
             meta: {
               icon: 'ic:round-menu',
-              title: $t('page.demos.nested.menu3'),
+              title: $t('demos.nested.menu3'),
             },
             children: [
               {
@@ -491,7 +491,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu3_1'),
+                  title: $t('demos.nested.menu3_1'),
                 },
               },
               {
@@ -499,7 +499,7 @@ const routes: RouteRecordRaw[] = [
                 path: 'menu3-2',
                 meta: {
                   icon: 'ic:round-menu',
-                  title: $t('page.demos.nested.menu3_2'),
+                  title: $t('demos.nested.menu3_2'),
                 },
                 children: [
                   {
@@ -510,7 +510,7 @@ const routes: RouteRecordRaw[] = [
                     meta: {
                       icon: 'ic:round-menu',
                       keepAlive: true,
-                      title: $t('page.demos.nested.menu3_2_1'),
+                      title: $t('demos.nested.menu3_2_1'),
                     },
                   },
                 ],

+ 26 - 26
playground/src/router/routes/modules/examples.ts

@@ -10,7 +10,7 @@ const routes: RouteRecordRaw[] = [
       icon: 'ion:layers-outline',
       keepAlive: true,
       order: 1000,
-      title: $t('page.examples.title'),
+      title: $t('examples.title'),
     },
     name: 'Examples',
     path: '/examples',
@@ -21,7 +21,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/examples/modal/index.vue'),
         meta: {
           icon: 'system-uicons:window-content',
-          title: $t('page.examples.modal.title'),
+          title: $t('examples.modal.title'),
         },
       },
       {
@@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/examples/drawer/index.vue'),
         meta: {
           icon: 'iconoir:drawer',
-          title: $t('page.examples.drawer.title'),
+          title: $t('examples.drawer.title'),
         },
       },
       {
@@ -39,7 +39,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/examples/ellipsis/index.vue'),
         meta: {
           icon: 'ion:ellipsis-horizontal',
-          title: $t('page.examples.ellipsis.title'),
+          title: $t('examples.ellipsis.title'),
         },
       },
       {
@@ -47,7 +47,7 @@ const routes: RouteRecordRaw[] = [
         path: '/examples/form',
         meta: {
           icon: 'mdi:form-select',
-          title: $t('page.examples.form.title'),
+          title: $t('examples.form.title'),
         },
         children: [
           {
@@ -55,7 +55,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/form/basic',
             component: () => import('#/views/examples/form/basic.vue'),
             meta: {
-              title: $t('page.examples.form.basic'),
+              title: $t('examples.form.basic'),
             },
           },
           {
@@ -63,7 +63,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/form/query',
             component: () => import('#/views/examples/form/query.vue'),
             meta: {
-              title: $t('page.examples.form.query'),
+              title: $t('examples.form.query'),
             },
           },
           {
@@ -71,7 +71,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/form/rules',
             component: () => import('#/views/examples/form/rules.vue'),
             meta: {
-              title: $t('page.examples.form.rules'),
+              title: $t('examples.form.rules'),
             },
           },
           {
@@ -79,7 +79,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/form/dynamic',
             component: () => import('#/views/examples/form/dynamic.vue'),
             meta: {
-              title: $t('page.examples.form.dynamic'),
+              title: $t('examples.form.dynamic'),
             },
           },
           {
@@ -87,7 +87,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/form/custom',
             component: () => import('#/views/examples/form/custom.vue'),
             meta: {
-              title: $t('page.examples.form.custom'),
+              title: $t('examples.form.custom'),
             },
           },
           {
@@ -95,7 +95,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/form/api',
             component: () => import('#/views/examples/form/api.vue'),
             meta: {
-              title: $t('page.examples.form.api'),
+              title: $t('examples.form.api'),
             },
           },
           {
@@ -103,7 +103,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/form/merge',
             component: () => import('#/views/examples/form/merge.vue'),
             meta: {
-              title: $t('page.examples.form.merge'),
+              title: $t('examples.form.merge'),
             },
           },
         ],
@@ -113,7 +113,7 @@ const routes: RouteRecordRaw[] = [
         path: '/examples/vxe-table',
         meta: {
           icon: 'lucide:table',
-          title: $t('page.examples.vxeTable.title'),
+          title: $t('examples.vxeTable.title'),
         },
         children: [
           {
@@ -121,7 +121,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/vxe-table/basic',
             component: () => import('#/views/examples/vxe-table/basic.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.basic'),
+              title: $t('examples.vxeTable.basic'),
             },
           },
           {
@@ -129,7 +129,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/vxe-table/remote',
             component: () => import('#/views/examples/vxe-table/remote.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.remote'),
+              title: $t('examples.vxeTable.remote'),
             },
           },
           {
@@ -137,7 +137,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/vxe-table/tree',
             component: () => import('#/views/examples/vxe-table/tree.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.tree'),
+              title: $t('examples.vxeTable.tree'),
             },
           },
           {
@@ -145,7 +145,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/vxe-table/fixed',
             component: () => import('#/views/examples/vxe-table/fixed.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.fixed'),
+              title: $t('examples.vxeTable.fixed'),
             },
           },
           {
@@ -154,7 +154,7 @@ const routes: RouteRecordRaw[] = [
             component: () =>
               import('#/views/examples/vxe-table/custom-cell.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.custom-cell'),
+              title: $t('examples.vxeTable.custom-cell'),
             },
           },
           {
@@ -162,7 +162,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/vxe-table/form',
             component: () => import('#/views/examples/vxe-table/form.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.form'),
+              title: $t('examples.vxeTable.form'),
             },
           },
           {
@@ -170,7 +170,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/vxe-table/edit-cell',
             component: () => import('#/views/examples/vxe-table/edit-cell.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.editCell'),
+              title: $t('examples.vxeTable.editCell'),
             },
           },
           {
@@ -178,7 +178,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/vxe-table/edit-row',
             component: () => import('#/views/examples/vxe-table/edit-row.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.editRow'),
+              title: $t('examples.vxeTable.editRow'),
             },
           },
           {
@@ -186,7 +186,7 @@ const routes: RouteRecordRaw[] = [
             path: '/examples/vxe-table/virtual',
             component: () => import('#/views/examples/vxe-table/virtual.vue'),
             meta: {
-              title: $t('page.examples.vxeTable.virtual'),
+              title: $t('examples.vxeTable.virtual'),
             },
           },
         ],
@@ -196,7 +196,7 @@ const routes: RouteRecordRaw[] = [
         path: '/examples/captcha',
         meta: {
           icon: 'logos:recaptcha',
-          title: $t('page.examples.captcha.title'),
+          title: $t('examples.captcha.title'),
         },
         children: [
           {
@@ -205,7 +205,7 @@ const routes: RouteRecordRaw[] = [
             component: () =>
               import('#/views/examples/captcha/slider-captcha.vue'),
             meta: {
-              title: $t('page.examples.captcha.sliderCaptcha'),
+              title: $t('examples.captcha.sliderCaptcha'),
             },
           },
           {
@@ -214,7 +214,7 @@ const routes: RouteRecordRaw[] = [
             component: () =>
               import('#/views/examples/captcha/slider-rotate-captcha.vue'),
             meta: {
-              title: $t('page.examples.captcha.sliderRotateCaptcha'),
+              title: $t('examples.captcha.sliderRotateCaptcha'),
             },
           },
           {
@@ -223,7 +223,7 @@ const routes: RouteRecordRaw[] = [
             component: () =>
               import('#/views/examples/captcha/point-selection-captcha.vue'),
             meta: {
-              title: $t('page.examples.captcha.pointSelection'),
+              title: $t('examples.captcha.pointSelection'),
             },
           },
         ],

+ 6 - 6
playground/src/router/routes/modules/vben.ts

@@ -20,7 +20,7 @@ const routes: RouteRecordRaw[] = [
       badgeType: 'dot',
       icon: VBEN_LOGO_URL,
       order: 9999,
-      title: $t('page.vben.title'),
+      title: $t('demos.vben.title'),
     },
     name: 'VbenProject',
     path: '/vben-admin',
@@ -31,7 +31,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/_core/about/index.vue'),
         meta: {
           icon: 'lucide:copyright',
-          title: $t('page.vben.about'),
+          title: $t('demos.vben.about'),
         },
       },
       {
@@ -41,7 +41,7 @@ const routes: RouteRecordRaw[] = [
         meta: {
           icon: 'lucide:book-open-text',
           link: VBEN_DOC_URL,
-          title: $t('page.vben.document'),
+          title: $t('demos.vben.document'),
         },
       },
       {
@@ -62,7 +62,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: SvgAntdvLogoIcon,
           link: VBEN_ANT_PREVIEW_URL,
-          title: $t('page.vben.antdv'),
+          title: $t('demos.vben.antdv'),
         },
       },
       {
@@ -73,7 +73,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:naiveui',
           link: VBEN_NAIVE_PREVIEW_URL,
-          title: $t('page.vben.naive-ui'),
+          title: $t('demos.vben.naive-ui'),
         },
       },
       {
@@ -84,7 +84,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:element',
           link: VBEN_ELE_PREVIEW_URL,
-          title: $t('page.vben.element-plus'),
+          title: $t('demos.vben.element-plus'),
         },
       },
     ],

+ 20 - 23
playground/src/views/examples/captcha/point-selection-captcha.vue

@@ -47,47 +47,44 @@ const handleClick = (point: CaptchaPoint) => {
 
 <template>
   <Page
-    :description="$t('page.examples.captcha.pageDescription')"
-    :title="$t('page.examples.captcha.pageTitle')"
+    :description="$t('examples.captcha.pageDescription')"
+    :title="$t('examples.captcha.pageTitle')"
   >
-    <Card
-      :title="$t('page.examples.captcha.basic')"
-      class="mb-4 overflow-x-auto"
-    >
+    <Card :title="$t('examples.captcha.basic')" class="mb-4 overflow-x-auto">
       <div class="mb-3 flex items-center justify-start">
         <Input
           v-model:value="params.title"
-          :placeholder="$t('page.examples.captcha.titlePlaceholder')"
+          :placeholder="$t('examples.captcha.titlePlaceholder')"
           class="w-64"
         />
         <Input
           v-model:value="params.captchaImageUrl"
-          :placeholder="$t('page.examples.captcha.captchaImageUrlPlaceholder')"
+          :placeholder="$t('examples.captcha.captchaImageUrlPlaceholder')"
           class="ml-8 w-64"
         />
         <div class="ml-8 flex w-96 items-center">
           <Switch
             v-model:checked="params.showHintImage"
-            :checked-children="$t('page.examples.captcha.hintImage')"
-            :un-checked-children="$t('page.examples.captcha.hintText')"
+            :checked-children="$t('examples.captcha.hintImage')"
+            :un-checked-children="$t('examples.captcha.hintText')"
             class="mr-4 w-40"
           />
           <Input
             v-show="params.showHintImage"
             v-model:value="params.hintImageUrl"
-            :placeholder="$t('page.examples.captcha.hintImagePlaceholder')"
+            :placeholder="$t('examples.captcha.hintImagePlaceholder')"
           />
           <Input
             v-show="!params.showHintImage"
             v-model:value="params.hintText"
-            :placeholder="$t('page.examples.captcha.hintTextPlaceholder')"
+            :placeholder="$t('examples.captcha.hintTextPlaceholder')"
           />
         </div>
 
         <Switch
           v-model:checked="params.showConfirm"
-          :checked-children="$t('page.examples.captcha.showConfirm')"
-          :un-checked-children="$t('page.examples.captcha.hideConfirm')"
+          :checked-children="$t('examples.captcha.showConfirm')"
+          :un-checked-children="$t('examples.captcha.hideConfirm')"
           class="ml-8 w-28"
         />
       </div>
@@ -96,7 +93,7 @@ const handleClick = (point: CaptchaPoint) => {
           <InputNumber
             v-model:value="params.width"
             :min="1"
-            :placeholder="$t('page.examples.captcha.widthPlaceholder')"
+            :placeholder="$t('examples.captcha.widthPlaceholder')"
             :precision="0"
             :step="1"
             class="w-64"
@@ -108,7 +105,7 @@ const handleClick = (point: CaptchaPoint) => {
           <InputNumber
             v-model:value="params.height"
             :min="1"
-            :placeholder="$t('page.examples.captcha.heightPlaceholder')"
+            :placeholder="$t('examples.captcha.heightPlaceholder')"
             :precision="0"
             :step="1"
             class="w-64"
@@ -120,7 +117,7 @@ const handleClick = (point: CaptchaPoint) => {
           <InputNumber
             v-model:value="params.paddingX"
             :min="1"
-            :placeholder="$t('page.examples.captcha.paddingXPlaceholder')"
+            :placeholder="$t('examples.captcha.paddingXPlaceholder')"
             :precision="0"
             :step="1"
             class="w-64"
@@ -132,7 +129,7 @@ const handleClick = (point: CaptchaPoint) => {
           <InputNumber
             v-model:value="params.paddingY"
             :min="1"
-            :placeholder="$t('page.examples.captcha.paddingYPlaceholder')"
+            :placeholder="$t('examples.captcha.paddingYPlaceholder')"
             :precision="0"
             :step="1"
             class="w-64"
@@ -159,23 +156,23 @@ const handleClick = (point: CaptchaPoint) => {
         @refresh="handleRefresh"
       >
         <template #title>
-          {{ params.title || $t('page.examples.captcha.captchaCardTitle') }}
+          {{ params.title || $t('examples.captcha.captchaCardTitle') }}
         </template>
       </PointSelectionCaptcha>
 
       <ol class="float-left p-5">
         <li v-for="point in selectedPoints" :key="point.i" class="flex">
           <span class="mr-3 w-16">{{
-            $t('page.examples.captcha.index') + point.i
+            $t('examples.captcha.index') + point.i
           }}</span>
           <span class="mr-3 w-52">{{
-            $t('page.examples.captcha.timestamp') + point.t
+            $t('examples.captcha.timestamp') + point.t
           }}</span>
           <span class="mr-3 w-16">{{
-            $t('page.examples.captcha.x') + point.x
+            $t('examples.captcha.x') + point.x
           }}</span>
           <span class="mr-3 w-16">{{
-            $t('page.examples.captcha.y') + point.y
+            $t('examples.captcha.y') + point.y
           }}</span>
         </li>
       </ol>

File diff suppressed because it is too large
+ 203 - 192
pnpm-lock.yaml


+ 8 - 8
pnpm-workspace.yaml

@@ -20,7 +20,7 @@ catalog:
   '@commitlint/cli': ^19.5.0
   '@commitlint/config-conventional': ^19.5.0
   '@ctrl/tinycolor': ^4.1.0
-  '@eslint/js': ^9.12.0
+  '@eslint/js': ^9.13.0
   '@faker-js/faker': ^9.0.3
   '@iconify/json': ^2.2.261
   '@iconify/tailwind': ^1.1.3
@@ -43,14 +43,14 @@ catalog:
   '@types/html-minifier-terser': ^7.0.2
   '@types/jsonwebtoken': ^9.0.7
   '@types/lodash.clonedeep': ^4.5.9
-  '@types/node': ^22.7.6
+  '@types/node': ^22.7.7
   '@types/nprogress': ^0.2.3
   '@types/postcss-import': ^14.0.3
   '@types/qrcode': ^1.5.5
   '@types/sortablejs': ^1.15.8
   '@typescript-eslint/eslint-plugin': ^8.10.0
   '@typescript-eslint/parser': ^8.10.0
-  '@vee-validate/zod': ^4.13.2
+  '@vee-validate/zod': ^4.14.3
   '@vite-pwa/vitepress': ^0.5.3
   '@vitejs/plugin-vue': ^5.1.4
   '@vitejs/plugin-vue-jsx': ^4.0.1
@@ -83,8 +83,8 @@ catalog:
   dotenv: ^16.4.5
   echarts: ^5.5.1
   element-plus: ^2.8.6
-  eslint: ^9.12.0
-  eslint-config-turbo: ^2.1.3
+  eslint: ^9.13.0
+  eslint-config-turbo: ^2.2.0
   eslint-plugin-command: ^0.2.6
   eslint-plugin-eslint-comments: ^3.2.0
   eslint-plugin-import-x: ^4.3.1
@@ -138,7 +138,7 @@ catalog:
   rimraf: ^6.0.1
   rollup: ^4.24.0
   rollup-plugin-visualizer: ^5.12.0
-  sass: ^1.80.2
+  sass: ^1.80.3
   sortablejs: ^1.15.3
   stylelint: ^16.10.0
   stylelint-config-recess-order: ^5.1.1
@@ -153,11 +153,11 @@ catalog:
   tailwindcss: ^3.4.14
   tailwindcss-animate: ^1.0.7
   theme-colors: ^0.1.0
-  turbo: ^2.1.3
+  turbo: ^2.2.0
   typescript: ^5.6.3
   unbuild: ^2.0.0
   unplugin-element-plus: ^0.8.0
-  vee-validate: ^4.13.2
+  vee-validate: ^4.14.3
   vite: ^5.4.9
   vite-plugin-compression: ^0.5.1
   vite-plugin-dts: 4.2.1

Some files were not shown because too many files changed in this diff