Dari kanan ke kiri. Cara mengubah antarmuka situs di bawah RTL

gambar


Kami baru-baru ini menerjemahkan versi 2GIS online ke dalam bahasa Arab, dan dalam artikel sebelumnya saya berbicara tentang teori yang diperlukan untuk ini - apa yang dir="rtl" , dengan aturan apa teks fokus campuran ditampilkan, dan bagaimana mengendalikan diri Anda.


Inilah saatnya untuk mulai berlatih - untuk mengubah seluruh antarmuka dari kanan ke kiri dengan upaya minimal sehingga bahkan orang Arab sejati tidak merasakan apa-apa.


Pada artikel ini, saya akan memberi tahu Anda cara membuat prototipe dengan cepat apa yang harus dilakukan dengan perakitan CSS dan kruk apa yang akan terurai di JS, catat sedikit tentang fitur terjemahan dan lokalisasi, ingat kembali properti logis dari CSS dan sentuh pada topik RTL di CSS-in-JS.


Gaya membalik


Ketika saya menerapkan atribut dir = "rtl" ke tag, hanya urutan implisit elemen yang berubah - misalnya, urutan sel tabel atau elemen flex. Tidak ada yang terjadi dengan nilai yang ditentukan secara eksplisit dalam gaya.

Ambil gaya beberapa notifikasi yang terletak di kanan bawah:


 .tooltip { position: 'absolute'; bottom: 10px; right: 10px; } 

dir="rtl" tidak akan memengaruhi gaya-gaya ini dengan cara apa pun - dalam versi RTL, tooltip juga akan berada di kanan, meskipun diharapkan di sebelah kiri.


Apa yang harus dilakukan Anda perlu mengganti right: 10px dengan left: 10px . Demikian juga dengan semua gaya lainnya. Penentuan posisi absolut, margin / bantalan, perataan teks - semuanya perlu diputar ke arah yang berlawanan untuk versi Arab.


Prototipe cepat


Untuk memulainya, Anda dapat, tanpa ragu-ragu, mengubah semua kejadian dari kiri ke kanan dan melakukan sedikit sihir dengan nilai steno:


  • kiri: 0 β†’ kanan: 0
  • padding-kiri: 4px β†’ padding-kanan: 4px
  • margin: 0 16px 0 0 β†’ margin: 0 0 0 16px

Plugin postcss-rtl cocok untuk ini. Nyaman - Anda hanya perlu memasukkannya ke daftar semua plugin postcss-project. Ia mengganti semua aturan yang diarahkan dengan yang dicerminkan dan membungkusnya dalam [dir="rtl"] . Sebagai contoh:


 /* input */ .foo { color: red; margin-left: 16px; } /* output */ [dir] .foo { color: red; } [dir="ltr"] .foo { margin-left: 16px; } [dir="rtl"] .foo { margin-right: 16px; } 

Setelah itu, Anda hanya perlu mengatur dir="rtl" dan hanya aturan yang diperlukan yang diterapkan secara otomatis. Semuanya berfungsi dan tampaknya hampir semuanya siap untuk produksi, tetapi solusi ini hanya cocok untuk prototipe cepat:


  • kekhususan setiap aturan meningkat . Ini belum tentu menjadi masalah, tetapi saya ingin menghindarinya;
  • manipulasi semacam itu menimbulkan bug . Misalnya, urutan properti mungkin rusak ;
  • ukuran file css terasa meningkat . [dir] ditambahkan ke setiap pemilih, setiap properti terarah digandakan. Dalam kasus kami, ukuran meningkat pada satu proyek sebesar 21%, pada proyek lain - sebesar 35%:

ukuran asli (gzip)ukuran dua arah (gzip)terus membengkak
2gis.ru272,3 kB329,7 kB21%
m.2gis.ru24,5 kB33,2 kB35%
habr.com33,1 kB41,4 kB25%

Apakah ada opsi yang lebih baik?


Perlu untuk mengumpulkan gaya untuk LTR dan RTL secara terpisah. Maka tidak perlu menyentuh pemilih dan ukuran css tidak akan berubah.


Untuk ini, saya memilih:


  1. RTLCSS - Perpustakaan ini berada di bawah tenda postcss-rtl.
  2. webpack-rtl-plugin adalah solusi turnkey untuk gaya yang dibangun melalui ExtractTextPlugin. RTLCSS yang sama di bawah tenda.

Dan dia mulai mengumpulkan RTL dan LTR ke file yang berbeda - styles.css dan styles.rtl.css . Satu-satunya minus merakit ke file yang berbeda adalah bahwa Anda tidak dapat mengganti dir on the fly tanpa terlebih dahulu mengunduh file yang diinginkan.


RTLCSS memungkinkan Anda untuk menggunakan arahan untuk mengontrol pemrosesan aturan tertentu, misalnya:


 .foo { /*rtl:ignore*/ right: 0; } .bar { font-size:16px/*rtl:14px*/; } 

Apa solusi lain yang ada?


Semua solusi yang ada hampir tidak berbeda dengan RTLCSS.


  • css-flip dari Twitter;
  • cssjanus dari Wikimedia;
  • Ya, dan postcss-rtl mendukung parameter onlyDirection , yang dengannya Anda dapat mengumpulkan gaya hanya untuk satu arah, tetapi ukurannya tetap tumbuh - misalnya, untuk seluler 2GIS, 18% bukan 35% (24,5 kB β†’ 29 kB).

Kapan arahan dibutuhkan?


Kapan gaya seharusnya tidak tergantung pada orientasi


Misalnya, sudut rotasi panah yang menunjukkan arah angin:


gambar
 .arrow._nw { /*rtl:ignore*/ transform: rotate(135deg); } 



Atau pudar untuk nomor telepon - angka selalu ditulis dari kiri ke kanan, yang berarti bahwa gradien harus selalu di kanan:


gambar


gambar


Kapan harus memusatkan ikon


Ini adalah kasus khusus dari paragraf sebelumnya. Jika kita memusatkan ikon asimetris melalui lekukan / posisi, kita menggeser bloknya ke samping, dan jika itu mencerminkan offset, ikon akan "bergerak" ke sisi lain:


gambar


Lebih baik untuk memusatkan ikon di svg sendiri dalam situasi seperti ini:



Ketika Anda perlu mengisolasi seluruh widget yang seharusnya tidak menanggapi RTL


Dalam kasus kami, ini adalah peta. Kami membungkus semua gayanya saat dirakit dalam arahan blok: /*rtl:begin:ignore*/ ... /*rtl:end:ignore*/ .


Apakah ada opsi yang lebih baik?


Solusi dengan aturan pembalikan berfungsi dengan baik, tetapi muncul pertanyaan - apakah itu penopang? Ketergantungan gaya pada arah adalah tugas alami untuk web modern, dan relevansinya tumbuh setiap tahun. Ini seharusnya tercermin dalam standar dan pendekatan modern. Dan ditemukan!


Properti yang logis


Untuk mengadaptasi tata letak untuk orientasi yang berbeda, ada standar untuk properti logis di css . Ini tidak hanya menyangkut arah dari kiri ke kanan dan dari kanan ke kiri, tetapi juga arah dari atas ke bawah, tetapi kami tidak akan mempertimbangkannya.


Kami sudah menggunakan sesuatu yang serupa pada flex-start dan grid - misalnya, flex-start , flex-end , grid-row-start , grid-column-end dilepaskan dari kiri / kanan.


Alih-alih konsep left , right , top dan bottom diusulkan untuk menggunakan inline-start , inline-end , block-start dan block-end . Alih-alih width dan height - inline-size dan inline-size block-size . Dan bukannya shortcands abcd - logical adcb logis (shortdes logis pergi berlawanan arah jarum jam). Juga, untuk memasangkan shortends yang ada, versi pasangan baru muncul - padding-block , margin-inline , border-color-inline , dll.


  • left: 0 β†’ inset-inline-start: 0
  • padding-left: 4px β†’ padding-inline-start: 4px
  • margin: 0 16px 0 0 β†’ margin: logical 0 0 0 16px
  • padding-top: 8px; padding-bottom: 16px β†’ padding-block: 8px 16px
  • margin-left: 4px; margin-right: 8px β†’ margin-inline: 4px 8px
  • text-align: right β†’ text-align: end

Dan singkatan yang sudah lama ditunggu-tunggu untuk penentuan posisi muncul:


  • left: 4px; right: 8px β†’ inset-inline: 4px 8px
  • top: 8px; bottom: 16px β†’ inset-block: 8px 16px
  • top: 0; right: 2px; bottom: 2px; left: 0 β†’ inset: logical 0 0 2px 2px

Ini sudah tersedia di firefox tanpa flag dan di browser webkit berbasis flag.


Kelebihan - solusinya adalah asli, itu akan bekerja tanpa perakitan / plugin sama sekali, jika browser yang diperlukan didukung. Tidak perlu arahan - cukup menulis left daripada inline-start , ketika Anda berarti fisik "kiri".


Kontra berasal dari pro - tanpa plugin, kode di sebagian besar browser tidak valid, Anda perlu melakukan banyak pekerjaan untuk menerjemahkan proyek besar yang ada.


Bagaimana cara terhubung?


Cara termudah adalah postcss-logis . Tanpa parameter dir , ia mengumpulkan gaya untuk kedua arah dengan cara yang sama seperti postcss-rtl, dengan parameter dir diberikan, hanya untuk arah yang ditentukan:


 .banner { color: #222222; inset: logical 0 5px 10px; padding-inline: 20px 40px; resize: block; transition: color 200ms; } /* becomes */ .banner { color: #222222; top: 0; left: 5px; bottom: 10px; right: 5px; &:dir(ltr) { padding-left: 20px; padding-right: 40px; } &:dir(rtl) { padding-right: 20px; padding-left: 40px; } resize: vertical; transition: color 200ms; } /* or, when used with { dir: 'ltr' } */ .banner { color: #222222; top: 0; left: 5px; bottom: 10px; right: 5px; padding-left: 20px; padding-right: 40px; resize: vertical; transition: color 200ms; } 

Bagaimana meyakinkan sebuah tim untuk mulai menulis offset-inline-start alih-alih kiri?


Tidak mungkin. Tapi kami memutuskan pada proyek kami untuk menyederhanakannya - tulis start: 0 bukannya offset-inline-start: 0 , segera setelah semua orang terbiasa, saya akan mulai memaksakan entri yang valid :)


RTL + CSS-in-JS = ️️ <3


CSS-in-JS tidak perlu dirakit terlebih dahulu. Ini berarti bahwa dalam runtime dimungkinkan untuk menentukan arah komponen dan memilih mana yang akan dibalik dan mana yang tidak. Berguna jika Anda perlu memasukkan beberapa widget yang tidak mendukung RTL sama sekali.


Secara umum, tugasnya adalah mengubah objek dari tipe { paddingInlineStart: '4px' } (atau { paddingLeft: '4px' } , jika tidak mungkin untuk beralih ke properti logis) menjadi objek dari tipe { paddingRight: '4px' } :


  1. Mempersenjatai dengan bidi-css-js atau rtl-css-js . Mereka menyediakan fungsi yang mengambil objek gaya dan mengembalikan ditransformasikan ke orientasi yang diinginkan.
  2. ???
  3. KEUNTUNGAN!

Bereaksi Contoh


Bungkus setiap komponen gaya dalam HOC yang menerima gaya:


 export default withStyles(styles)(Button); 

Dia mengambil arah komponen dari konteks dan memilih gaya akhir:


 function withStyles(styles) { const { ltrStyles, rtlStyles } = bidi(styles); return function WithStyles(WrappedComponent) { ... render() { return <WrappedComponent {...this.props} styles={this.context.dir === 'rtl' ? rtlStyles : ltrStyles} />; }; }; ... }; } 

Dan penyedia melemparkan fokus pada konteks:


 <DirectionProvider dir="rtl"> ... <Button /> ... 

Airbnb menggunakan pendekatan serupa: https://github.com/airbnb/react-with-styles-interface-aphrodite#built-in-rtl-support , jika aphrodite sudah digunakan pada proyek, Anda dapat menggunakan solusi yang sudah jadi ini.


Untuk JSS, ini masih lebih sederhana - Anda hanya perlu mengaktifkan jss-rtl :


 jss.use(rtl()); 

komponen-gaya


 const Button = styled.button` background: #222; margin-left: 12px; `; 

Bagaimana jika kita bekerja dengan string template, dan bukan dengan objek? Semuanya rumit, tetapi ada solusinya - hitung nama properti dari arah yang ditentukan dalam properti:


 const marginStart = props => props.theme.dir === "rtl" ? "margin-left" : "margin-right"; const Button = styled.button` background: #222; ${marginStart}: 12px; `; 

Tetapi tampaknya lebih mudah untuk beralih dari garis ke objek, komponen-komponen gaya telah mampu melakukan ini sejak versi 3.3.0 .


Fitur terjemahan dan pelokalan


Kami menemukan bagian teknisnya. Mereka mengisolasi konten dari orientasi yang tidak ditentukan, gaya cermin, menempatkan pengecualian di tempat yang tepat, menerjemahkan teks ke dalam bahasa Arab. Semuanya tampak siap - ketika beralih bahasa, seluruh antarmuka muncul di sisi lain layar, tidak ada tata letak di tempat dan semuanya terlihat lebih baik daripada di situs Arab mana pun.


Kami menunjukkan Arab asli, dan ...


Ternyata tidak semua penutur bahasa Arab tahu apa itu Twitter. Ini berlaku untuk hampir semua kata dalam bahasa Inggris. Untuk kasus seperti itu, ada transliterasi Arab: "ΨͺويΨͺΨ±".


Ternyata dalam bahasa Arab ada koma, dan fakta bahwa kita di mana-mana dalam kode disatukan melalui β€œ,”, dalam bahasa Arab, kita perlu disatukan melalui β€œΨŒβ€.


Ternyata di beberapa negara Muslim kalender resmi adalah Islam. Itu adalah bulan dan formula terjemahan yang biasa sangat diperlukan.


Ternyata di Dubai tidak ada suhu negatif dan tanda tambah dalam perkiraan +40 tidak masuk akal.


Bukan hanya mengambil dan meniru gaya


Jika kita melakukan dir="auto" pada elemen blok dan isinya ternyata LTR, teks akan mengalahkan ke sisi kiri wadah, bahkan jika itu sekitar RTL. Ini hanya dapat disembuhkan dengan secara eksplisit mengatur text-align: right . Anda bahkan dapat menerapkan ini ke seluruh halaman dalam versi Arab - nilai properti ini diwarisi.


Ikon juga tidak secara otomatis mirror. Dan tanpa ini, ikon arah, seperti panah di galeri, dapat melihat ke arah yang salah. Bayangkan ini satu-satunya kasus di mana panah yang dibuat melalui perbatasan membenarkan diri mereka sendiri!


Transformasi sederhana akan membantu mencerminkan ikon:


 [dir="rtl"] .my-icon { transform: scaleX(-1); } 

Benar, itu tidak akan membantu jika ikon berisi huruf atau angka. Maka Anda harus membuat dua ikon berbeda dan menempelkannya secara kondisional:


gambar


Namun, ternyata tidak semua elemen antarmuka perlu dicerminkan. Misalnya, dalam kasus kami, kami memutuskan untuk meninggalkan kotak centang normal:


gambar


gambar


Saya tidak tahu cara memilih elemen seperti itu dari seluruh antarmuka. Hanya penutur asli yang dapat membantu di sini, yang akan mengatakan apa yang akrab baginya dan apa yang tidak.


Input pengguna


Bahkan jika kita sepenuhnya mengendalikan semua data aplikasi kita, itu bisa menjadi input pengguna. Misalnya, nama file. Anda bahkan dapat membuat dan memberikan file .js sebagai .png - kerentanan seperti itu ada di Telegram :


cool_picture * U + 202E * gnp.js β†’ cool_picture sj.png

Dalam kasus seperti itu, perlu memfilter karakter utf yang tidak pantas dari string.


Membalik skrip


Sintaksnya sedikit berubah dalam javascript RTL. Loop yang terlihat seperti ini:


 for (let i = 0; i < arr.length; i++) { 

Sekarang Anda perlu menulis seperti ini:


 for (++i; length.arr > i; let 0 = i) { 

Lelucon.


Yang perlu Anda lakukan adalah menghindari konsep "kiri" dan "kanan" dalam kode Anda. Misalnya, kami mengalami masalah dalam menghitung koordinat tengah layar - sebelum semua kartu digantung di sebelah kiri, dan sekarang di sebelah kanan, tetapi kode aplikasi tidak mengetahuinya. Semua perhitungan dan gaya inline harus dilakukan, dengan mempertimbangkan fokus dasar.


Kecerdasan cepat


Dalam beberapa situasi, sulit untuk menerapkan dukungan RTL di beberapa komponen sistem. Maka Anda perlu mencoba menyesuaikan komponen ini untuk RTL dari luar, tetapi tinggalkan LTR di dalam.


Misalnya, kami memiliki slider. Ini hanya mendukung nilai-nilai terurut positif. Ini bisa linear dan logaritmik (pada awalnya, kepadatan nilai lebih kecil dari pada akhir). Anda harus merefleksikannya sambil mempertahankan perilaku skala.


gambar


Anda dapat membalik slider menggunakan transform: scaleX(-1) . Maka Anda harus membalikkan pekerjaan dengan mouse (klik dan seret) relatif ke tengah slider. Opsi yang salah.


Ada opsi lain - untuk memutar sumbu ke arah lain, hanya mengubah transfer dan penerimaan nilai dari slider. Jika ini adalah skala linier, alih-alih set nilai [10, 100, 1000] kami akan mentransfer set [N-1000, N-100, N-10], dan di handler kami akan mengubahnya kembali. Untuk skala logaritmik, alih-alih set [10, 100, 1000], kita lulus [1/1000, 1/100, 1/10]:


 function flipSliderValues(values, scale, isRtl) { if (!isRtl) { return values; } if (scale === 'log') { // [A, B] --> [1/B, 1/A] return values.map(x => 1 / x).reverse(); } // [A, B] --> [MAX-B, MAX-A] return values.map(x => Number.MAX_SAFE_INTEGER - x).reverse(); }; 

Ini adalah bagaimana slider mulai mendukung RTL, meskipun dia sendiri tidak tahu tentang itu.


Buku cerita


Berbeda dengan pengaturan huruf pada beberapa IE9, Anda tidak perlu menjalankan browser terpisah untuk memverifikasi pengaturan huruf di bawah RTL. Anda bahkan dapat mengeset dan melihat tata letak LTR dan RTL secara bersamaan dalam satu jendela. Untuk melakukan ini, Anda dapat, misalnya, membuat dekorator di buku cerita , yang merender dua versi cerita sekaligus:


gambar


Tangkapan layar menunjukkan bahwa tanpa isolasi text-overflow: ellipsis tidak berperilaku seperti yang kita inginkan - lebih baik segera memperbaikinya.


Adalah jauh lebih mudah untuk mendukung RTL segera selama tata letak daripada menguji benar-benar seluruh proyek nanti.


Masalah yang belum terpecahkan


Pengetahuan teori tidak sepenuhnya membantu menyelesaikan semua masalah. Saya akan memberikan satu contoh.


Sebuah petunjuk yang muncul di arah teks saat Anda mengetik. Ketika kita berbicara tentang input multibahasa, kita tidak tahu sebelumnya ke arah mana untuk menampilkan prompt ini:




Anda perlu mencoba menghindari masalah seperti itu pada tahap desain dan kadang-kadang meninggalkan solusi yang jelas untuk LTR dan tidak berlaku di RTL. Dalam hal ini, saat menavigasi petunjuknya, Anda dapat mengganti seluruh teks (seperti, misalnya, Yandex atau Google).


Kesimpulan


RTL bukan hanya "membalikkan segalanya"


Penting untuk memperhitungkan kekhasan bahasa, Anda tidak perlu membalik sesuatu, Anda perlu menyesuaikan sesuatu yang lain. Di suatu tempat dalam logika itu mutlak perlu untuk meninggalkan "kanan" / "kiri".


Sangat sulit untuk melakukan sesuatu tanpa mengetahui bahasanya


Anda akan berpikir bahwa semuanya siap sampai Anda menunjukkan proyek Anda kepada penutur asli. Berkembang dari sudut pandang seseorang yang tidak tahu bahasa apa pun . Lagipula, bahkan kata-kata yang jelas bagi Anda, seperti, misalnya, "Twitter", mungkin harus diterjemahkan. Dan tanda baca itu ternyata tidak sama di seluruh planet.


Resep terakhir


Sulit untuk menggambarkan dalam satu daftar semua yang dibahas dalam dua artikel. Tidak akan ada langkah-langkah, tapi inilah hal utama yang harus dilakukan:


  • pastikan untuk menemukan penutur asli dan tunjukkan kepadanya prototipe sedini mungkin;
  • Kumpulkan gaya untuk LTR dan RTL ke file yang berbeda. Untuk ini, rtlcss dan webpack-rtl-plugin cocok;
  • tambahkan pengecualian untuk segala sesuatu yang tidak perlu dibalik dan jelas mencerminkan apa yang tidak berbalik sendiri;
  • mengisolasi semua konten sewenang-wenang menggunakan <bdi> dan dir="auto" ;
  • secara eksplisit mengatur perataan text-align ke seluruh halaman;
  • Hindari left / right dalam kode js ketika yang Anda maksud mulai dan berakhir.

Bersiaplah untuk menghabiskan waktu paling banyak pada detail terkecil.


Bersiap terlebih dahulu tidak sulit


Dan beberapa tips bagi mereka yang tidak akan mengadaptasi situs ke RTL, tetapi ingin meletakkan sedotan:


  • jangan menggunakan properti direction untuk tujuan lain;
  • untuk jaga-jaga, masih mengisolasi semua konten yang sewenang-wenang (dan memang, bahkan dalam antarmuka bahasa Inggris, pengguna dapat menulis sesuatu dalam bahasa Arab dan merusak segalanya);
  • jika memungkinkan, gunakan properti css logis ;
  • periksa tata letak tidak hanya di browser yang berbeda, tetapi juga kadang-kadang di RTL, setidaknya demi rasa ingin tahu. Lebih baik kontrol tata letak di bawah RTL menggunakan alat seperti buku cerita ;
  • jangan izinkan konstruksi bahasa hardcode (misalnya, rangkaian string yang dipisahkan oleh koma), jika mungkin, konfigurasikan semuanya, termasuk tanda baca. Ini berguna tidak hanya untuk RTL - misalnya, dalam bahasa Yunani tanda tanya adalah ";".

Aturan-aturan ini seharusnya tidak merepotkan. Tetapi jika tiba-tiba keinginan untuk meluncurkan versi RTL datang, itu akan jauh lebih murah daripada yang bisa.

Source: https://habr.com/ru/post/id416635/


All Articles