iT邦幫忙

2022 iThome 鐵人賽

DAY 26
1
Modern Web

前端技能樹的十萬個為什麼系列 第 26

Day 26 - 為什麼要用 react-i18next

  • 分享至 

  • xImage
  •  

前言

身為一個國際化的產品,能夠在產品中切換多國語言,絕對是必須的!這時 i18n 的處理便非常重要

i18n 等於國際化(internationalization),因為 i 到 n 之間有 18 個英文字母,因此常以 i18n 代替

雖然大概知道說,要多國語就需要使用像 react-i18next 這種多國語處理套件,但具體來說解決了哪些問題,還是值得來好好看看!

先想一下

  • react-i18next 是在什麼樣的時代誕生的?
  • react-i18next 怎麼解決問題?
  • react-i18next 的優缺點是什麼?
  • react-i18next 適合什麼情境?

react-i18next 是在什麼樣的時代誕生的?

在還沒有一些 i18n 套件之前,往往需要手動處理掉許多事情,會用到 redux 之類的工具來管理語言,以中英雙語為例,可能會經過以下步驟:

建構多國語翻譯檔

建立 zh.json 與 en.json 兩個多國語翻譯檔(key-value):

// zh.json
{
   "selectLanguage": "選擇語言",
   "languageEn": "英文",
   "languageZh": "中文",
   "welcome": "歡迎",
   "hello": "你好"
}

// en.json
{
   "selectLanguage": "Select language",
   "languageEn": "English",
   "languageZh": "Chinese",
   "welcome": "Welcome",
   "hello": "Hello"
}

建置翻譯系統

翻譯檔是整個 app 範圍內都需要用到的資料,因此可以使用 redux 來管理。

reducer 會像這樣:

const getLanguage = language => {
  let messages = {};
  switch (language) {
    case 'zh':
      messages = Object.assign(messages, require(`../i18n/zh.json`));
      break;
    default:
    case 'en':
      messages = Object.assign(messages, require(`../i18n/en.json`));
      break;
  }
  return messages;
};

// 預設中文
const initialState = {
  locale: 'zh',
  messages: getLanguage('zh')
};

const intlData = (state = initialState, action) => {
  if (action === undefined) return state;
  switch (action.type) {
    case 'UPDATE_LANGUAGE':
      return {
        locale: action.language,
        messages: getLanguage(action.language)
      };
    default:
      return state;
  }
};
export default intlData;

action 會像這樣:

const updateLanguage = language => {
  return dispatch => {
    dispatch({
      type: 'UPDATE_LANGUAGE',
      language
    });
  };
};

export default {
  updateLanguage
};

使用與切換語系

接下來懂 redux 的人就比較清楚了,這部分可以選擇看是要用 HOC 的方式把多國語注入 component,或者用 hook 的方式,反正只要能夠從 redux 取得翻譯檔即可。

以上就算完成一個最基礎的手作 i18n 系統,可以見到其實不算太難,但就是麻煩,而且許多國際化的產品都有類似的需求,秉持著 DRY 的精神,是該有個工具可以套用解決了。

react-i18next 怎麼解決問題?

i18next 是用 JS 編寫的多語系框架,提供標準的 i18n 功能,可以套用在不同前端框架和語言上。

react-i18next 則是作為 React 與 i18next 的橋樑。使用 i18next 函式庫進行設定,並使用 react-i18next 將其多語系功能串連至 React,甚至 React-Native。

雖然實際的翻譯內容(zh.json、en.json 等)還是要自己建,不過最主要關於 redux 的那個部分,就由 react-i18next 處理掉了,甚至還提供了三種方式來取得翻譯:

透過 hook

import React from 'react';
import { useTranslation } from 'react-i18next';

export function MyComponent() {
  const { t } = useTranslation();

  return <p>{t('my translated text')}</p>
}

透過 HOC

import React from 'react';
import { withTranslation } from 'react-i18next';

function MyComponent({ t }) {
  return <p>{t('my translated text')}</p>
}

export default withTranslation()(MyComponent);

透過 RenderProps

import React from 'react';
import { Translation } from 'react-i18next';

export function MyComponent() {
  return (
    <Translation>
      {
        (t) => <p>{t('my translated text')}</p>
      }
    </Translation>
  )
}

切割不同情境的翻譯檔

react-i18next 也支援切割不同情境的翻譯檔,透過 namespace 去切開,比如「首頁」一個 json 檔,「商品頁」另一個 json 檔,這樣在未來程式規模擴大時,就不會一整包 json 幾萬行。

而且還支援 React Suspence,比如一開始不會立刻把所有中文語系 load 進來,畢竟很多東西其實剛開始根本用不到,可以根據現在要顯示哪一個 namespace,如果還沒 load 完成,就先 suspence。

多國語安插變數

react-i18next 支援 interpolation,比如如果要顯示「哈囉,小明 你好」這樣一串 greeting 的文字,但中間這個「小明」會根據使用者名稱變化,這時就可以在翻譯檔中安插變數:

// zh.json
{
   "greeting": "哈囉,{{name}} 你好"
}

然後使用的時候需要多帶一個 object 參數,並指定 name 的值即可:

import React from 'react';
import { useTranslation } from 'react-i18next';

export function MyComponent() {
  const { t } = useTranslation();

  return <p>{t('greeting', { name: '小明' })}</p>
}

Plugin 支援

同時,也可以使用其他 i18next 的 plugin,用來擴充更多實用的功能。

比如透過 i18next-browser-languagedetector,可以偵測使用者瀏覽器的語系,還有像是 i18next-http-backend 可以透過 http 從後端抓翻譯檔,更多 plugin 可以參考 plugins-and-utils

react-i18next 的優缺點是什麼?

優點

  • i18next 不僅限於 React,學一次可以用在其他地方
  • 處理掉複雜的多國語管理,並可以使用 HOC 或 hook 等方式取值
  • 支援 SSR 的 i18n
  • i18next 歷史悠久,有許多 plugin 支援

缺點

缺點找不太到XD 頂多是需要先去理解語系檔案的拆分,以及使用的規則吧!盡量避免一整包很大的 namespace,其實相對比較難踩到雷。

react-i18next 適合什麼情境?

這應該毫無懸念,只要需要多國語就用吧!能夠處理掉許多 i18n 會遇到的雜事,唯一要看的可能就在於用哪個套件吧!另一個討論度很高的是 react-intl,也可以研究看看差異。

當然今天討論的都是工程這一端的事情,翻譯的部分可以考慮 i18next 搭配的 locize (付費),或者如果有特別的 domain,也需要找到專業的翻譯人員。

結語

心智圖放大版

今天研究完才知道,i18next 是一個很老牌的多國語套件,基本上什麼大風大浪都見過,沒有多國語的問題難得倒它,可以說,在 i18n 這個領域,都已經有相對成熟的解法了,是值得開心的事情!

參考資料

i18next
react-i18next
implementing multi language i18n without any library in react native


上一篇
Day 25 - 為什麼要用 Lodash
下一篇
Day 27 - 為什麼要用 cookie
系列文
前端技能樹的十萬個為什麼30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Dylan
iT邦新手 1 級 ‧ 2022-11-04 22:41:54

上面某段 code 似乎有錯,setLanguage 應該是 getLanguage ?

// 預設中文
const initialState = {
  locale: 'zh',
  messages: setLanguage('zh')
};
ycchiuuuu iT邦新手 4 級 ‧ 2022-11-07 18:10:42 檢舉

感謝勘誤!使用 getLanguage 命名較好

我要留言

立即登入留言