The Language Switcher Just Prefixes /en, It Doesn't Translate Slugs
Plenty of bilingual sites keep a slug-mapping table to translate URLs between languages — a database entry saying /blog/pindah-ke-astro in Indonesian equals /en/blog/moving-to-astro in English. This site doesn’t have anything like that.
How it works: localePath and alternateLocalePath in src/lib/i18n-url.ts just prefix or strip /en from the current path. That’s the whole thing. Combined with Astro’s built-in i18n routing (defaultLocale: 'id', prefixDefaultLocale: false), that’s the entire language-switching logic on this site.
The consequence: every content collection entry has to use the exact same filename in the id/ and en/ folders. src/content/blog/id/pindah-ke-astro.md and src/content/blog/en/pindah-ke-astro.md have to match, or the switcher links straight to a 404.
Why I accept that trade-off: simplicity. No separate mapping table to maintain, no database query, no API call just to figure out the equivalent URL. Just filename discipline.
The real downside I’m aware of: if I ever want a different English slug for local SEO keywords (Indonesian slug using an Indonesian term, English slug using the term people actually search for), I’d have to refactor this into an explicit mapping. Not needed yet at this blog’s scale, and I’d rather keep the consistency than optimize prematurely.
A constraint as small as “filenames must match” looks trivial, but it’s exactly why i18n-url.ts is a dozen-odd lines of code with zero extra dependencies.