Aplikasi React / Redux dan Multilingual (Internasionalisasi) - Arsitektur

119

Saya membuat aplikasi yang harus tersedia dalam berbagai bahasa dan lokal.

Pertanyaan saya bukanlah semata-mata teknis, melainkan tentang arsitektur, dan pola yang sebenarnya digunakan orang dalam produksi untuk memecahkan masalah ini. Saya tidak bisa menemukan "buku masak" untuk itu, jadi saya beralih ke situs web Tanya Jawab favorit saya :)

Berikut adalah persyaratan saya (mereka benar-benar "standar"):

  • Pengguna dapat memilih bahasa (sepele)
  • Setelah mengubah bahasa, antarmuka akan menerjemahkan secara otomatis ke bahasa baru yang dipilih
  • Saya tidak terlalu khawatir tentang pemformatan angka, tanggal, dll. Saat ini, saya ingin solusi sederhana untuk menerjemahkan string

Berikut adalah solusi yang mungkin saya pikirkan:

Setiap komponen berurusan dengan terjemahan secara terpisah

Ini berarti bahwa setiap komponen memiliki, misalnya satu set file en.json, fr.json dll. Di sampingnya dengan string yang diterjemahkan. Dan fungsi pembantu untuk membantu membaca nilai-nilai yang bergantung pada bahasa yang dipilih.

  • Pro: lebih menghormati filosofi React, setiap komponen "berdiri sendiri"
  • Kekurangan: Anda tidak dapat memusatkan semua terjemahan dalam file (untuk meminta orang lain menambahkan bahasa baru misalnya)
  • Kekurangan: Anda masih harus menggunakan bahasa saat ini sebagai alat bantu, di setiap komponen berdarah dan anak-anaknya

Setiap komponen menerima terjemahan melalui props

Jadi mereka tidak mengetahui bahasa saat ini, mereka hanya mengambil daftar string sebagai alat peraga yang kebetulan cocok dengan bahasa saat ini

  • Pro: karena string itu datang "dari atas", mereka bisa dipusatkan di suatu tempat
  • Kekurangan: Setiap komponen sekarang terikat ke dalam sistem terjemahan, Anda tidak bisa menggunakannya kembali, Anda perlu menentukan string yang benar setiap saat

Anda melewati props sedikit dan mungkin menggunakan hal konteks untuk menurunkan bahasa saat ini

  • Pro: sebagian besar transparan, tidak harus melewati bahasa saat ini dan / atau terjemahan melalui alat peraga setiap saat
  • Cons: sepertinya tidak praktis untuk digunakan

Jika Anda punya ide lain, silakan katakan!

Bagaimana Anda melakukannya?

Antoine Jaussoin
sumber
2
Saya lebih suka ide objek kunci dengan string terjemahan yang diturunkan sebagai prop, Anda tidak harus melewatkan setiap string sebagai prop secara individual. Mengubah ini di tingkat atas akan memicu perenderan ulang. Saya tidak berpikir menggunakan konteks adalah ide yang baik untuk ini, dan setiap komponen yang memiliki akses ke file terjemahan membuatnya kurang "bodoh" dan portabel sebenarnya imo (dan lebih sulit untuk mendapatkan aplikasi untuk merender ulang pada perubahan bahasa).
Dominic
1
Sebenarnya menurut facebook.github.io/react/docs/context.html , menggunakan konteks untuk berbagi bahasa saat ini adalah salah satu kasus penggunaan yang sah. Pendekatan yang saya coba sekarang adalah menggunakan ini ditambah Komponen Orde Tinggi untuk menangani logika mengekstraksi string untuk komponen tertentu (mungkin berdasarkan beberapa kunci)
Antoine Jaussoin
1
Mungkin Anda juga bisa melihat Instan . Mereka menangani masalah ini dengan cara yang sangat berbeda dengan menanganinya di frontend ala Optimizely (alias mengubah DOM saat memuat).
Marcel Panse
1
Tidak buruk sama sekali! Ini memang binatang yang sama sekali berbeda (yang mengikat Anda ke layanan yang mungkin perlu Anda bayar jika situs web Anda berkembang), tetapi saya suka idenya dan mungkin memang sepadan untuk situs web kecil yang Anda butuhkan untuk berjalan dengan cepat!
Antoine Jaussoin
4
Selain itu, Anda mungkin ingin menyebutkan bahwa Anda adalah salah satu pendiri Instan, alih-alih mengatakan "Mereka" seolah-olah Anda tidak ada hubungannya dengan mereka :)
Antoine Jaussoin

Jawaban:

110

Setelah mencoba beberapa solusi, saya rasa saya menemukan satu yang bekerja dengan baik dan seharusnya menjadi solusi idiomatik untuk React 0.14 (yaitu tidak menggunakan mixin, tetapi Komponen Orde Tinggi) ( edit : juga baik-baik saja dengan React 15 tentunya! ).

Jadi inilah solusinya, dimulai dari bawah (komponen individu):

Komponen

Satu-satunya hal yang dibutuhkan komponen Anda (menurut konvensi), adalah stringsprops. Ini harus berupa objek yang berisi berbagai string yang dibutuhkan Komponen Anda, tetapi sebenarnya bentuknya terserah Anda.

Itu memang berisi terjemahan default, jadi Anda dapat menggunakan komponen di tempat lain tanpa perlu memberikan terjemahan apa pun (ini akan berfungsi di luar kotak dengan bahasa default, bahasa Inggris dalam contoh ini)

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);

Komponen Tingkat Tinggi

Pada cuplikan sebelumnya, Anda mungkin telah memperhatikan ini di baris terakhir: translate('MyComponent')(MyComponent)

translate dalam hal ini adalah Higher Order Component yang membungkus komponen Anda, dan menyediakan beberapa fungsionalitas tambahan (konstruksi ini menggantikan mixin versi React sebelumnya).

Argumen pertama adalah kunci yang akan digunakan untuk mencari terjemahan dalam file terjemahan (Saya menggunakan nama komponen di sini, tetapi bisa jadi apa saja). Yang kedua (perhatikan bahwa fungsinya di-curry, untuk memungkinkan dekorator ES7) adalah Komponen itu sendiri untuk dibungkus.

Berikut kode untuk komponen terjemahan:

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;
    };
}

Ini bukan sihir: ini hanya akan membaca bahasa saat ini dari konteks (dan konteks itu tidak tersebar di seluruh basis kode, hanya digunakan di sini di pembungkus ini), dan kemudian mendapatkan objek string yang relevan dari file yang dimuat. Potongan logika ini cukup naif dalam contoh ini, dapat dilakukan sesuai keinginan Anda.

Bagian penting adalah mengambil bahasa saat ini dari konteks dan mengubahnya menjadi string, mengingat kunci yang disediakan.

Di bagian paling atas hierarki

Pada komponen root, Anda hanya perlu menyetel bahasa saat ini dari status Anda saat ini. Contoh berikut menggunakan Redux sebagai implementasi seperti Flux, tetapi dapat dengan mudah dikonversi menggunakan kerangka / pola / pustaka lainnya.

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);

Dan untuk menyelesaikan, file terjemahan:

File Terjemahan

// 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'
    }
};

apa yang kalian pikirkan?

Saya pikir ini memecahkan semua masalah yang saya coba hindari dalam pertanyaan saya: logika terjemahan tidak berdarah di seluruh kode sumber, itu cukup terisolasi dan memungkinkan menggunakan kembali komponen tanpa itu.

Misalnya, MyComponent tidak perlu dibungkus oleh translate () dan dapat dipisahkan, memungkinkannya digunakan kembali oleh orang lain yang ingin menyediakan stringsdengan caranya sendiri.

[Edit: 31/03/2016]: Saya baru-baru ini bekerja di Retrospective Board (untuk Agile Retrospectives), dibangun dengan React & Redux, dan multibahasa. Karena cukup banyak orang yang menanyakan contoh kehidupan nyata di komentar, ini dia:

Anda dapat menemukan kodenya di sini: https://github.com/antoinejaussoin/retro-board/tree/master

Antoine Jaussoin
sumber
Ini adalah solusi yang keren .. bertanya-tanya apakah Anda masih setuju dengan ini setelah beberapa bulan? Saya belum menemukan banyak nasihat dalam cara nasihat tentang pola untuk online ini
Damon
2
Saya sebenarnya, saya menemukan itu bekerja dengan sempurna (untuk kebutuhan saya). Itu membuat komponen bekerja tanpa terjemahan secara default, dan terjemahan hanya muncul di atasnya tanpa komponen menyadarinya
Antoine Jaussoin
1
@ l.cetinsoy Anda dapat menggunakan dangerouslySetInnerHTMLprop, perhatikan implikasinya (secara manual membersihkan input). Lihat facebook.github.io/react/tips/dangerously-set-inner-html.html
Teodor Sandu
6
Apakah ada alasan mengapa Anda belum mencoba react-intl?
SureshCS
1
Sangat suka solusi ini. Satu hal yang saya tambahkan yang menurut kami sangat berguna untuk konsistensi dan penghematan waktu adalah jika Anda memiliki banyak komponen dengan string umum, Anda dapat memanfaatkan variabel dan menyebarkan objek misalnyaconst formStrings = { cancel, create, required }; export default { fooForm: { ...formStrings, foo: 'foo' }, barForm: { ...formStrings, bar: 'bar' } }
Huw Davies
18

Dari pengalaman saya, pendekatan terbaik adalah membuat status redux i18n dan menggunakannya, karena berbagai alasan:

1- Ini akan memungkinkan Anda untuk meneruskan nilai awal dari database, file lokal atau bahkan dari mesin templat seperti EJS atau giok

2- Saat pengguna mengubah bahasa, Anda dapat mengubah seluruh bahasa aplikasi bahkan tanpa menyegarkan UI.

3- Ketika pengguna mengubah bahasa, ini juga akan memungkinkan Anda untuk mengambil bahasa baru dari API, file lokal atau bahkan dari konstanta

4- Anda juga dapat menyimpan hal-hal penting lainnya dengan string seperti zona waktu, mata uang, arah (RTL / LTR) dan daftar bahasa yang tersedia

5- Anda dapat menentukan bahasa perubahan sebagai tindakan redux normal

6- Anda dapat memiliki string backend dan front end di satu tempat, misalnya dalam kasus saya, saya menggunakan i18n-node untuk pelokalan dan ketika pengguna mengubah bahasa UI, saya hanya melakukan panggilan API normal dan di backend, saya hanya kembali i18n.getCatalog(req)ini akan mengembalikan semua string pengguna hanya untuk bahasa saat ini

Saran saya untuk status awal i18n adalah:

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

Modul ekstra berguna untuk i18n:

1- string-template ini akan memungkinkan Anda untuk memasukkan nilai di antara string katalog Anda misalnya:

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

2- format-manusia modul ini akan memungkinkan Anda untuk mengonversi angka ke / dari string yang dapat dibaca manusia, misalnya:

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

3- momentjs tanggal dan waktu perpustakaan npm paling terkenal, Anda dapat menerjemahkan momen tetapi sudah memiliki terjemahan bawaan hanya Anda perlu melewati bahasa negara saat ini misalnya:

import moment from "moment";

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

Pembaruan (14/06/2019)

Saat ini sudah banyak framework yang mengimplementasikan konsep yang sama menggunakan react context API (tanpa redux), saya pribadi merekomendasikan I18next

Tarif Alnamrouti
sumber
Apakah pendekatan ini akan berhasil untuk lebih dari dua bahasa? Mempertimbangkan pengaturan katalog
tempranova
Turun memilih. Ini tidak menjawab pertanyaan itu. OP meminta ide arsitektur, bukan saran atau perbandingan perpustakaan i18n mana pun.
TrungDQ
9
Saya menyarankan katalog i18n sebagai status redux, sepertinya Anda tidak mengerti redux
Tarif Alnamrouti
5

Solusi Antoine berfungsi dengan baik, tetapi memiliki beberapa peringatan:

  • Ini menggunakan konteks React secara langsung, yang cenderung saya hindari ketika sudah menggunakan Redux
  • Ini mengimpor langsung frasa dari file, yang bisa menjadi masalah jika Anda ingin mengambil bahasa yang diperlukan saat runtime, sisi klien
  • Itu tidak menggunakan perpustakaan i18n apa pun, yang ringan, tetapi tidak memberi Anda akses ke fungsi terjemahan praktis seperti pluralisasi dan interpolasi

Itulah mengapa kami membuat redux-polyglot di atas Redux dan Polyglot AirBNB .
(Saya salah satu penulis)

Ini menyediakan:

  • peredam untuk menyimpan bahasa dan pesan terkait di toko Redux Anda. Anda dapat menyediakan keduanya dengan:
    • middleware yang dapat Anda konfigurasikan untuk menangkap tindakan tertentu, mengurangi bahasa saat ini, dan mendapatkan / mengambil pesan terkait.
    • pengiriman langsung setLanguage(lang, messages)
  • sebuah getP(state)pemilih yang mengambil sebuah Pobjek yang mengekspos 4 metode:
    • t(key): fungsi T poliglot asli
    • tc(key): terjemahan dengan huruf besar
    • tu(key): terjemahan huruf besar
    • tm(morphism)(key): terjemahan ubahsuaian yang diubah ukurannya
  • a getLocale(state)pemilih untuk mendapatkan bahasa saat ini
  • translatekomponen tingkat tinggi untuk meningkatkan komponen React Anda dengan menyuntikkan pobjek ke props

Contoh penggunaan sederhana:

mengirimkan bahasa baru:

import setLanguage from 'redux-polyglot/setLanguage';

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

dalam komponen:

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);

Tolong beri tahu saya jika Anda memiliki pertanyaan / saran!

Jalil
sumber
1
Frase asli yang jauh lebih baik untuk diterjemahkan. Dan untuk membuat alat yang mengurai semua komponen untuk _()fungsi misalnya untuk mendapatkan semua string tersebut. Jadi Anda dapat menerjemahkan file dalam bahasa dengan lebih mudah dan tidak mengacaukan variabel gila. Pada beberapa kasus halaman arahan membutuhkan bagian tertentu dari tata letak untuk ditampilkan secara berbeda. Jadi beberapa fungsi cerdas tentang bagaimana memilih default vs pilihan lain yang memungkinkan harus tersedia juga.
Roman M. Koss
Hai @Jalil, apakah ada contoh lengkap middleware?
ArkadyB
Hai @ArkadyB, Kami menggunakannya pada produksi di beberapa proyek yang tidak bersumber terbuka. Anda dapat menemukan informasi lebih lanjut tentang modul README: npmjs.com/package/redux-polyglot Apakah Anda memiliki pertanyaan / kesulitan dalam menggunakannya?
Jalil
Masalah utama saya dengan ini dan polyglot.js adalah bahwa ia benar-benar menemukan kembali roda daripada membangun di atas file PO. Perpustakaan alternatif ini terlihat menjanjikan npmjs.com/package/redux-i18n . Saya tidak berpikir itu melakukan jauh berbeda - itu hanya menyediakan lapisan tambahan untuk mengkonversi ke dan dari file PO.
icc97
2

Dari penelitian saya, tampaknya ada dua pendekatan utama yang digunakan untuk i18n di JavaScript, ICU dan gettext .

Saya hanya pernah menggunakan gettext, jadi saya bias.

Yang mengherankan saya adalah betapa buruknya dukungan tersebut. Saya berasal dari dunia PHP, baik CakePHP atau WordPress. Dalam kedua situasi tersebut, ini adalah standar dasar bahwa semua string hanya dikelilingi oleh __(''), kemudian semakin jauh Anda mendapatkan terjemahan menggunakan file PO dengan sangat mudah.

gettext

Anda sudah terbiasa dengan sprintf untuk memformat string dan file PO akan diterjemahkan dengan mudah oleh ribuan agensi yang berbeda.

Ada dua opsi populer:

  1. i18next , dengan penggunaan yang dijelaskan oleh entri blog arkency.com ini
  2. Jed , dengan penggunaan yang dijelaskan oleh pos sentry.io dan pos React + Redux ini ,

Keduanya memiliki dukungan gaya gettext, format string gaya sprintf, dan impor / ekspor ke file PO.

i18next memiliki ekstensi React yang dikembangkan sendiri. Jed tidak. Sentry.io tampaknya menggunakan integrasi kustom Jed dengan React. The Bereaksi + Redux pos , menyarankan menggunakan

Alat: jed + po2json + jsxgettext

Namun Jed tampak seperti implementasi yang lebih fokus pada gettext - itulah maksud yang diungkapkan, sedangkan i18next hanya memilikinya sebagai opsi.

ICU

Ini memiliki lebih banyak dukungan untuk kasus tepi seputar terjemahan, misalnya untuk menangani gender. Saya pikir Anda akan melihat manfaat dari ini jika Anda memiliki bahasa yang lebih kompleks untuk diterjemahkan.

Opsi populer untuk ini adalah messageformat.js . Dibahas secara singkat di tutorial blog sentry.io ini . messageformat.js sebenarnya dikembangkan oleh orang yang sama yang menulis Jed. Dia membuat klaim yang cukup kuat untuk menggunakan ICU :

Jed adalah fitur terlengkap menurut saya. Saya senang memperbaiki bug, tetapi secara umum saya tidak tertarik untuk menambahkan lebih banyak ke pustaka.

Saya juga memelihara messageformat.js. Jika Anda tidak secara khusus memerlukan implementasi gettext, saya mungkin menyarankan menggunakan MessageFormat, karena memiliki dukungan yang lebih baik untuk bentuk jamak / gender dan memiliki data lokal bawaan.

Perbandingan kasar

gettext dengan 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 (tebakan terbaik saya dari membaca panduan ini ):

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.dll
sumber
Turun memilih. Ini tidak menjawab pertanyaan itu. OP meminta ide arsitektur, bukan saran atau perbandingan perpustakaan i18n mana pun.
TrungDQ
@TrungDQ Inilah yang ditanyakan OP: "Pertanyaan saya bukan semata-mata teknis, melainkan tentang arsitektur, dan pola yang sebenarnya digunakan orang dalam produksi untuk memecahkan masalah ini." . Ini adalah dua pola yang digunakan dalam produksi.
icc97
Menurut pendapat saya, jawaban ini tidak memberikan informasi yang saya (dan orang lain) cari. Informasi yang Anda berikan bermanfaat, tetapi mungkin untuk pertanyaan lain. Saya hanya ingin memberikan downvote saya untuk membuat jawaban yang benar muncul di atas (saya harap).
TrungDQ
@TrungDQ Jika bukan itu yang Anda cari, maka cukup beri suara positif pada yang Anda gunakan dan abaikan yang lain daripada turunkan jawaban yang benar-benar valid yang tidak sesuai dengan bagian spesifik dari pertanyaan yang Anda minati.
icc97
1

Jika belum selesai, lihat di https://react.i18next.com/ mungkin merupakan saran yang bagus. Ini didasarkan pada i18next: belajar sekali - menerjemahkan di mana saja.

Kode Anda akan terlihat seperti ini:

<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>

Dilengkapi dengan sampel untuk:

  • webpack
  • cra
  • expo.js
  • next.js
  • integrasi buku cerita
  • kekalapan
  • dat
  • ...

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

Selain itu Anda juga harus mempertimbangkan alur kerja selama pengembangan dan nanti untuk penerjemah Anda -> https://www.youtube.com/watch?v=9NOzJhgmyQE

jamuhl
sumber
Ini tidak menjawab pertanyaan itu. OP meminta ide arsitektur, bukan saran atau perbandingan perpustakaan i18n mana pun.
TrungDQ
@TrungDQ seperti dengan komentar Anda pada jawaban saya yang Anda berikan suara negatif - OP meminta solusi saat ini yang digunakan dalam produksi. Namun saya telah menyarankan i18next dalam jawaban saya sejak bulan Februari
icc97
0

Saya ingin mengusulkan solusi sederhana menggunakan create-react-app .

Aplikasi akan dibangun untuk setiap bahasa secara terpisah, oleh karena itu seluruh logika terjemahan akan dikeluarkan dari aplikasi.

Server web akan menyajikan bahasa yang benar secara otomatis, tergantung pada header Terima-Bahasa , atau secara manual dengan menyetel cookie .

Sebagian besar, kami tidak mengubah bahasa lebih dari sekali, jika pernah sama sekali)

Data terjemahan dimasukkan ke dalam file komponen yang sama, yang menggunakannya, bersama gaya, html, dan kode.

Dan di sini kami memiliki komponen independen sepenuhnya yang bertanggung jawab atas status, tampilan, terjemahannya sendiri:

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',
    }
);

Tambahkan variabel lingkungan bahasa ke package.json Anda

"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",

Hanya itu saja!

Juga jawaban asli saya menyertakan pendekatan yang lebih monolitik dengan file json tunggal untuk setiap terjemahan:

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
sumber
Bukankah itu hanya bekerja pada waktu kompilasi? Tanpa kemampuan bagi pengguna untuk mengubah bahasa dengan cepat? Itu kemudian akan menjadi kasus penggunaan yang berbeda.
Antoine Jaussoin
Aplikasi akan dikompilasi untuk setiap bahasa yang dibutuhkan. Server web akan menyajikan versi yang benar secara otomatis, bergantung pada tajuk "Terima-Bahasa", atau oleh cookie yang ditetapkan oleh pengguna dengan cepat. Dengan melakukan ini, seluruh logika terjemahan dapat dipindahkan dari aplikasi.
Igor Sukharev