// The i18n block every rendered view receives. EJS passes a template's locals down into its // includes, so injecting this at the top level is what lets any partial — core or plugin — call // `t(...)` and read `locale` without its caller threading them through. import { DEFAULT_LOCALE } from "./catalog.ts"; import { ENGLISH } from "./english.ts"; import { chosenLocale, localeHref, localeLabel, textDirection } from "./locale.ts"; import type { Translate } from "./translate.ts"; export interface LocaleChoice { current: boolean; href: string; // this same page in that locale label: string; // the locale named in its own language tag: string; } export interface I18nLocals { dir: "ltr" | "rtl"; // True when switching language cannot stay on this page (its URL answers no GET, so the picker // points elsewhere) — the page then says so, because what it leaves behind may be a one-time // secret that cannot be shown again. leavesPage: boolean; locale: string; localeHref: (href: string) => string; // The locale to carry as a hidden field, or null when the visitor never asked for one. A GET form // replaces the whole query string, so a link-carrying wrapper can't reach it — the form must. localeParam: string | null; localeSwitch: LocaleChoice[]; locales: string[]; t: Translate; } // Just the request fields a render needs, so this module stays a leaf of src/i18n/ rather than // depending on the HTTP layer that calls it. export interface I18nRequest { locale: string; localeHref: (href: string) => string; locales: string[]; // Where the language picker points — "this page", except after a POST, whose URL may answer no // GET at all; the host then resolves the nearest page that does (app.ts → switchBase). switchBase: string; t: Translate; url: URL; } // For a render with no request behind it — a partial exercised directly, a one-off render: English, // left-to-right, no language picker. export const ENGLISH_LOCALS: I18nLocals = { dir: "ltr", leavesPage: false, locale: DEFAULT_LOCALE, localeHref: (href) => href, localeParam: null, localeSwitch: [], locales: [DEFAULT_LOCALE], t: ENGLISH, }; export function i18nLocals(ctx: I18nRequest): I18nLocals { const carried = chosenLocale(ctx); return { dir: textDirection(ctx.locale), leavesPage: ctx.switchBase !== `${ctx.url.pathname}${ctx.url.search}`, locale: ctx.locale, localeHref: (href) => ctx.localeHref(href), localeParam: carried, localeSwitch: ctx.locales.map((tag) => ({ current: tag === ctx.locale, href: localeHref(ctx.switchBase, tag), label: localeLabel(tag), tag })), locales: ctx.locales, t: ctx.t, }; }