Skip to content

Root Layout di [locale]: html lang Server-Rendered Next.js

Adityo Guni Waluyo

Root layout yang pindah ke segmen [locale] membuat html lang benar dari server, menghapus bootstrap bahasa di klien, dan meloloskan audit struktur 24 halaman.

Ringkasan

Gue awalnya kira skrip deteksi bahasa di klien yang salah, ternyata konsepnya emang nggak bener: html lang harus diset dari server. Solusinya simpel, cukup pindah root layout ke folder app/[lang] terus hapus skripnya. Hasilnya? 24 halaman lolos audit dan screen reader baca konten dengan pelafalan yang bener sejak awal.

Skrip kecil yang menebak bahasa

Di salah satu project company profile, saya membuka app/layout.tsx dan menemukan skrip inline yang tugasnya menebak bahasa pengguna: cek localStorage, fallback ke deteksi browser, lalu menempelkan atribut bahasa ke tag <html> sebelum halaman selesai hidrasi. Tebakan pertama saya: logika deteksinya yang salah. Mungkin regex-nya kurang ketat atau listener-nya telat. Saya hampir menulis ulang skrip itu jadi lebih agresif. Untung saya sempat membaca ulang dokumentasi resmi Next.js dulu, dan baru menyadari masalahnya bukan di logika skrip, tapi di konsepnya: atribut bahasa di <html> wajib benar langsung dari render server, bukan ditebak di klien.

Kenapa tag html dari server itu penting

Panduan i18n Next.js menulis polanya secara eksplisit: semua special files nested di app/[lang] supaya router meneruskan parameter lang ke setiap layout dan page [1], dan contoh resminya memakai <html lang={(await params).lang}> di layout [1]. Root layout sendiri boleh berada di bawah segmen dinamis, dokumentasi konvensi layout memakai persis kasus i18n ini sebagai contohnya [2]. Sisi yang paling sering dilupakan: deklarasi bahasa adalah urusan aksesibilitas, bukan cuma SEO. MDN menjelaskan atribut lang adalah mekanisme yang benar untuk deklarasi bahasa, dan tujuannya supaya teknologi bantu seperti screen reader memakai pelafalan yang tepat [3]. Skrip klien berarti ada jeda: dokumen sudah dibaca sebelum atributnya benar.

Pindah satu folder, tiga rapor diperbaiki

Perbaikannya satu gerakan: pindahkan root layout ke app/[locale]/layout.tsx, hapus skrip bootstrap bahasa di klien, dan biarkan html lang terisi dari parameter route di server. Root layout di posisi baru tetap wajib mendefinisikan <html> dan <body> [2], dan sesuai aturan dokumentasi saya tidak menambah tag head manual di situ: Metadata API yang otomatis mengurus streaming dan de-duplikasi elemen head [2]. Locale yang tak dikenal ditangkap hasLocale yang mempersempit tipe ke daftar locale yang didukung [1].

Setelah layoutnya pindah, dua fitur bawaan pola ini ikut kebuka. Pertama, locale yang tidak dikenal tidak lagi jadi crash eksotis: hasLocale mempersempit tipe ke daftar locale yang didukung dan memastikan pengunjung dapat halaman 404, bukan error runtime [1]. Kedua, generateStaticParams bisa dipasang di layout untuk pra-render semua varian bahasa saat build [1], jadi permintaan pertama untuk /en pun tidak jadi kelinci uji. Menariknya, panduan yang sama menyarankan deteksi bahasa browser tetap jadi urusan layer redirect yang membaca Accept-Language, bukan urusan tag html [1]. Deteksi boleh dinamis; deklarasi bahasa dokumen harus statis dan benar.

Struktur ini juga nggak terasa usr sekali dipakai. Next.js mendukung i18n lewat sub-path kayak /en/products atau domain khusus bahasa [1], dan pola [lang] di path teratas adalah bentuk sub-path yang paling gampang dioperasikan. Parameter lang yang tadi diterima layout juga nggak berhenti di situ: komponen server di kedalaman mana pun bisa membacanya tanpa prop-drilling, sesuai dokumentasi soal kebutuhan locale di utilitas bersama dan komponen nested [1]. Situs dua bahasa jadi konsisten dari tag pembuka sampai komponen terdalam.

Soal dampak ke pembaca screen reader, senada dengan penjelasan MDN: tujuan utama deklarasi bahasa adalah supaya teknologi bantu memakai pelafalan yang benar [3]. Kalau atributnya muncul belakangan lewat skrip, pembacaan awal tetap salah. Perbaikan di level template server menutup celah itu sepenuhnya, dan itu sebabnya perubahan ini terasa lebih seperti perbaikan aksesibilitas daripada sekadar kepatuhan SEO.

Audit 24 halaman berubah jadi hijau

Hasil nyatanya dari satu commit: audit struktur yang mengecek satu H1, level heading, alt, dan landmark kini meloloskan 24 dari 24 halaman, tanpa lagi peringatan soal deklarasi bahasa yang hilang atau berubah di tengah jalan. Yang lebih penting dari angka: screen reader sekarang menerima konteks bahasa yang benar sejak baris pertama dokumen. Saya pribadi makin yakin pendekatan server-rendered untuk hal gini. Fleksibilitas deteksi di klien nggak ada gunanya kalo yang dikorbankan fondasi aksesibilitas. Kalo project kamu multi-bahasa, jangan kompromi di level tag <html> — biarkan server jadi sumber kebenarannya, karena dia satu-satunya yang bisa menjamin urutannya.

Sources

[1] Next.js docs: Internationalization guide
[2] Next.js docs: layout file convention
[3] MDN: HTML lang global attribute

Artikel terkait