Laporan penjualan toko buku Budi ditujukan bagi pembaca Indonesia: ada tanggal rapat, omzet harian, dan harga buku. Menampilkan semuanya dengan penggabungan string manual segera bermasalah — nama bulan harus diterjemahkan sendiri, pemisah ribuan jadi titik bukan koma, dan angka desimal Indonesia memakai koma sedangkan 3.14 adalah penulisan gaya Inggris. Format seperti ini adalah pengetahuan yang berubah-ubah antar bahasa dan negara, dan tepat karena itulah JavaScript tidak menyuruh kita menulisnya sendiri.

Intl adalah API bawaan JavaScript untuk pekerjaan yang bergantung pada locale — gabungan aturan bahasa dan negara (misalnya id-ID untuk bahasa Indonesia di Indonesia). Chapter ini memakai tiga alat utamanya: Intl.DateTimeFormat untuk tanggal, Intl.NumberFormat untuk angka dan mata uang, serta localeCompare untuk pengurutan kata. Semuanya diuji langsung di Node v24.21.0 dengan locale id-ID.

Memformat Tanggal dan Angka untuk Pembaca Indonesia #

Berkas berikut menata satu laporan lengkap: dua bentuk tanggal, angka besar, angka desimal, dan dua mata uang:

// laporan-penjualan.js — Intl untuk tanggal, angka, dan mata uang id-ID
// Keluaran tanggal memakai timeZone "Asia/Jakarta" agar deterministik.

// --- 1. tanggal dalam format Indonesia ---
const tanggal = new Date("2026-08-17T02:00:00Z"); // pukul 09.00 WIB

const formatTanggal = new Intl.DateTimeFormat("id-ID", {
  weekday: "long",
  day: "numeric",
  month: "long",
  year: "numeric",
  timeZone: "Asia/Jakarta",
});
console.log(formatTanggal.format(tanggal));

const formatLengkap = new Intl.DateTimeFormat("id-ID", {
  dateStyle: "full",
  timeStyle: "medium",
  timeZone: "Asia/Jakarta",
});
console.log(formatLengkap.format(tanggal));

// --- 2. angka dan desimal ---
const angka = new Intl.NumberFormat("id-ID");
console.log(angka.format(1500000));
console.log(angka.format(3.14));

// --- 3. mata uang ---
const rupiah = new Intl.NumberFormat("id-ID", {
  style: "currency",
  currency: "IDR",
  maximumFractionDigits: 0,
});
console.log(rupiah.format(85000));

const dolar = new Intl.NumberFormat("id-ID", { style: "currency", currency: "USD" });
console.log(dolar.format(12.5));

// --- 4. pengurutan kata sesuai abjad Indonesia ---
const menu = ["Es", "dadu", "Cokelat", "bakso", "Ayam"];
console.log(menu.slice().sort());
console.log(menu.slice().sort((a, b) => a.localeCompare(b, "id")));

Output yang Dihasilkan:

text
Senin, 17 Agustus 2026
Senin, 17 Agustus 2026 pukul 09.00.00
1.500.000
3,14
Rp 85.000
US$12,50
[ 'Ayam', 'Cokelat', 'Es', 'bakso', 'dadu' ]
[ 'Ayam', 'bakso', 'Cokelat', 'dadu', 'Es' ]

Mari kita telaah baris-baris pentingnya:

  1. Baris 5 (new Date("2026-08-17T02:00:00Z")): tanggal dibuat dari string ISO berzona eksplisit seperti yang kita latih di chapter Dates. Pukul 02.00 UTC sama dengan pukul 09.00 WIB — dan formatter nanti yang menerjemahkannya.
  2. Baris 7–14 (formatter komponen): Intl.DateTimeFormat menerima locale ("id-ID") dan object opsi. Opsi weekday: "long", month: "long", dan seterusnya memilih komponen mana yang tampil; timeZone: "Asia/Jakarta" memastikan jam yang ditampilkan adalah jam WIB. Method format() menghasilkan “Senin, 17 Agustus 2026” — nama hari dan bulan dalam bahasa Indonesia, tanpa satu pun kamus dari kita.
  3. Baris 16–21 (dateStyle dan timeStyle): bila tidak butuh kontrol per komponen, opsi dateStyle: "full" dan timeStyle: "medium" memberikan format baku siap pakai: “Senin, 17 Agustus 2026 pukul 09.00.00”. Gaya baku ini menjamin konsistensi antar bagian aplikasi.
  4. Baris 24–26 (Intl.NumberFormat): angka 1500000 diformat menjadi 1.500.000 — pemisah ribuan titik — dan 3.14 menjadi 3,14 karena desimal Indonesia memakai koma. Keduanya tetap string hasil format; nilai number aslinya tidak berubah.
  5. Baris 29–37 (mata uang): opsi style: "currency" dengan currency: "IDR" menambahkan lambang Rp dan pemisah ribuan. Opsi maximumFractionDigits: 0 menghilangkan desimal (tanpanya muncul ,00 di belakang). Untuk USD, formatter bahkan tahu lambangnya berubah bentuk di locale Indonesia: US$12,50.
  6. Baris 40–42 (pengurutan): sort() tanpa pembanding mengurutkan menurut urutan kode Unicode — huruf besar semua di depan. Dengan a.localeCompare(b, "id"), hasilnya mengikuti kaidah abjad Indonesia dan tidak peka huruf besar-kecil pada urutan utamanya.

Opsi formatter angka tidak berhenti di sana. minimumFractionDigits dan maximumFractionDigits mengunci banyaknya desimal — pasangan ini yang membuat 5 tampil sebagai 5,00 pada kolom harga yang selalu berdesimal:

js
console.log(new Intl.NumberFormat("id-ID", { minimumFractionDigits: 2 }).format(5));

Output yang Dihasilkan:

text
5,00

Spasi Non-Breaking di Belakang Rp #

Satu detail hasil rupiah.format(85000) mudah terlewat: antara Rp dan angka terdapat bukan spasi biasa, melainkan spasi non-breaking (karakter U+00A0) — spasi yang tidak boleh menjadi titik pindah baris. Kita bisa membuktikannya di kode:

js
const rupiah = new Intl.NumberFormat("id-ID", {
  style: "currency",
  currency: "IDR",
  maximumFractionDigits: 0,
});
const teks = rupiah.format(85000);
console.log(teks.includes("\u00A0"));

Output yang Dihasilkan:

text
true

Bentuk Bawaan dan Pemeriksaan Locale #

Tanpa opsi apa pun, Intl.DateTimeFormat("id-ID") memberikan format ringkas standar untuk locale tersebut. Kita juga bisa menanyakan locale yang benar-benar dipakai runtime:

js
const tanggal = new Date("2026-08-17T02:00:00Z");
console.log(new Intl.DateTimeFormat("id-ID").format(tanggal));
console.log(new Intl.DateTimeFormat("id-ID").resolvedOptions().locale);

Output yang Dihasilkan:

text
17/8/2026
id-ID

Baris kedua penting saat aplikasi berjalan di lingkungan yang tidak kita kendalikan: resolvedOptions().locale melaporkan locale efektif yang dipilih runtime. Bila data locale untuk id-ID tidak tersedia (misalnya pada build Node yang ditanam tanpa ICU lengkap — kejadian yang jarang pada versi resmi), Intl akan berjalan pada locale cadangan dan formatnya berubah; pemeriksaan kecil ini membuat kondisi itu terlihat sebelum pengguna menemukannya.

Sebagai catatan penutup: method seperti toLocaleString yang mungkin pernah Anda lihat pada number dan Date — (1500000).toLocaleString("id-ID") menghasilkan 1.500.000 — adalah jalan pintas yang memakai mesin Intl yang sama di balik layar. Formatter eksplisit (Intl.NumberFormat) tetap pilihan utama karena opsinya lengkap dan objeknya bisa dipakai ulang, tetapi keduanya membaca data locale yang sama, sehingga hasilnya konsisten satu sama lain.

Mengurutkan Kata dengan localeCompare #

Pengurutan menurut abjad ternyata juga aturan locale. Method localeCompare(pembanding, locale) membandingkan dua string menurut kaidah locale yang diberikan, dan dipakai sebagai pembanding sort:

text
default sort : [ 'Ayam', 'Cokelat', 'Es', 'bakso', 'dadu' ]
localeCompare: [ 'Ayam', 'bakso', 'Cokelat', 'dadu', 'Es' ]

Pada urutan default, Es dan Cokelat mendahului bakso karena kode huruf besar lebih kecil daripada huruf kecil. localeCompare bekerja pada tingkat dasar (dasar abjad, tanpa memandang besar-kecil), sehingga hasilnya terbaca seperti kamus: Ayam, bakso, Cokelat, dadu, Es. Huruf besar-kecil tidak hilang sama sekali — ia baru menentukan ketika dua kata dasarnya sama, dan nilai kembalian method ini bisa lebih dari sekadar -1, 0, dan 1. Karena itu, jangan menggantungkan logika pada nilai persisnya; gunakan hanya tandanya (negatif, nol, positif):

js
console.log("bakso".localeCompare("BAKSO", "id"));

Output yang Dihasilkan:

text
-1

Kedua kata itu dasarnya identik — localeCompare menganggapnya berdampingan — dan pada tingkat pembandingan berikutnya bakso (huruf kecil) mendahului BAKSO, sehingga kembaliannya -1. Untuk daftar yang diurutkan berulang kali, new Intl.Collator("id") menyediakan pembanding yang setara dan lebih efisien — keluarga Intl yang sama dengan DateTimeFormat dan NumberFormat.

Tanggal kini tampil rapi, angka dan mata uang sesuai kaidah Indonesia. Skenario berikutnya menggeser perhatian ke tempat lain: teks yang kita kirim lewat URL. Tautan pencarian dengan judul berisi spasi dan tanda & butuh perlakuan khusus agar tidak terpecah — itu kerja URL encoding di chapter berikutnya.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

laporan-penjualan.js

Unduh berkas
javascript
// laporan-penjualan.js — Intl untuk tanggal, angka, dan mata uang id-ID
// Keluaran tanggal memakai timeZone "Asia/Jakarta" agar deterministik.

// --- 1. tanggal dalam format Indonesia ---
const tanggal = new Date("2026-08-17T02:00:00Z"); // pukul 09.00 WIB

const formatTanggal = new Intl.DateTimeFormat("id-ID", {
  weekday: "long",
  day: "numeric",
  month: "long",
  year: "numeric",
  timeZone: "Asia/Jakarta",
});
console.log(formatTanggal.format(tanggal));

const formatLengkap = new Intl.DateTimeFormat("id-ID", {
  dateStyle: "full",
  timeStyle: "medium",
  timeZone: "Asia/Jakarta",
});
console.log(formatLengkap.format(tanggal));

// --- 2. angka dan desimal ---
const angka = new Intl.NumberFormat("id-ID");
console.log(angka.format(1500000));
console.log(angka.format(3.14));

// --- 3. mata uang ---
const rupiah = new Intl.NumberFormat("id-ID", {
  style: "currency",
  currency: "IDR",
  maximumFractionDigits: 0,
});
console.log(rupiah.format(85000));

const dolar = new Intl.NumberFormat("id-ID", { style: "currency", currency: "USD" });
console.log(dolar.format(12.5));

// --- 4. pengurutan kata sesuai abjad Indonesia ---
const menu = ["Es", "dadu", "Cokelat", "bakso", "Ayam"];
console.log(menu.slice().sort());
console.log(menu.slice().sort((a, b) => a.localeCompare(b, "id")));