使用 react-i18next 的瑣碎注意事項

已經解決的,也有還沒解決的 i18next 使用經驗

使用套件

"react": ^17.0.1,
"next": 11.0.1,
"next-i18next": ^8.5.5,
"typescript": 4.3.2

覺得 i18next 的文件寫得夠完整,所以在決定作品集要做多國語系時,用這一款來輔助,避免重新發明輪胎,next-i18next 又做好許多 Next.jsi18next 之間的整合。然而,使用上還是有向技術社群求助的經驗,在這一篇集合起來。

使用特殊符號與 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 的缺點是無法放置其他元件,結構寫起來很不一樣。

TypeScript 的陣列問題

語系檔使用陣列的方法:

"document":
{
  "items":
  [
    {
      "name":
        "name 0",
      "description":
        "description 0"
    },
    {
      "name":
        "name 1",
      "description":
        "description 1"
    }
  ]
}

這樣的 TS 寫法,在稍微舊一點的 react-i18next 版本是可行的

type itemProps = {
  name: string;
  description: string;
  index: number;
};

{t<itemProps[]>('document.items', {returnObjects: true}).map(({ name, description, index }: itemProps) => (
  <li key={index}>
     <strong dangerouslySetInnerHTML={{__html: name}} />
     <Paragraph dangerouslySetInnerHTML={{__html: description}} />
   </li>
   )
)}

然而在最近的版本出現 TS 錯誤:

Type 'itemProps[]' does not satisfy the constraint 'string | TemplateStringsArray'.
  Property 'raw' is missing in type 'itemProps[]' but required in type 'TemplateStringsArray'.

.map 也有:

Property 'map' does not exist on type 'TFunctionResult'.
  Property 'map' does not exist on type 'string'.ts(2339)

目前試出不會錯誤的定義,得要這樣做:

type itemProps = {
  [x: string]: any;
};

只是定義為 any,就沒有使用 TS 的意義。目前還沒有找到明確的解答。