十分钟入门前端最佳的语言国际化方案

1,641 阅读5分钟

首先,我们先来介绍一下,什么是语言国际化。i18n(其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数)是“国际化”的简称。在资讯领域,国际化(i18n)指让网站无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。简单来说,就是你的网站可以有多种语言。

在项目有语言国际化需求的时候,我们通常会选择相应的库,比如使用Vue框架时,我们会选择vue-i18n,当我们使react时,我们也许会使用react-int等,但在具体实践中,往往只是会用这个库还不行,你还得解决如何同步UI框架的组件语言国际化,以及如何处理从浏览器获取默认语言同步问题。总的来说,要充分考虑到这三个环境的语言问题。

现在我就以在Vue项目下,使用vue-i18n整个框架,并且同步更改UI框架Vuetify的组件语言国际化来作为例子,一步一步实现整个项目的语言国际化。

安装

npm
npm install --save vue-i18n@next
yarn
yarn add vue-i18n@next

1.1 定义好语言模版

安装好这个库之后,我们可以先在src目录下新建一个i18n文件夹,然后在messages文件夹下面定义好语言模版(名称没有硬性要求,后面会说到命名方案),如图所示:

1.2 然后,将Vue-i18n引入到Vue项目中

import Vue from 'vue'
import VueI18n from 'vue-i18n'

import Store from '@/store'
import messages from './messages/en'  //默认语言 
Vue.use(VueI18n)
new Vue({
  router,
  i18n,
  vuetify,
  store,
  render: (h) => h(App)
}).$mount('#app')

这样注入到Vue对象中,我们就可以这样更改语言了,通过

i18n.locale=lang 去更改你需要的语言,就可以自动获取相应的语言了,在模版中使用$t('hello')来翻译。

vue-i18n更多使用姿势看这里:kazupon.github.io/vuei18n/int…

我就不过多讲解了,我主要说一下与UI框架的同步、异步加载和默认语言处理问题。

这里为了更好的性能,默认只加载一种语言,因为当语言过多时,全部加载存在性能问题,所以采取了异步加载语言模版的方案。

在i18n文件下,新建index.ts入口文件。

异步加载代码如下:

/**
 * @functin setLang - 设置应用语言
 * @param {string} lang - 要设置语言的名称
 * @return {string} lang - 语言名称
 */
function _set(lang: string): string {
  i18n.locale = lang
  // i18n.fallbackLocale = lang
  Axios.defaults.headers.common['Accept-Language'] = lang
  Store.__s('app.language', lang)
  return lang
}

/**
 * @functin loadLangAsync - 异步加载语言模版
 * @param {string} lang - 需要加载的语言名称
 * @return {string} lang - 加载好的语言名称
 */
export function setLang(lang: string): Promise<string> {
  if (__LOCALE__ !== __LANGS__) { // ___LOCALE__  是本地已经加载好的语言模版数组
    if (!__LANGS__.includes(lang)) { // 本地没有,则加载
      return import(/* webpackChunkName: "lang-[request]" */ `@/i18n/messages/${lang}`).then(msgs => {
        i18n.setLocaleMessage(lang, msgs.default[lang])
        __LANGS__.push(lang)
        return _set(lang)
      })
    }
    return Promise.resolve(_set(lang))
  }
  return Promise.resolve(lang)
}

1.3 持久化和默认语言问题

const __LANGS__ = ['enUS'] // 默认语言
let __LOCALE__ = Store.state.app.language // 获取本地存储的语言
// 首次加载没有存储的语言,则默认使用浏览器的语言
if (!__LOCALE__) {
  __LOCALE__ = window.navigator.language.split('-').join('')
  Store.commit('SETLANG', __LOCALE__)
}

设置好语言应该添加到vuex的store里面,并且同步到localstorage,做好语言持久化处理。

这时候,异步加载和默认语言处理问题已经解决好了,现在我们再来说同步Vuetify组件国际化问题。

1.4 同步UI组件国际化

不同UI框架都有自己的组件国际化API,vuetify提供的是

this.$vuetify.lang.current = lang

因此,我们只要调用vue-i18n的setLang方法时,同步调用这个方法就好了。

 lang() {
      setLang(this.d_language)
      this.setVuetifyLang(this.d_language)
 }

这时,又有一个命名的问题,我们的vue-i18n这个插件可以自己命名语言模版名,但是vuetify的语言名已经命名好了,无法更改的,如下:

因此,这里需要我们的模版名称应该和这里的命名一致,如简体中文的模版名应该为zhHans,但是我们默认语言处理是以浏览器的语言来处理的,浏览器的语言名使用SO 639-1标准 为各种语言定义了缩略词,就是以一种简称代替某种语言,如英文用en,中文用zh,部分列表如下:

因为名称不一致,为了统一命名,我们还应该添加一个翻译表,以浏览器的语言为标准,建立语言模版名,并且将与vutify组件不一致的名称翻译过来,如浏览器的enUS对应vuetify的en,部分翻译如下:

/**
 *  使 I18n and vuetify 保持一致
 */
export const TranslateTable: TranslateItem = {
  enUS: 'en',
  zhCN: 'zhHans',
  zhTW: 'zhHant',
  ja: 'ja',
  ko: 'ko'
}

当然,vuetify的模版语言可以使可以按需加载的

/**
 * the vuetify-i18n language list
 * see more :  https://vuetifyjs.com/en/customization/internationalization/
 */
const localList = ['zhHans', 'en', 'ko', 'zhHant', 'ja'] //加载自己需要的语言
// webpack的api,自动模块化加载
const files = require.context('vuetify/lib/locale/', true, /\.js$/)
const locales: Array<any> = []
files.keys().forEach(key => {
  const languageName = key
    .replace('./', '')
    .replace('.js', '')
    .replace('-', '')
  if (localList.includes(languageName)) {
    locales.push({ [languageName]: files(key).default })
  }
})

Tips:这里有个小技巧,当默认语言为英语时,我们可以在默认不添加翻译,如this.$t("hello"),当没有对应翻译时,会翻译成hello,这样是不是很方便呢?

所以我的项目中,都是以英文作为翻译的key,就不用添加英文的翻译了,懒人必备。

至此,我们就完成所有的语言国际化工作了,是不是很简单呢?

赶快Get吧,10分钟就可以轻松入门。

完整的封装如下:

/**
 * vue-i18n
 * see more : https://kazupon.github.io/vue-i18n/zh/guide/lazy-loading.html
 */
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import Axios from 'axios'

import Store from '@/store'
import messages from './messages/en'

Vue.use(VueI18n)

const __LANGS__ = ['enUS']
let __LOCALE__ = Store.__s('app.language')

if (!__LOCALE__) {
  __LOCALE__ = window.navigator.language.split('-').join('')
  Store.__s('app.language', __LOCALE__)
}

const i18n = new VueI18n({
  locale: __LOCALE__,
  fallbackLocale: 'enUS',
  silentTranslationWarn: false,
  messages
})

/**
 * @functin setLang - set the app's language
 * @param {string} lang - the language will be setted
 * @return {string} lang - langguage name
 */
function _set(lang: string): string {
  i18n.locale = lang
  // i18n.fallbackLocale = lang
  Axios.defaults.headers.common['Accept-Language'] = lang
  Store.__s('app.language', lang)
  return lang
}

/**
 * @functin loadLangAsync - load language asynchronous
 * @param {string} lang - the language will be loading
 * @return {string} lang - loaded language name
 */
export function setLang(lang: string): Promise<string> {
  if (__LOCALE__ !== __LANGS__) {
    if (!__LANGS__.includes(lang)) {
      return import(/* webpackChunkName: "lang-[request]" */ `@/i18n/messages/${lang}`).then(msgs => {
        i18n.setLocaleMessage(lang, msgs.default[lang])
        __LANGS__.push(lang)
        return _set(lang)
      })
    }
    return Promise.resolve(_set(lang))
  }
  return Promise.resolve(lang)
}

setLang(__LOCALE__) // initialization
export default i18n

如果需要源码的话,请关注wx公众号「前端攻城之路」,回复“语言国际化”,将自动获取到源码链接。

支持

如果这篇文章对你有帮助或者有启发的话,我想请你关注我,让我们一起在前端攻城路上进阶。