Character Encoding & Unicode — Web Fundamentals

Pernah melihat teks Café padahal harusnya Café? Atau emoji yang jadi ??? Atau "😀".length mengembalikan 2 di JavaScript? Semua itu gara-gara character…

Komputer cuma menyimpan angka. Character encoding adalah aturan untuk mengubah huruf jadi angka-angka itu, lalu kembali lagi jadi huruf. Kalau aturan waktu menyimpan beda dengan waktu membaca, teksnya rusak: Café tampil sebagai Café.

🧪 Tebakan cepat: satu emoji senyum di JavaScript, "\u{1F600}".length, hasilnya 1, 2, atau 4? Tebak dulu, jawabannya ada di bagian "Kenapa panjang emoji di JavaScript itu 2".

Huruf, code point, lalu byte

Unicode memberi setiap karakter di dunia, termasuk huruf Arab, huruf Jepang, dan emoji, satu nomor unik yang disebut code point. Code point ditulis U+ lalu angka heksadesimal (basis 16: digit 0 sampai 9, lalu A sampai F). UTF-8 adalah aturan menyimpan nomor itu sebagai 1 sampai 4 byte. Satu byte adalah angka 0 sampai 255, biasa ditulis heksa 00 sampai FF:

Karakter       Code point   Byte UTF-8
A              U+0041       41
é              U+00E9       C3 A9
中             U+4E2D       E4 B8 AD
emoji senyum   U+1F600      F0 9F 98 80

Karena itu UTF-8 jadi standar web, dipakai sekitar 98% website: cocok dengan ASCII, bisa menyimpan semua karakter Unicode, dan hemat untuk teks Inggris yang cuma 1 byte per huruf.

Mojibake: byte dibaca dengan aturan yang salah

Café dalam UTF-8 adalah 5 byte: 43 61 66 C3 A9. Kalau byte itu dibaca dengan Windows-1252, encoding lama yang menganggap 1 byte = 1 huruf, C3 jadi à dan A9 jadi ©. Hasilnya Café. Teks rusak kayak gini namanya mojibake.

Obatnya: pakai UTF-8 di semua tempat, dan kasih tahu browser. Di HTML, taruh ini di awal <head>:

<meta charset="utf-8">

Dari server, lewat header Content-Type: text/html; charset=utf-8.

Kenapa panjang emoji di JavaScript itu 2

String JavaScript disimpan dalam UTF-16, potongan 16 bit yang disebut code unit. Code point sampai U+FFFF (bagian ini disebut BMP) muat di 1 code unit. Code point di atasnya, termasuk kebanyakan emoji, butuh 2 code unit yang disebut surrogate pair. .length menghitung code unit, bukan karakter:

"Halo".length;                // 4
"\u{1F600}Budi".length;       // 6, emoji dihitung 2
[..."\u{1F600}Budi"].length;  // 5

Jawaban tebakannya: 2. Untuk menghitung karakter, pakai [...teks].length.

Karakter di URL: encodeURIComponent

URL cuma boleh berisi sebagian karakter ASCII. Karakter lain, dan karakter yang punya arti di URL (spasi, &, =, ?, /, #), harus di-percent-encode: tiap byte UTF-8-nya jadi % plus dua digit heksa. encodeURIComponent mengerjakan itu:

encodeURIComponent("hello world"); // "hello%20world"
encodeURIComponent("café");        // "caf%C3%A9"
encodeURIComponent("Budi & Ani");  // "Budi%20%26%20Ani"
encodeURIComponent("c/d");         // "c%2Fd"

%C3%A9 adalah dua byte UTF-8 dari é. Untuk query string (bagian URL setelah ?), encode setiap key dan setiap value, gabung jadi key=value, lalu sambung pasangannya dengan &:

const data = { kota: "Jakarta Selatan", menu: "nasi & teh" };
Object.entries(data)
  .map(([key, value]) => encodeURIComponent(key) + "=" + encodeURIComponent(value))
  .join("&");
// "kota=Jakarta%20Selatan&menu=nasi%20%26%20teh"

⚠️ Jebakan yang sering ketemu

TL;DR: Unicode memberi tiap karakter nomor (code point, misalnya U+00E9), dan UTF-8 menyimpannya sebagai 1 sampai 4 byte: ASCII 1 byte, é 2, huruf Jepang 3, emoji 4. Byte yang dibaca dengan encoding lain jadi mojibake, jadi pakai UTF-8 di mana-mana dan tulis <meta charset="utf-8">. Di JavaScript, .length menghitung code unit UTF-16 (emoji = 2), sedangkan [...teks].length menghitung karakter. Untuk URL, encodeURIComponent mengubah tiap byte jadi %XX, misalnya spasi jadi %20.

Yang akan kamu pelajari