前言
一般在實作網站的本地化(Localization)時,都會將翻譯文案寫在 JSON 檔或 YAML 檔裡,當文案越來越多時,要管理的翻譯鍵(Key)會越來越多,而工程師就必須因為文案修改而頻繁更版。
受到 Lokalise 啟發,實作了 Localiser 線上翻譯管理系統。可以讓 PM 或相關人員透過 Localiser 後台直接線上更新翻譯內容,如此一來就可以解除 PM 必須將文案交付給工程師,並且還要經過層層部署才能更新網頁內容的依賴關係。
此系統主要有以下功能:
- 認證
- 管理員權限、專案權限
- 線上修改
- 提供前端使用的 API
- 快取
專案架構
分為 3 個部分:
- localiser:後端
- localiser-ui:前端
- localiser-cli:命令列工具
後端
資料模型
主要模型有:
- User:使用者
- Project:專案
- Language:語言
- Key:翻譯鍵
- Value:翻譯值
API
後端主要會有 4 個公開的 API 可以使用。
- 獲取語言代碼:- GET- /api/project/:id/locales
 
- 刪除語言代碼快取:- DELETE- /api/project/:id/locales
 
- 獲取翻譯文案:- GET- /api/project/:id/messages
 
- 刪除翻譯文案快取:- DELETE- /api/project/:id/messages
 
角色
後端有兩種不同作用域的角色,一種是系統層級的角色,一種是專案層級的角色。
系統層級
| Ability | Admin | User | 
| USER_VIEW | ✔️ | ✔️ | 
| USER_CREATE | ✔️ |  | 
| USER_UPDATE | ✔️ |  | 
| USER_DELETE | ✔️ |  | 
| PROJECT_VIEW | ✔️ | ✔️ | 
| PROJECT_CREATE | ✔️ | ✔️ | 
專案層級
| Ability | Owner | Maintainer | Reporter | Guest | 
| PROJECT_UPDATE | ✔️ | ✔️ |  |  | 
| PROJECT_DELETE | ✔️ |  |  |  | 
| LANGUAGE_CREATE | ✔️ |  |  |  | 
| LANGUAGE_UPDATE | ✔️ | ✔️ |  |  | 
| LANGUAGE_DELETE | ✔️ |  |  |  | 
| KEY_CREATE | ✔️ | ✔️ | ✔️ |  | 
| KEY_UPDATE | ✔️ | ✔️ | ✔️ |  | 
| KEY_DELETE | ✔️ | ✔️ | ✔️ |  | 
| VALUE_CREATE | ✔️ | ✔️ | ✔️ |  | 
| VALUE_UPDATE | ✔️ | ✔️ | ✔️ |  | 
| VALUE_DELETE | ✔️ | ✔️ | ✔️ |  | 
前端
實作
在 src/plugins/i18n.js 檔:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 
 | import { nextTick } from 'vue';import { createI18n } from 'vue-i18n/index';
 import * as actions from '@/actions';
 
 
 const LOCALES = Object.freeze({
 en: 'en',
 zh: 'zh_TW',
 zh_TW: 'zh_TW',
 'zh-TW': 'zh_TW',
 });
 
 
 const language = localStorage.getItem('locale') || window.navigator.language;
 
 
 export const DEFAULT_LOCALE = language in LOCALES ? LOCALES[language] : LOCALES.en;
 
 
 const i18n = createI18n({
 legacy: false,
 locale: DEFAULT_LOCALE,
 });
 
 
 export const setLanguage = (locale) => {
 i18n.global.locale.value = locale;
 document.documentElement.lang = locale;
 localStorage.setItem('locale', locale);
 };
 
 
 export const loadMessage = async (locale) => {
 try {
 
 const message = await actions.project.fetchMessages({
 projectId: process.env.VUE_APP_API_PROJECT_ID,
 locale,
 });
 i18n.global.setLocaleMessage(locale, message);
 } catch {
 
 const message = await import( `@/assets/lang/${locale}.json`);
 i18n.global.setLocaleMessage(locale, message);
 }
 return nextTick();
 };
 
 export default i18n;
 
 | 
在 main.js 檔:
| 12
 3
 4
 5
 6
 7
 
 | import { createApp } from 'vue';import App from './App.vue';
 import i18n from './plugins/i18n';
 
 createApp(App)
 .use(i18n)
 .mount('#app');
 
 | 
在 App.vue 檔:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | import i18n, {DEFAULT_LOCALE,
 loadMessage,
 setLanguage,
 } from '@/plugins/i18n';
 
 export default {
 name: 'App',
 setup() {
 (async () => {
 await loadMessage(DEFAULT_LOCALE);
 setLanguage(DEFAULT_LOCALE);
 })();
 const changeLanguage = async (locale) => {
 if (!i18n.global.availableLocales.includes(locale)) {
 await loadMessage(locale);
 }
 setLanguage(locale);
 };
 return {
 changeLanguage,
 };
 },
 };
 
 | 
命令列工具
命令列工具主要是用來將翻譯文案拉取至本地專案以進行版本控制。使用時,要先將執行檔下載下來,並設置到環境變數。
在要使用 Localiser 的專案新增 localiser.yaml 檔:
| 12
 3
 4
 
 | ---endpoint: http://localhost:8000/api
 project_id: 1
 output_directory: src/assets/lang
 
 | 
執行以下指令,即可將翻譯文案下載下來。
程式碼