Skip to Content
Housr WebappInternationalisation

Internationalization (i18n)

Dictionary System

The app supports two locales: us and uk. Translations are stored as flat JSON files:

  • src/dictionaries/us.json
  • src/dictionaries/uk.json

Each dictionary is a flat Record<string, string> (no nesting). Keys include things like app store URLs, locale-specific copy, and feature labels.

Loading Dictionaries

Dictionaries are loaded server-side via getDictionary() in src/app/[locale]/dictionaries.ts:

import "server-only"; const dictionaries = { us: () => import("@/dictionaries/us.json").then((module) => module.default), uk: () => import("@/dictionaries/uk.json").then((module) => module.default), }; export const getDictionary = async (locale: "us" | "uk") => dictionaries[locale]();

The "server-only" import ensures this module cannot be imported from client components, preventing the dictionary JSON from being bundled into client-side JavaScript.

How Dictionaries Flow Through Components

  1. src/app/[locale]/layout.tsx loads the dictionary and passes it to SmartHeaderLayout:
const dictionary = await getDictionary(validatedLocale); return <SmartHeaderLayout dictionary={dictionary}>{children}</SmartHeaderLayout>;
  1. SmartHeaderLayout passes the dictionary to HeaderLayout
  2. HeaderLayout passes it to Navigation, FooterLayout, and any mainSection / customCta that accepts it as a render prop:
mainSection: ({ dictionary }: { dictionary?: Record<string, string> }) => { return <HomeHeader dictionary={dictionary || {}} />; },

Dictionaries are passed as props through the component tree — there is no React Context provider for i18n.

Locale Detection in Client Components

Client components cannot access the [locale] route param directly. Instead, they derive the locale from the current pathname:

import { getCurrentLocale } from "@/lib/utils/locale"; import { usePathname } from "next/navigation"; const pathname = usePathname(); const locale = getCurrentLocale(pathname);

When constructing links, use getLocaleAwareHref() from src/lib/utils.ts to prepend the correct locale prefix:

import { getLocaleAwareHref } from "@/lib/utils"; const href = getLocaleAwareHref("/about", locale); // Returns "/en-us/about" or "/en-gb/about"

Region-Specific Content

Route-Level Restrictions

Routes can be restricted to specific regions via the allowedRegions field in the route config:

"/bills": { allowedRegions: ["uk"], // Only accessible in UK }, "/colleges": { allowedRegions: ["us"], // Only accessible in US },

SmartHeaderLayout enforces this by redirecting users who access a region-restricted page from the wrong locale.

Content-Level Filtering

Blog posts from Contentstack have a region field ("us", "uk", or "both") that is used to filter content per locale:

posts.filter( (p) => !p.region || p.region === "both" || p.region === locale, );

Locale-Dependent Titles

Route config titles can be functions that receive the locale for conditional text:

"/roomie": { title: ({ locale }) => `Find your perfect ${locale === "uk" ? "housemates" : "roommates"} on Housr`, },
Last updated on