身為一個國際化的產品,能夠在產品中切換多國語言,絕對是必須的!這時 i18n 的處理便非常重要
i18n 等於國際化(internationalization),因為 i 到 n 之間有 18 個英文字母,因此常以 i18n 代替
雖然大概知道說,要多國語就需要使用像 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 的精神,是該有個工具可以套用解決了。
i18next 是用 JS 編寫的多語系框架,提供標準的 i18n 功能,可以套用在不同前端框架和語言上。
而 react-i18next 則是作為 React 與 i18next 的橋樑。使用 i18next 函式庫進行設定,並使用 react-i18next 將其多語系功能串連至 React,甚至 React-Native。
雖然實際的翻譯內容(zh.json、en.json 等)還是要自己建,不過最主要關於 redux 的那個部分,就由 react-i18next 處理掉了,甚至還提供了三種方式來取得翻譯:
import React from 'react';
import { useTranslation } from 'react-i18next';
export function MyComponent() {
const { t } = useTranslation();
return <p>{t('my translated text')}</p>
}
import React from 'react';
import { withTranslation } from 'react-i18next';
function MyComponent({ t }) {
return <p>{t('my translated text')}</p>
}
export default withTranslation()(MyComponent);
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>
}
同時,也可以使用其他 i18next 的 plugin,用來擴充更多實用的功能。
比如透過 i18next-browser-languagedetector,可以偵測使用者瀏覽器的語系,還有像是 i18next-http-backend 可以透過 http 從後端抓翻譯檔,更多 plugin 可以參考 plugins-and-utils。
缺點找不太到XD 頂多是需要先去理解語系檔案的拆分,以及使用的規則吧!盡量避免一整包很大的 namespace,其實相對比較難踩到雷。
這應該毫無懸念,只要需要多國語就用吧!能夠處理掉許多 i18n 會遇到的雜事,唯一要看的可能就在於用哪個套件吧!另一個討論度很高的是 react-intl,也可以研究看看差異。
當然今天討論的都是工程這一端的事情,翻譯的部分可以考慮 i18next 搭配的 locize (付費),或者如果有特別的 domain,也需要找到專業的翻譯人員。
今天研究完才知道,i18next 是一個很老牌的多國語套件,基本上什麼大風大浪都見過,沒有多國語的問題難得倒它,可以說,在 i18n 這個領域,都已經有相對成熟的解法了,是值得開心的事情!
i18next
react-i18next
implementing multi language i18n without any library in react native
上面某段 code 似乎有錯,setLanguage
應該是 getLanguage
?
// 預設中文
const initialState = {
locale: 'zh',
messages: setLanguage('zh')
};
感謝勘誤!使用 getLanguage
命名較好