Locales, layoutContext, and hreflang in JSkelet
· 2 min read
Content products rarely launch in one language of UI chrome. JSkelet keeps localisation in the server render: pathname → locale → content dictionary → complete HTML. There is no client locale store racing the URL, and no empty shell waiting to “detect language.”
Resolve locale from the path
Define locales and path helpers once (localePath, alternatePaths). Register routes per locale or with a locale prefix strategy your app owns. hooks.layoutContext({ pathname }) (or equivalent) loads the active dictionary, builds nav with localised labels, and sets lang for <html>.
Language switching is a normal navigation to the alternate href. View transitions and caches then see stable URLs — the document you requested is the document you get.
Content dictionaries beat string soup
Keep structured copy in modules per locale (nav, page titles, blog UI, docs labels). Templates receive t and rarely hard-code English. Shared shape across locales makes missing keys obvious in review.
Formatting helpers belong in modules templates can call as components when the expression language cannot concatenate or format freely.
Editorial language vs chrome language
Many teams localise chrome first and keep long-form bodies in one editorial language until translation budget arrives. That is a product choice, not a framework limitation. When true translations exist, store per-locale bodies or linked translation records; routing and hreflang stay the same.
Be honest in SEO: alternates that share body text are “localised shell” pages until content diverges. Still better than a JS redirect guessing Accept-Language after paint.
Metadata and alternates
Emit hreflang links and a canonical that match your URL policy. Open Graph locale hints can follow the active chrome locale. Sitemaps should list each locale’s public URLs you intend to index.
SITE_URL must be correct in production so absolute alternate hrefs do not leak localhost.
Caching implications
Each locale path is typically its own HTML cache key. Invalidation after publish must include every locale variant you cache for that content. Forgetting /tr/blog while updating /blog is a classic partial-stale bug.
Pitfalls
- Treating
/as a prefix of every localised path when marking nav active - Translating marketing tone with raw machine output next to careful English product copy
- Putting locale in a cookie that disagrees with the path
- Caching one HTML blob for all languages
JSkelet’s model is simple on purpose: the URL is the locale, the server fills chrome from dictionaries, and the HTML cache stores the finished document. That is how multilingual content sites stay crawlable and fast without a client i18n runtime.
Comments
No comments yet.