在 Nuxtjs 設置並使用多國語言
Nuxtjs: i18n + Vuex + 下拉選單
我正在開發個人部落格網站, 要有多國語言切換的功能 (中文/英文), 看了網路上許多的技術文 (中英文皆有), 多數是針對 vue-cli
專案設置的, 我沒有看到一篇中文文章 (有簡體中文的, 但是沒有詳細說明) 談到 Nuxtjs + i18n 這個主題的, 英文的倒是不少, 不過要做不少修正才能達到我要的需求和功能. 以下就來談談我是怎麼在 Nuxtjs 專案使用下拉式選單做到中英文切換網頁語言並且在切換頁面的情形下仍然保持狀態.
相信有訪問過 wiki 的人都會發現當我們切換語言選項 (繁體中文 <=> 簡體中文), 假設我們選擇了繁體中文, 當我們切換到另一個頁面 (比如左邊的 "討論" 選項), 字體會自動恢復到 default 值 (簡體中文), 很明顯這個網站並沒有紀錄我們在下拉選單選擇的項目. 這是很不好的使用者體驗. 我的網站要使用者選擇合適的語言後, 不論切換到哪個頁面, 或是重整網頁 (選擇性), 仍然維持設定, 提供最佳的使用者體驗.
Tech Stack
- NuxtJS (功能最強大的 VueJS 框架)
- 下拉式選單 (select+option 標籤)
- Vuex (狀態管理系統, 所以的組件都可以直接存取資料)
- (選擇性) Local Storage (瀏覽器的內存, 儲存語言選項 (英文/中文)
專案架構
步驟
安裝 vue-i18n 套件 npm i vue-i18n
在 components
目錄下建立 LangSwitcher.vue
和 Navbar.vue
LangSwitcher.vue (下拉選單組件)
<template>
<select
v-model="$store.state.lang"
@change="setLang($store.state.lang)"
><option
v-for="item in optionLang"
:value="item.value"
:key="item.id"
>{{ item.text }}
</option>
</select>
</template><script>
export default {
data() {
return {
optionLang: [
{ text: 'English', value: 'en', id: 1 },
{ text: '中文', value: 'zh', id: 2 }
]
}
},
methods: {
setLang(value) {
this.$store.commit('setLang', value)
this.$i18n.locale = value
}
},
}
</script>
說明:
- 下拉選單由
select
和option
標籤組成. v-model
綁定store
的lang
state- 選單改變時會觸發
setLang
函式,setLang
做兩件事:
- 把選單值 (en/zh) 賦值給 store 的
lang
- 把選單值賦值給
i18n.locale
Navbar.vue (菜單組件)
<template>
<div class="main-header">
<div class="navbar">
<NuxtLink to="/">
<div class="logo">{{ $t('title') }}</div>
</NuxtLink>
<div class="menu-right">
<div class="menu">
<NuxtLink to="/">{{ $t('navbarHome') }}</NuxtLink>
<NuxtLink to="/blog">{{ $t('navbarBlog') }}</NuxtLink>
<NuxtLink to="/about">{{ $t('navbarAbout') }}</NuxtLink>
<NuxtLink to="/work">{{ $t('navbarWork') }}</NuxtLink>
<LangSwitcher />
</div>
</div>
</div>
</div>
</template><style scoped>
.menu-right {
display: flex;
align-items: center;
}
.main-header {
border-bottom: var(--bottom);
}
.navbar {
height: 112px;
padding: 0 15px;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.logo {
font-weight: 400;
font-size: 38px;
color: var(--primary);
text-transform: uppercase;
}
.menu {
width: 525px;
display: flex;
justify-content: space-between;
}
@media (max-width: 992px) {
.navbar {
height: 90px;
}
.logo {
font-size: 32px;
}
}
a {
text-decoration: none;
color: var(--item);
font-size: 22px;
font-weight: 300;
}
a.nuxt-link-exact-active {
color: var(--primary);
font-weight: 400;
}
</style>
說明:
i18n 套件會根據 i18n.locale
物件的內容 (中文或英文) 把值設定給 store 的 lang
state, 然後我們在組件內使用 {{$t(variable)}}
把值動態渲染出來
在 layouts\default.vue
貼上下列內容
<template>
<div>
<Navbar />
<Nuxt class="style" />
</div>
</template><style>
@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;600;700&display=swap');:root {
--primary: #278f6e;
--item: #707070;
--subtitle: #313131;
--bottom: 1px solid #efefef;
--brDefault: 30px;
}
.style {
display: flex;
height: 30vh;
justify-content: center;
align-items: center;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
font-family: 'Rubik', sans-serif;
background: var(--bg-color);
}
</style>
建立 locales
資料夾, 新增 en.json
和 zh.json
en.json 內容
{
"title": "LOGO",
"navbarHome": "Home",
"navbarBlog": "Blog",
"navbarAbout": "About",
"navbarWork": "My Work"
}
zh.json
{
"title": "標識",
"navbarHome": "首頁",
"navbarBlog": "部落格",
"navbarAbout": "關於我",
"navbarWork": "作品集"
}
說明:
語言的資料存放在 locales
資料夾內, 如果要新增其他語言, 比如法文, 就新增一個 fr.json
檔案, 然後加入法文資料.
在 pages
資料夾下建立 about.vue
, blog.vue
, work.vue
index.vue 內容
<template>
<div class="home-page">
<h1>{{ $t('homepage') }}</h1>
</div>
</template>
about.vue 內容
<template>
<div class="about-page">
<h1>{{ $t('navbarAbout') }}</h1>
</div>
</template>
blog.vue 內容
<template>
<div class="blog-page">
<h1>{{ $t('navbarBlog') }}</h1>
</div>
</template>
work.vue 內容
<template>
<div class="work-page">
<h1>{{ $t('navbarWork') }}</h1>
</div>
</template>
在 plugins
資料夾建立 i18n.js
i18n.js 內容
import Vue from 'vue'
import VueI18n from 'vue-i18n'Vue.use(VueI18n)export default ({ app, store }) => {
app.i18n = new VueI18n({
locale: store.state.locale,
fallbackLocale: 'en',
messages: {
en: require('~/locales/en.json'),
zh: require('~/locales/zh.json'),
},
}) app.i18n.path = (link) => {
if (app.i18n.locale === app.i18n.fallbackLocale) {
return `/${link}`
}
return `/${app.i18n.locale}/${link}`
}
}
說明:
i18n 插件的檔案, 1. 把 i18n.locale
和 store 做綁定; 2. 引入 語言資料 (en.json/zh.json)
在 store 資料夾建立 index.js
, state.js
, mutations.js
index.js 內容
import Vuex from 'vuex'
import { state } from './state.js'
import { mutations } from './mutations.js'const createStore = () => {
return new Vuex.Store({
state,
mutations,
})
}export default createStore
state.js 內容
export const state = {
lang: 'en'
}
mutations.js 內容
export const mutations = {
setLang(state, data) {
state.lang = data
}
}
說明:
index.js
設定並且註冊vuex
state.js
設置lang 的初始值為英文
在 nuxt.config.js
註冊 i18n
export default {
plugins: [
'~/plugins/i18n.js'
]
}
我們也可以把 lang
存到 localStorage, 這樣網頁重整語言設定也不會跑掉, 不過我覺得沒有這個必要, 因為現在是多語言時代, 英文/中文交替使用, 不需要說只要選擇了某個語言網頁就永久記住, 只要切換頁面維持狀態即可.