自定义翻译组件导出功能

时间:2018-02-07 21:11:40

标签: reactjs typescript redux internationalization

我正在为i18n编写自己的组件。它被称为翻译,并连接到我的翻译所在的redux商店。

我知道有i18n库,但我想避免使用HOC,因为它很难为应用程序编写测试。

就是这样(我正在使用Typescript):

import * as React from 'react'
import { connect } from 'react-redux'
import IStoreState from '../../store/IStoreState'
import { LocalizationData } from '../../apiTypes/localizationData'
import { Translation } from '../../apiTypes/translation'

interface IProps {
  localizationData: LocalizationData | null
  context: string
  options?: Translation
  fallback?: string
}

class Translate extends React.Component<IProps, {}> {
  public render() {
    const { localizationData, context, options, fallback } = this.props
    return <span>{localizationData && this.translate(context, options, fallback)}</span>
  }

  public translate = (context: string, options?: Translation, fallback?: string): string => {
    const { localizationData } = this.props

    if (localizationData) {
      let res: string = localizationData.translations![context]
      if (options && res && res) {
        // replace optional strings
        for (const key in options) {
          if (options.hasOwnProperty(key)) {
            res = res.replace(`%${key}`, options[key] + '')
          }
        }
      }
      // fallback to fallback string OR last segment of translation key
      if (!res) {
        res = fallback || context.substr(context.lastIndexOf('.') + 1)
      }
      return res
    }
    return ''
  }
}

function mapStateToProps(state: IStoreState) {
  return {
    localizationData: state.localizationData,
  }
}

export default connect(mapStateToProps)(Translate)

它的使用方式如下:

import './components/Translate'

public render() {
  return (
    <div>
      <Translate context="page.title" />
    </div>
  );
}

这很好但我希望能够将它作为一个功能使用,以便能够做到这样的事情:

public render() {
  return (
    <div>
      <img alt={t('image.title')}/>
    </div>
  );
}

我该怎么做?

1 个答案:

答案 0 :(得分:0)

我看到两个选项:

  1. <Translate />更改为功能无状态组件。然后,返回一个字符串而不是一个span。
  2. FSC翻译:

    import translate from '../../services/translate';
    
    const Translate = props => {
        const { localizationData, context, options, fallback } = props;
        return localizationData && translate(context, options, fallback)
    };
    
    function mapStateToProps(state: IStoreState) {
        return {
            localizationData: state.localizationData,
        }
    }
    
    export default connect(mapStateToProps)(Translate)
    

    这会将您的使用情况改为:

    import './components/Translate'
    
    public render() {
      return (
        <div>
          <span><Translate context="page.title" /></span>
        </div>
      );
    }
    

    但它也允许您将组件作为返回字符串的函数调用:

    public render() {
      return (
        <div>
          <img alt={Translate({ context: 'image.title' })}/>
        </div>
      );
    }
    
    1. 甚至更好,从组件中抽象出translate函数,并将其存储在可以在任何地方使用的单独文件中。
    2. 然后<Translate />成为:

      import * as React from 'react'
      import { connect } from 'react-redux'
      import IStoreState from '../../store/IStoreState'
      import { LocalizationData } from '../../apiTypes/localizationData'
      import { Translation } from '../../apiTypes/translation'
      import translate from '../../services/translate';
      
      interface IProps {
        localizationData: LocalizationData | null
        context: string
        options?: Translation
        fallback?: string
      }
      
      class Translate extends React.Component<IProps, {}> {
        public render() {
          const { localizationData, context, options, fallback } = this.props
          return <span>{localizationData && translate(context, options, fallback, localizationData)}</span>
        }
      
      function mapStateToProps(state: IStoreState) {
        return {
          localizationData: state.localizationData,
        }
      }
      
      export default connect(mapStateToProps)(Translate)
      

      translate.js

      const translate = (context: string, options?: Translation, fallback?: string, localizationData: object): string => {
          if (localizationData) {
            let res: string = localizationData.translations![context]
            if (options && res && res) {
              // replace optional strings
              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  res = res.replace(`%${key}`, options[key] + '')
                }
              }
            }
            // fallback to fallback string OR last segment of translation key
            if (!res) {
              res = fallback || context.substr(context.lastIndexOf('.') + 1)
            }
            return res
          }
          return ''
        }
      
      export default translate;