Parcourir la source

feat: 添加国际化操作

xyh il y a 2 ans
Parent
commit
b028aef247

+ 1 - 0
package.json

@@ -92,6 +92,7 @@
     "veboundary": "1.2.2",
     "vee-validate": "^4.9.3",
     "vue": "^3.3.2",
+    "vue-i18n": "^9.2.2",
     "vue-router": "^4.2.0",
     "zod": "^3.21.4"
   },

+ 54 - 1
pnpm-lock.yaml

@@ -31,6 +31,9 @@ dependencies:
   vue:
     specifier: ^3.3.2
     version: 3.3.2
+  vue-i18n:
+    specifier: ^9.2.2
+    version: 9.2.2(vue@3.3.2)
   vue-router:
     specifier: ^4.2.0
     version: 4.2.0(vue@3.3.2)
@@ -1024,6 +1027,44 @@ packages:
       vue: 3.3.2
     dev: false
 
+  /@intlify/core-base@9.2.2:
+    resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/devtools-if': 9.2.2
+      '@intlify/message-compiler': 9.2.2
+      '@intlify/shared': 9.2.2
+      '@intlify/vue-devtools': 9.2.2
+    dev: false
+
+  /@intlify/devtools-if@9.2.2:
+    resolution: {integrity: sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/shared': 9.2.2
+    dev: false
+
+  /@intlify/message-compiler@9.2.2:
+    resolution: {integrity: sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/shared': 9.2.2
+      source-map: 0.6.1
+    dev: false
+
+  /@intlify/shared@9.2.2:
+    resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==}
+    engines: {node: '>= 14'}
+    dev: false
+
+  /@intlify/vue-devtools@9.2.2:
+    resolution: {integrity: sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/core-base': 9.2.2
+      '@intlify/shared': 9.2.2
+    dev: false
+
   /@jridgewell/resolve-uri@3.1.1:
     resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==}
     engines: {node: '>=6.0.0'}
@@ -6088,7 +6129,6 @@ packages:
   /source-map@0.6.1:
     resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
   /source-map@0.7.4:
     resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
@@ -6973,6 +7013,19 @@ packages:
       - supports-color
     dev: true
 
+  /vue-i18n@9.2.2(vue@3.3.2):
+    resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==}
+    engines: {node: '>= 14'}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      '@intlify/core-base': 9.2.2
+      '@intlify/shared': 9.2.2
+      '@intlify/vue-devtools': 9.2.2
+      '@vue/devtools-api': 6.5.0
+      vue: 3.3.2
+    dev: false
+
   /vue-router@4.2.0(vue@3.3.2):
     resolution: {integrity: sha512-c+usESa6ZoWsm4PPdzRSyenp5A4dsUtnDJnrI03fY1IpIihA9TK3x5ffgkFDpjhLJZewsXoKURapNLFdZjuqTg==}
     peerDependencies:

BIN
src/assets/images/login/background.jpg


+ 31 - 0
src/locales/index.ts

@@ -0,0 +1,31 @@
+import {createI18n} from 'vue-i18n';
+import loginMessage from './login';
+
+function initLocalLanguage() {
+  const language = navigator.language.toLocaleLowerCase();
+  switch (true) {
+    case language.indexOf('zh') >= 0:
+      return 'zh';
+    case language.indexOf('ko') >= 0:
+      return 'ko';
+    default:
+      return 'zh';
+  }
+}
+
+const i18n = createI18n({
+  locale: initLocalLanguage(),
+  messages: {
+    zh: {
+      login: loginMessage.cn,
+    },
+    ko: {
+      login: loginMessage.ko,
+    },
+  },
+  legacy: false,
+  fallbackLocale: 'zh',
+  missingWarn: true,
+});
+
+export default i18n;

+ 20 - 0
src/locales/login.ts

@@ -0,0 +1,20 @@
+export default {
+  cn: {
+    title: '供应商管理系统',
+    tabs: ['供应商管理', '企业管理'],
+    placeholder: {userName: '用户名', password: '密码', company: '企业名称'},
+    remberPassword: '记住密码',
+    forgetPassword: '忘记密码',
+    loginBtnText: '登录',
+    regist: '申请成为供应商',
+  },
+  ko: {
+    title: '供应商管理系统',
+    tabs: ['供应商管理', '企业管理'],
+    placeholder: {userName: '用户名', password: '密码', company: '企业名称'},
+    remberPassword: '记住密码',
+    forgetPassword: '忘记密码',
+    loginBtnText: '登录',
+    regist: '申请成为供应商',
+  },
+};

+ 6 - 1
src/main.ts

@@ -7,6 +7,7 @@ import App from './App.vue';
 import {router} from '@routes';
 import {VueQueryPlugin, QueryClient} from '@tanstack/vue-query';
 import {createPinia} from 'pinia';
+import i18n from '@locales';
 
 const client = new QueryClient({
   defaultOptions: {
@@ -19,6 +20,10 @@ const client = new QueryClient({
 
 const app = createApp(App);
 
-app.use(router).use(createPinia()).use(VueQueryPlugin, {queryClient: client});
+app
+  .use(router)
+  .use(i18n)
+  .use(createPinia())
+  .use(VueQueryPlugin, {queryClient: client});
 
 app.mount('#app');

+ 3 - 0
src/pages/login/index.css

@@ -4,6 +4,9 @@ main {
   justify-content: center;
   width: 100vw;
   height: 100vh;
+  background-image: url('@assets/images/login/background.jpg');
+  background-repeat: no-repeat;
+  background-size: cover;
 }
 
 .container {

+ 1 - 1
src/pages/login/index.vue

@@ -14,7 +14,7 @@ const {onSubmit} = useFormState();
       <div class="display">
         <div class="logo">
           <img :src="logo" />
-          <h1>供应商管理系统</h1>
+          <h1 v-t="'login.title'" />
         </div>
 
         <img :src="displayImg" class="display-icon" />

+ 33 - 13
src/pages/login/login-info/index.vue

@@ -8,8 +8,10 @@ import {PreviewOpen, PreviewCloseOne} from '@icon-park/vue-next';
 import {useToggle} from '@vueuse/core';
 import {RouterLink} from 'vue-router';
 import {useFieldItem} from './hooks';
+import {useI18n} from 'vue-i18n';
 
 defineOptions({name: 'LoginInfo'});
+
 const active = ref(0);
 function onTabClick(value: number) {
   active.value = value;
@@ -17,28 +19,40 @@ function onTabClick(value: number) {
 
 const [isPassword, togglePassword] = useToggle(true);
 const [remberPassword, toggleRemberPassword] = useToggle();
+
 const {value: nameValue, errorMessage: nameErrorMessage} = useFieldItem('name');
 const {value: passwordValue, errorMessage: passwordErrorMessage} =
   useFieldItem('password');
 const {value: companyValue, errorMessage: companyErrorMessage} =
   useFieldItem('company');
+
+const {t} = useI18n();
 </script>
 
 <template>
   <h2 class="login-title">Login</h2>
   <div class="tab">
-    <span :class="{'tab-item-active': active === 0}" @click="onTabClick(0)">
-      供应商管理
-    </span>
-    <span :class="{'tab-item-active': active === 1}" @click="onTabClick(1)">
-      企业管理
-    </span>
+    <span
+      v-t="'login.tabs[0]'"
+      :class="{'tab-item-active': active === 0}"
+      @click="onTabClick(0)"
+    />
+
+    <span
+      v-t="'login.tabs[1]'"
+      :class="{'tab-item-active': active === 1}"
+      @click="onTabClick(1)"
+    />
 
     <i class="tab-indicator"></i>
   </div>
 
   <div class="info">
-    <ElInput v-model="nameValue" name="userName" placeholder="用户名">
+    <ElInput
+      v-model="nameValue"
+      name="userName"
+      :placeholder="t('login.placeholder.userName')"
+    >
       <template #prefix>
         <img :src="userIcon" class="input-icon" />
       </template>
@@ -50,7 +64,7 @@ const {value: companyValue, errorMessage: companyErrorMessage} =
     <ElInput
       v-model="passwordValue"
       name="password"
-      placeholder="密码"
+      :placeholder="t('login.placeholder.password')"
       :type="isPassword ? 'password' : 'text'"
     >
       <template #prefix>
@@ -71,7 +85,11 @@ const {value: companyValue, errorMessage: companyErrorMessage} =
       {{ passwordErrorMessage }}
     </p>
 
-    <ElInput v-model="companyValue" name="userName" placeholder="企业名称">
+    <ElInput
+      v-model="companyValue"
+      name="userName"
+      :placeholder="t('login.placeholder.company')"
+    >
       <template #prefix>
         <img :src="enterpriseIcon" class="input-icon" />
       </template>
@@ -85,15 +103,17 @@ const {value: companyValue, errorMessage: companyErrorMessage} =
         :class="{'rember-password': remberPassword}"
         @click="toggleRemberPassword(!remberPassword)"
       >
-        记住密码
+        {{ t('login.remberPassword') }}
       </span>
-      <span>忘记密码</span>
+      <span>{{ t('login.forgetPassword') }}</span>
     </div>
 
-    <ElButton native-type="submit" class="login-btn">登录</ElButton>
+    <ElButton native-type="submit" class="login-btn">
+      {{ t('login.loginBtnText') }}
+    </ElButton>
   </div>
 
-  <RouterLink to="/" class="regist">申请成为供应商</RouterLink>
+  <RouterLink to="/" class="regist">{{ t('login.regist') }}</RouterLink>
 </template>
 
 <style scoped lang="css">

+ 2 - 1
tsconfig.paths.json

@@ -11,7 +11,8 @@
       "@pages/*": ["./pages/*"],
       "@stores": ["./stores/index"],
       "@models": ["./models/index"],
-      "@utils": ["./utils/index"]
+      "@utils": ["./utils/index"],
+      "@locales": ["./locales/index"]
     }
   }
 }

+ 1 - 0
vite.config.ts

@@ -74,6 +74,7 @@ export default defineConfig({
       '@stores': resolve(__dirname, 'src/stores'),
       '@models': resolve(__dirname, 'src/models'),
       '@utils': resolve(__dirname, 'src/utils'),
+      '@locales': resolve(__dirname, 'src/locales'),
     },
   },
 });