React / Redux und mehrsprachige (Internationalisierung) Apps - Architektur

119

Ich erstelle eine App, die in mehreren Sprachen und Gebietsschemas verfügbar sein muss.

Meine Frage ist nicht rein technisch, sondern vielmehr nach der Architektur und den Mustern, die die Leute tatsächlich in der Produktion verwenden, um dieses Problem zu lösen. Ich konnte nirgendwo ein "Kochbuch" dafür finden, also wende ich mich meiner Lieblings-Q / A-Website zu :)

Hier sind meine Anforderungen (sie sind wirklich "Standard"):

  • Der Benutzer kann die Sprache wählen (trivial)
  • Beim Ändern der Sprache sollte die Benutzeroberfläche automatisch in die neu ausgewählte Sprache übersetzt werden
  • Ich mache mir im Moment keine allzu großen Sorgen um das Formatieren von Zahlen, Datumsangaben usw. Ich möchte eine einfache Lösung, um nur Zeichenfolgen zu übersetzen

Hier sind die möglichen Lösungen, an die ich denken könnte:

Jede Komponente befasst sich isoliert mit der Übersetzung

Dies bedeutet, dass jede Komponente beispielsweise eine Reihe von Dateien en.json, fr.json usw. mit den übersetzten Zeichenfolgen enthält. Und eine Hilfsfunktion zum Lesen der Werte von denen, die von der ausgewählten Sprache abhängen.

  • Pro: Respektvoller gegenüber der React-Philosophie: Jede Komponente ist "eigenständig".
  • Nachteile: Sie können nicht alle Übersetzungen in einer Datei zentralisieren (damit beispielsweise eine andere Person eine neue Sprache hinzufügt).
  • Nachteile: Sie müssen immer noch die aktuelle Sprache als Requisite in jeder blutigen Komponente und ihren Kindern weitergeben

Jede Komponente erhält die Übersetzungen über die Requisiten

Sie kennen die aktuelle Sprache nicht, sondern nehmen lediglich eine Liste von Zeichenfolgen als Requisiten, die zufällig der aktuellen Sprache entsprechen

  • Pro: Da diese Zeichenfolgen "von oben" kommen, können sie irgendwo zentralisiert werden
  • Nachteile: Jede Komponente ist jetzt in das Übersetzungssystem eingebunden. Sie können nicht nur eine wiederverwenden, sondern müssen jedes Mal die richtigen Zeichenfolgen angeben

Sie umgehen die Requisiten ein wenig und verwenden möglicherweise das Kontext- Ding, um die aktuelle Sprache weiterzugeben

  • Pro: Es ist größtenteils transparent und muss nicht ständig die aktuelle Sprache und / oder Übersetzungen über Requisiten weitergeben
  • Nachteile: Die Verwendung ist umständlich

Wenn Sie eine andere Idee haben, sagen Sie bitte!

Wie machst du das?

Antoine Jaussoin
quelle
2
Ich bevorzuge die Idee eines Objekts aus Schlüsseln mit Übersetzungszeichenfolgen, das als Requisite weitergegeben wird. Sie müssen nicht jede Zeichenfolge einzeln als Requisite übergeben. Wenn Sie dies auf oberster Ebene ändern, wird ein erneutes Rendern ausgelöst. Ich denke nicht, dass die Verwendung von Kontext eine gute Idee dafür ist, und jede Komponente, die Zugriff auf die Übersetzungsdatei hat, macht sie imo weniger "dumm" und portabel (und es ist schwieriger, die App bei einem Sprachwechsel neu zu rendern).
Dominic
1
Laut facebook.github.io/react/docs/context.html ist die Verwendung des Kontexts zum Teilen der aktuellen Sprache einer der legitimen Anwendungsfälle. Der Ansatz, den ich jetzt versuche, besteht darin, dies plus eine Komponente höherer Ordnung zu verwenden, um die Logik des Extrahierens der Zeichenfolgen für diese bestimmte Komponente (wahrscheinlich basierend auf einem Schlüssel) zu behandeln
Antoine Jaussoin
1
Vielleicht können Sie sich auch Instant ansehen . Sie behandeln dieses Problem auf eine völlig andere Art und Weise, indem sie es im Frontend ala Optimizely (auch bekannt als Ändern des DOM beim Laden) angehen.
Marcel Panse
1
Gar nicht so schlecht! Es ist in der Tat ein völlig anderes Biest (das Sie an einen Service bindet, den Sie möglicherweise bezahlen müssen, wenn Ihre Website wächst), aber ich mag die Idee und es lohnt sich in der Tat wahrscheinlich für eine kleine Website, die Sie benötigen, um schnell zum Laufen zu kommen!
Antoine Jaussoin
4
Vielleicht möchten Sie auch erwähnen, dass Sie Mitbegründer von Instant sind, anstatt "Sie" zu sagen, als hätten Sie nichts mit ihnen zu tun :)
Antoine Jaussoin

Antworten:

109

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 stringsRequisiten. 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 stringsauf 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

Antoine Jaussoin
quelle
Dies ist eine coole Lösung. Sie fragen sich, ob Sie nach ein paar Monaten noch an Bord sind? Ich habe nicht viele Ratschläge in Bezug auf Muster zu diesem Thema online gefunden
Damon
2
Ich bin tatsächlich der Meinung, dass dies perfekt funktioniert (für meine Bedürfnisse).
Dadurch
1
@ l.cetinsoy Sie können die dangerouslySetInnerHTMLRequisite verwenden, achten Sie nur auf die Auswirkungen (manuelle Bereinigung der Eingabe). Siehe facebook.github.io/react/tips/dangerously-set-inner-html.html
Teodor Sandu
6
Gibt es einen Grund, warum Sie nicht versucht haben, zu reagieren?
SureshCS
1
Mag diese Lösung wirklich. Eine Sache, die wir für Konsistenz und Zeitersparnis sehr nützlich fanden, ist, dass Sie, wenn Sie viele Komponenten mit gemeinsamen Zeichenfolgen haben, Variablen nutzen und sich über Objekte const formStrings = { cancel, create, required }; export default { fooForm: { ...formStrings, foo: 'foo' }, barForm: { ...formStrings, bar: 'bar' } }
verteilen
18

Aus meiner Erfahrung besteht der beste Ansatz darin, einen i18n-Redux-Status zu erstellen und ihn aus vielen Gründen zu verwenden:

1- Auf diese Weise können Sie den Anfangswert aus der Datenbank, der lokalen Datei oder sogar aus einer Vorlagen-Engine wie EJS oder Jade übergeben

2- Wenn der Benutzer die Sprache ändert, können Sie die gesamte Anwendungssprache ändern, ohne die Benutzeroberfläche zu aktualisieren.

3- Wenn der Benutzer die Sprache ändert, können Sie die neue Sprache auch von der API, der lokalen Datei oder sogar von Konstanten abrufen

4- Sie können auch andere wichtige Dinge mit den Zeichenfolgen wie Zeitzone, Währung, Richtung (RTL / LTR) und Liste der verfügbaren Sprachen speichern

5- Sie können die Änderungssprache als normale Redux-Aktion definieren

6- Sie können Ihre Backend- und Frontend-Zeichenfolgen an einem Ort haben. In meinem Fall verwende ich beispielsweise i18n-node zur Lokalisierung. Wenn der Benutzer die UI-Sprache ändert, führe ich einfach einen normalen API-Aufruf aus und im Backend kehre ich einfach zurück i18n.getCatalog(req)Dadurch werden alle Benutzerzeichenfolgen nur für die aktuelle Sprache zurückgegeben

Mein Vorschlag für den i18n-Ausgangszustand lautet:

{
  "language":"ar",
  "availableLanguages":[
    {"code":"en","name": "English"},
    {"code":"ar","name":"عربي"}
  ],
  "catalog":[
     "Hello":"مرحباً",
     "Thank You":"شكراً",
     "You have {count} new messages":"لديك {count} رسائل جديدة"
   ],
  "timezone":"",
  "currency":"",
  "direction":"rtl",
}

Zusätzliche nützliche Module für i18n:

1- String-Vorlage Damit können Sie Werte zwischen Ihre Katalog-Strings einfügen, zum Beispiel:

import template from "string-template";
const count = 7;
//....
template(i18n.catalog["You have {count} new messages"],{count}) // لديك ٧ رسائل جديدة

2- Human-Format Mit diesem Modul können Sie eine Zahl in eine von Menschen lesbare Zeichenfolge konvertieren, zum Beispiel:

import humanFormat from "human-format";
//...
humanFormat(1337); // => '1.34 k'
// you can pass your own translated scale, e.g: humanFormat(1337,MyScale)

3- momentjs die berühmteste Datums- und Uhrzeit- npm-Bibliothek, Sie können Moment übersetzen, aber es hat bereits eine eingebaute Übersetzung, nur müssen Sie die aktuelle Staatssprache übergeben, zum Beispiel:

import moment from "moment";

const umoment = moment().locale(i18n.language);
umoment.format('MMMM Do YYYY, h:mm:ss a'); // أيار مايو ٢ ٢٠١٧، ٥:١٩:٥٥ م

Update (14/06/2019)

Derzeit gibt es viele Frameworks, die dasselbe Konzept mithilfe der React Context API (ohne Redux) implementieren. Ich persönlich habe I18next empfohlen

Fareed Alnamrouti
quelle
Würde dieser Ansatz auch für mehr als zwei Sprachen funktionieren? In Anbetracht der Einrichtung des Katalogs
Tempranova
Down stimmte ab. Dies beantwortet die Frage nicht. OP bat um eine Architekturidee, nicht um einen Vorschlag oder Vergleich einer i18n-Bibliothek.
TrungDQ
9
Ich schlug i18n Katalog als Redux-Zustand vor, es scheint, dass Sie Redux nicht verstehen
Fareed Alnamrouti
5

Antoines Lösung funktioniert gut, hat aber einige Einschränkungen:

  • Es verwendet direkt den React-Kontext, den ich eher vermeide, wenn ich bereits Redux verwende
  • Es importiert direkt Phrasen aus einer Datei, was problematisch sein kann, wenn Sie die benötigte Sprache zur Laufzeit clientseitig abrufen möchten
  • Es verwendet keine i18n-Bibliothek, die leichtgewichtig ist, bietet Ihnen jedoch keinen Zugriff auf praktische Übersetzungsfunktionen wie Pluralisierung und Interpolation

Aus diesem Grund haben wir Redux-Polyglot sowohl auf Redux als auch auf AirBNBs Polyglot aufgebaut .
(Ich bin einer der Autoren)

Es bietet :

  • Ein Reduzierer zum Speichern der Sprache und der entsprechenden Nachrichten in Ihrem Redux-Speicher. Sie können beides liefern durch:
    • Eine Middleware, die Sie konfigurieren können, um bestimmte Aktionen abzufangen, die aktuelle Sprache abzuziehen und zugehörige Nachrichten abzurufen / abzurufen.
    • direkter Versand von setLanguage(lang, messages)
  • Ein getP(state)Selektor, der ein PObjekt abruft , das 4 Methoden verfügbar macht:
    • t(key): ursprüngliche polyglotte T-Funktion
    • tc(key): großgeschriebene Übersetzung
    • tu(key): Übersetzung in Großbuchstaben
    • tm(morphism)(key): benutzerdefinierte verwandelte Übersetzung
  • ein getLocale(state)Selektor, um die aktuelle Sprache zu erhalten
  • Eine translateKomponente höherer Ordnung zur Verbesserung Ihrer React-Komponenten durch Injizieren des pObjekts in Requisiten

Einfaches Verwendungsbeispiel:

neue Sprache versenden:

import setLanguage from 'redux-polyglot/setLanguage';

store.dispatch(setLanguage('en', {
    common: { hello_world: 'Hello world' } } }
}));

in Komponente:

import React, { PropTypes } from 'react';
import translate from 'redux-polyglot/translate';

const MyComponent = props => (
  <div className='someId'>
    {props.p.t('common.hello_world')}
  </div>
);
MyComponent.propTypes = {
  p: PropTypes.shape({t: PropTypes.func.isRequired}).isRequired,
}
export default translate(MyComponent);

Bitte sagen Sie mir, wenn Sie Fragen oder Anregungen haben!

Jalil
quelle
1
Viel bessere Originalphrasen, die übersetzt werden müssen. Und um ein Tool zu erstellen, das alle Komponenten nach _()Funktionen analysiert, um beispielsweise alle diese Zeichenfolgen abzurufen. So können Sie es in einer Sprachdatei einfacher übersetzen und sich nicht mit verrückten Variablen anlegen. In einigen Fällen benötigen Zielseiten einen bestimmten Teil des Layouts, um anders angezeigt zu werden. Daher sollte auch eine intelligente Funktion zur Auswahl der Standardeinstellung im Vergleich zu anderen möglichen Optionen verfügbar sein.
Roman M. Koss
Hallo @Jalil, gibt es irgendwo ein vollständiges Beispiel mit Middleware?
ArkadyB
Hallo @ArkadyB, wir verwenden es in der Produktion für mehrere Projekte, die nicht Open-Source sind. Weitere Informationen zum Modul README finden Sie unter: npmjs.com/package/redux-polyglot Haben Sie Fragen oder Schwierigkeiten bei der Verwendung?
Jalil
Mein Hauptproblem bei this und polyglot.js ist, dass das Rad komplett neu erfunden wird, anstatt auf PO-Dateien aufzubauen. Diese alternative Bibliothek sieht vielversprechend aus npmjs.com/package/redux-i18n . Ich denke nicht, dass dies viel anders ist - es bietet lediglich eine zusätzliche Ebene zum Konvertieren in und aus PO-Dateien.
icc97
2

Aus meiner Forschung zu diesem Thema scheinen zwei Hauptansätze für i18n in JavaScript, ICU und gettext verwendet zu werden .

Ich habe immer nur gettext verwendet, daher bin ich voreingenommen.

Was mich erstaunt ist, wie schlecht die Unterstützung ist. Ich komme aus der PHP-Welt, entweder CakePHP oder WordPress. In beiden Situationen ist es ein grundlegender Standard, dass alle Zeichenfolgen einfach von umgeben sind. Später erhalten Sie __('')dann ganz einfach Übersetzungen mit PO-Dateien.

gettext

Sie sind mit sprintf vertraut, um Zeichenfolgen zu formatieren, und PO-Dateien werden von Tausenden verschiedener Agenturen problemlos übersetzt.

Es gibt zwei beliebte Optionen:

  1. i18next , mit der in diesem Blogbeitrag von arkency.com beschriebenen Verwendung
  2. Jed , mit der Verwendung, die durch den Beitrag sentry.io und diesen Beitrag React + Redux beschrieben wird ,

Beide unterstützen gettext style, sprintf formatieren Zeichenfolgen und importieren / exportieren in PO-Dateien.

i18next verfügt über eine eigenständige React-Erweiterung . Jed nicht. Sentry.io scheint eine benutzerdefinierte Integration von Jed mit React zu verwenden. Der React + Redux-Beitrag schlägt die Verwendung vor

Tools: jed + po2json + jsxgettext

Jed scheint jedoch eine gettextorientiertere Implementierung zu sein - das heißt, es ist eine ausdrückliche Absicht, bei der i18next dies nur als Option hat.

Intensivstation

Dies hat mehr Unterstützung für die Randfälle bei Übersetzungen, z. B. für den Umgang mit Geschlecht. Ich denke, Sie werden die Vorteile davon sehen, wenn Sie komplexere Sprachen zum Übersetzen haben.

Eine beliebte Option hierfür ist messageformat.js . In diesem sentry.io-Blog-Tutorial kurz besprochen . messageformat.js wird tatsächlich von derselben Person entwickelt, die Jed geschrieben hat. Er macht ziemlich starke Behauptungen für die Nutzung der Intensivstation :

Jed ist meiner Meinung nach komplett. Ich bin froh, Fehler zu beheben, bin aber im Allgemeinen nicht daran interessiert, der Bibliothek weitere hinzuzufügen.

Ich pflege auch messageformat.js. Wenn Sie keine gettext-Implementierung benötigen, kann ich stattdessen die Verwendung von MessageFormat vorschlagen, da es Plural / Geschlecht besser unterstützt und integrierte Gebietsschemadaten enthält.

Grober Vergleich

gettext mit sprintf:

i18next.t('Hello world!');
i18next.t(
    'The first 4 letters of the english alphabet are: %s, %s, %s and %s', 
    { postProcess: 'sprintf', sprintf: ['a', 'b', 'c', 'd'] }
);

messageformat.js (meine beste Vermutung beim Lesen des Handbuchs ):

mf.compile('Hello world!')();
mf.compile(
    'The first 4 letters of the english alphabet are: {s1}, {s2}, {s3} and {s4}'
)({ s1: 'a', s2: 'b', s3: 'c', s4: 'd' });
icc97
quelle
Down stimmte ab. Dies beantwortet die Frage nicht. OP bat um eine Architekturidee, nicht um einen Vorschlag oder Vergleich einer i18n-Bibliothek.
TrungDQ
@TrungDQ Dies ist, was das OP fragte: "Meine Frage ist nicht rein technisch, sondern betrifft die Architektur und die Muster, die die Leute tatsächlich in der Produktion verwenden , um dieses Problem zu lösen." . Dies sind zwei Muster, die in der Produktion verwendet werden.
icc97
Meiner Meinung nach enthält diese Antwort nicht die Informationen, nach denen ich (und andere) suchen. Die von Ihnen angegebenen Informationen sind hilfreich, aber möglicherweise für eine andere Frage. Ich möchte nur meine Ablehnung abgeben, damit die richtige Antwort ganz oben auftaucht (hoffe ich).
TrungDQ
@TrungDQ Wenn es nicht das ist, wonach Sie suchen, stimmen Sie einfach diejenige ab, die Sie verwendet haben, und ignorieren Sie die anderen, anstatt perfekt gültige Antworten herabzustimmen, die nicht dem spezifischen Teil der Frage entsprechen, an der Sie interessiert sind.
icc97
1

Wenn Sie noch nicht fertig sind, ist ein Blick auf https://react.i18next.com/ möglicherweise ein guter Rat. Es basiert auf i18next: einmal lernen - überall übersetzen.

Ihr Code sieht ungefähr so ​​aus:

<div>{t('simpleContent')}</div>
<Trans i18nKey="userMessagesUnread" count={count}>
  Hello <strong title={t('nameTitle')}>{{name}}</strong>, you have {{count}} unread message. <Link to="/msgs">Go to messages</Link>.
</Trans>

Kommt mit Proben für:

  • Webpack
  • cra
  • expo.js
  • next.js
  • Storybook-Integration
  • blenden
  • dat
  • ...

https://github.com/i18next/react-i18next/tree/master/example

Außerdem sollten Sie den Workflow während der Entwicklung und später für Ihre Übersetzer berücksichtigen -> https://www.youtube.com/watch?v=9NOzJhgmyQE

Jamuhl
quelle
Dies beantwortet die Frage nicht. OP bat um eine Architekturidee, nicht um einen Vorschlag oder Vergleich einer i18n-Bibliothek.
TrungDQ
@TrungDQ wie bei Ihrem Kommentar zu meiner Antwort, die Sie abgelehnt haben - das OP fragte nach aktuellen Lösungen für die Produktion. Allerdings hatte ich i18next in meiner Antwort von zurück im Februar vorgeschlagen
icc97
0

Ich möchte eine einfache Lösung mit der Create-React-App vorschlagen .

Die Anwendung wird für jede Sprache separat erstellt, daher wird die gesamte Übersetzungslogik aus der Anwendung entfernt.

Der Webserver stellt die richtige Sprache automatisch bereit, abhängig vom Header " Accept-Language" oder manuell, indem ein Cookie gesetzt wird .

Meistens ändern wir die Sprache nicht mehr als einmal, wenn überhaupt)

Übersetzungsdaten werden in derselben Komponentendatei abgelegt, die sie verwendet, zusammen mit Stilen, HTML und Code.

Und hier haben wir eine völlig unabhängige Komponente, die für ihren eigenen Zustand, ihre eigene Ansicht und Übersetzung verantwortlich ist:

import React from 'react';
import {withStyles} from 'material-ui/styles';
import {languageForm} from './common-language';
const {REACT_APP_LANGUAGE: LANGUAGE} = process.env;
export let language; // define and export language if you wish
class Component extends React.Component {
    render() {
        return (
            <div className={this.props.classes.someStyle}>
                <h2>{language.title}</h2>
                <p>{language.description}</p>
                <p>{language.amount}</p>
                <button>{languageForm.save}</button>
            </div>
        );
    }
}
const styles = theme => ({
    someStyle: {padding: 10},
});
export default withStyles(styles)(Component);
// sets laguage at build time
language = (
    LANGUAGE === 'ru' ? { // Russian
        title: 'Транзакции',
        description: 'Описание',
        amount: 'Сумма',
    } :
    LANGUAGE === 'ee' ? { // Estonian
        title: 'Tehingud',
        description: 'Kirjeldus',
        amount: 'Summa',
    } :
    { // default language // English
        title: 'Transactions',
        description: 'Description',
        amount: 'Sum',
    }
);

Fügen Sie Ihrer package.json eine Sprachumgebungsvariable hinzu

"start": "REACT_APP_LANGUAGE=ru npm-run-all -p watch-css start-js",
"build": "REACT_APP_LANGUAGE=ru npm-run-all build-css build-js",

Das ist es!

Meine ursprüngliche Antwort beinhaltete auch einen monolithischeren Ansatz mit einer einzelnen JSON-Datei für jede Übersetzung:

lang / ru.json

{"hello": "Привет"}

lib / lang.js

export default require(`../lang/${process.env.REACT_APP_LANGUAGE}.json`);

src / App.jsx

import lang from '../lib/lang.js';
console.log(lang.hello);
Igor Sukharev
quelle
Würde es nicht nur zur Kompilierungszeit funktionieren? Ohne die Möglichkeit für den Benutzer, die Sprache im laufenden Betrieb zu ändern? Das wäre dann ein anderer Anwendungsfall.
Antoine Jaussoin
Die App wird für jede benötigte Sprache kompiliert. Der Webserver stellt automatisch die richtige Version bereit, abhängig vom Header "Accept-Language" oder von einem vom Benutzer im laufenden Betrieb gesetzten Cookie. Auf diese Weise kann die gesamte Übersetzungslogik aus der App entfernt werden.
Igor Sukharev