Fitur & Build Notes · · 3 menit baca
i18n dwibahasa dengan Intlayer di Cloudflare Workers (Hono JSX)
Cara situs ini melayani Bahasa Indonesia di root dan English di /en dengan Intlayer: dictionary per halaman, routing ganda, hreflang — plus jebakan crash 'reading locales' di Workers dan solusinya.
Situs ini dwibahasa: Bahasa Indonesia di root (/) dan English di /en/*, ditenagai Intlayer. Dokumentasi ini mencatat arsitekturnya — termasuk satu jebakan besar saat menjalankan Intlayer di Cloudflare Workers yang tidak ada di dokumentasi mana pun.
Arsitektur: dictionary per halaman
Setiap halaman punya satu dictionary di src/content/*.content.ts. Semua copy dideklarasikan sekali dengan node t() berisi kedua bahasa:
import { t } from "@intlayer/core/transpiler";
import type { Dictionary } from "intlayer";
export default {
key: "home",
content: {
hero: {
titleA: t({ id: "Sistem e-commerce,", en: "E-commerce systems," }),
titleB: t({ id: "bukan sekadar website.", en: "not websites." }),
},
},
} satisfies Dictionary;
Komponen menerima locale dan me-resolve dictionary per request:
export const Home = ({ site, posts, locale }: Props) => {
const d = dict(homeContent, locale); // seluruh t() jadi string sesuai bahasa
return <Base site={site} locale={locale} title={d.meta.title} path="/">…</Base>;
};
Routing: default tanpa prefix, sekunder di /en
export const href = (l: Locale, path: string): string =>
l === "id" ? path : path === "/" ? "/en" : "/en" + path;
// registrasi ganda — satu handler, dua URL:
const page = (path: string, render: (c: Ctx, locale: Locale) => Response) => {
publicRoutes.get(path, (c) => render(c, "id"));
publicRoutes.get(href("en", path), (c) => render(c, "en"));
};
Setiap halaman mirror memasang hreflang resiprokal (x-default menunjuk versi Indonesia) — syarat mutlak supaya Google menghormatinya.
⚠️ Jebakan di Cloudflare Workers
Impor dari barrel intlayer langsung crash saat module load di Workers:
Uncaught TypeError: Cannot read properties of undefined (reading 'locales')
Penyebabnya: barrel intlayer membaca konfigurasi hasil build (@intlayer/config/built) di module scope — konfigurasi itu disuntik oleh plugin bundler Next/Vite, yang tidak ada di pipeline Wrangler. Solusinya tiga lapis:
// 1. Impor runtime dari subpath @intlayer/core (murni, tanpa config):
import { getDictionary, translationPlugin } from "@intlayer/core/interpreter";
import { t } from "@intlayer/core/transpiler";
// 2. Suplai plugin translasi eksplisit — default-nya membaca config yang tidak ada:
export const dict = <T extends Dictionary>(d: T, l: Locale) =>
getDictionary(d, l, [translationPlugin(l, "id")]);
// 3. wrangler.jsonc — intlayer menyentuh global `process`:
{ "compatibility_flags": ["nodejs_compat"] }
Impor tipe (import type { Dictionary } from "intlayer") tetap aman — terhapus saat compile.
Hasil
Satu handler per halaman, dua bahasa, nol JavaScript tambahan di browser, dan copy tidak pernah bercabang dua di dua file berbeda. Semua string hidup berdampingan (id dan en bersebelahan), jadi terjemahan tidak pernah tertinggal.
Mau fitur seperti ini dibangun untuk bisnismu?