Problem
Site is English-only. Two costs:
- SEO — non-English markets don't index us; `hreflang` alternates absent.
- AI agents — agents querying in non-English contexts get nothing back.
- Brand reach — VLLNT readership is global; English-only is a self-imposed ceiling.
Goal
First-class internationalization, leveraging existing MDX content pipeline. Co-locate translations next to source MDX — no separate translation tree.
Architecture (file layout)
apps/registry/
├── i18n/
│ ├── routing.ts ← next-intl config: locales, defaultLocale, localePrefix
│ └── request.ts ← message loader (per-request locale resolution)
├── messages/
│ ├── en.json ← UI strings (chrome, buttons, search, errors)
│ ├── fr.json
│ └── es.json
├── app/
│ └── [locale]/ ← all routes under here
│ ├── page.tsx
│ ├── components/...
│ ├── docs/...
│ └── ...
└── content/
└── pages/
├── home/
│ ├── en.mdx
│ ├── fr.mdx
│ └── es.mdx
├── docs/installation/
│ ├── en.mdx
│ └── fr.mdx
├── philosophy/
│ ├── en.mdx
│ └── fr.mdx
└── design/
├── en.mdx
└── fr.mdx
Convention: every routable MDX page lives in a folder named for its slug, with one file per locale. The MDX loader in `page.tsx` resolves `/.mdx`, falling back to `/en.mdx` if the locale file is missing.
Library choice
`next-intl` — decision locked.
- Native App Router support.
- Locale-prefixed routes (`/fr/components/button`) with `localePrefix: 'as-needed'` (English at root, others prefixed).
- Server Components first.
- ``, `useRouter`, `usePathname` wrappers for locale-aware navigation.
- Mature, actively maintained, used in production at scale.
Alternatives considered + rejected: `next-i18next` (Pages Router era), `@lingui/core` (heavier toolchain), DIY (not worth it).
Initial locales
Phase 1: `en` + `fr` (founder-quality translations possible).
Phase 2: `es`, `de`, `pt-BR`, `zh-CN` (community / AI-assisted, reviewed before merge).
Acceptance criteria
Routing & config
MDX pipeline
UI strings
SEO
Component pages
Locale switcher
CI
Out of scope
Depends on
References
Success metrics
Problem
Site is English-only. Two costs:
Goal
First-class internationalization, leveraging existing MDX content pipeline. Co-locate translations next to source MDX — no separate translation tree.
Architecture (file layout)
Convention: every routable MDX page lives in a folder named for its slug, with one file per locale. The MDX loader in `page.tsx` resolves `/.mdx`, falling back to `/en.mdx` if the locale file is missing.
Library choice
`next-intl` — decision locked.
Alternatives considered + rejected: `next-i18next` (Pages Router era), `@lingui/core` (heavier toolchain), DIY (not worth it).
Initial locales
Phase 1: `en` + `fr` (founder-quality translations possible).
Phase 2: `es`, `de`, `pt-BR`, `zh-CN` (community / AI-assisted, reviewed before merge).
Acceptance criteria
Routing & config
MDX pipeline
UI strings
SEO
Component pages
Locale switcher
CI
Out of scope
Depends on
References
Success metrics