React小知識(3) - 國際化中碰到的問題

windyrain發表於2019-01-13

引言

react的國際化方案,可以說是十分成熟了。

react-intl 一用上,文字,日期、貨幣統統搞定。

這裡想和大家交流的並不是如何使用 react-intl ,抑或如何在 redux 框架中去使用。

而是一個小知識,如何在 placeholder 等屬性中支援多語言,並且你只需按照 react-intl 的方式去維護多套語言庫。

因為像 placeholder , title 這種 HTML 自帶的屬性,元件庫向來都是直接支援的,那麼問題來了,使用 react-intl 中的 <FormattedMessage /> 作為一個 Object 是無法用到這些屬性中去的,那麼我們應該怎麼去處理這個問題呢?

他山之石,可以攻玉

下面先說幾個我搜尋到的方法。

  • 方法一:injectIntl() 是官方推薦的方法。
  • 方法二:利用 <FormattedMessage /> 的回撥方法。

以上兩種方法都可以在這個issue中找到。

兩種方式的實現原理很清晰。

方法一,強調使用高階元件來處理這個問題,將 intl 注入到元件的 props 中,最後使用 intl.formatMessage 來解決問題。

React小知識(3) - 國際化中碰到的問題

方法二,很巧妙的利用到了 <FormattedMessage /> 元件的返回值,函式式的渲染了自身的子元件。

React小知識(3) - 國際化中碰到的問題

這兩種思路固然都有可取之處,但是也有相應的問題。

方法一: 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 中的 idvalues 屬性,獲取值並進行替換,返回真正的值。

使用方法:

<Input 
  value={showName}
  // placeholder="支援(1-10)位中文、英文與數字"
  placeholder={intl.formatMessage({id: "intl.showName.placeholder"})}
  onChange={this.handleNameChange.bind(this)}
  />
複製程式碼

總結

  • 學習框架和類庫時,要學習精華之處,同時謹記自己一樣有寫出適合自己專案程式碼的類庫的能力,多分析,勤思考。

-----------------------分割線-----------------------

評論中提到的問題

redux中如何使用及如何動態切換語言

React小知識(3) - 國際化中碰到的問題

貼上一段專案中的實現

redux Providerreact-intl IntlProvider 並沒有衝突,可以直接巢狀使用。

動態切換語言,我這裡的思路是把ReactDOM.render抽成一個方法,切換的使用再次呼叫就可以了。僅供參考,可能有更好的思路。

效果:

  • 中文

React小知識(3) - 國際化中碰到的問題

  • 英文

React小知識(3) - 國際化中碰到的問題

圖中部分欄位還未翻譯,故切換時沒有變更。

相關文章