当前位置:   article > 正文

Vue前端使用I18实现中英文切换_前端中英文转换怎么操作

前端中英文转换怎么操作

一、配置vue-i18

1. 进入src/locales/index.js

  1. import { createI18n } from 'vue-i18n'
  2. import zhCN from 'ant-design-vue/es/locale/zh_CN'
  3. import enGB from 'ant-design-vue/es/locale/en_GB'
  4. import zh_cn from './lang/zh-cn.js'
  5. import en from './lang/en.js'
  6. import tool from '@/utils/tool'
  7. import sysConfig from '@/config/index'
  8. export const messages = {
  9. 'zh-cn': {
  10. lang: zhCN,
  11. ...zh_cn
  12. },
  13. en: {
  14. lang: enGB,
  15. ...en
  16. }
  17. }
  18. const i18n = createI18n({
  19. locale: tool.data.get('APP_LANG') || sysConfig.LANG,
  20. fallbackLocale: 'zh-cn',
  21. globalInjection: true,
  22. messages
  23. })
  24. export default i18n

2. 查看 src/utils/tool.js文件

  1. import { notification } from 'ant-design-vue';
  2. const tool = {}
  3. // 以下使用中英翻译用到该方法!!!
  4. // localStorage
  5. tool.data = {
  6. set(table, settings) {
  7. const _set = JSON.stringify(settings)
  8. return localStorage.setItem(table, _set)
  9. },
  10. get(table) {
  11. let data = localStorage.getItem(table)
  12. try {
  13. data = JSON.parse(data)
  14. } catch (err) {
  15. return null
  16. }
  17. return data
  18. },
  19. remove(table) {
  20. return localStorage.removeItem(table)
  21. },
  22. clear() {
  23. return localStorage.clear()
  24. }
  25. }
  26. // sessionStorage
  27. tool.session = {
  28. set(table, settings) {
  29. const _set = JSON.stringify(settings)
  30. return sessionStorage.setItem(table, _set)
  31. },
  32. get(table) {
  33. let data = sessionStorage.getItem(table)
  34. try {
  35. data = JSON.parse(data)
  36. } catch (err) {
  37. return null
  38. }
  39. return data
  40. },
  41. remove(table) {
  42. return sessionStorage.removeItem(table)
  43. },
  44. clear() {
  45. return sessionStorage.clear()
  46. }
  47. }
  48. // 千分符
  49. tool.groupSeparator = (num) => {
  50. num = `${num}`
  51. if (!num.includes('.')) num += '.'
  52. return num
  53. .replace(/(\d)(?=(\d{3})+\.)/g, ($0, $1) => {
  54. return `${$1},`
  55. })
  56. .replace(/\.$/, '')
  57. }
  58. // 获取所有字典数组
  59. tool.dictDataAll = () => {
  60. return tool.data.get('DICT_TYPE_TREE_DATA')
  61. }
  62. // 字典翻译方法,界面插槽使用方法 {{ $TOOL.dictType('sex', record.sex) }}
  63. tool.dictTypeData = (dictValue, value) => {
  64. const dictTypeTree = tool.dictDataAll()
  65. if (!dictTypeTree) {
  66. return '需重新登录'
  67. }
  68. const tree = dictTypeTree.find((item) => item.dictValue === dictValue)
  69. if (!tree) {
  70. return '无此字典'
  71. }
  72. const children = tree.children
  73. const dict = children.find((item) => item.dictValue === value)
  74. return dict?.name || '无此字典'
  75. }
  76. // 获取某个code下字典的列表,多用于字典下拉框
  77. tool.dictTypeList = (dictValue) => {
  78. const dictTypeTree = tool.dictDataAll()
  79. if (!dictTypeTree) {
  80. return []
  81. }
  82. const tree = dictTypeTree.find((item) => item.dictValue === dictValue)
  83. if (tree && tree.children) {
  84. return tree.children
  85. }
  86. return []
  87. }
  88. // 获取某个code下字典的列表,基于dictTypeList 改进,保留老的,逐步替换
  89. tool.dictList = (dictValue) => {
  90. const dictTypeTree = tool.dictDataAll()
  91. if (!dictTypeTree) {
  92. return []
  93. }
  94. const tree = dictTypeTree.find((item) => item.dictValue === dictValue)
  95. if (tree) {
  96. return tree.children.map((item) => {
  97. return {
  98. value: item['dictValue'],
  99. label: item['name']
  100. }
  101. })
  102. }
  103. return []
  104. }
  105. // 生成UUID
  106. tool.snowyUuid = () => {
  107. let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
  108. let r = (Math.random() * 16) | 0,
  109. v = c === 'x' ? r : (r & 0x3) | 0x8
  110. return v.toString(16)
  111. })
  112. // 首字符转换成字母
  113. return 'xn' + uuid.slice(2)
  114. }
  115. tool.sortBy = (property) => {
  116. return function (value1, value2) {
  117. let a = value1[property]
  118. let b = value2[property]
  119. return a < b ? 1 : a > b ? -1 : 0
  120. }
  121. }
  122. tool.sortBy = (property, asc) => {
  123. //默认升序,不能写在闭包里面,写在闭包里面是无效的……asc没反应就
  124. if (asc === undefined) {
  125. asc = -1
  126. } else {
  127. asc = asc ? -1 : 1
  128. }
  129. return function (value1, value2) {
  130. let a = value1[property]
  131. let b = value2[property]
  132. return a < b ? asc : a > b ? asc * -1 : 0
  133. }
  134. }
  135. /**
  136. * 打开消息面板
  137. * @param type 类型,success,info,warning,error
  138. * @param message 标题
  139. * @param description 备注
  140. * @param duration 弹出时间,默认3s
  141. */
  142. export const showNotification = (type, message, description = '', duration = 3) => {
  143. notification[type]({
  144. message,
  145. description,
  146. duration
  147. })
  148. }
  149. // 文件处理工具end
  150. export default tool

3. 查看 src/locales/lang/zh-cn.js、src/locales/lang/en.js

  1. import 'dayjs/locale/zh-cn'
  2. export default {
  3. common: {
  4. searchButton: '查询',
  5. resetButton: '重置',
  6. addButton: '增加',
  7. editButton: '编辑',
  8. removeButton: '删除',
  9. batchRemoveButton: '批量删除',
  10. detailButton: '详情',
  11. searchKey: '关键词',
  12. imports: '导入',
  13. more: '更多',
  14. export: '导出',
  15. },
  16. model: {
  17. user: '用户',
  18. org: '机构',
  19. pos: '职位',
  20. role: '角色',
  21. bizUser: '人员'
  22. },
  23. login: {
  24. signInTitle: '用户登录',
  25. forgetPassword: '忘记密码',
  26. signIn: '登录',
  27. signInOther: '其他登录方式',
  28. accountPlaceholder: '请输入账号',
  29. accountError: '请输入账号',
  30. PWPlaceholder: '请输入密码',
  31. PWError: '请输入密码',
  32. validLaceholder: '请输入验证码',
  33. validError: '请输入验证码',
  34. accountPassword: '账号密码',
  35. phoneSms: '手机号登录',
  36. phonePlaceholder: '请输入手机号',
  37. smsCodePlaceholder: '请输入短信验证码',
  38. getSmsCode: '获取验证码',
  39. machineValidation: '机器验证',
  40. sendingSmsMessage: '短信发送中',
  41. newPwdPlaceholder: '请输入新密码',
  42. backLogin: '返回登录',
  43. restPassword: '重置密码',
  44. emailPlaceholder: '请输入邮箱号',
  45. emailCodePlaceholder: '请输入邮件验证码',
  46. restPhoneType: '手机号找回',
  47. restEmailType: '邮箱找回',
  48. sysName: '管理'
  49. },
  50. user: {
  51. userStatus: '用户状态',
  52. resetPassword: '重置密码',
  53. role: '角色',
  54. batchExportButton: '批量导出',
  55. grantRole: '授权角色',
  56. grantResource: '授权资源',
  57. grantPermission: '授权权限',
  58. exportUserInfo: '导出信息',
  59. placeholderNameAndSearchKey: '请输入姓名或关键词',
  60. placeholderUserStatus: '请选择状态',
  61. popconfirmDeleteUser: '确定要删除吗?',
  62. popconfirmResatUserPwd: '确定要重置吗?'
  63. }
  64. }
  1. export default {
  2. common: {
  3. searchButton: 'search',
  4. resetButton: 'reset',
  5. addButton: 'add',
  6. editButton: 'edit',
  7. removeButton: 'delete',
  8. batchRemoveButton: 'batch Remove',
  9. detailButton: 'detail',
  10. searchKey: 'Search Key',
  11. imports: 'Import',
  12. more: 'More',
  13. export: 'Export',
  14. },
  15. model: {
  16. user: 'user',
  17. org: 'org',
  18. pos: 'pos',
  19. role: 'role',
  20. bizUser: 'bizUser'
  21. },
  22. login: {
  23. signInTitle: 'Sign in',
  24. forgetPassword: 'Forget password',
  25. signIn: 'Sign in',
  26. signInOther: 'Sign in with',
  27. accountPlaceholder: 'Please input a user account',
  28. accountError: 'Please input a user account',
  29. PWPlaceholder: 'Please input a password',
  30. PWError: 'Please input a password',
  31. validLaceholder: 'Please input a valid',
  32. validError: 'Please input a valid',
  33. accountPassword: 'Account Password',
  34. phoneSms: 'Phone SMS',
  35. phonePlaceholder: 'Please input a phone',
  36. smsCodePlaceholder: 'Please input a SMS code',
  37. getSmsCode: 'SMS code',
  38. machineValidation: 'Machine Validation',
  39. sendingSmsMessage: 'Sending SMS Message',
  40. newPwdPlaceholder: 'Please input a new password',
  41. backLogin: 'Back Login',
  42. restPassword: 'Rest Password',
  43. emailPlaceholder: 'Please input a email',
  44. emailCodePlaceholder: 'Please input a Email code',
  45. restPhoneType: 'For phone rest',
  46. restEmailType: 'For email rest',
  47. sysName: 'management'
  48. },
  49. user: {
  50. userStatus: 'User Status',
  51. resetPassword: 'Reset Password',
  52. role: 'Role',
  53. batchExportButton: 'Batch Export',
  54. grantRole: 'Grant Role',
  55. grantResource: 'Grant Resource',
  56. grantPermission: 'Grant Permission',
  57. exportUserInfo: 'Export UserInfo',
  58. placeholderNameAndSearchKey: 'Please enter your name or keyword',
  59. placeholderUserStatus: 'Please select status',
  60. popconfirmDeleteUser: 'Are you sure you want to delete it?',
  61. popconfirmResatUserPwd: 'Are you sure you want to reset?'
  62. }
  63. }

二、配置页面

进入需要有翻译功能的页面进行配置,如 login.vue

1. HTML内容

  1. <template>
  2. <div class="login_background">
  3. <div class="login_main">
  4. <!-- 配置小地球显示选择中英文翻译 -->
  5. <div class="login_config">
  6. <a-dropdown>
  7. <global-outlined />
  8. <template #overlay>
  9. <a-menu>
  10. <a-menu-item
  11. v-for="item in lang"
  12. :key="item.value"
  13. :command="item"
  14. :class="{ selected: config.lang === item.value }"
  15. @click="configLang(item.value)"
  16. >
  17. {{ item.name }}
  18. </a-menu-item>
  19. </a-menu>
  20. </template>
  21. </a-dropdown>
  22. </div>
  23. <!-- 配置登录表单 -->
  24. <div class="login-form">
  25. <a-card>
  26. <div class="login-header">
  27. <div class="logo">
  28. <!-- 以下是通过sysBaseConfig动态获取图片和名称,此时名称是写死的并且需要中英翻译 -->
  29. <img :alt="sysBaseConfig.SNOWY_SYS_NAME" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
  30. <!-- <label>{{ sysBaseConfig.SNOWY_SYS_NAME }}</label>-->
  31. <!-- 因为配置文件的login中写了sysNme属性,使用{{ $t('login.sysName') }}进行显示 -->
  32. <label>{{ $t('login.sysName') }}</label>
  33. </div>
  34. </div>
  35. <a-tabs v-model:activeKey="activeKey">
  36. <!-- 以下使用到配置文件的login中accountPassword、accountPlaceholder、PWPlaceholder、validLaceholder、signIn字段实现中英文翻译 -->
  37. <a-tab-pane key="userAccount" :tab="$t('login.accountPassword')">
  38. <a-form ref="loginForm" :model="ruleForm" :rules="rules">
  39. <a-form-item name="account">
  40. <a-input
  41. v-model:value="ruleForm.account"
  42. :placeholder="$t('login.accountPlaceholder')"
  43. size="large"
  44. @keyup.enter="login"
  45. >
  46. <template #prefix>
  47. <UserOutlined class="login-icon-gray" />
  48. </template>
  49. </a-input>
  50. </a-form-item>
  51. <a-form-item name="password">
  52. <a-input-password
  53. v-model:value="ruleForm.password"
  54. :placeholder="$t('login.PWPlaceholder')"
  55. size="large"
  56. autocomplete="off"
  57. @keyup.enter="login"
  58. >
  59. <template #prefix>
  60. <LockOutlined class="login-icon-gray" />
  61. </template>
  62. </a-input-password>
  63. </a-form-item>
  64. <a-form-item name="validCode" v-if="captchaOpen === 'true'">
  65. <a-row :gutter="8">
  66. <a-col :span="17">
  67. <a-input
  68. v-model:value="ruleForm.validCode"
  69. :placeholder="$t('login.validLaceholder')"
  70. size="large"
  71. @keyup.enter="login"
  72. >
  73. <template #prefix>
  74. <verified-outlined class="login-icon-gray" />
  75. </template>
  76. </a-input>
  77. </a-col>
  78. <a-col :span="7">
  79. <img :src="validCodeBase64" class="login-validCode-img" @click="loginCaptcha" />
  80. </a-col>
  81. </a-row>
  82. </a-form-item>
  83. <a-form-item>
  84. <a href="/findpwd" style="color: #0d84ff">{{ $t('login.forgetPassword') }}?</a>
  85. </a-form-item>
  86. <a-form-item>
  87. <a-button type="primary" class="w-full" :loading="loading" round size="large" @click="login"
  88. >{{ $t('login.signIn') }}
  89. </a-button>
  90. </a-form-item>
  91. </a-form>
  92. </a-tab-pane>
  93. <!-- <a-tab-pane key="userSms" :tab="$t('login.phoneSms')" force-render>-->
  94. <!-- <phone-login-form />-->
  95. <!-- </a-tab-pane>-->
  96. </a-tabs>
  97. </a-card>
  98. </div>
  99. </div>
  100. </div>
  101. </template>

2. JS内容

  1. <script>
  2. import loginApi from '@/api/auth/loginApi'
  3. import phoneLoginForm from './phoneLoginForm.vue'
  4. import threeLogin from './threeLogin.vue'
  5. import smCrypto from '@/utils/smCrypto'
  6. import { required } from '@/utils/formRules'
  7. import { afterLogin } from './util'
  8. import config from '@/config'
  9. import configApi from '@/api/dev/configApi'
  10. import tool from '@/utils/tool'
  11. import { globalStore, iframeStore, keepAliveStore, viewTagsStore } from '@/store'
  12. import { mapActions, mapState } from 'pinia'
  13. export default {
  14. name: 'Login',
  15. components: {
  16. },
  17. data() {
  18. return {
  19. activeKey: 'userAccount',
  20. captchaOpen: config.SYS_BASE_CONFIG.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN,
  21. validCodeBase64: '',
  22. ruleForm: {
  23. account: '',
  24. password: '',
  25. validCode: '',
  26. validCodeReqNo: '',
  27. autologin: false
  28. },
  29. rules: {
  30. account: [required(this.$t('login.accountError'), 'blur')],
  31. password: [required(this.$t('login.PWError'), 'blur')]
  32. },
  33. loading: false,
  34. // 添加中英文切换功能(实现小地球图标的显示与切换)
  35. config: {
  36. lang: tool.data.get('APP_LANG') || this.$CONFIG.LANG,
  37. theme: tool.data.get('APP_THEME') || 'default'
  38. },
  39. // 小地球图标切换语言功能选择
  40. lang: [
  41. {
  42. name: '简体中文',
  43. value: 'zh-cn'
  44. },
  45. {
  46. name: 'English',
  47. value: 'en'
  48. }
  49. ]
  50. }
  51. },
  52. computed: {
  53. ...mapState(globalStore, ['sysBaseConfig']),
  54. },
  55. watch: {
  56. 'config.theme': function (val) {
  57. document.body.setAttribute('data-theme', val)
  58. },
  59. // 添加中英文切换功能的配置实现(html中config.lang === item.value)
  60. 'config.lang': function (val) {
  61. this.$i18n.locale = val
  62. tool.data.set('APP_LANG', val)
  63. }
  64. },
  65. created() {
  66. this.clearViewTags()
  67. this.clearKeepLive()
  68. this.clearIframeList()
  69. },
  70. mounted() {
  71. let formData = ref(config.SYS_BASE_CONFIG)
  72. configApi.configSysBaseList().then((data) => {
  73. if (data) {
  74. data.forEach((item) => {
  75. formData.value[item.configKey] = item.configValue
  76. })
  77. this.captchaOpen = formData.value.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN
  78. tool.data.set('SNOWY_SYS_BASE_CONFIG', formData.value)
  79. this.setSysBaseConfig(formData.value)
  80. this.refreshSwitch()
  81. }
  82. })
  83. },
  84. methods: {
  85. ...mapActions(keepAliveStore, ['clearKeepLive']),
  86. ...mapActions(viewTagsStore, ['clearViewTags']),
  87. ...mapActions(iframeStore, ['clearIframeList']),
  88. ...mapActions(globalStore, ['setSysBaseConfig']),
  89. // 点击语言后实现切换的方法,在HTML中@click="configLang(item.value)"
  90. configLang(key) {
  91. this.config.lang = key
  92. },
  93. // 通过开关加载内容
  94. refreshSwitch() {
  95. // 判断是否开启验证码
  96. if (this.captchaOpen === 'true') {
  97. // 加载验证码
  98. this.loginCaptcha()
  99. // 加入校验
  100. this.rules.validCode = [required(this.$t('login.validError'), 'blur')]
  101. }
  102. },
  103. // 获取验证码
  104. loginCaptcha() {
  105. loginApi.getPicCaptcha().then((data) => {
  106. this.validCodeBase64 = data.validCodeBase64
  107. this.ruleForm.validCodeReqNo = data.validCodeReqNo
  108. })
  109. },
  110. // 用户名密码登录
  111. async login() {
  112. this.$refs.loginForm.validate().then(async () => {
  113. this.loading = true
  114. const loginData = {
  115. account: this.ruleForm.account,
  116. // 密码进行SM2加密,传输过程中看到的只有密文,后端存储使用hash
  117. password: smCrypto.doSm2Encrypt(this.ruleForm.password),
  118. validCode: this.ruleForm.validCode,
  119. validCodeReqNo: this.ruleForm.validCodeReqNo
  120. }
  121. // 获取token
  122. try {
  123. const loginToken = await loginApi.login(loginData)
  124. await afterLogin(loginToken)
  125. } catch (err) {
  126. this.loading = false
  127. this.loginCaptcha()
  128. }
  129. })
  130. },
  131. }
  132. }
  133. </script>
  134. <!-- 引入该login页面的样式配置 -->
  135. <style lang="less">
  136. @import 'login';
  137. </style>

三、主要步骤

方法一

① 页面上添加小地球选择语言显示选项

  1. <a-dropdown>
  2. <global-outlined />
  3. <template #overlay>
  4. <a-menu>
  5. <a-menu-item
  6. v-for="item in lang"
  7. :key="item.value"
  8. :command="item"
  9. :class="{ selected: config.lang === item.value }"
  10. @click="configLang(item.value)"
  11. >
  12. {{ item.name }}
  13. </a-menu-item>
  14. </a-menu>
  15. </template>
  16. </a-dropdown>

② 实现添加的小地球功能

  1. export default {
  2. data() {
  3. return {
  4. lang: [],
  5. config: {
  6. // lang 属性的值是通过 tool.data.get('APP_LANG') || this.$CONFIG.LANG 表达式获取的
  7. // 如果 tool.data.get('APP_LANG') 返回一个真值(非空、非undefined、非false等),则使用该值作为 lang 的值;否则,使用 this.$CONFIG.LANG 作为默认值。
  8. lang: tool.data.get('APP_LANG') || this.$CONFIG.LANG,
  9. }
  10. }
  11. },
  12. watch: { // 当 config.lang 发生变化时,即语言设置发生变化时,触发绑定的回调函数
  13. 'config.lang': function (val) {
  14. // 将新的语言设置应用到 $i18n 实例中,以改变应用程序的语言
  15. this.$i18n.locale = val
  16. // 将新的语言设置存储到 tool.data 中,以便在后续访问或导航到新页面时可以获取并应用语言设置
  17. tool.data.set('APP_LANG', val)
  18. }
  19. },
  20. methods: {
  21. // 更新 config.lang 的值
  22. configLang(key) {
  23. this.config.lang = key
  24. },
  25. }
  26. }

③ 实现语言的切换选项显示

在上述 return {} 中的 lang: [] 填入数据

  1. lang: [
  2. {
  3. name: '简体中文',
  4. value: 'zh-cn'
  5. },
  6. {
  7. name: 'English',
  8. value: 'en'
  9. }
  10. ]
方法二

① 页面上添加小地球选择语言显示选项

  1. <a-dropdown v-if="!ismobile" class="panel-item">
  2. <global-outlined />
  3. <template #overlay>
  4. <a-menu :selected-keys="lang">
  5. <a-menu-item key="zh-cn" @click="handleIn18('zh-cn')">
  6. <span>简体中文</span>
  7. </a-menu-item>
  8. <a-menu-item key="en" @click="handleIn18('en')">
  9. <span>English</span>
  10. </a-menu-item>
  11. </a-menu>
  12. </template>
  13. </a-dropdown>

② 在methods中添加点击选项后的切换功能

  1. data() {
  2. return {
  3. lang: [],
  4. }
  5. },
  6. methods: {
  7. // 设置多语言语种
  8. handleIn18(key) {
  9. // 将 this.lang 数据初始化为空数组
  10. this.lang = []
  11. // 将传入的 key 值添加到 this.lang 数组中,用于保存语言数据
  12. this.lang.push(key)
  13. // 将语言设置应用到 $i18n 实例中,以改变应用程序的语言
  14. this.$i18n.locale = key
  15. // 将语言设置存储到 this.$TOOL.data 中,使用键名 'APP_LANG' 来标识。这样可以将语言设置持久化,以便在后续访问或导航到新页面时可以获取并应用语言设置
  16. this.$TOOL.data.set('APP_LANG', key)
  17. },
  18. }

四、多页面使用相同语言

在以上方法中都将使得所有的页面用同一种语言,因为:

方法一中

  1. 'config.lang': function (val) {
  2. // 将新的语言设置应用到 $i18n 实例中,以改变应用程序的语言
  3. this.$i18n.locale = val
  4. // 将新的语言设置存储到 tool.data 中,以便在后续访问或导航到新页面时可以获取并应用语言设置
  5. tool.data.set('APP_LANG', val)
  6. }

方法二中

  1. // 将语言设置应用到 $i18n 实例中,以改变应用程序的语言
  2. this.$i18n.locale = key
  3. // 将语言设置存储到 this.$TOOL.data 中,使用键名 'APP_LANG' 来标识。这样可以将语言设置持久化,以便在后续访问或导航到新页面时可以获取并应用语言设置
  4. this.$TOOL.data.set('APP_LANG', key)

lang 语言选择列表:

注意:如果想要获取的 lang 语言数据列表一致,而不需要自己重新定义的话,可以在此基础上添加如下代码:

  1. created() {
  2. // 获取默认语言,根据之前存储的语言设置或默认的语言设置来确定数组的内容
  3. // this.$TOOL.data.get('APP_LANG') 从 this.$TOOL.data 中获取键名为 'APP_LANG' 的值,即之前存储的语言设置。如果之前没有存储语言设置,这个表达式的结果将为 undefined
  4. // this.$CONFIG.LANG 是一个变量,表示默认的语言设置
  5. // this.lang 的初始化通过 new Array() 创建一个新的数组。数组内容是通过三元运算符 || 进行判断
  6. // 如果 this.$TOOL.data.get('APP_LANG') 有值,则使用它作为数组的唯一元素;否则,使用 this.$CONFIG.LANG 作为数组的唯一元素。
  7. this.lang = new Array(this.$TOOL.data.get('APP_LANG') || this.$CONFIG.LANG)
  8. },

五、$t('') 无效

在一般情况下,只要在全局定义了 vue-i18(即配置了vue-i8),那么一般情况下是有用的,不管是哪种使用方式,如下两种示例:

  1. <label>{{ $t('login.sysName') }}</label>
  2. 只是用label举例并不表示只有label有用
  1. 以下是 :tab、:placeholder 的举例
  2. <a-tab-pane key="userAccount" :tab="$t('login.accountPassword')">
  3. <a-form ref="loginForm" :model="ruleForm" :rules="rules">
  4. <a-form-item name="account">
  5. <a-input
  6. v-model:value="ruleForm.account"
  7. :placeholder="$t('login.accountPlaceholder')"
  8. size="large"
  9. @keyup.enter="login"
  10. >
  11. <template #prefix>
  12. <UserOutlined class="login-icon-gray" />
  13. </template>
  14. </a-input>
  15. </a-form-item>
  16. <a-form-item name="password">
  17. <a-input-password
  18. v-model:value="ruleForm.password"
  19. :placeholder="$t('login.PWPlaceholder')"
  20. size="large"
  21. autocomplete="off"
  22. @keyup.enter="login"
  23. >
  24. <template #prefix>
  25. <LockOutlined class="login-icon-gray" />
  26. </template>
  27. </a-input-password>
  28. </a-form-item>
  29. </a-form>

以上都属于正确演示,都是有效的,但是如果使用第二种方式,且直接用tab、:placeholder而没有加冒号 : 则失效,包括其他的比如 :title 等都要加 : 才有效。

原因:冒号“:”其实是v-bind的缩写,冒号后面绑定为变量,会动态变化的值;一般属性后面为常量。由于当前需要切换语言所以是动态的值绑定,需要冒号:或v-bind:才可变。

六、遍历List进行中英文切换

以下假设List集合中的字段为中文,英文的话更简单,就不举例了。

zh-cn.js

  1. menu: {
  2. '系统首页': '系统首页',
  3. '个人中心' : '个人中心',
  4. '其他': '其他'
  5. }
  6. 如果集合中的数据是中文就写中文的字段并使用单引号,如果是英文就不要单引号

en.js

  1. menu: {
  2. '系统首页': 'System Home',
  3. '个人中心' : 'Personal Center',
  4. '其他': 'Other'
  5. }
  6. 如果集合中的数据是中文就写中文的字段并使用单引号,如果是英文就不要单引号

HTML中

  1. <!-- v-for="shortcut in shortcutList" 遍历集合shortcutList中的每一个数据并将其对象命名为shortcut -->
  2. <a-col :span="6" :key="shortcut.id" v-for="shortcut in shortcutList">
  3. <shortcutCard
  4. :icon="shortcut.icon ? shortcut.icon : 'menu-outlined'"
  5. :label="$t('menu.'+shortcut.title)"
  6. @click="leaveFor(shortcut.path)"
  7. />
  8. </a-col>
  9. 其中 :label="$t('menu.'+shortcut.title)" 即引入menu中变量shortcut.title的值的数据

显示效果如下(List中没有的数据不会显示,比如像上面说些的'其他'字段):

所以应用主要是:

  1. {{ $t('menu.' + shortcut.title) }}
  2. 或者
  3. :title="$t('menu.' + shortcut.title)"

七、在HTML、JS中使用$t()

1、HTML中使用$t()
  1. {{ $t('menu.title'}}
  2. 或者
  3. :title="$t('menu.title')"
2、JS中使用$t()

使用 this 引用 $t() 即 this.$t() ,注意JS中的 属性 :this.$t() 中属性是不需要冒号“:”的!!!

  1. handleUser(key) {
  2. // 个人中心页面跳转
  3. if (key === 'uc') {
  4. router.push({ path: '/usercenter' })
  5. }
  6. // 点击清理缓存
  7. if (key === 'clearCache') {
  8. this.$confirm({
  9. // 提示
  10. title: this.$t('login.info'),
  11. // 提示内容
  12. content: this.$t('login.delCache'),
  13. icon: createVNode(ExclamationCircleOutlined),
  14. maskClosable: false,
  15. // 确认按钮
  16. okText: this.$t('button.sure'),
  17. // 取消按钮
  18. cancelText: this.$t('button.cancel'),
  19. onOk() {
  20. message.loading('正在清理中...', 1)
  21. tool.data.clear()
  22. setTimeout(() => {
  23. router.replace({ path: '/login' })
  24. location.reload()
  25. }, 100)
  26. },
  27. onCancel() {}
  28. })
  29. }
  30. // 退出登录
  31. if (key === 'outLogin') {
  32. this.$confirm({
  33. // 提示
  34. title: this.$t('login.info'),
  35. // 提示内容
  36. content: this.$t('login.logoutMessage'),
  37. icon: createVNode(ExclamationCircleOutlined),
  38. maskClosable: false,
  39. // 确认按钮
  40. okText: this.$t('button.sure'),
  41. // 取消按钮
  42. cancelText: this.$t('button.cancel'),
  43. onOk() {
  44. // 取得缓存中的token
  45. const token = tool.data.get('TOKEN')
  46. const param = {
  47. token: token
  48. }
  49. message.loading('退出中...', 1)
  50. loginApi
  51. .logout(param)
  52. .then(() => {
  53. // message.c
  54. // 清理掉个人的一些信息
  55. tool.data.remove('TOKEN')
  56. tool.data.remove('USER_INFO')
  57. tool.data.remove('MENU')
  58. tool.data.remove('PERMISSIONS')
  59. router.replace({ path: '/login' })
  60. })
  61. .catch(() => {
  62. tool.data.clear()
  63. router.replace({ path: '/login' })
  64. location.reload()
  65. })
  66. },
  67. onCancel() {}
  68. })
  69. }
  70. },

以下是调用 handleUser 方法的HTML:

  1. <a-menu>
  2. <a-menu-item key="uc" @click="handleUser('uc')">
  3. <UserOutlined style="margin-right: 8px" />
  4. <span>{{ $t('index.userCenter') }}</span>
  5. </a-menu-item>
  6. <a-menu-item key="clearCache" @click="handleUser('clearCache')">
  7. <loading3-quarters-outlined style="margin-right: 8px" />
  8. <span>{{ $t('index.cleanCache') }}</span>
  9. </a-menu-item>
  10. <a-menu-divider />
  11. <a-menu-item key="outLogin" @click="handleUser('outLogin')">
  12. <export-outlined style="margin-right: 8px" />
  13. <span>{{ $t('index.logout') }}</span>
  14. </a-menu-item>
  15. </a-menu>

效果如图:

3、JS里的HTML部分中使用$t()
  1. renderAlert() {
  2. // 绘制统计列数据
  3. // eslint-disable-next-line no-unused-vars
  4. const needTotalItems = this.needTotalList.map((item) => {
  5. return (
  6. <span className="mr-3">
  7. {item.title} 总计{' '}
  8. <a className="font-6">{!item.customRender ? item.total : item.customRender(item.total)}</a>
  9. </span>
  10. )
  11. })
  12. // 绘制 清空 按钮
  13. // eslint-disable-next-line no-unused-vars
  14. const clearItem =
  15. typeof this.alert === 'boolean' && this.alert
  16. ? this.renderClear(this.clearSelected)
  17. : typeof this.alert.clear === 'function'
  18. ? this.renderClear(this.alert.clear)
  19. : null
  20. // 绘制 alert 组件
  21. if (alert) {
  22. const message = (
  23. <div>
  24. <span className="mr-3">
  25. 已选择 : <a className="font-6">{this.selectedRows.length}</a>
  26. </span>
  27. {needTotalItems}
  28. {clearItem}
  29. </div>
  30. )
  31. return <a-alert showIcon class="mb-4" message={message} />
  32. }
  33. },
  34. renderClear(callback) {
  35. if (this.selectedRowKeys.length <= 0) return null
  36. return (
  37. <a
  38. className="ml-6"
  39. onClick={() => {
  40. callback()
  41. this.clearSelected()
  42. }}>
  43. {' '}
  44. 清空{' '}
  45. </a>
  46. )
  47. },

切换多语言,修改代码:

  1. renderClear(callback) {
  2. if (this.selectedRowKeys.length <= 0) return null
  3. return (
  4. <a
  5. className="ml-6"
  6. onClick={() => {
  7. callback()
  8. this.clearSelected()
  9. }}>
  10. {' '}
  11. <!-- 修改了此处 -->
  12. { this.$t('button.clean') }{' '}
  13. </a>
  14. )
  15. },
  16. renderAlert() {
  17. // 绘制统计列数据
  18. // eslint-disable-next-line no-unused-vars
  19. const needTotalItems = this.needTotalList.map((item) => {
  20. return (
  21. <span className="mr-3">
  22. {item.title} 总计{' '}
  23. <a className="font-6">{!item.customRender ? item.total : item.customRender(item.total)}</a>
  24. </span>
  25. )
  26. })
  27. // 绘制 清空 按钮
  28. // eslint-disable-next-line no-unused-vars
  29. const clearItem =
  30. typeof this.alert === 'boolean' && this.alert
  31. ? this.renderClear(this.clearSelected)
  32. : typeof this.alert.clear === 'function'
  33. ? this.renderClear(this.alert.clear)
  34. : null
  35. // 绘制 alert 组件
  36. if (alert) {
  37. const message = (
  38. <div>
  39. <span className="mr-3">
  40. <!-- 修改了此处 -->
  41. { this.$t('table.selected') } : <a className="font-6">{this.selectedRows.length}</a>
  42. </span>
  43. {needTotalItems}
  44. {clearItem}
  45. </div>
  46. )
  47. return <a-alert showIcon class="mb-4" message={message} />
  48. }
  49. },

4、对antd的<s-table>表头国际化i18n使用$t()

原始表格HTML:

  1. <s-table
  2. ref="table"
  3. :columns="columns"
  4. :data="loadData"
  5. :alert="options.alert.show"
  6. bordered
  7. :row-key="(record) => record.id"
  8. :tool-config="toolConfig"
  9. :row-selection="options.rowSelection"
  10. >
  11. <template #operator class="table-operator">
  12. <a-space>
  13. <a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('sysSeriesAdd')">
  14. <template #icon><plus-outlined /></template>
  15. {{ $t('button.add') }}
  16. </a-button>
  17. <xn-batch-delete
  18. v-if="hasPerm('sysSeriesBatchDelete')"
  19. :selectedRowKeys="selectedRowKeys"
  20. @batchDelete="deleteBatchSysSeries"
  21. />
  22. </a-space>
  23. </template>
  24. <template #bodyCell="{ column, record }">
  25. <template v-if="column.dataIndex === 'picUrl'">
  26. <img :src="record.picUrl" alt="图片" style="width: 80px; height: 100px">
  27. </template>
  28. <template v-if="column.dataIndex === 'isOver'">
  29. {{ $TOOL.dictTypeData('COMMON_YES_OR_NO', record.isOver) }}
  30. </template>
  31. <template v-if="column.dataIndex === 'online'">
  32. {{ $TOOL.dictTypeData('COMMON_YES_OR_NO', record.online) }}
  33. </template>
  34. <template v-if="column.dataIndex === 'action'">
  35. <a-space>
  36. <a @click="batchBind(record)" v-if="hasPerm('sysSeriesBind')">{{ $t('button.bind') }}</a>
  37. <a-divider type="vertical" v-if="hasPerm(['sysSeriesBind'], 'and')" />
  38. <a @click="formRef.onOpen(record)" v-if="hasPerm('sysSeriesEdit')">{{ $t('common.editButton') }}</a>
  39. <a-divider type="vertical" v-if="hasPerm(['sysSeriesEdit', 'sysSeriesDelete'], 'and')" />
  40. <a-popconfirm :title="$t('user.popconfirmDeleteUser')" @confirm="deleteSysSeries(record)">
  41. <a-button type="link" danger size="small" v-if="hasPerm('sysSeriesDelete')">{{ $t('button.delete') }}</a-button>
  42. </a-popconfirm>
  43. </a-space>
  44. </template>
  45. </template>
  46. </s-table>

对表头以数组形式定义:

  1. const columns = [
  2. {
  3. title: '剧名',
  4. dataIndex: 'name'
  5. },
  6. {
  7. title: '总集数',
  8. dataIndex: 'totalVideo'
  9. },
  10. {
  11. title: '是否完结',
  12. dataIndex: 'isOver'
  13. },
  14. {
  15. title: '封面图',
  16. dataIndex: 'picUrl',
  17. },
  18. {
  19. title: '扩展信息',
  20. dataIndex: 'extJson',
  21. ellipsis: true
  22. },
  23. {
  24. title: '是否上架',
  25. dataIndex: 'online'
  26. },
  27. {
  28. title: '创建时间',
  29. dataIndex: 'createTime'
  30. },
  31. {
  32. title: '创建用户',
  33. dataIndex: 'createrName'
  34. }
  35. ]

因为JS部分使用的是setup,并不是export default,在其中引用 $t 或者 this.$t 都报错,找不到 $t,所以国际化之后需要引入i18n,不是内部下载的vue-i18n:import { useI18n } from "vue-i18n";,而是自己编写的配置:import i18n from '@/locales';,

如图:再:const { t } = i18n.global;,

代码如下:

  1. <script setup name="xxx">
  2. // 从 @/locales 中导入一个名为 i18n 的对象(locales 文件用于处理国际化(i18n)相关的配置和逻辑)
  3. import i18n from '@/locales'
  4. // 从 i18n.global 对象中解构赋值一个名为 t 的变量
  5. // t 是一个函数或方法,用于国际化文本的翻译
  6. // i18n.global 是一个全局的 i18n 实例,包含了初始化和配置的信息。
  7. const { t } = i18n.global
  8. // 测试,刷新是识别到语言并进行打印
  9. console.log("打印出:", t('model.user'))
  10. </script>

切换后刷新如下图:

由于以上方法只能在页面刷新是获取到当前语言,所以需要在切换语言是进行页面刷新:

  1. <a-dropdown>
  2. <global-outlined />
  3. <template #overlay>
  4. <a-menu :selected-keys="lang">
  5. <a-menu-item key="zh-cn" @click="handleIn18('zh-cn')">
  6. <span>简体中文</span>
  7. </a-menu-item>
  8. <a-menu-item key="en" @click="handleIn18('en')">
  9. <span>English</span>
  10. </a-menu-item>
  11. </a-menu>
  12. </template>
  13. </a-dropdown>
  1. // 设置多语言语种
  2. handleIn18(key) {
  3. this.lang = []
  4. this.lang.push(key)
  5. this.$i18n.locale = key
  6. this.$TOOL.data.set('APP_LANG', key)
  7. // 页面刷新
  8. window.location.reload()
  9. },

之后的使用直接 t('xxx.xxx') 即可:

  1. const columns = [
  2. {
  3. title: t('table.seriesName'),
  4. dataIndex: 'name',
  5. },
  6. {
  7. title: t('table.allPlay'),
  8. dataIndex: 'totalVideo'
  9. },
  10. {
  11. title: t('table.isOver'),
  12. dataIndex: 'isOver'
  13. },
  14. {
  15. title: t('table.image'),
  16. dataIndex: 'picUrl',
  17. },
  18. {
  19. title: t('form.expand'),
  20. dataIndex: 'extJson',
  21. ellipsis: true
  22. },
  23. {
  24. title: t('table.isOn'),
  25. dataIndex: 'online'
  26. },
  27. {
  28. title: t('table.createTime'),
  29. dataIndex: 'createTime'
  30. },
  31. {
  32. title: t('table.createUser'),
  33. dataIndex: 'createrName'
  34. }
  35. ]

将判断部分也进行修改:

  1. <template #bodyCell="{ column, record }">
  2. <template v-if="column.dataIndex === 'picUrl'">
  3. <img :src="record.picUrl" alt="图片" style="width: 80px; height: 100px">
  4. </template>
  5. <template v-if="column.dataIndex === 'isOver'">
  6. <div v-if="record.isOver === 'YES'">{{ $t('table.yes') }}</div>
  7. <div v-else>{{ $t('table.no') }}</div>
  8. </template>
  9. <template v-if="column.dataIndex === 'online'">
  10. <div v-if="record.online === 'YES'">{{ $t('table.yes') }}</div>
  11. <div v-else>{{ $t('table.no') }}</div>
  12. </template>
  13. </template>
5、关于setup函数中的i18n的使用

即第4点antd中描述的那样:

  1. // 引入自己配置的关于i18n的文件夹
  2. import i18n from "@/locales";
  3. const { t } = i18n.global

之后所有的需要 $t 或者 this.$t 的地方都使用 t 即可。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号