環境與套件
"react": ^17.0.1,
"next": 12.1.6,
"next-i18next": ^11.0.0,
"typescript": 4.5.5
覺得 i18next 的文件寫得夠完整,所以在決定作品集要做多國語系時,用這一款來輔助,避免重新發明輪胎,next-i18next 又做好許多 Next.js
和 i18next
之間的整合。然而,使用上還是有向技術社群求助的經驗,在這一篇集合起來。
接下來的內容
TypeScript 的陣列問題
在語系檔裡面,會有使用陣列的情境:
"document":
{
"items":
[
{
"name":
"name 0",
"description":
"description 0"
},
{
"name":
"name 1",
"description":
"description 1"
}
]
}
如果按照直覺來設定陣列型別:
type ItemProps = {
name: string;
description: string;
index: number;
};
{t('document.items', {returnObjects: true}).map(({ name, description, index }: ItemProps) => (
<li key={index}>
<strong dangerouslySetInnerHTML={{__html: name}} />
<Paragraph dangerouslySetInnerHTML={{__html: description}} />
</li>
)
)}
就會遇到錯誤訊息:
Property 'map' does not exist on type 'string'.
在 stackoverflow 找到解法:
{t<string, ItemProps[]>('document.items', {returnObjects: true}).map(({ name, description, index }) => (
<li key={index}>
<strong dangerouslySetInnerHTML={{__html: name}} />
<Paragraph dangerouslySetInnerHTML={{__html: description}} />
</li>
)
)}
並且從前端社群得知這是設定為泛型,因為 i18next 的 returnObjects 允許回傳任何形式的物件,這是在告知型別檢查器說:這裡是陣列。
直接看放在 CodeSandbox 的範例程式碼:
來源:Typescript i18next does not satisfy the constraint 'string | TemplateStringsArray NextJS
使用特殊符號與 HTML 語法
撰寫 HTML 的時候,遇到特殊符號,例如 ©,會去 Character Entity Reference 查詢(關鍵字打 html5 entity),並且寫成 ©
。這樣寫在語系檔裡面,就會照樣輸出 ©
。
必須依賴 React 的 dangerouslySetInnerHTML
來顯示:
<ContentTitle dangerouslySetInnerHTML={{__html: t('intro.title')}} />
若在語系檔裡帶 HTML,也必須用這個方法來顯示,並且在雙引號加上 escape:
{
"description":
"Get started with <a class=\"paragraph-link\" href=\"https://...\" target=\"_blank\" rel=\"noopener\">Onboarding</a>."
}
dangerouslySetInnerHTML
的缺點是無法放置其他元件,結構寫起來很不一樣。
結論
react-i18next
的說明文件上其實有 TypeScript 型別設定的教學,不過跟著做之後,在使用 next-i18next
的 Next.js 專案沒有效果(全部都是 any
)。