Noto Sans JP versi lengkap berukuran sekitar 4,5 MB. Satu berkas. Untuk satu bobot huruf saja.
Angka itu bukan anomali. Itu konsekuensi wajar dari sistem tulisan yang memuat lebih dari sepuluh ribu glyph di dalam satu tabel cmap. Tim Chrome sendiri mengakuinya secara terbuka dalam panduan resmi praktik terbaik font, yang menyebut optimasi font untuk bahasa CJK sebagai tantangan yang berdiri sendiri.
Masalahnya, hampir semua tutorial performa web ditulis dengan asumsi font Latin 80 KB.
Di titik itulah subsetting font jepang untuk web berhenti menjadi optimasi opsional dan berubah menjadi syarat kelayakan.
Landasannya bukan sekadar intuisi lapangan. Studi eksperimental tentang peran format font dalam membangun aplikasi web yang efisien mengukur konsumsi CPU, memori, waktu muat, dan energi pada empat format berbeda — OTF, TTF, WOFF, dan WOFF2 — lalu menemukan perbedaan signifikan pada seluruh pasangan perbandingan.
WOFF2 unggul di hampir semua metrik, kecuali alokasi memori.
Kami mengangkat tema ini karena satu pola yang terus berulang: banyak tim sudah rapi mengurus gambar, lazy loading, dan code splitting, tapi tetap gagal melewati ambang LCP. Penyebabnya satu berkas font multibahasa yang tidak pernah dibuka datanya.
Artikel ini bukan daftar tips. Ini catatan kerja.
Dengan perintah yang bisa langsung kamu jalankan, angka yang bisa kamu bantah, dan jebakan yang sudah saya tabrak sendiri.
Inti dari seluruh artikel ini: font CJK bukan masalah tipografi, melainkan masalah arsitektur pengiriman aset. Selama kamu memperlakukannya seperti font Latin, tidak ada
preloadatau CDN yang bisa menyelamatkan LCP-mu.
1. Kenapa Font Jepang Berat Sejak Lahir
Sebelum memotong apa pun, kamu perlu paham apa yang sebenarnya kamu potong. Berat font Jepang bukan hasil kemalasan desainer, melainkan konsekuensi matematis dari jumlah karakter yang harus didukung. Memahami anatominya membuat keputusan teknismu jauh lebih tepat sasaran.
Tiga Sistem Tulisan dalam Satu Berkas
Bahasa Jepang menumpuk tiga sistem tulisan sekaligus:
-
Hiragana (
U+3040–309F) — sekitar 90 karakter aktif -
Katakana (
U+30A0–30FF) — sekitar 90 karakter aktif -
Kanji (
U+4E00–9FFF) — lebih dari 20.000 karakter di blok CJK Unified Ideographs
Ditambah tanda baca khusus (U+3000–303F) dan katakana setengah lebar (U+FF65–FF9F).
Font Latin biasanya berisi 200–1.000 glyph. Font Jepang lengkap bisa menembus 20.000. Perbandingannya bukan dua kali lipat, tapi dua orde magnitudo.
Efek Berantai ke Core Web Vitals
Berkas font besar tidak hanya memperlambat unduhan. Ia menyandera render.
Browser Chromium dan Firefox memblokir penggambaran teks hingga tiga detik saat font belum tiba. Safari memblokirnya tanpa batas waktu.
Artinya satu berkas 4,5 MB di jaringan 4G yang sedang padat bisa menahan FCP, menyeret LCP, dan begitu font akhirnya masuk lalu ditukar dengan fallback, ia menyumbang CLS.
Tiga metrik rusak. Dari satu berkas.
2. Subsetting dan unicode-range: Dua Alat, Dua Pekerjaan
Dua istilah ini paling sering tertukar, bahkan di antara developer yang sudah lama bekerja dengan Core Web Vitals. Keduanya memang saling melengkapi, tapi bekerja di lapisan yang sama sekali berbeda. Salah memahami pembagian tugasnya membuat sebagian tim mengerjakan satu hal dua kali.
Subsetting: Memotong di Level Berkas
Subsetting membuang glyph dari berkas font itu sendiri. Berkas keluar sudah lebih kecil sebelum sampai ke server.
Alat yang lazim dipakai: pyftsubset dari fontTools, glyphhanger, atau subfont.
Hasilnya permanen. Karakter yang dibuang tidak bisa dipanggil kembali.
unicode-range: Menunda di Level Browser
unicode-range tidak memotong apa pun. Ia memberi tahu browser rentang karakter apa yang ditangani sebuah berkas, sehingga berkas itu hanya diunduh jika halaman benar-benar memuat karakter dari rentang tersebut.
Ini mekanisme yang dipakai Google Fonts untuk memecah font CJK menjadi lebih dari seratus irisan kecil.
Penjelasan teknis paling jernih soal pemecahan berkas ini pernah ditulis Yaser Adel Mehraban, Google Developer Expert yang lama menulis soal performa web, dalam artikelnya Web font optimisation di DEV. Ia mendemonstrasikan pemisahan satu font menjadi subset Latin dan Jepang dengan deskriptor yang sama.
Kenapa Keduanya Dipakai Bersamaan
| Aspek | Subsetting | unicode-range |
|---|---|---|
| Lapisan kerja | Berkas font | Deklarasi CSS |
| Yang dikurangi | Ukuran berkas | Jumlah berkas yang diunduh |
| Sifat | Permanen | Kondisional saat runtime |
| Risiko utama | Karakter hilang | Berkas terlalu banyak (request overhead) |
| Cocok untuk | Konten statis dan terprediksi | Situs multibahasa dengan konten dinamis |
Kombinasi keduanya adalah pola yang dipakai di produksi: potong dulu ke rentang yang relevan, lalu pecah hasilnya dan biarkan browser memilih.
3. Panduan Praktis: Subsetting Font Jepang untuk Web dari Nol
Bagian ini sengaja saya tulis sebagai prosedur, bukan narasi. Kamu bisa menyalin perintahnya apa adanya dan menjalankannya di terminal mana pun yang punya Python. Seluruh langkah subsetting font jepang untuk web di bawah ini memakai perkakas sumber terbuka tanpa layanan pihak ketiga.
Langkah Kerja
Satu — Periksa lisensi lebih dulu.
Tidak semua lisensi font mengizinkan modifikasi berkas. Noto dan sebagian besar font SIL Open Font License mengizinkan. Font komersial sering tidak. Cek sebelum menyentuh apa pun.
Dua — Pasang perkakasnya.
pip install fonttools brotli zopfli
Tiga — Kumpulkan karakter yang benar-benar dipakai.
Kalau kontenmu statis, ekstrak teksnya ke satu berkas:
glyphhanger https://situsmu.test --formats=woff2 --subset="*.ttf"
Empat — Potong dengan rentang eksplisit.
Untuk konten dinamis, pakai rentang Unicode alih-alih daftar karakter:
pyftsubset NotoSansJP-Regular.ttf \
--unicodes="U+3000-303F,U+3040-309F,U+30A0-30FF,U+FF00-FFEF,U+4E00-9FAF" \
--layout-features="kern,liga,palt" \
--flavor=woff2 \
--output-file="NotoSansJP-subset.woff2"
Lima — Deklarasikan dengan unicode-range dan font-display.
@font-face {
font-family: "NotoSansJP";
src: url("/fonts/NotoSansJP-subset.woff2") format("woff2");
unicode-range: U+3000-303F, U+3040-309F, U+30A0-30FF, U+4E00-9FAF;
font-display: swap;
}
Enam — Ukur ulang, jangan berasumsi.
Jalankan Lighthouse dan bandingkan data lapangan di CrUX, bukan hanya skor lab.
Skema HowTo untuk Structured Data
Tempel blok ini di <head> halaman panduanmu supaya prosedurnya terbaca mesin pencari dan mesin jawaban:
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "Cara Subsetting Font Jepang untuk Web",
"description": "Prosedur memperkecil berkas font CJK menggunakan pyftsubset dan unicode-range agar LCP tetap stabil.",
"totalTime": "PT25M",
"tool": [
{ "@type": "HowToTool", "name": "fontTools (pyftsubset)" },
{ "@type": "HowToTool", "name": "glyphhanger" }
],
"step": [
{
"@type": "HowToStep",
"position": 1,
"name": "Verifikasi lisensi font",
"text": "Pastikan lisensi font mengizinkan modifikasi dan penyematan di web."
},
{
"@type": "HowToStep",
"position": 2,
"name": "Pasang perkakas",
"text": "Instal fontTools, brotli, dan zopfli melalui pip."
},
{
"@type": "HowToStep",
"position": 3,
"name": "Kumpulkan karakter aktif",
"text": "Ekstrak karakter yang benar-benar dipakai halaman menggunakan glyphhanger."
},
{
"@type": "HowToStep",
"position": 4,
"name": "Potong berkas font",
"text": "Jalankan pyftsubset dengan rentang Unicode eksplisit dan keluaran WOFF2."
},
{
"@type": "HowToStep",
"position": 5,
"name": "Deklarasikan di CSS",
"text": "Tulis aturan @font-face dengan unicode-range dan strategi font-display yang sesuai."
},
{
"@type": "HowToStep",
"position": 6,
"name": "Ukur ulang",
"text": "Bandingkan hasil Lighthouse dengan data lapangan CrUX sebelum menyatakan selesai."
}
]
}
4. Angka Nyata: Apa yang Berubah Setelah Dipotong
Optimasi tanpa pengukuran hanya keyakinan pribadi. Tabel di bawah merangkum perbandingan berkas dari satu halaman pendaftaran bilingual Indonesia–Jepang yang saya tangani, dijalankan di profil Slow 4G Chrome DevTools dengan cache kosong.
| Konfigurasi | Ukuran font terkirim | Berkas font | Catatan |
|---|---|---|---|
| Noto Sans JP penuh (TTF) | ~4.500 KB | 1 | Baseline sebelum optimasi |
| Konversi ke WOFF2 saja | ~1.700 KB | 1 | Kompresi Brotli, tanpa pemotongan |
| Subset Jōyō Kanji + kana | ~310 KB | 1 | Cukup untuk konten umum |
Subset + pemecahan unicode-range
|
~95 KB | 3 | Hanya irisan terpakai yang diunduh |
Halaman uji coba versi produksinya bisa kamu periksa sendiri di situs kursus bahasa Jepang di Karawang — buka DevTools, saring tab Network ke Font, dan bandingkan dengan situs berbahasa Jepang lain yang biasa kamu kunjungi.
Cara Membaca Tabel Ini
Perhatikan lompatan terbesar terjadi di baris ketiga, bukan keempat.
Artinya: kalau waktumu terbatas, kerjakan subsetting lebih dulu. Pemecahan unicode-range adalah penyempurnaan, bukan fondasi.
Dan satu peringatan penting — jangan menyalin angka ini ke laporanmu. Ukur situsmu sendiri. Distribusi karakter setiap situs berbeda, dan itulah variabel yang paling menentukan.
5. Lima Jebakan yang Membatalkan Semua Penghematan
Saya sudah menabrak keempat jebakan pertama secara langsung, dan yang kelima saya temukan saat audit situs orang lain. Semuanya punya pola yang sama: penghematan berhasil di angka berkas, tapi hilang lagi di pengalaman pengguna nyata.
Salah Memilih font-display
font-display: swap menampilkan teks cepat, tapi menyumbang CLS saat font asli akhirnya masuk. font-display: optional mengunci CLS di nol, tapi font bisa tidak terpakai sama sekali di koneksi lambat.
Untuk situs berbahasa Jepang, optional sering lebih aman. Perbedaan bentuk antara fallback sistem dan font Jepang kustom biasanya tidak sedramatis pada font Latin.
Melupakan size-adjust
Kalau kamu tetap memilih swap, sandingkan dengan size-adjust dan ascent-override agar dimensi fallback mendekati font asli. Ini menekan pergeseran tata letak tanpa mengorbankan kecepatan tampil teks.
preload yang Membunuh unicode-range
Ini jebakan paling halus. preload mengabaikan deklarasi unicode-range. Kalau kamu memecah font jadi lima irisan lalu mem-preload semuanya, browser mengunduh kelimanya — persis hal yang tadi kamu hindari.
Konten Dinamis yang Melampaui Subset
Nama pengguna, komentar, atau konten dari CMS bisa memuat kanji langka di luar subset. Hasilnya karakter kotak alias tofu.
Solusinya: selalu sediakan rantai fallback ke font sistem Jepang.
font-family: "NotoSansJP", "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo, sans-serif;
Menyandarkan Segalanya pada ime-mode
Properti CSS ime-mode sudah usang dan tidak dapat diandalkan lintas browser. Jangan memakainya untuk mengatur perilaku input Jepang. Gunakan atribut inputmode sebagai petunjuk, lalu normalisasi karakter lebar penuh di sisi kode.
6. Pertanyaan yang Sering Muncul
Beberapa pertanyaan ini muncul berulang kali dari pembaca dan rekan kerja setelah saya membagikan proses ini di internal tim. Saya kumpulkan yang paling sering ditanyakan beserta jawaban singkatnya.
Apakah subsetting font jepang untuk web berisiko melanggar lisensi?
Bergantung pada lisensinya. Font dengan SIL Open Font License seperti keluarga Noto umumnya mengizinkan modifikasi dan penyematan. Font komersial banyak yang melarang. Baca berkas lisensinya sebelum memotong apa pun.
Berapa banyak irisan unicode-range yang ideal?
Tidak ada angka tunggal. Google Fonts memakai lebih dari seratus irisan berukuran 100–200 karakter. Untuk situs skala kecil dan menengah, tiga hingga delapan irisan biasanya sudah memberi hasil terbaik tanpa menimbulkan request overhead berlebihan.
Apakah variable font membantu untuk bahasa Jepang?
Jarang. Variable font menghemat ketika kamu butuh banyak bobot dari satu keluarga. Untuk CJK, jumlah glyph tetap jadi penentu dominan, sehingga variable font justru bisa menghasilkan berkas lebih besar.
Bagaimana kalau saya memakai Google Fonts, bukan self-hosting?
Google Fonts sudah menerapkan pemecahan otomatis. Justru risikonya muncul saat kamu berpindah ke self-hosting dan lupa mereplikasi unicode-range — banyak tim tanpa sadar malah menaikkan ukraan berkas setelah "mengoptimalkan".
Apakah teknik ini berlaku untuk Mandarin dan Korea?
Sebagian besar iya. Prinsipnya identik. Yang berbeda hanya rentang Unicode dan frekuensi karakternya. Korea menambahkan 11.172 blok suku kata Hangul, sedangkan Mandarin punya distribusi karakter yang lebih merata dibanding Jepang.
Ketika Kilobyte Menentukan Siapa yang Sempat Membaca
Menutup artikel ini, ada satu hal yang ingin saya tegaskan: pekerjaan ini tidak glamor. Tidak ada framework baru, tidak ada arsitektur yang layak dipamerkan di konferensi. Hanya berkas font yang dipotong dengan sabar, lalu diukur ulang. Tapi justru di situlah selisih antara halaman yang dibaca dan halaman yang ditinggalkan sebelum teksnya sempat muncul.
"Premature optimization is the root of all evil."
Kalimat itu milik Donald Knuth, ilmuwan komputer Amerika yang menulis The Art of Computer Programming dan — ini bagian yang sering dilupakan — menciptakan TeX serta METAFONT karena kecewa pada kualitas penjajaran huruf pada naskah bukunya sendiri. Ia praktis meletakkan fondasi tipografi digital modern.
Kutipannya sering dipakai untuk menolak optimasi. Padahal maksudnya justru sebaliknya: optimasi menjadi evil ketika dilakukan tanpa pengukuran, bukan ketika dilakukan lebih awal.
Berkas font 4,5 MB bukan optimasi prematur. Itu masalah yang sudah terukur, sudah terbukti mengganggu, dan punya solusi yang jelas.
Kalau kamu sedang menangani situs dengan konten Jepang — dokumentasi teknis, portal karier, atau platform pembelajaran bahasa — mulailah dari tab Network. Saring ke Font. Lihat angkanya.
Sering kali, satu baris pyftsubset menyelesaikan apa yang tiga sprint refactor gagal perbaiki.
Kalau kamu punya angka pengukuran sendiri yang berbeda dari tabel di atas, saya betul-betul ingin melihatnya di kolom komentar. Terutama kalau hasilnya membantah temuan saya.
Top comments (0)