引言
react
的國際化方案,可以說是十分成熟了。
react-intl
一用上,文字,日期、貨幣統統搞定。
這裡想和大家交流的並不是如何使用 react-intl
,抑或如何在 redux
框架中去使用。
而是一個小知識,如何在 placeholder
等屬性中支援多語言,並且你只需按照 react-intl
的方式去維護多套語言庫。
因為像 placeholder
, title
這種 HTML
自帶的屬性,元件庫向來都是直接支援的,那麼問題來了,使用 react-intl
中的 <FormattedMessage />
作為一個 Object
是無法用到這些屬性中去的,那麼我們應該怎麼去處理這個問題呢?
他山之石,可以攻玉
下面先說幾個我搜尋到的方法。
- 方法一:
injectIntl()
是官方推薦的方法。 - 方法二:利用
<FormattedMessage />
的回撥方法。
以上兩種方法都可以在這個issue中找到。
兩種方式的實現原理很清晰。
方法一,強調使用高階元件來處理這個問題,將 intl
注入到元件的 props
中,最後使用 intl.formatMessage
來解決問題。
方法二,很巧妙的利用到了 <FormattedMessage />
元件的返回值,函式式的渲染了自身的子元件。
這兩種思路固然都有可取之處,但是也有相應的問題。
方法一: injectIntl()
如果沒有從全域性層面出發,去封裝的這種只是用於渲染的高階元件,帶來的成本,可想而知。
舉個例子,我們有時候會封裝一些自己的基礎業務元件 BaseApp
,他們本身並沒有render方法,在不同許可權的客戶訪問時,實現不同的派生類( StaffApp
, CustomerApp
),對基礎業務元件進行呼叫。
class BaseApp extends Component {
renderPersonInfo() {}
renderDataAnalytical() {}
renderTasks() {}
}
class StaffApp extends BaseApp {
render() {
return (
{
this.renderPersonInfo(),
this.renderDataAnalytical()
}
)
}
}
class CustomerApp extends BaseApp {
render() {
return (
{
this.renderPersonInfo(),
this.renderTasks()
}
)
}
}
複製程式碼
假設這個時候我們對 BaseApp
進行 injectIntl
,那麼原本可以繼承的方法,就全部失效了。所以我們必須對派生類進行 injectIntl
, 相當於,本來要做一次的事情,做了 N
次。
當然,我們可以把 BaseApp
中用到 placeholder
的元件,單獨的抽出來,進行 injectIntl
,不失為一種解決方案。
所以雖然 injectIntl
是把好刀,但是用得好,用不好,就要看自己對整個專案的理解了。
方法二:元件返回值
拋開這麼寫,到底是否優美不談。
最大的問題,還是帶來了不必要的 dom
結構,因為 <FormattedMessage />
元件是會渲染 <span />
標籤的。
想象下你的 input
元件外面還包了一個 span
是不是瑟瑟發抖呢。
用自己的思路,試試看
基於上述情況,我認為以上兩種方法,不太適合目前的專案,那就是要自己造輪子啦。
開心,又可以自己造輪子啦。
先亮程式碼
const intl = {
lang: {}
};
intl.formatMessage = (props) => {
let message = intl.lang[props.id] || props.defaultMessage || props.id;
if (!props.values) return message;
const keys = Object.keys(props.values);
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i];
message = message.replace(new RegExp(`{${key}}`, "g"), props.values[key]);
}
return message;
}
export default intl;
複製程式碼
原理比較簡單,兩步。
程式初始化的時候,對 intl.lang
賦值,同 react-intl
。
利用 props
中的 id
和 values
屬性,獲取值並進行替換,返回真正的值。
使用方法:
<Input
value={showName}
// placeholder="支援(1-10)位中文、英文與數字"
placeholder={intl.formatMessage({id: "intl.showName.placeholder"})}
onChange={this.handleNameChange.bind(this)}
/>
複製程式碼
總結
- 學習框架和類庫時,要學習精華之處,同時謹記自己一樣有寫出適合自己專案程式碼的類庫的能力,多分析,勤思考。
-----------------------分割線-----------------------
評論中提到的問題
redux中如何使用及如何動態切換語言
貼上一段專案中的實現
redux Provider
和 react-intl IntlProvider
並沒有衝突,可以直接巢狀使用。
動態切換語言,我這裡的思路是把ReactDOM.render
抽成一個方法,切換的使用再次呼叫就可以了。僅供參考,可能有更好的思路。
效果:
- 中文
- 英文
圖中部分欄位還未翻譯,故切換時沒有變更。