Nachdem ich einige Lösungen ausprobiert hatte, fand ich eine, die gut funktioniert und eine idiomatische Lösung für React 0.14 sein sollte (dh es werden keine Mixins, sondern Komponenten höherer Ordnung verwendet) ( bearbeiten : natürlich auch mit React 15 vollkommen in Ordnung! ).
Also hier die Lösung, beginnend am Boden (die einzelnen Komponenten):
Die Komponente
Das einzige, was Ihre Komponente (gemäß Konvention) benötigen würde, sind strings
Requisiten. Es sollte ein Objekt sein, das die verschiedenen Zeichenfolgen enthält, die Ihre Komponente benötigt, aber die Form liegt ganz bei Ihnen.
Es enthält die Standardübersetzungen, sodass Sie die Komponente an einem anderen Ort verwenden können, ohne eine Übersetzung bereitstellen zu müssen (dies würde mit der Standardsprache Englisch in diesem Beispiel sofort funktionieren).
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);
Die Komponente höherer Ordnung
Im vorherigen Snippet haben Sie dies möglicherweise in der letzten Zeile bemerkt:
translate('MyComponent')(MyComponent)
translate
In diesem Fall handelt es sich um eine Komponente höherer Ordnung, die Ihre Komponente umschließt und einige zusätzliche Funktionen bietet (diese Konstruktion ersetzt die Mixins früherer Versionen von React).
Das erste Argument ist ein Schlüssel, mit dem die Übersetzungen in der Übersetzungsdatei nachgeschlagen werden (ich habe hier den Namen der Komponente verwendet, aber es kann alles sein). Die zweite (beachten Sie, dass die Funktion Curry ist, damit ES7-Dekorateure verwendet werden können) ist die Komponente selbst, die verpackt wird.
Hier ist der Code für die Übersetzungskomponente:
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;
};
}
Es ist keine Zauberei: Es liest nur die aktuelle Sprache aus dem Kontext (und dieser Kontext blutet nicht über die gesamte Codebasis, die hier in diesem Wrapper verwendet wird) und ruft dann das relevante Zeichenfolgenobjekt aus geladenen Dateien ab. Diese Logik ist in diesem Beispiel ziemlich naiv und könnte so gemacht werden, wie Sie es wirklich wollen.
Das wichtige Stück ist, dass es die aktuelle Sprache aus dem Kontext nimmt und diese mit dem bereitgestellten Schlüssel in Zeichenfolgen umwandelt.
Ganz oben in der Hierarchie
In der Stammkomponente müssen Sie nur die aktuelle Sprache aus Ihrem aktuellen Status festlegen. Im folgenden Beispiel wird Redux als Flux-ähnliche Implementierung verwendet, es kann jedoch problemlos mit jedem anderen Framework / Muster / jeder anderen Bibliothek konvertiert werden.
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);
Und zum Abschluss die Übersetzungsdateien:
Übersetzungsdateien
// 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'
}
};
Was denkt ihr?
Ich denke, es löst alle Probleme, die ich in meiner Frage zu vermeiden versucht habe: Die Übersetzungslogik blutet nicht im gesamten Quellcode, sie ist ziemlich isoliert und ermöglicht die Wiederverwendung der Komponenten ohne sie.
Zum Beispiel muss MyComponent nicht von translate () umbrochen werden und kann separat sein, sodass es von jedem anderen wiederverwendet werden kann, der das strings
auf eigene Faust bereitstellen möchte .
[Edit: 31/03/2016]: Ich habe kürzlich an einem Retrospective Board (für Agile Retrospectives) gearbeitet, das mit React & Redux erstellt wurde und mehrsprachig ist. Da ziemlich viele Leute in den Kommentaren nach einem realen Beispiel gefragt haben, ist es hier:
Den Code finden Sie hier: https://github.com/antoinejaussoin/retro-board/tree/master