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?
quelle
Antworten:
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).
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:
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.
Und zum Abschluss die Übersetzungsdateien:
Übersetzungsdateien
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
quelle
dangerouslySetInnerHTML
Requisite verwenden, achten Sie nur auf die Auswirkungen (manuelle Bereinigung der Eingabe). Siehe facebook.github.io/react/tips/dangerously-set-inner-html.htmlconst formStrings = { cancel, create, required }; export default { fooForm: { ...formStrings, foo: 'foo' }, barForm: { ...formStrings, bar: 'bar' } }
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ückgegebenMein Vorschlag für den i18n-Ausgangszustand lautet:
Zusätzliche nützliche Module für i18n:
1- String-Vorlage Damit können Sie Werte zwischen Ihre Katalog-Strings einfügen, zum Beispiel:
2- Human-Format Mit diesem Modul können Sie eine Zahl in eine von Menschen lesbare Zeichenfolge konvertieren, zum Beispiel:
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:
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
quelle
Antoines Lösung funktioniert gut, hat aber einige Einschränkungen:
Aus diesem Grund haben wir Redux-Polyglot sowohl auf Redux als auch auf AirBNBs Polyglot aufgebaut .
(Ich bin einer der Autoren)
Es bietet :
setLanguage(lang, messages)
getP(state)
Selektor, der einP
Objekt abruft , das 4 Methoden verfügbar macht:t(key)
: ursprüngliche polyglotte T-Funktiontc(key)
: großgeschriebene Übersetzungtu(key)
: Übersetzung in Großbuchstabentm(morphism)(key)
: benutzerdefinierte verwandelte ÜbersetzunggetLocale(state)
Selektor, um die aktuelle Sprache zu erhaltentranslate
Komponente höherer Ordnung zur Verbesserung Ihrer React-Komponenten durch Injizieren desp
Objekts in RequisitenEinfaches Verwendungsbeispiel:
neue Sprache versenden:
in Komponente:
Bitte sagen Sie mir, wenn Sie Fragen oder Anregungen haben!
quelle
_()
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.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:
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
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 :
Grober Vergleich
gettext mit sprintf:
messageformat.js (meine beste Vermutung beim Lesen des Handbuchs ):
quelle
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:
Kommt mit Proben für:
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
quelle
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:
Fügen Sie Ihrer package.json eine Sprachumgebungsvariable hinzu
Das ist es!
Meine ursprüngliche Antwort beinhaltete auch einen monolithischeren Ansatz mit einer einzelnen JSON-Datei für jede Übersetzung:
lang / ru.json
lib / lang.js
src / App.jsx
quelle