討論如何給Hugo靜態網站加入多國語言支援。
因為Ivon偶爾會在Reddit發技術文,加上有寫過一些還算認真下去研究的essay,所以想讓自己網站有個地方可以收錄英文版文章,而非流落於Medium、Github Gists等各大平台。
Hugo預設對多語系網站(multilingual mode)有不錯的支援,它能夠依照不同語言分類文章,顯示「切換語言」的按鈕,方便使用者檢視不同語言的同一個網頁。
在瀏覽本網站首頁的時候,應該會在網站右上角看到切換語言的按鈕。目前這個網站有中文內容(https://ivonblog.com/
),還有英文內容(https://ivonblog.com/en-us/
),就是透過Hugo的多語系支援實現的。
絕對不是套個Google翻譯的JavaScript元件就打發掉的喔~Hugo能讓你真正的架設多語系網站。
這裡Ivon分享我是怎麼設定多語系Hugo網站的。
1. 多語系網站架構#
沒有一定答案,取決於兩個因素,一個是Hugo官方文件的設定,還有你用的Hugo主題怎麼實現多語系支援的。
請先閱讀 Hugo Multilingual mode,了解基本概念。撰文當下我使用的Hugo主題是 Blowfish,作者提供了簡單的方式來設定多語系內容。不過其他主題的觀念是相通的。
Hugo的設計是讓你可以用每個語言一個資料夾,或者一個語言一個檔案對應來實現多語系。
- 假設今天要做中文和英文的雙語版網站,那麼在Hugo網站根目錄的結構可以長這樣:
.
├── content.en-us
│ └── posts
│ └── post1.md
├── content.zh-tw
│ └── posts
│ └── post1.md
如果對Hugo的內容管理有些概念,那麼應該知道
content
目錄下存放的就是網站的各種頁面了,例如文章、關於、分類、標籤等等頁面。在這裡,
contnet.en-us
是英文網站,content.zh-tw
則是中文網站。這二個目錄下有兩個一樣檔名的文章post1.md
,Hugo知道這兩篇文章是互為翻譯的對應關係,所以在生成網頁之後,便會在該個頁面顯示語言切換按鈕。以上內容也可以改寫成這樣:不分成兩個目錄,而是將不同語言的Markdown檔案放在同一個目錄下,這樣Hugo也會把他們當成互為翻譯的文章,不過我覺得這樣不好整理檔案,所以不會這樣做。
.
├── content
│ └── posts
│ ├── post1.en-us.md
│ └── post1.zh-tw.md
- 以此類推,如果我們用同樣的邏輯下去設計,很容易就能做出雙語版網站,讓每個中文版頁面都有英文版的對應頁面。
.
├── content.en-us
│ ├── about
│ ├── categories
│ ├── tags
│ └── posts
├── content.zh-tw
│ ├── about
│ ├── categories
│ ├── tags
│ └── posts
這樣Hugo網站正式上線後,中文的內容會位於
https://example.com/
,英文內容是位於https://example.com/en-us/
。嗯,為什麼只有英文內容有語系代碼?這個下面會討論。這裡要提醒一點的是,不是每個網頁都一定要有對應的多語系網頁。像我很多篇中文文章都沒有英文版,那麼瀏覽該頁面的時候就不會顯示語系切換按鈕。
你可能會好奇我是怎麼處理雙語文章的,還真沒其他辦法,就是手寫!有時候我不會一字一句將中文翻譯成英文,而是即興創作一下,修改用詞增添在地化趣味,反之亦同。
當然你要像 Microsoft Learn的程式碼參考文件那樣,套用正式規範下去處理多語言內容那也可以,要做Wiki類的多人協作網站就需要詳細標準。
2. 調整Hugo的設定檔,新增多語系#
Hugo的config裡面可以設定各個語言網站的要素。Hugo允許你將各個語言的定義都寫在一個檔案,或是拆開來寫。
根據
Blowfish主題文件,作者採取的是將各個設定檔拆開撰寫的作法,它的設定檔位於Hugo網站根目錄/config/_default/
- 最重要的得從
config.toml
開始。這個檔案要定義Hugo網站預設的語言。defaultContentLanguage
設定為zh-tw,正體中文。
defaultContentLanguage = "zh-tw"
defaultContentLanguageInSubdir = false
有趣的事情來了,如果加上
defaultContentLanguageInSubdir = true
,網站預設語言的路徑也會加上語系代碼。也就是說,中文內容會從https://example.com/
變成https://example.com/zh-tw/
。這個看你如何抉擇,我是因為以前就習慣中文內容都在域名之下,所以懶得改了。維持中文內容在https://example.com/
,英文內容在https://example.com/en-us/
的模式。再來要針對每個語系新增設定檔,我目前有這些檔案,命名規則一目了然,所有設定檔後面加上語系代碼。
.
├── config.toml
├── languages.en-us.toml
├── languages.zh-tw.toml
├── menus.en-us.toml
├── menus.zh-tw.toml
├── params.en-us.toml
└── params.zh-tw.toml
languages.*.toml
是關於該語系的設定檔,包括在地化內容,例如languages.zh-tw.toml
我是這樣寫的,languageCode和isoCode在Hugo官網都查得到。
languageCode = "zh-TW"
languageName = "正體中文" # 顯示的字串可以自訂
weight = 1
title = "Ivon的部落格"
contentDir = "content.zh-tw" # 指定正體中文的文章所在目錄
hasCJKLanguage = true
[params]
displayName = "中文" # 顯示的字串可以自訂
isoCode = "zh-TW"
rtl = false
dateFormat = "2006年1月2日" # 指定正體中文的時間表示方法
description = "歡迎來到Ivon的部落格"
copyright = "歡迎分享Ivon的部落格(ivonblog.com)的文章"
- 對比英文版
languages.en-us.toml
:
languageCode = "en"
languageName = "English (US)"
weight = 1
title = "Ivon's Blog"
contentDir = "content.en-us" # 指定英文文章所在目錄
[params]
displayName = "English"
isoCode = "en"
rtl = false
dateFormat = "2006-01-02"
description = "Welcome to Ivon's blog"
copyright = "You are welcome to share articles of Ivon's Blog (ivonblog.com)."
- 上面我的範例是每個語系都有自己的設定,方便詳細自定義不同語系網頁顯示的內容。不過有些設定是可以所有語系共享(或者講繼承),那就不需要分成多個語系的檔案撰寫。以Blowfish主題的設計來說,
params.*.toml
調整的是網站顏色相關的設定,沒有任何在地化內容,那麼其實沒有必要分成兩個檔案。
3. 用i18n翻譯多語系網站的字串#
Hugo提供i18n的機制,讓使用者能將網頁的選單和按鈕,翻譯成對應語言的字串。
通常Hugo的主題開發者不會將選單的字串硬編碼為英文,而是透過
i18n
目錄下的字串翻譯。例如,Blowfish主題
layouts/404.html
的404網頁,有放一個i18n的區塊:
i18n "error.404_error" | emojify
i18n "error.404_description" | emojify
- 它就會去讀取
i18n
目錄下的zh-TW.yaml
所寫的鍵值,依照不同語言顯示不同的字串。
error:
404_title: "404找不網頁"
404_error: "網頁不見了。"
404_description: "返回首頁"
- 由此可知,
i18n
目錄之下,不同檔案都有自己的翻譯鍵值:
.
├── en-US.yaml
├── zh-TW.yaml
- 順便分享一下我是怎麼把語言按鈕做成國旗的,Blowfish的主題會去讀取i18n的翻譯,那麼我就把language的鍵值設定為Emoji就好了:
global:
language: "🇹🇼"
- 你可以到
themes/主題目錄/i18n
,將裡面的檔案複製到Hugo網站根目錄/i18n
,按照喜好調整字串的翻譯內容。在設計partial和shortcode的時候,別忘了善用i18n的功能,讓你的HTML元件支援多語言。
4. 修正提交給Google的sitemap#
Google Search Console是提高網站搜尋曝光率之用。
Hugo生成多語言網站後,會改在該語言的目錄中生成sitemap,所以應該改提交:https://example.com/zh-tw/sitemap.xml
和https://example.com/en-us/sitemap.xml
。