'use client'; import { createContext, useContext, useMemo, ReactNode } from 'react'; import { Locale, DEFAULT_LOCALE, isLocale, LOCALE_META, isAfricanCountry } from '@/lib/locales'; import { getTranslations, TFunction } from '@/lib/i18n'; /** * Client-side locale + region context (Session 12; Session 13 added * the `country` field from the CF-IPCountry header). * * The root layout (server component) resolves the locale + country * from request headers and passes them as props to ``. * From there every client component can `useT()` / `useRegion()` * without prop-drilling or repeating the resolution. * * Memoized: the `t` function and derived booleans are stable per * render of the provider, so consumers don't re-render on every * parent render. */ interface LocaleContextValue { locale: Locale; dir: 'ltr' | 'rtl'; t: TFunction; // Session 13 region fields. country: string; // 'NG', 'US', '' (unknown / non-Cloudflare path) inAfrica: boolean; // true when country ∈ AFRICAN_COUNTRIES } const LocaleContext = createContext(null); export function LocaleProvider({ locale, country = '', children, }: { locale: string; country?: string; children: ReactNode }) { const value = useMemo(() => { const resolved: Locale = isLocale(locale) ? locale : DEFAULT_LOCALE; const bundle = getTranslations(resolved); const cc = String(country || '').toUpperCase(); return { locale: resolved, dir: LOCALE_META[resolved].dir, t: bundle.t, country: cc, inAfrica: isAfricanCountry(cc), }; }, [locale, country]); return {children}; } export function useT(): TFunction { const ctx = useContext(LocaleContext); if (!ctx) { // Fall back to English silently — better than a crash if some // component renders outside the provider (test envs, storybook). return getTranslations('en').t; } return ctx.t; } export function useLocale(): { locale: Locale; dir: 'ltr' | 'rtl' } { const ctx = useContext(LocaleContext); if (!ctx) return { locale: DEFAULT_LOCALE, dir: 'ltr' }; return { locale: ctx.locale, dir: ctx.dir }; } /** * Session 13 — region hook for components that need to gate by * geography (pricing, regulatory disclaimers, regional payment * methods). Returns `inAfrica: false` when country is unknown * (degrade-closed: don't surface region-specific UX on unverified * traffic). */ export function useRegion(): { country: string; inAfrica: boolean } { const ctx = useContext(LocaleContext); if (!ctx) return { country: '', inAfrica: false }; return { country: ctx.country, inAfrica: ctx.inAfrica }; }