antd源码-locale国际化解析

antd源码-locale国际化解析

antd的国际化是利用react的context来完成的。

组件内部

我找了一个需要国际化的组件,popconfirm

源码

 renderConfirm = ({ getPrefixCls }: ConfigConsumerProps) => {
    const { prefixCls: customizePrefixCls, placement, ...restProps } = this.props;
    const prefixCls = getPrefixCls('popover', customizePrefixCls);

    const overlay = (
      <LocaleReceiver componentName="Popconfirm" defaultLocale={defaultLocale.Popconfirm}>
        {(popconfirmLocale: PopconfirmLocale) => this.renderOverlay(prefixCls, popconfirmLocale)}
      </LocaleReceiver>
    );

    return (
      <Tooltip
        {...restProps}
        prefixCls={prefixCls}
        placement={placement}
        onVisibleChange={this.onVisibleChange}
        visible={this.state.visible}
        overlay={overlay}
        ref={this.saveTooltip}
      />
    );
  };

国际化相关的是LocaleReceiver这个组件,这边传递了components和defaultLocale属性到组件内,

LocaleReceiver (国际化消费组件)

export default class LocaleReceiver extends React.Component<LocaleReceiverProps> {
  static defaultProps = {
    componentName: 'global',
  };
	// 当前组件的contet里面有antLocale对象。如果我们没有配置的时候,。没有服务提供者的话当前,antLocale是没有的,为undefined
  static contextTypes = {
    antLocale: PropTypes.object,
  };

  context: LocaleReceiverContext;

  getLocale() {
    const { componentName, defaultLocale } = this.props;
    const locale: object | Function =
      defaultLocale || (defaultLocaleData as LocaleInterface)[componentName || 'global'];
    const { antLocale } = this.context;
    const localeFromContext = componentName && antLocale ? antLocale[componentName] : {};
    return {
      ...(typeof locale === 'function' ? locale() : locale),
      ...(localeFromContext || {}),
    };
  }

  getLocaleCode() {
    const { antLocale } = this.context;
    const localeCode = antLocale && antLocale.locale;
    // Had use LocaleProvide but didn't set locale
    if (antLocale && antLocale.exist && !localeCode) {
      return defaultLocaleData.locale;
    }
    return localeCode;
  }

  render() {
      // 返回调用props.children传递的函数,
      //在popconfirm组件是  {(popconfirmLocale: PopconfirmLocale) => 		       this.renderOverlay(prefixCls, popconfirmLocale)},
     // 组件内部的由国际化包裹的组件的匿名函数得到当前组件的国际化配置,
    return this.props.children(this.getLocale(), this.getLocaleCode(), this.context.antLocale);
  }
}

this.getLocale(),获得的是一个对象,是当前组件的需要的国际化配置json,renderOverlay函数内部可以通过popconfirmLocale.okText 获得其描述。当我们没有对其locale配置的时候,默认取的是英文。

LocaleProvider(国际化提供组件)

export default class LocaleProvider extends React.Component<LocaleProviderProps, any> {
  static propTypes = {
    locale: PropTypes.object,
  };
	
  static defaultProps = {
    locale: {},
  };
	// 定义子组件的context
  static childContextTypes = {
    antLocale: PropTypes.object,
  };

  constructor(props: LocaleProviderProps) {
    super(props);
    setMomentLocale(props.locale);
    changeConfirmLocale(props.locale && props.locale.Modal);

    warning(
      props._ANT_MARK__ === ANT_MARK,
      'LocaleProvider',
      '`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead: http://u.ant.design/locale',
    );
  }
	// 有改变的时候修改context的antLocale属性
  getChildContext() {
    return {
      antLocale: {
        ...this.props.locale,
        exist: true,
      },
    };
  }

  componentDidUpdate(prevProps: LocaleProviderProps) {
    const { locale } = this.props;
    if (prevProps.locale !== locale) {
      setMomentLocale(locale);
      changeConfirmLocale(locale && locale.Modal);
    }
  }

  componentWillUnmount() {
    changeConfirmLocale();
  }

  render() {
    return this.props.children;
  }
}

总结

  1. antd国际化主要运用context,每一个需要用到国际化的组件用context消费组件包裹,取到语言包,如果最外层没有用configprovider包裹就取默认的英文,如果有自己配置语言,就使用当前的语言包。

  2. 国际化语言包的配置,antd以组件的维度,去组织国际化json,那在实际我们开发的项目中是否可以借鉴呢?

    我认为可行,国际语言包,按页面分割文件,页面语言文件承载页面组件的国际化json,公用组件用共用组件文件,切割开文件,分清楚语言包内容的位置,便于查找和修改。


版权声明:本文为luo_qianyu原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。