// The labels each component takes, built from a message lookup. Every key the account page uses is // named here and nowhere else, so `__tests__/i18n.test.ts` can run these builders against the // catalogues and fail on a key either language lacks -- the page itself holds no string. // // `t` takes a key relative to the module (`export.heading`); the page binds it to // `i18n.message("account." + key)`. The app's `app.failed` is passed in already looked up. import { RECENT_SIGN_IN_SECONDS } from "../contract/constants.js" import type { DeletionStatus } from "../contract/procedures.js" import type { AccountAnalyticsLabels } from "./account-analytics.js" import type { AccountDeletionLabels } from "./account-deletion.js" import type { AccountExportLabels } from "./account-export.js" export type Translate = (key: string, values?: Record) => string /** The sections this module has a name for; another module's key is shown as it is. */ const NAMED_SECTIONS = ["account", "guides", "support"] as const export function sectionNames(t: Translate): Record { return Object.fromEntries(NAMED_SECTIONS.map((key) => [key, t(`sections.${key}`)])) } const named = (keys: readonly string[], names: Readonly>): string => keys.map((key) => names[key] ?? key).join(", ") export function pageLabels(t: Translate) { return { title: t("title"), intro: t("intro"), link: t("link"), profileHeading: t("profile.heading"), name: t("profile.name"), email: t("profile.email"), } } export function exportLabels(t: Translate, failed: string): AccountExportLabels { return { heading: t("export.heading"), body: t("export.body"), submit: t("export.submit"), busy: t("export.busy"), done: t("export.done"), partial: t("export.partial", { sections: "{sections}" }), filename: t("export.filename"), failed, sectionNames: sectionNames(t), } } export function deletionLabels( t: Translate, failed: string, status: Pick, email: string, ): AccountDeletionLabels { const names = sectionNames(t) const organizations = (list: readonly { name: string }[]) => list.map((org) => org.name).join(", ") return { heading: t("deletion.heading"), body: t("deletion.body"), services: status.modules.length > 0 ? t("deletion.services", { modules: named(status.modules, names) }) : "", ...(status.lastOwnerOf.length > 0 ? { lastOwner: t("deletion.lastOwner", { organizations: organizations(status.lastOwnerOf) }) } : {}), ...(status.onlyMemberOf.length > 0 ? { onlyMember: t("deletion.onlyMember", { organizations: organizations(status.onlyMemberOf) }) } : {}), reauth: t("deletion.reauth", { minutes: Math.round(RECENT_SIGN_IN_SECONDS / 60) }), reauthLink: t("deletion.reauthLink"), notInteractive: t("deletion.notInteractive"), confirmLabel: t("deletion.confirmLabel"), confirmHint: t("deletion.confirmHint", { email }), submit: t("deletion.submit"), busy: t("deletion.busy"), completed: t("deletion.completed"), erasing: t("deletion.erasing", { modules: "{modules}" }), home: t("deletion.home"), failed, sectionNames: names, } } /** The platform stores an analytics request reports on (ADR-049 ยง3). */ const ANALYTICS_STORES = ["session_replay", "errors", "proxy_logs", "traces", "logs", "analytics"] as const const ANALYTICS_STATUSES = ["pending", "running", "completed", "failed", "expired"] as const export function analyticsLabels(t: Translate, failed: string): AccountAnalyticsLabels { return { heading: t("analytics.heading"), body: t("analytics.body"), erase: t("analytics.erase"), exportSubmit: t("analytics.exportSubmit"), busy: t("analytics.busy"), confirmErase: t("analytics.confirmErase"), confirm: t("analytics.confirm"), cancel: t("analytics.cancel"), download: t("analytics.download"), filename: t("analytics.filename"), status: Object.fromEntries( ANALYTICS_STATUSES.map((key) => [key, t(`analytics.status.${key}`)]), ) as AccountAnalyticsLabels["status"], store: t("analytics.store"), storeStatus: t("analytics.storeStatus"), deleted: t("analytics.deleted"), anonymized: t("analytics.anonymized"), exported: t("analytics.exported"), storeNames: Object.fromEntries(ANALYTICS_STORES.map((key) => [key, t(`analytics.stores.${key}`)])), failed, } }