Monorepo for CodeDay's web applications and shared packages.
| Path | Description |
|---|---|
apps/www | Main CodeDay website (Next.js Pages Router) |
packages/topo | Shared design system (Chakra UI components, theme, region detection) |
packages/i18n | Shared internationalization (Paraglide JS messages + runtime) |
packages/utils | Shared utilities (GraphQL fetch, debug, etc.) |
packages/topocons | Icon library |
packages/tsconfig | Shared TypeScript configuration |
pnpm install
pnpm run buildThe monorepo has two independent localization layers. Both are provided by shared packages so any app in the monorepo can use them.
| Layer | Purpose | Provided by | Example values |
|---|---|---|---|
| Locale (language) | UI string translations | packages/i18n | en, es |
| Region | Domain-specific data (phone numbers, emails, legal) | packages/topo (Region) | us, eu, uk, ca, in |
Translations are powered by Paraglide JS.
Message files live in packages/i18n/messages/{locale}.json. Keys are prefixed
by package — topo_* for design-system strings, www_* for app strings.
URL structure: Every page URL includes a locale prefix — /en/about,
/es/about. If a user visits a bare path like /about, middleware detects
the browser's preferred language and redirects.
import*asmfrom"@codeday/i18n/messages";functionFooter(){return<p>{m.topo_footer_copyright({currentYear: "2026"})}</p>;}Client-side (React components):
import{getLocale}from"@codeday/i18n/runtime";constlocale=getLocale();// "en" | "es"Server-side (getStaticProps / getServerSideProps):
import{getLocaleFromContext}from"@codeday/i18n/next-pages";exportconstgetStaticProps: GetStaticProps=async(ctx)=>{constlocale=getLocaleFromContext(ctx);// ...};To use m.*() inside data-fetching functions, wrap with the locale helper:
import{withLocaleStaticProps}from"@codeday/i18n/next-pages";import*asmfrom"@codeday/i18n/messages";exportconstgetStaticProps=withLocaleStaticProps(async(ctx)=>{return{props: {title: m.page_title()}};});- Add the locale code to
packages/i18n/project.inlang/settings.json→locales - Create
packages/i18n/messages/{locale}.jsonwith translated values - In each app: add the locale to
next.config.ts→i18n.localesandAVAILABLE_LOCALESinmiddleware.ts
| Import path | Contents |
|---|---|
@codeday/i18n/messages | Type-safe message functions (m.key()) |
@codeday/i18n/runtime | getLocale(), setLocale(), overwriteGetLocale(), baseLocale, locales, Locale type |
@codeday/i18n/next-pages | getLocaleFromContext(), withLocaleStaticProps(), withLocaleServerSideProps() |
Region codes are resolved from the top-level domain the visitor is using.
This is independent of UI language — a user on codeday.fr gets region eu
but can still view the site in English.
@codeday/topo/Region ships a built-in TLD_REGION_MAP:
| TLD | Region | TLD | Region | |
|---|---|---|---|---|
.org | us | .ee | eu | |
.us | us | .se | eu | |
.ca | ca | .it | eu | |
.co.uk | uk | .fr | eu | |
.in | in | .es | eu | |
.ch | eu | |||
.be | eu |
Apps can pass an optional overrides map to add new TLDs or override
specific full domain names.
- In middleware, resolve and forward the region as a header:
import{getRegionFromHostname,REGION_HEADER}from"@codeday/topo/Region/config";// inside middleware():constregion=getRegionFromHostname(request.headers.get("host"));constheaders=newHeaders(request.headers);headers.set(REGION_HEADER,region);returnNextResponse.next({request: { headers }});- In
_app.tsx, wrap with<RegionProvider>:
import{RegionProvider,getRegionFromHostname}from"@codeday/topo/Region";constregion=useMemo(()=>getRegionFromHostname(typeofwindow!=="undefined" ? window.location.hostname : undefined),[],);return<RegionProvidervalue={region}>...</RegionProvider>;Pass an overrides map to getRegionFromHostname. Full-domain keys take
priority, then override TLD keys, then the built-in TLD_REGION_MAP.
constoverrides={"staging.codeday.org": "eu","dev": "us"};constregion=getRegionFromHostname("staging.codeday.org",overrides);// "eu"Client-side (React components):
import{useRegion}from"@codeday/topo/Region";constregion=useRegion();// "us" | "eu" | "uk" | ...Server-side (getServerSideProps):
import{getRegionFromContext}from"@codeday/topo/Region";exportconstgetServerSideProps: GetServerSideProps=async(ctx)=>{constregion=getRegionFromContext(ctx);return{props: { region }};};
getRegionFromContextonly works withgetServerSideProps(it reads request headers). IngetStaticPropsthere is no request — usegetRegionFromHostname()with a known hostname, or resolve the region client-side.
Add an entry to TLD_REGION_MAP in packages/topo/src/Region/config.ts.
No other files need to change.
| Import path | Contents |
|---|---|
@codeday/topo/Region/config | getRegionFromHostname(), TLD_REGION_MAP, DEFAULT_REGION, REGION_HEADER, RegionMap type — Edge-safe, no React |
@codeday/topo/Region | Everything from config + RegionProvider, useRegion(), getRegionFromContext() |
_defaultlocale sentinel — Next.js Pages Router requires adefaultLocale. We use_defaultso that bare paths are detectable by middleware (otherwise they'd be silently served as English). Middleware redirects_defaultto the browser's preferred language.@codeday/topo/Region/configvs@codeday/topo/Region— Middleware runs in the Edge runtime, which cannot import React or Node.js built-ins. The/configmodule is kept dependency-free so both middleware and the full app can use it.