隨着國際化發展,多語言的需求愈來愈常見,單一的語言已經遠不能知足需求了。做爲一個組件庫,支持多語言也是基本能力。 多語言功能的本質實際上是文本的替換,一個詞彙「OK」,在英文語境下是「OK」,日語語境下是「確認」,中文語境下多是「肯定」也多是「確認」「好的」等等。html
本文將以簡單組件爲切入點,向你們展現Fusion組件庫是如何支持多語言能力的。react
咱們以一個常見的組件Search舉例,用戶輸入內容後可經過點擊「搜索」、「清除」按鈕觸發相應的事件,簡化代碼後以下:git
class Search extends React.Component {
render() {
return (
<div> <input /> <button>搜索</button> <button>清除</button> </div>
);
}
}
export default Search;
複製代碼
多語言處理最簡單直接的辦法是直接替換文本,不一樣語言環境下可能要將「搜索」替換爲「search」、「サーチ」,將「清除」替換爲「clear」、"クリア"等。同時做爲一個組件庫,涉及到的大可能是簡單詞彙而不是句子,所以咱們首選配置化的方式:github
// 抽取語言包
// search-en-us.js
{
search: 'search',
clear: 'clear'
}
// search-zh-cn.js
{
search: '搜索',
clear: '清除'
}
複製代碼
import searchZhCN from 'search-zh-cn';
class Search extends React.Component {
static propTypes = {
locale: PropTypes.object
};
static defaultProps = {
locale: searchZhCN
};
render() {
return (
<div> <input /> <button>{locale.search}</button> <button>{locale.clear}</button> </div>
);
}
}
export default Search;
複製代碼
這樣就實現了單個組件Search的多語言支持。web
可是,爲每一個組件對應一個語言包文件的作法顯然很不方便。Fusion Next做爲一個PC端的React組件庫有50+組件,內置詞彙70多條,目前有13個組件須要國際化語言能力。ide
以語種爲單位,將同一種語言下的映射關係放到一個文件裏進行處理的方式更爲高效。函數
爲便於維護管理,加強可拓展性,咱們以語種爲單位抽取語言包。將同一語種下全部組件的語言對象{key: '文案'}
放到一塊兒,以displayName做爲key,語言對象做爲value,調整語言包以下:ui
// 抽取語言包
// zh-cn.js
{
Search: {
search: '搜索',
clear: '清除'
},
Dialog: {},
...
}
複製代碼
這也是Fusion如今語言包的結構 unpkg.com/@alifd/next… 因爲語言包結構的調整,須要同時修改Search組件語言對象的默認值:this
import zhCN from 'zh-cn';
class Search extends React.Component {
...
static defaultProps = {
locale: zhCN.Search
}
...
}
export default Search;
複製代碼
在使用時,用戶將語言包對象以props參數的形式傳給組件便可直接改變文案:spa
import jaJP from 'xxxx/ja-jp.js';
<Search locale={jaJP.Search}>
<Dialog locale={jaJP.Dialog}>
複製代碼
然而,在web應用愈來愈複雜的如今,不少項目裏裏可能會用到幾十甚至上百個組件,這樣給每一個組件手動傳locale參數的方式一方面比較蠢,另外一方面開發者須要關心locale參數,在語言切換時改變值的內容。
而且語言的設置大都是以項目(或者頁面)爲單位的,有沒有更聰明、對開發者更友好的使用方式呢?
import zhCN from 'zh-cn';
<ConfigProvider locale={zhCN}> <Search /> <Dialog /> </ConfigProvider>
複製代碼
使用者在使用時基礎組件時不用關心locale的變化,子組件們共享了<ConfigProvider>
組件上傳入的語言配置,更改這一配置能夠一鍵設置子組件的語言包。如何實現的這一功能呢?
React中,若是不想經過逐層傳遞props或者state的方式來傳遞數據,不如考慮考慮Context。
藉助Context能夠實現跨層級的組件數據傳遞。
它的使用場景是生產者消費者模式,在上面的例子中,<ConfigProvider>
就是生產者,<Search> <Dialog>
組件就是消費者。 他們分別經過一系列屬性方法(childContextTypes屬性 getChildContext方法
/contextTypes屬性
),創建起一條通訊線。
// 生產者
class ConfigProvider extends React.Component {
// 聲明Context對象
static childContextTypes = {
nextLocale: PropTypes.object
}
// 返回Context對象
getChildContext () {
return {
nextLocale: {}
}
}
render () {
return this.props.children;
}
}
複製代碼
// 消費者
import zhCN from 'zh-cn';
class Search extends React.Component {
static propTypes = {
locale: PropTypes.object
};
static defaultProps = {
locale: zhCN.Search
};
// 聲明須要使用的Context屬性
static contextTypes = {
nextLocale: PropTypes.object
};
render() {
const locale = Object.assign({}, nextLocale['Search'], locale);
return (
<div> <input /> <button>{locale.search}</button> <button>{locale.clear}</button> </div>
);
}
}
export default Search;
複製代碼
這樣,直接給<ConfigProvider>
傳遞國際化參數,就能夠改變其子組件所使用的語言包。
數據傳遞的問題解決了,按照這個思路對組件進行改造就能夠完美支持一鍵切換語言了~ 事實上,這個解決方案通用性很強,只要子組件(包括自定義組件)都按上面的方式進行改造,就能夠支持語言包的切換。
但同時咱們也發現,改造工做高度重複,都是新增contextTypes靜態屬性、對props和context上的locale進行merge。有沒有對開發者(基礎組件開發者、業務組件開發者)更友好的方式來下降這部分重複性工做呢?
Fusion爲Util類組件ConfigProvider增長了一個靜態方法ConfigProvider.config(Component)
,在這個函數裏進行對於locale的改造工做,它返回一個新的受控制的高階組件(HOC)NewComponent。
NewComponent 至關於被 ConfigProvider 代理了一層。
在ConfigProvider.config()這個函數裏
這樣,只要子組件通過該函數處理,就可讓ConfigProvider
「遙控」語言包切換
import zhCN from 'zh-cn';
class Search extends React.Component {
static propTypes = {
locale: PropTypes.object
};
static defaultProps = {
locale: zhCN.Search
};
render() {
return (
<div> <input /> <button>{locale.search}</button> <button>{locale.clear}</button> </div>
);
}
}
// 通過統一處理
export default ConfigProvider.config(Search);
複製代碼
ConfigProvider.config(Component)的語言包文案分發處理邏輯簡化以下:
// ConfigProvider.jsx
function config(Component) {
class ConfigedComponent extends React.Component {
static propTypes = {
...(Component.propTypes || {}),
locale: PropTypes.object,
};
static contextTypes = {
...(Component.contextTypes || {}),
nextLocale: PropTypes.object,
};
render() {
// 組件props上直接設置
const { locale } = this.props;
// ConfigProvider"遙控"設置
const { nextLocale = {} } = this.context;
// 組件上直接設置語言包,優先級高於在父組件ConfigProvider上設置。
const newLocale = Object.assign({},
nextLocale[Component.displayName],
locale
);
return (
<Component locale={newLocale}/> ); } } return ConfigedComponent; } 複製代碼
這樣就基本完成了組件庫的多語言能力建設,這也是Fusion Next組件庫的多語言支持的思路。
除此以外,ConfigProvider還有內置了其餘通用能力,例如組件的鏡像反轉RTL,pure render開關、修改樣式的默認前綴等,更多能夠查看 ConfigProvider源代碼 和 使用文檔 瞭解。