前言

因為求職需求,需要把網站轉換成英文,但是也想保留中文,開始尋找方法可以做中英文切換。
剛好看到 Hexo - Butterfly 官方網站,發現他們的網站就有中英文切換的功能,但是找遍了各個網站都沒有人說明。
所以只好看source code來了解是怎麼做的。

奮鬥了 2 天,終於找到方法了,以下是我整理的方法。

Step 1. 開設一個 private en repository

靈感主要參考上述連結,主要運作原理就是透過建立多個 GitHub Pages,基本上會有一個專門運行中文的 repository,另外再開設一個專門運行英文的 repository,透過設定不同的 config.yml_config.butterfly.yml來達到中英文切換的效果。

以下是我建立的兩個 repository

建立特定語言的 GitHub Pages

Step 2. 設置 [en/zh] config

先用_config.yml複製出兩個檔案分別是 config-en.ymlconfig-zh.yml檔案,並且做以下設定。

config-en.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 設定成英文
language: en

# 添加 en
url: https://<url-url>/en
root: /en/

# 指定啟動的路徑
source_dir: source-en
public_dir: public-en

# 把中文的 source 進行排除
include:
exclude:
ignore:
- source/

config-zh.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 設定成中文 
language: zh-TW

# 使用原本的url
url: https://shannonhung.github.io
root: /

# 指定啟動的路徑
source_dir: source
public_dir: public

# 把英文的 source 進行排除
include:
exclude:
ignore:
- source-en/

Step 3. 設定 切換語言的 js

我是在這個btf.js看到的,你可以在 source/self 中建立這個 btf.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
(() => {
// 判斷是否為英文
const isIncludeEN = item => {
const key = '/en/'
return item.includes(key)
}

// 建立 重新導向到不同語言的 url
window.loadFullPage = (url) => {
window.location.href = url
}

// 重新導向
const eventFn = (elements, includeEN) => {
elements.forEach(item => {
if (!includeEN || !isIncludeEN(item.href)) {
item.href = `javascript:loadFullPage('${item.href}');`
}
})
}

// 判斷目前是否為英文
const nowIncludeEN = isIncludeEN(window.location.href)

// 這邊記得改成你的 url
const selector = nowIncludeEN
? document.querySelectorAll('a[href^="https://<your-url>"]')
: document.querySelectorAll('a[href^="/en/"]')

eventFn(selector, nowIncludeEN)
})()

Step 4. 建立 source-en 資料夾

接下來很重要的一步,那就是把整個 source 資料夾複製,並且重新命名為 source-en,並確認 btf.js 有在 source-en/self 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
├── node_modules
├── scaffolds
├── source
├── source-en # <== here
├── themes
├── _config.butterfly.yml
├── _config.yml
├── config-butterfly-en.yml
├── config-en.yml
├── db.json
├── package-lock.json
├── package.json
└── yarn.lock

Step 5. 建立 [en/zh] Butterfly config

請先複製你自己的 _config.butterfly.yml,並且重新命名為 config-butterfly-en.ymlconfig-butterfly-zh.yml,主要改動以下部分:

  1. 並設定 Butterfly config 需要引入剛剛建立的 btf.js至不同的檔案
  2. 還有網站的目錄,你可以改成相對的語言目錄

config-butterfly-zh.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 把目錄改成中文
menu:
找文章||fas fa-list:
目錄: /categories/ || fas fa-folder-open
標籤: /tags/ || fas fa-tags
文章: /archives/ || fas fa-archive
首頁: / || fas fa-home
關於我: /about/ || fas fa-heart
相關連結: /link/ || fas fa-link
# 添加以下內容 注意: English 請使用 /en/ 來導入,中文則是 /
語言||fas fa-language:
English: /en/ || fas fa-e
中文: / || fas fa-c

# inject 剛剛建立好的 js
...
inject:
bottom:
- <script data-pjax src="/self/btf.js"></script>

config-butterfly-en.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 使用 英文描述你的目錄 
menu:
# Archives: /archives/ || fas fa-archive
Categroies: /categories/ || fas fa-folder-open
Find Posts ||fas fa-list:
By Tags: /tags/ || fas fa-tags
By Posts: /archives/ || fas fa-archive
Home Page: / || fas fa-home
About Me: /about/ || fas fa-heart
Links: /link/ || fas fa-link
# 以下網址記得修改成你的 但是這裡又不太一樣,
# English 請使用 / 來導入,中文則是 https://ShannonHung.github.io/ 你的網址
Language||fas fa-language:
English: / || fas fa-e
中文: https://ShannonHung.github.io/ || fas fa-c

# inject 剛剛建立好的 js
...
inject:
bottom:
- <script data-pjax src="/en/self/btf.js"></script>

注意!!
config-butterfly-en.yml 中任何 /img/ 圖片 或是 /self/ 的 js 檔案,都要改成 /en/img/ 或是 /en/self/,這樣才能正確顯示圖片或是引入 js 檔案。

Step 6. 於 package.json 建立腳本

最後一步,就是更新 package.json,讓他可以產生public-en的資料夾,以便後續推到 repository en 中。

pcakage.json

1
2
3
4
5
6
7
8
9
...
"scripts": {
"push": "hexo clean && hexo g && hexo douban && gulp && hexo deploy",
"show": "hexo clean && hexo g && hexo s",
"update": "git init && git add . && git commit -m 'backup' && git push origin main",
"kk": "hexo clean && hexo g && hexo deploy",
"en": "hexo clean && hexo g --config config-en.yml && hexo s --config config-en.yml"
},
...

Finally:腳本部署

現在我們準備建立腳本做部署,請先確認你有以下檔案:

  • config-butterfly-en.yml
  • config-butterfly-zh.yml
  • config-en.yml
  • config-zh.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.
├── node_modules
├── public
├── public-en
├── scaffolds
├── source
├── source-en
├── themes
├── _config.butterfly.yml # 沒有沒關係,腳本會自動建立
├── _config.yml # <== 沒有沒關係,腳本會自動建立
├── config-butterfly-en.yml # <== 這裡 en 的 butterfly config
├── config-butterfly-zh.yml # <== 這裡 zh 的 butterfly config
├── config-en.yml # <== 這裡 en 的 config
├── config-zh.yml # <== 這裡 zh 的 config
├── db.json
├── package-lock.json
├── package.json
└── yarn.lock

然後於跟目錄中建立一個 deploy.sh,內容如下:

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
50
51
52
#!/bin/bash

while [[ "$#" -gt 0 ]]; do
case $1 in
-h|--help)
echo "Usage: bash.sh [en|zh|all|show <en|zh>|-h]"
echo "Options:"
echo " [deploy|d] en Deploy English configuration to GitHub Pages"
echo " [deploy|d] zh Deploy Chinese configuration to GitHub Pages"
echo " [deploy|d] all Deploy both English and Chinese configurations to GitHub Pages"
echo " [show|s] en Execute npm run show for English configuration"
echo " [show|s] zh Execute npm run show for Chinese configuration"
echo " -h, --help Display this help message"
exit 0
;;
d | deploy)
lang=$2
if [ "$lang" = "en" ] || [ "$lang" = "zh" ]; then
cp "config-$lang.yml" _config.yml
cp "config-butterfly-$lang.yml" "_config.butterfly.yml"
npm run kk
echo "Deploy $1 success!"
elif [ "$lang" = "all" ]; then
for lang_choice in "zh" "en"; do
cp "config-$lang_choice.yml" _config.yml
cp "config-butterfly-$lang_choice.yml" "_config.butterfly.yml"
npm run kk
echo "Deploy $lang_choice success!"
done
else
echo "Error! Please input 'en' or 'zh' or 'all'!"
fi
;;
s | show)
lang=$2
if [ "$lang" = "en" ] || [ "$lang" = "zh" ]; then
cp "config-$lang.yml" _config.yml
cp "config-butterfly-$lang.yml" "_config.butterfly.yml"
npm run show
echo "Running npm show!"
else
echo "Error! Please use './bash.sh show en' or './bash.sh show zh'!"
fi
shift # Move to the next argument after 'show'
;;
*)
echo "Error! Please input deploy <en|zh|all> or 'show <en|zh>' or '-h' for help!"
exit 1
;;
esac
shift
done

記得把它改成可執行檔,然後可以透過以下指令進行部署囉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
chmod +x deploy.sh

# 尋求幫助
./deploy.sh -h
./deploy.sh --help

# 推送英文 到 en repository
./deploy.sh deploy en
./deploy.sh d en

# 推送中文 到 zh repository
./deploy.sh deploy zh
./deploy.sh d zh

# 本地運行英文
./deploy.sh show en
./deploy.sh s en

# 本地運行中文
./deploy.sh show zh
./deploy.sh s z

進階:不同文章切換中英文

如果你希望可以透過點擊 bottom 來達到中英文切換的效果如下:

那就繼續看下去吧!

Step1: 改寫 config-butterfly-<lang>.yml

config-butterfly-zh.yml

1
2
3
4
5
6
7
8
translate:
# 開啟簡繁轉換 button
enable: true
# 預設繁體
default:
# 如果是 `1` 就是中文 `3` 則是英文
# the language of website (1 - Traditional Chinese/ 2 - Simplified Chinese)/ 3 - English
defaultEncoding: 1

config-butterfly-.yml

1
2
3
4
5
6
7
8
# Conversion between Traditional and Simplified Chinese (簡繁轉換)
translate:
# 開啟簡繁轉換 button
enable: true
# 預設英文
default: En
# 如果是 `1` 就是中文 `3` 則是英文
defaultEncoding: 3

Step2. 撰寫 tw_en.js 檔案

接下來我們要寫一個 js 檔案,在任意一個頁面時,點擊 bottom 就可以切換中英文,邏輯是:

  • 如果目前網站是:https://<rul>/en/posts/<post1>
    • 當點擊 EN bottom 時,就會導向 https://<rul>/posts/<post1>
    • 簡單來說就是把 /en 移除
  • 如果目前網站是:https://<rul>/posts/<post1>
    • 當點擊 EN bottom 時,就會導向 https://<rul>/en/posts/<post1>
    • 簡單來說就是在第一個 / 後面加上 /en

因此我們把邏輯撰寫於 source/self/tw_en.jssource-en/self/tw_en.js 如下程式碼:

tw_en.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
50
51
52
53
54
55
56
57
58
59
document.addEventListener("DOMContentLoaded", function () {
const { defaultEncoding, translateDelay, msgToTraditionalChinese } =
GLOBAL_CONFIG.translate;
const msgToEnglish = "EN";
const snackbarData = GLOBAL_CONFIG.Snackbar;
let currentEncoding = defaultEncoding;
const targetEncodingCookie = "translate-en-cht";
let targetEncoding =
saveToLocal.get(targetEncodingCookie) === undefined
? defaultEncoding
: Number(saveToLocal.get("translate-en-cht"));
let translateButtonObject;
const isSnackbar = snackbarData !== undefined;

const isIncludeEN = (item) => {
const key = "/en/";
return item.includes(key);
};

const nowIncludeEN = isIncludeEN(window.location.href);

function translatePage() {
let currentUrl = window.location.href;

if (nowIncludeEN) {
// 把文字顯示成繁體中文
translateButtonObject.textContent = msgToTraditionalChinese;
// 然後導向繁體中文的網址 目前網址 /en/... => /...
let newUrl = currentUrl.replace("/en/", "/");
console.log(`Redirect to ${newUrl}`);
window.location.href = newUrl;
} else {
// 把文字顯示成英文
translateButtonObject.textContent = msgToEnglish;
// 然後導向英文的網址 目前網址 /... => /en/...
let newUrl = currentUrl.replace(/^(https?:\/\/[^\/]+)(\/)?/, "$1/en/");
window.location.href = newUrl;
}
}

function translateInitialization() {
translateButtonObject = document.getElementById("translateLink");
if (translateButtonObject) {
if (nowIncludeEN) {
translateButtonObject.textContent = msgToTraditionalChinese;
} else {
translateButtonObject.textContent = msgToEnglish;
}
}
}

document.addEventListener("pjax:complete", translateInitialization);

window.translateFn = {
translatePage,
};

translateInitialization();
});

Step3. 設定 js 於 config 中

打開 config-butterfly-<lang>.yml,並且設定 inject 如下:

config-butterfly-en.yml

1
2
3
4
5
6
inject:
head:
...
bottom:
- <script data-pjax src="/en/self/btf.js"></script>
- <script data-pjax src="/en/self/tw_en.js"></script> # <== 這裡

config-butterfly-zh.yml

1
2
3
4
5
6
inject:
head:
...
bottom:
- <script data-pjax src="/self/btf.js"></script>
- <script data-pjax src="/self/tw_en.js"></script> # <== 這裡