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?
sumber
Jawaban:
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
strings
props. 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)
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:
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.
Dan untuk menyelesaikan, file terjemahan:
File Terjemahan
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
strings
dengan 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
sumber
dangerouslySetInnerHTML
prop, perhatikan implikasinya (secara manual membersihkan input). Lihat facebook.github.io/react/tips/dangerously-set-inner-html.htmlconst formStrings = { cancel, create, required }; export default { fooForm: { ...formStrings, foo: 'foo' }, barForm: { ...formStrings, bar: 'bar' } }
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 iniSaran saya untuk status awal i18n adalah:
Modul ekstra berguna untuk i18n:
1- string-template ini akan memungkinkan Anda untuk memasukkan nilai di antara string katalog Anda misalnya:
2- format-manusia modul ini akan memungkinkan Anda untuk mengonversi angka ke / dari string yang dapat dibaca manusia, misalnya:
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:
Pembaruan (14/06/2019)
Saat ini sudah banyak framework yang mengimplementasikan konsep yang sama menggunakan react context API (tanpa redux), saya pribadi merekomendasikan I18next
sumber
Solusi Antoine berfungsi dengan baik, tetapi memiliki beberapa peringatan:
Itulah mengapa kami membuat redux-polyglot di atas Redux dan Polyglot AirBNB .
(Saya salah satu penulis)
Ini menyediakan:
setLanguage(lang, messages)
getP(state)
pemilih yang mengambil sebuahP
objek yang mengekspos 4 metode:t(key)
: fungsi T poliglot aslitc(key)
: terjemahan dengan huruf besartu(key)
: terjemahan huruf besartm(morphism)(key)
: terjemahan ubahsuaian yang diubah ukurannyagetLocale(state)
pemilih untuk mendapatkan bahasa saat initranslate
komponen tingkat tinggi untuk meningkatkan komponen React Anda dengan menyuntikkanp
objek ke propsContoh penggunaan sederhana:
mengirimkan bahasa baru:
dalam komponen:
Tolong beri tahu saya jika Anda memiliki pertanyaan / saran!
sumber
_()
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.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:
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
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 :
Perbandingan kasar
gettext dengan sprintf:
messageformat.js (tebakan terbaik saya dari membaca panduan ini ):
sumber
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:
Dilengkapi dengan sampel untuk:
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
sumber
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:
Tambahkan variabel lingkungan bahasa ke package.json Anda
Hanya itu saja!
Juga jawaban asli saya menyertakan pendekatan yang lebih monolitik dengan file json tunggal untuk setiap terjemahan:
lang / ru.json
lib / lang.js
src / App.jsx
sumber