Skip to content

国际化处理方案

基于 vue-i18n

安装

bash

npm install vue-i18n@next

webpack

文件目录

  • 创建目录
bash

├── src
   ├── assets
   ├── components
   ├── i18n
   ├── langs
   ├── en.js
   ├── zh.js
   ├── ja.js
   ├── index.js
   ├── i18n.js

i18n.js

js
import { createI18n } from "vue-i18n";

import messages from "./langs/index.js";

//从localStorage中拿到用户的语言选择,如果没有,那默认英文。
const i18n = createI18n({
  legacy: false, //这个legacy必须加
  locale: localStorage.lang || "en",
  messages,
});

export default i18n;

index.js

js
import en from "./en";
import cn from "./cn";
import ja from "./ja";
export default {
  en,
  cn,
  ja,
};

cn.js

js
const cn = {
  message: {
    hello: "你好",
    themeChange: "主题更换",
    textChange: "文字更换",
    activetextChange: "选中更换",
    route: {
      profile: "个人中心",
      login: "登录",
      register: "注册",
      home: "首页",
      user: "用户",
      userManage: "用户管理",
      roleList: "角色列表",
      permissionList: "权限列表",
      excelImport: "Excel导入",
      article: "文章管理",
      articleRanking: "文章等级",
      articleCreate: "文章创建",
    },
  },
};

export default cn;

en.js

js
const en = {
  message: {
    hello: "hello",
    themeChange: "themeChange",
    textChange: "textChange",
    activetextChange: "activetextChange",
    route: {
      profile: "profile",
      login: "login",
      register: "register",
      home: "home",
      user: "user",
      userManage: "userManage",
      roleList: "roleList",
      permissionList: "permissionList",
      excelImport: "excelImport",
      article: "article",
      articleRanking: "articleRanking",
      articleCreate: "articleCreate",
    },
  },
};

export default en;

ja.js

js
const ja = {
  message: {
    hello: "日本语hello",
    themeChange: "themeChange",
    textChange: "textChange",
    activetextChange: "activetextChange",
    route: {
      profile: "profile",
      login: "login",
      register: "register",
      home: "home",
      user: "user",
      userManage: "userManage",
      roleList: "roleList",
      permissionList: "permissionList",
      excelImport: "excelImport",
      article: "article",
      articleRanking: "articleRanking",
      articleCreate: "articleCreate",
    },
  },
};

export default ja;

注意

在配置语言包的时候,key 值一定要保持同意,因为 i18n 是通过你的 key 值来切换语言,如果 key 值有误,就不能切换

main.js

js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import i18n from "./i18n/i18n.js";
import "normalize.css";

const app = createApp(App);

app.use(router).use(i18n);

app.mount("#app");

配置页面

vue
<template>
  <div>
    <p>{{ $t("message.hello") }}</p>
    <p>{{ title }}</p>

    <button @click="switchLang('en')">英语</button>
    <button @click="switchLang('cn')">中文</button>
    <button @click="switchLang('ja')">日语</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
import { useI18n } from "vue-i18n";
export default defineComponent({
  setup() {
    const { t } = useI18n();
    const data = reactive({
      message: "首页",
      title: t("message.hello"),
      switchLang(lang: string) {
        //把语言保存在localStorage中
        localStorage.setItem("lang", lang);
        location.reload();
      },
    });

    return {
      ...toRefs(data),
    };
  },
});
</script>

<style scoped></style>

vite 版本

安装

bash
npm install vue-i18n@next

目录

  • 创建目录
bash

├── src
   ├── assets
   ├── components
   ├── i18n
   ├── langs
   ├── en.js
   ├── zh.js
   ├── ja.js
   ├── index.js

index.js

js
import { createI18n } from "vue-i18n";

// 导入你自己创建的语言 js 文件

import cn from "./langs/cn";

import en from "./langs/en";

import ja from "./langs/ja";

// 创建 i18n 对象
const i18n = createI18n({
  legacy: false,
  globalInjection: true, // 全局模式,可以直接使用 $t
  locale: localStorage.getItem("lang") || "cn",
  messages: {
    cn,
    en,
    ja,
  },
});

export default i18n;

cn.js

js
const cn = {
  message: {
    hello: "你好",
    themeChange: "主题更换",
    textChange: "文字更换",
    activetextChange: "选中更换",
    route: {
      profile: "个人中心",
      login: "登录",
      register: "注册",
      home: "首页",
      user: "用户",
      userManage: "用户管理",
      roleList: "角色列表",
      permissionList: "权限列表",
      excelImport: "Excel导入",
      article: "文章管理",
      articleRanking: "文章等级",
      articleCreate: "文章创建",
    },
  },
};

export default cn;

en.js

js
const en = {
  message: {
    hello: "hello",
    themeChange: "themeChange",
    textChange: "textChange",
    activetextChange: "activetextChange",
    route: {
      profile: "profile",
      login: "login",
      register: "register",
      home: "home",
      user: "user",
      userManage: "userManage",
      roleList: "roleList",
      permissionList: "permissionList",
      excelImport: "excelImport",
      article: "article",
      articleRanking: "articleRanking",
      articleCreate: "articleCreate",
    },
  },
};

export default en;

ja.js

js
const ja = {
  message: {
    hello: "日本语hello",
    themeChange: "themeChange",
    textChange: "textChange",
    activetextChange: "activetextChange",
    route: {
      profile: "profile",
      login: "login",
      register: "register",
      home: "home",
      user: "user",
      userManage: "userManage",
      roleList: "roleList",
      permissionList: "permissionList",
      excelImport: "excelImport",
      article: "article",
      articleRanking: "articleRanking",
      articleCreate: "articleCreate",
    },
  },
};

export default ja;

main.js

js
// 引入i18n.js

import i18n from "@/i18n/index";

app.use(i18n);

封装中英文切换组件

  • components/i18n/index.vue
vue
<template>
  <el-dropdown>
    <span class="imgicon">
      <SvgIcon icon="language" :size="size" :color="color"></SvgIcon>
    </span>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item :disabled="flag" @click="changelan('cn')"
          >中文</el-dropdown-item
        >
        <el-dropdown-item :disabled="!flag" @click="changelan('en')"
          >英文</el-dropdown-item
        >
      </el-dropdown-menu>
    </template>
  </el-dropdown>
</template>

<script setup>
import { useI18n } from "vue-i18n";
defineProps({
  color: {
    type: String,
    default: "#fff",
  },
  size: {
    type: String,
    default: "36",
  },
});
// 切换语言的方法
const i18n = useI18n();

let flag = computed(() => {
  const result = i18n.locale.value;
  if (result == "cn") {
    return true;
  } else {
    return false;
  }
});

const changelan = (val) => {
  i18n.locale.value = val;
  localStorage.setItem("lang", val);
  location.reload();
};
</script>
<style scoped>
:deep(.el-tooltip__trigger:focus-visible) {
  outline: unset;
}
.el-dropdown {
  float: right;
  margin-right: 20px;
  .imgicon {
    width: 30px;
    height: 30px;
    float: right;
    display: block;
    img {
      display: block;
      width: 100%;
      height: 100%;
    }
  }
}
</style>

(2) 封装 hooks 方法 切换语言

  • hooks/usei18n.js
js
import { useI18n } from "vue-i18n";

const usei18n = () => {
  const { t } = useI18n();

  const test_login_title = ref(t("message.hello"));

  const switchLanguage = (lang) => {
    localStorage.setItem("lang", lang);
    location.reload();
  };

  return { test_login_title, switchLanguage };
};

export default usei18n;

(3) 封装工具类

  • utils/i18n.js
js
import { watch } from "vue";

import i18nAll from "@/i18n";

import { useI18n } from "vue-i18n";

export function generateTitle(title) {
  return i18nAll.global.t("message.route." + title);
}

/**
 *
 * @param  {...any} cbs 所有的回调
 */
export function watchSwitchLang(...cbs) {
  // 切换语言的方法
  const i18n = useI18n();
  watch(
    () => i18n.locale.value,
    () => {
      cbs.forEach((cb) => cb(i18n.locale.value));
    }
  );
}

Elementui-plus 国际化

vue
<template>
  <el-config-provider :locale="localLanguage">
    <router-view></router-view>
  </el-config-provider>
</template>
<script setup>
import zhCn from "element-plus/es/locale/lang/zh-cn";

import en from "element-plus/es/locale/lang/en";

import { useI18n } from "vue-i18n";

// 切换语言的方法
const i18n = useI18n();

const localLanguage = computed(() => {
  const result = i18n.locale.value;
  if (result == "cn") {
    return zhCn;
  } else {
    return en;
  }
});
</script>

页面上使用

  • 登录页
vue
<!--
 * @Author: jsopy
 * @Date: 2025-01-11 09:10:41
 * @LastEditTime: 2025-01-18 07:19:19
 * @FilePath: /adminvue/src/pages/login/index.vue
 * @Description:
 *
-->
<template>
  <div>
    <div class="login-container">
      <el-form
        class="login-form"
        ref="loginFromRef"
        :model="loginForm"
        :rules="loginRules"
      >
        <div class="title-container">
          <div class="title">用户登陆</div>
          <div class="langselecticon">
            <I18n></I18n>
          </div>
        </div>

        <el-form-item prop="username">
          <span class="svg-container">
            <SvgIcon icon="user" size="16" />
          </span>
          <el-input
            placeholder="username"
            name="username"
            type="text"
            v-model="loginForm.username"
          />
        </el-form-item>

        <el-form-item prop="password">
          <span class="svg-container">
            <SvgIcon icon="password" size="16" />
          </span>
          <el-input
            placeholder="password"
            name="password"
            :type="passwordType"
            v-model="loginForm.password"
          />
          <span class="show-pwd">
            <SvgIcon
              :icon="passwordType === 'password' ? 'eye' : 'eye-open'"
              size="14"
              @click="onChangePwdType"
            />
          </span>
        </el-form-item>

        <el-button
          type="primary"
          style="width: 100%; margin-bottom: 30px; background-color: cadetblue"
          :loading="loading"
          @click="handleLogin"
          >登陆</el-button
        >

        <div class="tips">
          <p>测试权限账号:</p>
          <p>提供三种权限账号:</p>
          <p>1. 超级管理员账号: super-admin</p>
          <p>2. 管理员账号:admin</p>
          <p>3. 测试可配置账号:test</p>
          <p>密码统一为:123456</p>
          <p>
            测试中英文切换
            <span> {{ $t("message.hello") }}</span>
          </p>
          <!--
                    <p>
                        <span>{{ test_login_title }}</span>
                        <el-button @click="switchLanguage('cn')">切换语言</el-button>
                    </p> -->
        </div>
      </el-form>
    </div>
  </div>
</template>

<script setup>
import useLogin from "@/hooks/login/useLogin";

// import usei18n from '@/hooks/login/usei18n';

let {
  loginForm,
  loginRules,
  passwordType,
  loading,
  loginFromRef,
  handleLogin,
  onChangePwdType,
} = useLogin();

// let { test_login_title, switchLanguage } = usei18n();
</script>

<style lang="scss" scoped>
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;
$cursor: #fff;
.login-container {
  min-height: 100vh;
  width: 100%;
  background-color: $bg;
  overflow: hidden;

  .login-form {
    position: relative;
    width: 520px;
    max-width: 100%;
    padding: 160px 35px 0;
    margin: 0 auto;
    overflow: hidden;

    :deep(.el-form-item) {
      border: 1px solid rgba(255, 255, 255, 0.1);
      background: rgba(0, 0, 0, 0.1);
      border-radius: 5px;
      color: #454545;
      margin-bottom: 50px;
    }
    :deep(.el-form-item__error) {
      font-size: 14px;
      left: 0px;
      top: 62px;
    }
    :deep(.el-input__wrapper) {
      background-color: rgba(0, 0, 0, 0);
      box-shadow: none;
    }

    :deep(.el-input) {
      display: inline-block;
      height: 47px;
      width: 85%;

      input {
        background: transparent;
        border: 0px;

        border-radius: 0px;
        padding: 5px 5px 12px 15px;
        color: $light_gray;
        height: 47px;
        caret-color: $cursor;
      }
    }
  }

  .tips {
    font-size: 16px;
    line-height: 28px;
    color: #fff;
    margin-bottom: 10px;

    span {
      &:first-of-type {
        margin-right: 16px;
      }
    }
  }

  .svg-container {
    padding: 6px 5px 6px 15px;
    color: $dark_gray;
    vertical-align: middle;
    display: inline-block;
  }

  .title-container {
    position: relative;
    width: 100%;
    height: 40px;
    margin-bottom: 15px;
    .title {
      font-size: 26px;
      color: $light_gray;
      position: absolute;
      left: 0px;
      top: 0px;
      width: 100%;
      height: 40px;
      text-align: center;
      font-weight: bold;
    }
    .langselecticon {
      position: absolute;
      right: 0px;
      top: 0px;
      width: 48px;
      height: 48px;
      z-index: 2;
    }

    :deep(.lang-select) {
      position: absolute;
      top: 4px;
      right: 0;
      background-color: white;
      font-size: 22px;
      padding: 4px;
      border-radius: 4px;
      cursor: pointer;
    }
  }

  .show-pwd {
    position: absolute;
    right: 10px;
    top: 7px;
    font-size: 16px;
    color: $dark_gray;
    cursor: pointer;
    user-select: none;
  }
}
</style>