Pluralization & Gender — i18n

Pluralization & Gender Pluralisasi bukan sekadar "1 = singular, 2+ = plural". Bahasa Arab punya 6 bentuk plural, Rusia punya 3, dan Jepang tidak punya plural…

Pluralisasi memilih bentuk kata menurut jumlah; gender memilih bentuk kata menurut siapa yang dibicarakan. Bayangin Bu Sri punya satu set stempel untuk menulis lama menginap di kuitansi. Untuk bahasa Indonesia cukup satu stempel: "2 malam", "11 malam". Untuk bahasa Inggris dua: "1 night" dan "2 nights". Untuk bahasa Arab enam, termasuk satu stempel khusus untuk tepat dua malam. Intl.PluralRules adalah petugas yang membaca angkanya lalu menyerahkan stempel yang benar.

🧪 Tebakan cepat: Dalam bahasa Arab, apakah 3 malam dan 103 malam memakai bentuk kata yang sama? Bagaimana dengan 2 malam dan 102 malam? Jawabannya ada di bagian "Angka mana masuk kategori mana".

Kategori jamak CLDR

CLDR (data locale yang dibawa browser) memberi tiap bahasa sebagian dari enam kategori: zero, one, two, few, many, other. Intl.PluralRules memberi tahu kategori sebuah angka:

new Intl.PluralRules('ar-EG').select(3);                          // "few"
new Intl.PluralRules('en-US').select(1);                          // "one"
new Intl.PluralRules('id-ID').resolvedOptions().pluralCategories; // ["other"]
Locale Kategori Contoh "malam"
id-ID other 1 malam, 2 malam
en-US one, other 1 night, 2 nights
ja-JP other 1泊, 2泊
ar-EG zero, one, two, few, many, other ليلة واحدة, ليلتان, ٣ ليالٍ, ١١ ليلة

Kategori itu nama, bukan angka. Di bahasa Prancis, 0 dan 1,5 juga one; di bahasa Rusia, 21 juga one. other wajib ada di setiap bahasa. Bahasa Indonesia dan Jepang gak punya penanda jamak, jadi satu bentuk other cukup.

Angka mana masuk kategori mana

Kategori bahasa Arab dihitung dari dua digit terakhir. Hasil new Intl.PluralRules('ar-EG').select(n), sama di Chromium 147, Firefox 146, WebKit 26.4, dan Node 22.22.1:

n Kategori Pesan nights (dari pelajaran react-intl)
0 zero ٠ ليلة
1 one ليلة واحدة
2 two ليلتان (bentuk dual, tanpa angka)
3 sampai 10 few ٣ ليالٍ
11 sampai 99 many ١١ ليلة
100, 101, 102 other ١٠٠ ليلة
103 sampai 110 few ١٠٣ ليالٍ
111 sampai 199 many ١١١ ليلة
1,5 other ١٫٥ ليلة

Jadi 3 dan 103 sama-sama few, sedangkan 2 itu two dan 102 itu other. Itu jawaban tebakannya. Pesan nights kami gak punya cabang zero, jadi 0 memakai other. Satu beda kecil antar mesin: urutan pluralCategories di Node 22.22.1 adalah few, many, one, two, zero, other, di tiga browser zero, one, two, few, many, other. Isinya sama.

Kenapa count === 1 ? ... : ... rusak

const nf = new Intl.NumberFormat('ar-EG');
const naive = (n) => (n === 1 ? 'ليلة واحدة' : nf.format(n) + ' ليالٍ');  // pola "satu atau banyak"
n naive(n) Yang benar
2 ٢ ليالٍ ليلتان
11 ١١ ليالٍ ١١ ليلة
100 ١٠٠ ليالٍ ١٠٠ ليلة

Pola dua cabang cuma cocok untuk bahasa yang punya dua bentuk. Bahasa Arab punya enam, bahasa Rusia punya one, few, many, dan other. Simpan semua bentuk di kamus, lalu biarkan Intl.PluralRules (lewat ICU plural atau akhiran _few di i18next) yang memilih. Angka desimal juga beda: new Intl.PluralRules('en-US', { minimumFractionDigits: 1 }).select(1) memberi "other", karena yang tertulis "1.0 nights".

Bilangan urutan: type: 'ordinal'

"Menginap ke-21" butuh aturan lain: ordinal.

const ord = new Intl.PluralRules('en-US', { type: 'ordinal' });
[1, 2, 3, 4, 11, 12, 13, 21, 22, 23, 111].map((n) => ord.select(n));
// ["one", "two", "few", "other", "other", "other", "other", "one", "two", "few", "other"]
{n, selectordinal, one {Your #st stay} two {Your #nd stay} few {Your #rd stay} other {Your #th stay}}

Gender dengan select

id-ID: Terima kasih, {gender, select, male {Pak} female {Bu} other {Kak}} {name}!
ar-EG: {gender, select, female {شكرًا لكِ يا {name}!} other {شكرًا لكَ يا {name}!}}
en-US: Thank you, {name}!

Plural dan select dalam satu pesan

Taruh select di luar dan plural di dalam, dengan kalimat utuh di tiap cabang, supaya penerjemah melihat kalimat lengkap:

en-US: {gender, select,
  male {He booked {n, plural, one {# night} other {# nights}}.}
  female {She booked {n, plural, one {# night} other {# nights}}.}
  other {They booked {n, plural, one {# night} other {# nights}}.}}
id-ID: {gender, select, male {Pak {name}} female {Bu {name}} other {Kak {name}}} memesan {n, plural, other {# malam}}.

intl-messageformat 12.1.3 memberi "She booked 2 nights.", "They booked 1 night.", dan "Bu Rina memesan 2 malam.". Di i18next, gabungannya memakai context plus count: key booked_male_one, booked_female_other, booked_one, booked_other, lalu t('booked', { context: gender, count: n }). Untuk gender yang gak diketahui, kirim context: undefined, bukan 'other': dengan 'other', i18next 26.4.3 membaca key booked_other (bentuk jamak) dan menulis "They booked 1 nights."

⚠️ Jebakan yang sering ketemu

TL;DR: CLDR punya enam kategori jamak: zero, one, two, few, many, other. Intl.PluralRules adalah API browser yang menentukan kategori sebuah angka untuk sebuah locale: id-ID dan ja-JP cuma other, en-US one dan other, ar-EG keenamnya (0 zero, 1 one, 2 two, 3 sampai 10 few, 11 sampai 99 many, 100 sampai 102 other, lalu 103 few lagi). Pluralisasi gak bisa sekadar mengecek count === 1, karena beberapa bahasa punya lebih dari dua bentuk jamak. Urutan memakai { type: 'ordinal' } (1st, 2nd, 3rd, 4th, 11th, 21st) atau ICU selectordinal. Gender memakai select dengan cabang other yang wajib: sapaan Pak/Bu/Kak di id-ID, لكَ/لكِ di ar-EG; gender tata bahasa (ليلة feminin) gak ada hubungannya dengan gender tamu, dan gender gak boleh ditebak dari nama. Gabungkan select di luar dan plural di dalam; di i18next pakai context plus count, dan kirim context: undefined untuk gender yang gak diketahui.

Yang akan kamu pelajari