[VueJS] Đa ngôn ngữ trong ứng dụng Vue
Vấn đề đặt ra đó là mình cần triển khai đa ngôn ngữ trên web app mà mình đang việc, vì vậy để giải quyết vấn đề này mình đã đi tìm một package có thể nhanh chóng hỗ trợ mình xử lý công việc này và mình đã tìm ra vue-i18n. Internationalization plugin of Vue.js Hỗ trợ DateTime localization ...
Vấn đề đặt ra đó là mình cần triển khai đa ngôn ngữ trên web app mà mình đang việc, vì vậy để giải quyết vấn đề này mình đã đi tìm một package có thể nhanh chóng hỗ trợ mình xử lý công việc này và mình đã tìm ra vue-i18n.
Internationalization plugin of Vue.js Hỗ trợ DateTime localization Number localization Locale messages syntax
Bài viết hôm nay mình xin phép được chia sẻ cách thức mình đã implement vue-i18n để đa ngôn ngữ ứng dụng của mình như thế nào và cũng rất mong nhận được sự góp ý từ mọi người để cách tiếp cận trở nên tốt hơn.
Kết quả demo test của mình
https://dibochit.herokuapp.com
Khởi tạo ứng dụng
Mình sử dụng vue-cli để việc khởi tạo ứng dụng một cách nhanh chóng và dễ dàng
$ npm install -g vue-cli $ vue init webpack-simple dibo-vue-i18n
Cài đặt vue-i18n
Để có thể sử dụng vue-i18n trong project của mình thì có một số cách cài đặt như sau:
- Download trực tiếp hoặc sử dụng link cdn
- Cài đặt thông qua npm
- Cài đặt thông qua yarn
- Hay clone trực tiếp từ github về và build
Với mình thì mình đã lựa chọn sử dụng npm
$ cd /path/to/dibo-vue-i18n $ npm install --save vue-i18n
Tạo locales folder để lưu trữ file ngôn ngữ dạng *.json
Cấu trúc thư mục project của mình giờ có dạng
dibo-vue-i18n | |__src | |__lang | |__vn.json |__en.json
Khởi tạo vue-i18n
Việc khởi tạo vue-18n là khá đơn giản:
- Tạo mới một instance
- Thêm locale messages: lựa chọn ngôn ngữ sẽ sử dụng ở đây mình sẽ chọn là en hay vn
- Thêm fallback lang là thằng language sẽ mặc định được nhận nếu ứng dụng không xác định được ngôn ngữ đã được lựa chọn
- Thông báo với Vue instance là mình sử dụng instance vủa vue-i18n vừa được khởi tạo và kết thúc
# main.js import Vue from 'vue' import App from './App.vue' import router from './router' import VueI18n from 'vue-i18n' import vnMessage from './lang/vn.json' import enMessage from './lang/en.json' Vue.use(VueI18n) const messages = { vn: vnMessage, en: enMessage, } const i18n = new VueI18n({ locale: 'vn', // set locale messages, fallbackLocale: 'vn', }) new Vue({ el: '#app', i18n, router, render: h => h(App) })
Một số lưu ý
- Bạn cần code đúng thứ tự import vue trước sau đó import vue-i18n
- Khai báo Vue.use(VueI18n) trước khi tạo mới instance của VueI18n
Thêm nội dung cho files lang
Mình lấy ví dụ với file vn.json và tương tự cho file en.json
# vn.json { "common": { "select_lang": "Ngôn ngữ" }, "login": { "title": "Đăng nhập vào dibochit", "buttons": { "login": "Đăng nhập" }, "input_text": { "email": "Địa chỉ gmail", "password": "Mật khẩu" }, "messages": { "none_account": "Tạo tài khoản mới", "register": "Đăng ký" } } }
Translate
Thay thế những nội dung mà mình muốn đa ngôn ngữ sử dụng template như sau
<p>{{ $t("common.select_lang") }}</p>
Và kết quả sẽ render ra dạng
<p>Ngôn ngữ</p>
Nếu muốn translate giá trị thuộc tính của thẻ ví dụ placeholder chẳng hạn sẽ làm như sau
<input type="text" :placeholder="$t('common.select_lang')" />
Kết quả hiện tại
Chú ý
- Việc tự động localization tùy tiện HTML sẽ rất nguy hiểm do dễ bị mắc phải lỗ hổng tấn công XSS. Chỉ sử dụng với content chắc chắn tin cậy và tuyệt đối không dùng với dữ liệu do người dùng cung cấp.
- Có một cách tổ chức file lang cũng hợp lý đó là chia ra theo component, mỗi component sẽ tương ứng 1 file
- Có nhiều format cho localization như
HTML formating
Named formatting truyền theo key
# lang_x.json hello: '{msg} world {year}' # component <p>{{ $t('message.hello', { msg: 'hello', year: '2017' }) }}</p>
List formatting
hello: '{0} world' <p>{{ $t('message.hello', ['hello']) }}</p>
hay Custom formatting
mình sẽ không nói rõ trong bài viết này mà các bạn có thể truy cập vào địa chỉ link bên trên để xem trong document của vue-i18n đã được viết khá chi tiết và rõ ràng.
Nếu mà chỉ thế là xong thì việc tích hợp vue-18n có thể coi là ez game quá nhể hihi :man_raising_hand: Bây giờ mình cần nghĩ tới là làm thế nào để thay đổi thằng locale trong main.js kia? Mình có thể làm theo một số cách được nhắc tới trên docuemnt như Global config Global method Instance property header + dynamic locale
Sự lựa chọn của mình đó là sử dụng kết hợp vuex- Centralized State Management for Vue.js
Cấu trúc lại project
Trước tiên mình sẽ tách VueI18n instance ra một file mới để có thể dễ dàng tái sử dụng code. Mình sẽ tạo file i18n.js trong thư mục lang. Cấu trúc thư mục lang hiện tại sẽ như sau
lang | |__i18n.js |__vn.json |__en.json
Tạo instance VueI18n và config locale, messages
#i18n.js import Vue from 'vue' import VueI18n from 'vue-i18n' import vnMessage from './vn.json' import enMessage from './en.json' Vue.use(VueI18n) const messages = { vn: vnMessage, en: enMessage, } const i18n = new VueI18n({ locale: 'vn', // set locale messages, fallbackLocale: 'vn', }) export default i18n
Giờ main.js sẽ ngắn gọn hơn, mình chỉ cần import instance VueI18n đươc export trong i18n.js vào và sử dụng. Mình cũng sẽ export Vue instance bằng biến một biến tên là app để lát nữa có thể sử dụng được app.$$18n trong vuex
#main.js import Vue from 'vue' import App from './App.vue' import router from './router' import i18n from './lang/i18n' const app = new Vue({ el: '#app', i18n, router, render: h => h(App) }) export default app
Cài đặt vuex
Trong khuôn khổ bài viết mình sẽ không trình bày nhiều về vuex, bạn có thể tham khảo tài liệu trên document của vuex
$ npm install --save vuex
Sử dụng kết hợp vuex và vue-i18n
Đơn giản nhất mình sẽ tạo mới file store.js dưới đường dẫn src/store.js
import Vue from 'vue' import Vuex from 'vuex' import app from './main' Vue.use(Vuex) export default new Vuex.store({ const mutations = { SET_LANG (state, payload) { app.$i18n.locale = payload } }, const actions = { setLang({commit}, payload) { commit('SET_LANG', payload) } } })
Thông báo với VueInstance rằng mình sẽ dùng Vuex store
#main.js ... import store from './store' export const app = new Vue({ el: '#app', i18n, router, store, ...
Việc cần làm bây giờ sẽ là call action setLang mỗi khi user selected language. Mình khởi tạo element select language trong App.vue nên giờ mình sẽ setup cho nó
<template> ... <div class="item" v-for="lang in optionLangs" :value="lang.value" @click.prevent="callSetLangActions">{{ lang.text }}</div> ... </template> <script> export default { data: () => ({ optionLangs: [ { text: 'Vietnamese', value: 'vn' }, { text: 'English', value: 'en' } ] }), methods: { callSetLangActions (event) { this.$store.dispatch('setLang', event.target.getAttribute('value')) } } } </script>
Kết quả
Mở extention Vue Devtools của google chrome lên và cảm nhận :3
- Cảm ơn bạn đã dành thời gian đọc hết bài chia sẻ của mình.
- Trên đây là bài chia sẻ của mình về một cách tiếp cận đơn giản để đa ngôn ngữ trong ứng dụng Vue sử dụng vue-i18n và vuex
- Đây chưa phải là cách tốt nhất các bạn có thể tìm hiểu thêm và lựa chọn phương pháp phù hợp nhất với riêng mình
Mở rộng
- Các bạn có thể tìm hiểu thêm sử dụng vue-localstorage để set expires time cho vuex store
- Khi ứng dụng của bạn phải đa ngôn ngữ cho nhiều ngôn ngữ khoảng 10 chẳng hạn, và mỗi file ngôn ngữ (*.json) lại tương đối lớn do đó bạn sẽ nghĩ tới việc không muốn load chúng vào trong ứng dụng một cách đồng thời thì lúc này hãy nghĩ tới dynamic loading, theo dõi issue này