Three languages on a site in one day via vibe-coding
Context
Agency work is not single-language: some clients prefer Ukrainian or English. “We will translate later” never ships, so we baked multilingual support into the first landing iteration and kept the sprint tight. Constraint: no CMS on day one, copy in the repo, predictable URLs for SEO and ads.
Approach
We used per-locale JSON dictionaries with a single TypeScript Dictionary type so the IDE autocompletes keys and we avoid typos in production. Routing uses an App Router [locale] segment, locales live in config, and isLocale guards the entry. Default Russian stays unprefixed (no /ru in the URL); ua and en keep path prefixes—matching user expectations and keeping share links obvious.
Middleware handles two jobs: normalise prefix-less requests to the internal ru locale and expose the active locale for the HTML lang attribute. The language switcher builds paths via a localizedPath helper so buttons do not duplicate routing logic.
What accelerated “vibe-coding”
First, strict discipline: one source of truth for strings—no duplicated sections across files. Second, automation—metadata and sitemap generation from the same locale list so alternate languages never go missing. Third, cohesive components: DictionaryProvider on the client for the header and CTAs, server-rendered pages for SEO copy.
Limitations
Static JSON is painful for frequent edits without a deploy—for the blog we layer MDX in-repo; a large editorial workflow might add a CMS later. For an agency landing, “code + dictionaries” shipped faster than wiring a headless CMS on day one.
Takeaway
Three languages in a day is not magic—it is upfront patterns and zero speculative abstractions. Next step: ship blog case studies on every locale as translations land.
Want the same localisation playbook? Book a strategy session—we will unpack your stack and priorities.
