[Day02] Vue i18n - 导入 & 基础用法

i18n 全写为 internationalization,俗称的多国语系也常被称之为本地化 (Localization)。

随着现在网路的发达和资讯的流通,开始出现许多跨国的服务和产品,这些服务和产品为了推广到更深或更多的国家,而必须提供当地的语言或者是多数人能读懂的语言给使用者,也因此制作多国语系的网站似乎渐渐变成前端工程师必备的技能之一,所以我们要介绍的第一个前端实战技能就是多国语系 i18n。

在 Vue 中要实作多国语系,最常用的套件就是 vue-i18n,接下来为了方便示范,我会使用 Vue CLI 来建置专案,那我们马上开始吧!

前置作业

使用 Vue CLI 建置专案 (记得要选 Vue 3.x )。

$ npm install -g @vue/cli
$ vue create [专案名称]
$ cd [专案名称]
$ npm run serve

接着只要输入 vue add i18n,并选择相关的配置就会开始安装。

$ vue add i18n

https://ithelp.ithome.com.tw/upload/images/20210917/20113487F9JQEHFKIN.png

  • ? The locale of project localization
    专案预设的地区
  • ? The fallback locale of project localization
    设定当前地区的语言没有词汇时,要用什麽地区来代替
  • ? The directory where store localization messages of project. It's stored under src directory.
    语系的档案要放在什麽资料夹
  • ? Enable legacy API (compatible [email protected]) mode ?
    是否启用 legacy API,选择 Yes 的话 mode 会设置为 legacy ,反之为 composition

安装完後,会发现 CLI 自动帮你建立或更动了很多档案,其中值得注意的有:

  • locales 资料夹 - 用来存放多国语系设定档的资料夹,於安装时第三步骤设定的资料夹。

  • en.json - 在 locales 资料夹底下的多国语系设定档,其档案格式为 json。

    {
      "message": "hello i18n !!"
    }
    
  • i18n.js - 它会将 locales 资料夹底下所有的语系档 (.json) 都载入,并 export instance 出去,後续有进阶设定时,都会在这支 js 设定。

    // i18n.js 
    
    import { createI18n } from 'vue-i18n'
    
    /**
     * Load locale messages
     *
     * The loaded `JSON` locale messages is pre-compiled by `@intlify/vue-i18n-loader`, which is integrated into `vue-cli-plugin-i18n`.
     * See: https://github.com/intlify/vue-i18n-loader#rocket-i18n-resource-pre-compilation
     */
    function loadLocaleMessages () {
      const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
      const messages = {}
      locales.keys().forEach(key => {
        const matched = key.match(/([A-Za-z0-9-_]+)\./i)
        if (matched && matched.length > 1) {
          const locale = matched[1]
          messages[locale] = locales(key).default
        }
      })
      return messages
    }
    
    export default createI18n({
      legacy: false,
      locale: process.env.VUE_APP_I18N_LOCALE || 'en',
      fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
      messages: loadLocaleMessages()
    })
    
  • main.js - 全局载入 i18n,这样整个专案都能共用一样的设定以及 locales 底下的语系档。

    import { createApp } from 'vue'
    import App from './App.vue'
    import i18n from './i18n'
    
    createApp(App).use(router).use(i18n).mount('#app')
    

自动生成的 HelloI18n.vue 因为後续不会用到,所以可以先删除 ~

基础用法

https://ithelp.ithome.com.tw/upload/images/20210917/20113487drYR1k1DsD.png

红色框框起来的是下面要示范的部分

现在要做的第一件事也是最重要的事就是先定义好语系档 (.json),格式是蛮单纯物件的 Key 和 Value,Key 值是待会会写到程序码中的值,而 Value 就是呈现在画面中的值,而我们也可以根据语意或是将画面相近的文字内容用一层一层的物件来分类。

// src/locales/en.json
{
  "subTitle": {
    "installedCliPlugins": "Install CLI Plugins",
    "essentialLinks": "Essential Links",
    "ecosystem": "Ecosystem"
  },
  "installedCliPlugins": {
    "babel": "babel",
    "eslint": "eslint"
  },
  ...
}

接着就是开始一个萝卜一个坑用 t(key, locale) 替换掉原先 hardcode 的文字就大功告成了,其中的两个参数分别是:

  • key - 在语系档中 key 。
  • locale - 指定要用哪一个语系来本地化,预设会是全局的 locale 值。
// src/components/HelloWorld.vue
<template>
	<div class="hello">
		...
    <h3>{{ t("subTitle.installedCliPlugins") }}</h3>
		<ul>
		  <li>
		    <a
		      href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
		      target="_blank"
		      rel="noopener"
		    >
		      {{ t("installedCliPlugins.babel") }}
		    </a>
		  </li>
		  <li>
		    <a
		      href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
		      target="_blank"
		      rel="noopener"
		    >
		      {{ t("installedCliPlugins.eslint") }}
		    </a>
		  </li>
		</ul>
    ...
	</div>
</template>

<script>
import { useI18n } from 'vue-i18n'
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  setup () {
    const { t } = useI18n()
    return {
      t
    }
  }
}
</script>

范例程序码

语系切换

https://i.imgur.com/EeoT3zX.gif

讲了半天终於要来实作 "多" 语系的部分了,不然前面好像只是换个方式写字串而已 XD

首先在 locales 底下新增专案所需支援的语系的语系档 (.json),所以现在我就新增一份 zh.json 来存放切换到繁体中文时要显示内容,Key 会维持不变,Value 的部分就是看在该语系想要呈现什麽文字内容。(请容许我无脑的用 Google 翻译中文)

// src/locales/zh.json
{
  "subTitle": {
	"installedCliPlugins": "已安装的 CLI 插件",
    "essentialLinks": "基本链接",
    "ecosystem": "生态系统"
  },
  "installedCliPlugins": {
    "babel": "babel",
    "eslint": "eslint"
  },
  ...
}

接着透过 useI18n() 来取得 locale ,locale 的值会是当前语系的值,如果改变它的值就会切换当前的语系,所以我们简单的透过 v-model + select 来实现了切换语系的功能。

// src/components/HelloWorld.vue
<template>
  <div class="hello">
    ...
    <select v-model="locale">
      <option
        v-for="item in localeOptions"
        :key="`locale-${item.lang}`"
        :value="item.lang"
      >
        {{ item.name }}
      </option>
    </select>
    <h3>{{ t("subTitle.installedCliPlugins") }}</h3>
    ...
  </div>
</template>

<script>
import { useI18n } from 'vue-i18n'
export default {
  ... 
  setup () {
    const { t, locale } = useI18n()
    return {
      t,
      locale,
      localeOptions: [
        {
          lang: 'en',
          name: 'English'
        },
        {
          lang: 'zh',
          name: '繁体中文'
        }
      ]
    }
  }
}
</script>

范例程序码

Component Injections

由於每次要将文字本地化都要先在 setup 透过 useI18n() 取得 t(),可是假如有 100 个 component 都要使用 t() 那岂不是要重复写 100 次一样的程序码片段 ?

<script>
import { useI18n } from 'vue-i18n'
export default {
  setup () {
    const { t } = useI18n()
    return {
      t
    }
  }
}
</script>

还好 vue i18n 有提供一个配置选项 globalInjection,设为 true 时它会将 t 全局注入至 component 中,所以我们只要多加一个 $ 前缀符号就能直接在 template 中使用了

// src/i18n.js
...
export default createI18n({
  ...
  globalInjection: true //  <------ 加上这一行
})
// src/components/HelloWorld.vue
<template>
	<div class="hello">
		...
    <h3>{{ $t("subTitle.installedCliPlugins") }}</h3>
    ...
	</div>
</template>

事实上将 globalInjection 设为 true 时,还会有其他的属性被注入至 component 中。

范例程序码

参考资料


今天的分享就到这边,如果大家对我分享的内容有兴趣欢迎点击追踪 & 订阅系列文章,如果对内容有任何疑问,或是文章内容有错误,都非常欢迎留言讨论或指教的!

明天要来分享的是 Vue i18n 主题的第二篇 Message Format Syntax,那我们明天见!


<<:  【Day 02】从零开始的 Line Chatbot 系统-序章 Part 2

>>:  Day 5 Swift语法-基础篇(3/5)-流程控制

如何与使用者对话

How to Talk to Users: Startup Ideas, Building Prod...

[ Day 19 ] 表单中的 Controlled Component

在网站开发时有时候会使用到表单的元件,而表单内大多是采用 input 的栏位来搜集使用者的资料的。...

Angular 深入浅出三十天:表单与测试 Day12 - 单元测试实作 - 被保人 by Template Driven Forms

今天我们要来为我们用 Template Driven Forms 所撰写的被保人表单写单元测试,如...

DAY12:Fragment(片段之简介)

今天,要来介绍Fragment(片段),它是Activity中的一部分,一个Activity可能有数...

Day27-更改UIButton的Image大小,代志不是你想的这麽简单

上一篇提到设置了一个Sign In With Apple的按钮,是使用UIButton里面的Imag...