@@ -0,0 +1,118 @@
|
||||
// The labels each component takes, built from a message lookup. Every key this module's pages use
|
||||
// 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.
|
||||
|
||||
import { KNOWLEDGE_REFUSALS, type KnowledgeRefusal } from "../contract/index.js"
|
||||
|
||||
export type Translate = (key: string, values?: Record<string, string | number>) => string
|
||||
|
||||
/** A typed refusal as a page or a form shows it. */
|
||||
export interface RefusalNotice {
|
||||
readonly code: KnowledgeRefusal
|
||||
readonly text: string
|
||||
}
|
||||
|
||||
export type RefusalLabels = Readonly<Record<KnowledgeRefusal, string>>
|
||||
|
||||
export const refusalLabels = (t: Translate): RefusalLabels =>
|
||||
Object.fromEntries(KNOWLEDGE_REFUSALS.map((code) => [code, t(`refusals.${code}`)])) as RefusalLabels
|
||||
|
||||
/** The typed refusal a procedure answered, or undefined for any other error (which the form words itself). */
|
||||
export function refusalOf(labels: RefusalLabels, error: unknown): RefusalNotice | undefined {
|
||||
const code = (error as { problem?: { code?: unknown } } | null)?.problem?.code
|
||||
return typeof code === "string" && code in labels
|
||||
? { code: code as KnowledgeRefusal, text: labels[code as KnowledgeRefusal] }
|
||||
: undefined
|
||||
}
|
||||
|
||||
/** The words for a failure a document row records: the file's own, or the service's refusal; else the code. */
|
||||
export function failureText(t: Translate, code: string): string {
|
||||
return failureLabels(t)[code] ?? code
|
||||
}
|
||||
|
||||
/** The failures a document row can name with words of their own: the file's, and the service's refusals. */
|
||||
const FILE_FAILURES = ["file_changed", "not_text", "not_indexable", "too_large"] as const
|
||||
|
||||
/** The words of each failure `failureText` knows, by code, for a page that words a live row in the browser. */
|
||||
export const failureLabels = (t: Translate): Readonly<Record<string, string>> =>
|
||||
Object.fromEntries([
|
||||
...KNOWLEDGE_REFUSALS.map((code) => [code, t(`refusals.${code}`)]),
|
||||
...FILE_FAILURES.map((code) => [code, t(`failures.${code}`)]),
|
||||
])
|
||||
|
||||
/** The status shape the labels render, declared here: ui reads no core/. */
|
||||
interface StatusCounts {
|
||||
documents: { queued: number; indexing: number; indexed: number; failed: number; cancelled: number }
|
||||
}
|
||||
|
||||
export interface StatusLabels {
|
||||
readonly heading: string
|
||||
readonly flagOff: string
|
||||
readonly state: string
|
||||
readonly reasons: string
|
||||
readonly indexing: string
|
||||
readonly documents: string
|
||||
}
|
||||
|
||||
export function statusLabels(t: Translate, status: StatusCounts): StatusLabels {
|
||||
return {
|
||||
heading: t("status.heading"),
|
||||
flagOff: t("status.flagOff"),
|
||||
state: t("status.state"),
|
||||
reasons: t("status.reasons"),
|
||||
indexing: t("status.indexing"),
|
||||
documents: t("status.documents", status.documents),
|
||||
}
|
||||
}
|
||||
|
||||
export const serviceStateLabel = (t: Translate, state: string): string => {
|
||||
switch (state) {
|
||||
case "ready":
|
||||
return t("status.stateReady")
|
||||
case "indexing":
|
||||
return t("status.stateIndexing")
|
||||
case "registered":
|
||||
return t("status.stateRegistered")
|
||||
case "degraded":
|
||||
return t("status.stateDegraded")
|
||||
case "disabled":
|
||||
return t("status.stateDisabled")
|
||||
default:
|
||||
return t("status.stateUnavailable")
|
||||
}
|
||||
}
|
||||
|
||||
export interface SearchFormLabels {
|
||||
readonly heading: string
|
||||
readonly label: string
|
||||
readonly placeholder: string
|
||||
readonly submit: string
|
||||
readonly busy: string
|
||||
readonly empty: string
|
||||
readonly results: string
|
||||
readonly snippet: string
|
||||
readonly source: string
|
||||
readonly chunk: string
|
||||
readonly page: string
|
||||
readonly disclosure: string
|
||||
readonly refusals: RefusalLabels
|
||||
}
|
||||
|
||||
export function searchFormLabels(t: Translate): SearchFormLabels {
|
||||
return {
|
||||
heading: t("search.heading"),
|
||||
label: t("search.label"),
|
||||
placeholder: t("search.placeholder"),
|
||||
submit: t("search.submit"),
|
||||
busy: t("search.busy"),
|
||||
empty: t("search.empty"),
|
||||
// Not filled in here: only the browser knows the count, and `formatMessage` brings the plural rules.
|
||||
results: t("search.results"),
|
||||
snippet: t("search.snippet"),
|
||||
source: t("search.source"),
|
||||
chunk: t("search.chunk", { chunk: "{chunk}" }),
|
||||
page: t("search.page", { page: "{page}" }),
|
||||
disclosure: t("search.disclosure"),
|
||||
refusals: refusalLabels(t),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user