我的问题不是纯粹的技术,而是关于建筑,以及人们实际在生产中使用的模式来解决这个问题。
我找不到任何地方任何“食谱”,所以我转向我最喜欢的Q / A网站:)
这里是我的要求(他们真的是“标准”):
>用户可以选择语言(平凡)
>更改语言后,界面应自动翻译为新的所选语言
>我不是太担心格式化数字,日期等,在这一刻,我想要一个简单的解决方案,只是翻译字符串
下面是我可以想到的可能的解决方案:
每个组件单独处理翻译
这意味着每个组件都有例如一组en.json,fr.json等文件以及翻译的字符串。以及帮助函数,帮助读取取决于所选语言的值。
> Pro:更尊重React的理念,每个组件都是“独立”
>缺点:您无法将所有翻译集中在文件中(例如要让其他人添加新语言)
>缺点:你仍然需要传递当前语言作为道具,在每个血腥的组件和他们的孩子
每个组件通过道具接收翻译
所以他们不知道当前的语言,他们只是把字符串列表作为道具,碰巧匹配当前语言
> Pro:因为那些字符串来自“顶部”,所以它们可以集中在某个地方
>缺点:每个组件现在绑定到翻译系统,你不能只是重复使用,你需要每次指定正确的字符串
你绕过道具有点,可能使用context东西传递当前语言
> Pro:它大部分是透明的,不必通过道具通过当前语言和/或翻译所有的时间
>缺点:使用看起来很麻烦
如果你有其他想法,请说!
你怎么做呢?
组件
你的组件唯一需要的(按照惯例),是一个字符串道具。
它应该是一个包含你的组件需要的各种字符串的对象,但真正的形状是由你决定的。
它包含默认翻译,所以你可以使用其他地方的组件,而不需要提供任何翻译(它将使用默认语言,在这个例子中的英语框)
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);
高阶组件
在上一个片段中,您可能已经在最后一行注意到了这一点:
translate(‘MyComponent’)(MyComponent)
在这种情况下,translate是一个高阶组件,它包装你的组件,并提供一些额外的功能(这个结构取代了以前的React版本的混合)。
第一个参数是一个键,用于查找翻译文件中的翻译(我在这里使用了组件的名称,但它可以是任何东西)。第二个(注意函数是curryed,允许ES7装饰器)是组件自身要包装。
这里是translate组件的代码:
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; }; }
这不是魔术:它只是从上下文中读取当前语言(并且上下文不会在整个代码库中流出,只是在这个包装器中使用),然后从加载的文件中获取相关的字符串对象。这个逻辑在这个例子中是相当朴素的,可以按照你想要的方式做。
重要的是,它从上下文中获取当前语言,并将其转换为字符串,给定所提供的键。
在层次结构的最顶端
在根组件上,您只需要从当前状态设置当前语言。以下示例使用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()包装,并且可以是单独的,允许任何想要以自己的意思提供字符串的人重用它。
[编辑:31/03/2016]:我最近在一个回顾董事会(敏捷回顾),用React& Redux,并且是多语言的。
由于很多人在评论中要求一个现实生活的例子,这里是:
你可以在这里找到代码:https://github.com/antoinejaussoin/retro-board/tree/master