Kaizen Forge is my software house and consultancy: a team of developers building software for clients. The site, at kaizenforge.tech, presents three services: AI consulting, software engineering and career coaching. It also explains how we work: fixed scope, timeline and price, in a six-stage method.
The main audience buys in Brazil, so Portuguese sits at the root of the domain. The other six languages serve people coming from abroad: English, Spanish, Japanese, German, Russian and Chinese.
I designed the site and I maintain it. The first commit is from 3 July 2026: the site started bilingual and gained the other five languages the same day. It runs in production on Vercel, and the latest commit in the repository is from 18 September 2026.
The site runs on Vercel. Both domains, registered with GoDaddy, use Vercel's nameservers, and vercel.json redirects www.kaizenforge.tech, kaizenforge.com.br and www.kaizenforge.com.br to kaizenforge.tech with a 308. On a visit, the next-intl middleware runs on the Edge runtime, reads the language prefix from the URL (the root is pt-BR) and rewrites the route to the [locale] segment. The page comes prerendered from Vercel's cache.
There is no database, API route or server-side state. Content lives in the 7 JSON catalogs and in the pages generated at build time, and the only state is the visitor's consent choice, kept in a browser cookie. There are no queues, crons or webhooks. The tracking layer accepts six providers through environment variables, but the production bundle checked on 22 Oct 2026 has none configured. The contact form is off too: contact goes through WhatsApp and email links.
Production deploys go out from a local session through the Vercel CLI. On GitHub, Actions runs CI on every pull request and on pushes to main and develop: lint with import boundaries, typecheck, tests, build, Storybook, Playwright end-to-end tests, Lighthouse and a bundle budget. A deploy workflow on Actions exists, but only as a manual trigger, and Dependabot handles dependency updates.
The root always serves pt-BR, with no browser language detection.
Alternative rejected: Redirecting based on the browser's Accept-Language.
Why: The main audience is Brazilian buyers, and stable URLs are better for SEO.
Portuguese slugs only in pt-BR. The other six languages use the English slugs, such as /ai-consulting.
Alternative rejected: Translating every page slug into every language.
Why: It makes no difference for SEO and avoids maintaining six slug sets.
A hand-written consent store on top of the EventTarget API.
Alternative rejected: Using Zustand.
Why: The tracking layer loads on every page. A library for such a small store would add bundle weight, and the native API also works outside React.
Explicit consent by default: nothing is measured until the visitor chooses.
Alternative rejected: Treating the pending state as permission.
Why: It is the default that fits LGPD and GDPR. The cost showed up in the pre-launch review: with no banner, tracking was off for everyone, so the banner went in, in all 7 languages.