Files
plainpages/src/i18n/view-locals.ts
T

72 lines
2.6 KiB
TypeScript

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