Intl API Browser — i18n

Intl API Browser Intl (Internationalization API) adalah API bawaan browser untuk formatting angka, tanggal, dan teks sesuai locale — tanpa library tambahan…

Intl adalah kumpulan pemformat bawaan JavaScript untuk angka, uang, tanggal, waktu relatif, jamak, daftar, urutan abjad, nama bahasa, dan pemotongan teks, semuanya sesuai locale. Bayangin resepsionis Homestay Kabut yang hafal kebiasaan tiap negara: kamu kasih angka, tanggal, atau daftar kamar plus locale tamunya, dia menuliskannya dengan cara tamu itu. Hafalannya berasal dari satu buku besar, CLDR (Unicode Common Locale Data Repository), yang dibawa tiap browser lewat pustaka ICU. Tiap browser membawa edisi yang berbeda, jadi jawabannya kadang beda sedikit. Pelajaran UI/UX "Internationalization (i18n)" sudah memakai NumberFormat, DateTimeFormat, dan PluralRules untuk kartu Bu Sri; di sini kita bongkar semua pemformatnya, plus bedanya di tiap browser.

🧪 Tebakan cepat: new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR' }).format(700000) menulis rupiah dengan berapa angka desimal di Chrome 147, dan di Node 22.22.1: sama-sama 2, sama-sama 0, atau beda? Jawabannya ada di bagian "NumberFormat: rupiah punya berapa desimal?".

Satu pola untuk semua pemformat

const money = new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 });
money.format(700000);            // "IDR 700,000"
money.formatToParts(700000);     // [{ type: "currency", value: "IDR" }, { type: "literal", value: "\u00A0" }, { type: "integer", value: "700" }, ...]
money.resolvedOptions().locale;  // "ja-JP"

Kalau browser gak punya data untuk locale yang diminta, dia diam-diam pindah ke locale lain. Bu Sri ingin versi Basa Jawa (jv): new Intl.NumberFormat('jv').resolvedOptions().locale memberi "jv" di Firefox 146, Safari 26.4, dan Node 22.22.1, tapi "en-US" di Chromium 147, karena Chrome gak membawa data bahasa Jawa dan jatuh ke bahasa default browser. Beri daftar cadangan: dengan ['jv', 'id'], Chrome memilih "id" dan menulis "Sabtu, 12 Desember 2026".

NumberFormat: rupiah punya berapa desimal?

Kami menjalankan kode yang sama di tiga mesin browser (lewat Playwright) dan di Node 22.22.1, yang membawa ICU 78.2 dengan CLDR 48:

Kode Chromium 147 Firefox 146 WebKit 26.4 (Safari) Node 22.22.1
new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR' }).format(700000) Rp 700.000,00 Rp 700.000,00 Rp700.000,00 Rp 700.000
.resolvedOptions().maximumFractionDigits untuk IDR 2 2 2 0
sama, plus maximumFractionDigits: 0 Rp 700.000 Rp 700.000 Rp700.000 Rp 700.000
new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(9800) ¥9,800 ¥9,800 ¥9,800 ¥9,800
new Intl.NumberFormat('ar-EG', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(700000) ٧٠٠٬٠٠٠ IDR ٧٠٠٬٠٠٠ IDR ٧٠٠٬٠٠٠ IDR ٧٠٠٬٠٠٠ IDR

Jumlah desimal tiap mata uang diambil dari data. Tabel ISO 4217 mencatat rupiah dengan 2 desimal. CLDR juga mencatat 2 sampai CLDR 47, lalu CLDR 48 (Oktober 2025) mengubah IDR jadi 0 desimal. V8, mesin JavaScript Chrome dan Node, mengambil desimal dari CLDR di dalam ICU-nya: Node 22.22.1 sudah membawa CLDR 48, jadi 0; Chromium 147 masih memberi 2. Firefox dan Safari memakai tabel ISO 4217, jadi 2. Jawabannya: beda, padahal kodenya sama persis. Itu jawaban tebakannya.

Aturannya: untuk rupiah, selalu sebut maximumFractionDigits: 0, supaya semua mesin menulis "Rp 700.000". Yang lain dari tabel:

DateTimeFormat dan timeZone

Check-in Bu Sri: Sabtu, 12 Desember 2026, pukul 14.00 WIB, yaitu 07.00 UTC.

const checkIn = new Date('2026-12-12T07:00:00Z');
const opts = { dateStyle: 'full', timeStyle: 'short', timeZone: 'Asia/Jakarta' };

new Intl.DateTimeFormat('id-ID', opts).format(checkIn); // "Sabtu, 12 Desember 2026 pukul 14.00"
new Intl.DateTimeFormat('en-US', opts).format(checkIn); // "Saturday, December 12, 2026 at 2:00 PM"
new Intl.DateTimeFormat('ja-JP', opts).format(checkIn); // "2026年12月12日土曜日 14:00"
new Intl.DateTimeFormat('ar-EG', opts).format(checkIn); // "السبت، ١٢ ديسمبر ٢٠٢٦ في ٢:٠٠ م"

new Intl.DateTimeFormat('id-ID', { dateStyle: 'long', timeZone: 'Asia/Jakarta' }).format(checkIn); // "12 Desember 2026"

RelativeTimeFormat, PluralRules, ListFormat

const rtf = new Intl.RelativeTimeFormat('id-ID', { numeric: 'auto' });
rtf.format(3, 'day');    // "dalam 3 hari"
rtf.format(1, 'day');    // "besok"
rtf.format(-1, 'day');   // "kemarin"

Collator, DisplayNames, Segmenter

const guests = ['Zahra', 'Ömer', 'Oscar', 'andi', 'Bu Sri', 'Émile'];
[...guests].sort();                                 // ["Bu Sri", "Oscar", "Zahra", "andi", "Émile", "Ömer"]
[...guests].sort(new Intl.Collator('id').compare);  // ["andi", "Bu Sri", "Émile", "Ömer", "Oscar", "Zahra"]
[...guests].sort(new Intl.Collator('sv').compare);  // ["andi", "Bu Sri", "Émile", "Oscar", "Zahra", "Ömer"]

const rooms = ['Kamar 10', 'Kamar 2', 'Kamar 1'];
rooms.sort(new Intl.Collator('id', { numeric: true }).compare); // ["Kamar 1", "Kamar 2", "Kamar 10"]

📌 Beda tiap mesin, diukur

Semua hasil di atas sama di empat mesin, kecuali yang ada di tabel ini:

Yang diukur Chromium 147 Firefox 146 WebKit 26.4 Node 22.22.1
Desimal bawaan IDR 2 2 2 0
Sesudah "Rp" di id-ID U+00A0 U+00A0 gak ada U+00A0
Simbol JPY di ja-JP ¥ ¥ ¥ ¥
Kalender ar-SA gregory gregory islamic-umalqura gregory
Data bahasa Jawa (jv) gak ada, jatuh ke en-US ada ada ada
Angka "3" di Segmenter kata ja isWordLike isWordLike bukan isWordLike isWordLike
ListFormat ar-EG polos polos tiap item diapit U+2068 dan U+2069 polos

Versinya: Chromium 147.0.7727.15, Firefox 146.0.1, dan WebKit 26.4 dari Playwright, serta Node 22.22.1 dengan ICU 78.2, CLDR 48, dan Unicode 17.0. Lab-nya menjalankan kode yang sama di browser-mu dan menaruh tabel ini di sampingnya.

⚠️ Jebakan yang sering ketemu

TL;DR: Intl memformat sesuai locale dengan data CLDR yang dibawa tiap browser lewat ICU. Polanya sama: new Intl.X(locale, opsi), lalu format(), formatToParts(), dan resolvedOptions() untuk melihat yang benar-benar dipakai; toLocaleString cuma jalan pintas ke Intl.NumberFormat. NumberFormat: desimal bawaan IDR adalah 2 di Chrome 147, Firefox 146, dan Safari 26.4, tapi 0 di Node 22.22.1 (CLDR 48), jadi sebut maximumFractionDigits: 0. DateTimeFormat: dateStyle: 'long' untuk "12 Desember 2026", selalu dengan timeZone: 'Asia/Jakarta'; Safari memakai kalender Hijriah untuk ar-SA. RelativeTimeFormat menulis "dalam 3 hari", PluralRules memilih kategori (ar punya enam), ListFormat menyambung daftar, Collator mengurutkan seperti kamus (dan numeric: true untuk "Kamar 2" sebelum "Kamar 10"), DisplayNames menulis "日本語" dan "العربية", Segmenter memotong teks Jepang yang tanpa spasi. Chrome 147 gak punya data bahasa Jawa, jadi cek resolvedOptions().locale.

Yang akan kamu pelajari