Skip to content

Repository files navigation

codeday/web

Monorepo for CodeDay's web applications and shared packages.

Structure

PathDescription
apps/wwwMain CodeDay website (Next.js Pages Router)
packages/topoShared design system (Chakra UI components, theme, region detection)
packages/i18nShared internationalization (Paraglide JS messages + runtime)
packages/utilsShared utilities (GraphQL fetch, debug, etc.)
packages/topoconsIcon library
packages/tsconfigShared TypeScript configuration

Getting started

pnpm install
pnpm run build

Internationalization (i18n)

The monorepo has two independent localization layers. Both are provided by shared packages so any app in the monorepo can use them.

LayerPurposeProvided byExample values
Locale (language)UI string translationspackages/i18nen, es
RegionDomain-specific data (phone numbers, emails, legal)packages/topo (Region)us, eu, uk, ca, in

Locale / Translations

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.

Using translations in components

import*asmfrom"@codeday/i18n/messages";functionFooter(){return<p>{m.topo_footer_copyright({currentYear: "2026"})}</p>;}

Getting the locale

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()}};});

Adding a new language

  1. Add the locale code to packages/i18n/project.inlang/settings.jsonlocales
  2. Create packages/i18n/messages/{locale}.json with translated values
  3. In each app: add the locale to next.config.tsi18n.locales and AVAILABLE_LOCALES in middleware.ts

i18n package exports

Import pathContents
@codeday/i18n/messagesType-safe message functions (m.key())
@codeday/i18n/runtimegetLocale(), setLocale(), overwriteGetLocale(), baseLocale, locales, Locale type
@codeday/i18n/next-pagesgetLocaleFromContext(), withLocaleStaticProps(), withLocaleServerSideProps()

Region

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:

TLDRegionTLDRegion
.orgus.eeeu
.usus.seeu
.caca.iteu
.co.ukuk.freu
.inin.eseu
.cheu
.beeu

Apps can pass an optional overrides map to add new TLDs or override specific full domain names.

Setting up region detection in an app

  1. 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 }});
  1. 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>;

Overriding regions for specific domains

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"

Getting the region

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 }};};

getRegionFromContext only works with getServerSideProps (it reads request headers). In getStaticProps there is no request — use getRegionFromHostname() with a known hostname, or resolve the region client-side.

Adding a new TLD

Add an entry to TLD_REGION_MAP in packages/topo/src/Region/config.ts. No other files need to change.

Region package exports

Import pathContents
@codeday/topo/Region/configgetRegionFromHostname(), TLD_REGION_MAP, DEFAULT_REGION, REGION_HEADER, RegionMap type — Edge-safe, no React
@codeday/topo/RegionEverything from config + RegionProvider, useRegion(), getRegionFromContext()

Architecture notes

  • _default locale sentinel — Next.js Pages Router requires a defaultLocale. We use _default so that bare paths are detectable by middleware (otherwise they'd be silently served as English). Middleware redirects _default to the browser's preferred language.
  • @codeday/topo/Region/config vs @codeday/topo/Region — Middleware runs in the Edge runtime, which cannot import React or Node.js built-ins. The /config module is kept dependency-free so both middleware and the full app can use it.

Releases

Packages

Contributors

Languages