實作「線上翻譯管理系統」應用程式

前言

一般在實作網站的本地化(Localization)時,都會將翻譯文案寫在 JSON 檔或 YAML 檔裡,當文案越來越多時,要管理的翻譯鍵(Key)會越來越多,而工程師就必須因為文案修改而頻繁更版。

受到 Lokalise 啟發,實作了 Localiser 線上翻譯管理系統。可以讓 PM 或相關人員透過 Localiser 後台直接線上更新翻譯內容,如此一來就可以解除 PM 必須將文案交付給工程師,並且還要經過層層部署才能更新網頁內容的依賴關係。

此系統主要有以下功能:

  • 認證
  • 管理員權限、專案權限
  • 線上修改
  • 提供前端使用的 API
  • 快取

專案架構

分為 3 個部分:

  1. localiser:後端
  2. localiser-ui:前端
  3. localiser-cli:命令列工具

後端

資料模型

主要模型有:

  1. User:使用者
  2. Project:專案
  3. Language:語言
  4. Key:翻譯鍵
  5. 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 檔:

1
2
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;

// 建立 I18n 實體
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(/* webpackChunkName: "locale-[request]" */ `@/assets/lang/${locale}.json`);
i18n.global.setLocaleMessage(locale, message);
}
return nextTick();
};

export default i18n;

main.js 檔:

1
2
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 檔:

1
2
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 檔:

1
2
3
4
---
endpoint: http://localhost:8000/api
project_id: 1
output_directory: src/assets/lang

執行以下指令,即可將翻譯文案下載下來。

1
localiser

程式碼