Skip to content
文章目录

i18n多语言

安装以及版本

pnpm add vue-i18n@9

配置

i18nFormat.ts

ts
// 配置日期格式化
export const datetimeFormats = {
  'en-US': {
    short: {
      year: 'numeric',
      month: 'short',
      day: 'numeric',
    },
    long: {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      weekday: 'long',
      hour: 'numeric',
      minute: 'numeric',
    },
  },
  'zh-CN': {
    short: {
      year: 'numeric',
      month: 'short',
      day: 'numeric',
    },
    long: {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      weekday: 'long',
      hour: 'numeric',
      minute: 'numeric',
      // 默认是24小时制,如果想显示为12小时制,则进行下面这个属性的设置
      //   hour12: true,
    },
  },
}

// 配置数字相关的格式化
export const numberFormats = {
  'en-US': {
    currency: {
      style: 'currency',
      currency: 'USD',
    },
    decimal: {
      style: 'decimal',
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    },
    percent: {
      style: 'percent',
      useGrouping: false,
    },
  },
  'zh-CN': {
    currency: {
      style: 'currency',
      currency: 'RMB',
    },
    decimal: {
      style: 'decimal',
      minimumSignificantDigits: 3,
      maximumSignificantDigits: 5,
    },
    percent: {
      style: 'percent',
      useGrouping: false,
    },
  },
}

zh-CN.json

json
{
  "test": "测试:新项",
  "person": {
    "name": "人员姓名"
  }
}

index.ts

ts
import zhCN from './lang/shared/zh-CN.json'
import enUS from './lang/shared/en-US.json'

export { datetimeFormats, numberFormats } from './i18nFormat'
export const sharedMessages = {
  'zh-CN': zhCN,
  'en-US': enUS,
}

i18n.ts

ts
import { nextTick } from 'vue'
import { createI18n } from 'vue-i18n'
import { sharedMessages, datetimeFormats, numberFormats } from '@/i18n/index'

export const i18n = createI18n({
  // @ts-ignore
  datetimeFormats,
  // 当前语言
  locale: 'zh-CN',
  // 当语言资源不存在时,使用的默认替换语言
  fallbackLocale: 'zh-CN',
  // 通过composition api方式使用,则需要将legacy设置为false
  legacy: false,
  messages: sharedMessages,
  numberFormats,
})

/**
 * 切换语言环境
 *
 * @param   {string}  locale  语言标识
 *
 * @return  {[type]}          [return description]
 */
export function setI18nLanguage(locale: string) {
  if (i18n.mode === 'legacy') {
    i18n.global.locale = locale
  } else {
    // @ts-ignore
    i18n.global.locale.value = locale
  }
  /**
   * NOTE:
   * If you need to specify the language setting for headers, such as the `fetch` API, set it here.
   * The following is an example for axios.
   *
   * axios.defaults.headers.common['Accept-Language'] = locale
   */
  document.documentElement.setAttribute('lang', locale)
}

/**
 * 动态加载多语言资源
 *
 * @param   {string}  locale  语言标识
 *
 */
export async function loadLocaleMessages(locale: string) {
  // 这里可以换成axios从服务器加载
  const messages = await import(/* webpackChunkName: "locale-[request]" */ `./i18n/lang/${locale}.json`)
  // 用新的语言数据覆盖已有的语言数据(要使用哪种自己选择)
  //   i18n.global.setLocaleMessage(locale, messages.default)
  return nextTick()
}

main.ts

ts
import { createApp } from 'vue'
import { setupI18n } from './i18n'

const i18n = setupI18n()
const app = createApp(App)

app.use(i18n)
app.mount('#app')

在 vue 组件(composition API)中使用

vue
<script setup lang="ts">
import { loadLocaleMessages, setI18nLanguage } from '@/i18n'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
const { t, locale, getLocaleMessage, d, n, availableLocales } = useI18n()
function onClick() {
  ElMessage({
    message: `${t('operate.add')}-${t('person.name')}`,
    type: 'success',
  })
}
function onLoadI18nResource(lang: string) {
  loadLocaleMessages(lang)
}
function onChangeLang(lang: string) {
  setI18nLanguage(lang)
}
</script>

<template>
  <el-button @click="onLoadI18nResource('zh-CN')"> 加载额外的中文i18n资源 </el-button>
  <el-button @click="onLoadI18nResource('en-US')"> 加载额外的英文i18n资源 </el-button>
  <el-button type="primary" @click="onClick">按钮</el-button>
  <p>{{ d(new Date(), 'long') }}</p>
  <p>{{ d(new Date(), 'short') }}</p>
  <p>{{ n(3250.86, 'currency') }}</p>
  <p>{{ n(0.99123, 'percent') }}</p>
  <p>{{ n(0.99123, 'percent', { minimumFractionDigits: 2 }) }}</p>
  <p>{{ n(12.11612345, 'decimal') }}</p>
  <p>{{ n(12145281111, 'decimal', 'en-US') }}</p>
  <p>当前使用语言:{{ locale }}</p>
  <p>可用语言:{{ availableLocales }}</p>
  两种语言切换方式:
  <h3>方式一</h3>
  <div>
    <el-button @click="onChangeLang('zh-CN')">切换为中文</el-button>
    <el-button @click="onChangeLang('en-US')">切换为英文</el-button>
  </div>
  <h3>方式二</h3>
  <select v-model="locale">
    <option v-for="oneLocale in availableLocales" :key="`locale-${oneLocale}`" :value="oneLocale">
      {{ oneLocale }}
    </option>
  </select>
  <p>{{ getLocaleMessage('zh-CN') }}</p>
  <p>{{ getLocaleMessage('en-US') }}</p>
  <p>{{ t('operate.add') }}</p>
  <p>{{ t('hello') }}</p>
  <p>{{ t('test') }}</p>
  <p>{{ t('person.name') }}</p>
  {{ $t('operate.add') }}
</template>

<style scoped></style>

特殊字符处理

如下字符在 i18n 中属于特殊字符

  • {
  • }
  • @
  • $
  • |

处理方式

ts
const messages = {
  'zh-CN': {
    address: "{account}{'@'}{domain}",
  },
}

使用{''}对特殊字符串进行包裹。如上面示例中的{'@'}

xml
<p>邮箱: {{ t('address', { account: 'foo', domain: 'domain.com' }) }}</p>

输出结果

xml
<p>邮箱: foo@domain.com</p>

多语言资源值中包含 html 标签的处理

ts
const messages = {
  en: {
    message: {
      hello: '你好 <br> 世界',
    },
  },
}

如果需要按照 html 标签输出, 则需要使用v-html指令

xml
<p v-html="$t('message.hello')"></p>

输入结果

xml
<p>hello
<!--<br> exists but is rendered as html and not a string-->
world</p>

useScope: 'local'useScope: 'global'的区别

不指定useScope值时,默认就是global

INFO

useScope: 'local' 一般用不到,可以不管

ts
useI18n({
  useScope: 'local',
})

区别就是当你指定为local时,你必须在当前组件,再另外配置一套 i18n 的配置,该配置只会再这个组件中生效,且该组件也只会使用你在这个组件中声明的配置,而忽略全局的配置。同时,你在这个组件中对i18n组件的修改,都只是对你这个单独的i18n生效,不会影响全局的i18n配置