I run bacotto , a B2B sales-list SaaS for the Japanese market. It started Japanese-only. Last week I made the whole thing bilingual — landing page, dashboard, auth, emails, ~17 blog posts, and 4,364 programmatic SEO pages. Here's the real write-up: the architecture, and the things that broke. The setup Next.js 15 (App Router), TypeScript, React 19 ~1,565 hard-coded Japanese strings across 40+ files 4,364 statically-generated programmatic SEO pages ( force-static , dynamicParams = false ) Goal: Japanese stays the default at / , English served from /en , no SEO regression Why next-intl I evaluated next-intl, Paraglide, and rolling my own. next-intl won because: App Router-native, works with Server Components without "use client" thrash Supports static generation — critical for the 4,364 force-static pages Built-in middleware for locale negotiation + hreflang helpers Paraglide is leaner, but with only two locales its tree-shaking edge didn't matter. Rolling my own across that much SEO surface was a trap.…