我正在构建一个需要支持多种语言和区域设置的应用。

我的问题不是纯粹的技术问题,而是关于架构以及人们在生产中实际使用来解决此问题的模式。 我找不到任何相关的“食谱”,所以我转向我最喜欢的问答网站:)

这是我的要求(它们确实是“标准”):

  • 用户可以选择语言(简单)
  • 更改语言后,界面应自动翻译为新选择的语言
  • 目前我不太担心数字、日期等的格式,我想要一个简单的解决方案来翻译字符串

以下是我能想到的可能的解决方案:

每个组件单独处理翻译

这意味着每个组件都有一组 en.json、fr.json 等文件以及翻译后的字符串。以及一个辅助函数,可帮助读取取决于所选语言的值。

  • 优点:更尊重 React 哲学,每个组件都是“独立的”
  • 缺点:您无法将所有翻译集中到一个文件中(例如让其他人添加新语言)
  • 缺点:您仍然需要在每个该死的组件及其子组件中传递当前语言作为 Prop

每个组件通过 props 接收翻译

所以他们不知道当前的语言,他们只是将一个字符串列表作为恰好与当前语言匹配的 Prop

  • 优点:由于这些字符串“来自顶部”,因此它们可以集中在某个地方
  • 缺点:每个组件现在都绑定(bind)到翻译系统中,您不能只重复使用一个组件,每次都需要指定正确的字符串

您稍微绕过了 props 并可能使用 context传递当前语言的东西

  • 优点:它基本上是透明的,不必一直通过 props 传递当前语言和/或翻译
  • 缺点:使用起来很麻烦

如果您有其他想法,请说出来!

你是怎么做到的?

请您参考如下方法:

在尝试了相当多的解决方案之后,我认为我找到了一个效果很好的解决方案,并且应该是 React 0.14 的惯用解决方案(即它不使用 mixin,而是使用高阶组件)(编辑 :当然,使用 React 15 也完全没问题!)。

这里是解决方案,从底部开始(各个组件):

组件

您的组件唯一需要的东西(按照惯例)是一个 strings 属性。 它应该是一个包含您的组件所需的各种字符串的对象,但实际上它的形状取决于您。

它确实包含默认翻译,因此您可以在其他地方使用该组件,而无需提供任何翻译(它可以使用默认语言(本例中为英语)开箱即用)

import { default as React, PropTypes } from 'react'; 
import translate from './translate'; 
 
class MyComponent extends React.Component { 
    render() { 
 
        return ( 
             <div> 
                { this.props.strings.someTranslatedText } 
             </div> 
        ); 
    } 
} 
 
MyComponent.propTypes = { 
    strings: PropTypes.object 
}; 
 
MyComponent.defaultProps = { 
     strings: { 
         someTranslatedText: 'Hello World' 
    } 
}; 
 
export default translate('MyComponent')(MyComponent); 

高阶组件

在前面的代码片段中,您可能已经注意到最后一行: 翻译('MyComponent')(MyComponent)

translate 在这种情况下是一个高阶组件,它包装您的组件,并提供一些额外的功能(此结构取代了以前版本的 React 的 mixin)。

第一个参数是一个键,用于在翻译文件中查找翻译(我在这里使用了组件的名称,但它可以是任何名称)。第二个(请注意,该函数已柯里化(Currying),以允许 ES7 装饰器)是要包装的组件本身。

这是翻译组件的代码:

import { default as React } from 'react'; 
import en from '../i18n/en'; 
import fr from '../i18n/fr'; 
 
const languages = { 
    en, 
    fr 
}; 
 
export default function translate(key) { 
    return Component => { 
        class TranslationComponent extends React.Component { 
            render() { 
                console.log('current language: ', this.context.currentLanguage); 
                var strings = languages[this.context.currentLanguage][key]; 
                return <Component {...this.props} {...this.state} strings={strings} />; 
            } 
        } 
 
        TranslationComponent.contextTypes = { 
            currentLanguage: React.PropTypes.string 
        }; 
 
        return TranslationComponent; 
    }; 
} 

这并不神奇:它只会从上下文中读取当前语言(并且该上下文不会渗透到整个代码库中,只是在此包装器中使用),然后从加载的文件中获取相关的字符串对象。在这个例子中,这段逻辑非常幼稚,可以按照你真正想要的方式完成。

重要的是,根据提供的 key ,它从上下文中获取当前语言并将其转换为字符串。

位于层次结构的最顶层

在根组件上,您只需从当前状态设置当前语言即可。以下示例使用 Redux 作为类似 Flux 的实现,但可以使用任何其他框架/模式/库轻松转换它。

import { default as React, PropTypes } from 'react'; 
import Menu from '../components/Menu'; 
import { connect } from 'react-redux'; 
import { changeLanguage } from '../state/lang'; 
 
class App extends React.Component { 
    render() { 
        return ( 
            <div> 
                <Menu onLanguageChange={this.props.changeLanguage}/> 
                <div className=""> 
                    {this.props.children} 
                </div> 
 
            </div> 
 
        ); 
    } 
 
    getChildContext() { 
        return { 
            currentLanguage: this.props.currentLanguage 
        }; 
    } 
} 
 
App.propTypes = { 
    children: PropTypes.object.isRequired, 
}; 
 
App.childContextTypes = { 
    currentLanguage: PropTypes.string.isRequired 
}; 
 
function select(state){ 
    return {user: state.auth.user, currentLanguage: state.lang.current}; 
} 
 
function mapDispatchToProps(dispatch){ 
    return { 
        changeLanguage: (lang) => dispatch(changeLanguage(lang)) 
    }; 
} 
 
export default connect(select, mapDispatchToProps)(App); 

最后,翻译文件:

翻译文件

// en.js 
export default { 
    MyComponent: { 
        someTranslatedText: 'Hello World' 
    }, 
    SomeOtherComponent: { 
        foo: 'bar' 
    } 
}; 
 
// fr.js 
export default { 
    MyComponent: { 
        someTranslatedText: 'Salut le monde' 
    }, 
    SomeOtherComponent: { 
        foo: 'bar mais en français' 
    } 
}; 

大家觉得怎么样?

我认为这解决了我在问题中试图避免的所有问题:翻译逻辑不会渗透到整个源代码中,它是相当独立的,并且允许在没有它的情况下重用组件。

例如,MyComponent 不需要由 translate() 包装,并且可以是独立的,从而允许其他希望通过自己的方式提供字符串的人重用它。

[编辑:2016 年 3 月 31 日]:我最近在一个回顾委员会(敏捷回顾)工作,该委员会使用 React 和 Redux 构建,并且是多语言的。 由于很多人在评论中要求提供现实生活中的示例,这里是:

您可以在此处找到代码:https://github.com/antoinejaussoin/retro-board/tree/master


评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!