Files
auth-portal/apps/web/src/components/reui/cascader/cascader-i18n.tsx
T
DenozordecandClaude Sonnet 5.5 7c4430d240 chore(web): обновить компоненты ReUI и перейти на TanStack Table v9
Обновлены из реестра data-grid, filters, badge, frame, alert, icon-tile, icon-stack,
timeline и зависимые примитивы @authportal/ui. @tanstack/react-table 8.21 → 9.3,
добавлены dnd-kit, react-virtual, date-fns. Фильтры AdminUsersGrid переведены на
дерево FilterQuery, таблицы — на useTable + dataGridFeatures. Русские подписи вынесены
в lib/reui-ru.ts. Импорты cn из реестра заменены на @authportal/ui/lib/utils.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-18 01:58:31 +07:00

92 lines
3.4 KiB
TypeScript

import type {
CascaderLabels,
CascaderMode,
} from "@/components/reui/cascader/cascader-types"
/** Name of the root level. Hoisted so the root announcement can reuse it. */
const ROOT_LEVEL = "Top level"
/** Hoisted for the same reason: several defaults end in an item count. */
const itemCount = (count: number) =>
`${count} ${count === 1 ? "item" : "items"}`
/**
* English defaults. Every string the primitive can render lives here, so a
* consumer can translate the whole surface by passing `labels`.
*/
export const CASCADER_LABELS: CascaderLabels = {
// Deliberately NOT lowercased. `toLowerCase()` is locale-hostile - it maps
// Turkish "İ" to a two-code-point sequence and German "İstanbul" style
// proper nouns lose their casing - and a label is already written the way
// its author wants it read.
search: (parentLabel) =>
parentLabel ? `Search ${parentLabel}...` : "Search...",
back: "Back",
loading: "Loading...",
loadingMore: "Loading more...",
loadMore: "Load more",
error: "Could not load items.",
retry: "Retry",
empty: "No results found.",
selectedCount: (count) => `${count} selected`,
breadcrumbLabel: "Breadcrumb",
chipsLabel: "Selected items",
removeChip: (label) => `Remove ${label}`,
pathSeparator: "/",
rootLevel: ROOT_LEVEL,
itemCount,
branchAffordance: "submenu",
selectedState: "selected",
partiallySelectedState: "partially selected",
columnsLabel: "Levels",
actionsLabel: "Actions",
submenuAffordance: "opens a menu",
panelLabel: "Options",
keyboardHint: (mode: CascaderMode, dir: "ltr" | "rtl") => {
// "Deeper" is the direction the text runs, so the level keys mirror in
// RTL and the hint has to name the mirrored pair there - an LTR-worded
// hint would teach exactly the wrong keys.
const open = dir === "rtl" ? "Left" : "Right"
const back = dir === "rtl" ? "Right" : "Left"
if (mode === "tree") {
return `Use the ${open} Arrow key to expand and the ${back} Arrow key to collapse.`
}
if (mode === "columns") {
return `Use the ${open} Arrow key to open the next column and the ${back} Arrow key to go back.`
}
return `Use the ${open} Arrow key to open a branch and the ${back} Arrow key to go back.`
},
rootAnnouncement: (count) => `${ROOT_LEVEL}, ${itemCount(count)}`,
expandedAnnouncement: (label, count) =>
`${label} expanded, ${itemCount(count)}`,
collapsedAnnouncement: (label) => `${label} collapsed`,
levelAnnouncement: (parentLabel, depth, count) =>
`${parentLabel}, level ${depth}, ${itemCount(count)}`,
resultsAnnouncement: (count) =>
count === 1 ? "1 result" : `${count} results`,
maxReachedAnnouncement: (max) => `Selection limit of ${max} reached`,
cascadeAnnouncement: (label, count, selecting) =>
`${label} ${selecting ? "selected" : "deselected"}, ${itemCount(count)} followed`,
searchingAnnouncement: "Searching...",
}
/**
* Shallow-merges consumer overrides over the defaults, so `labels` can carry a
* single key without restating the rest.
*/
export function resolveCascaderLabels(
labels?: Partial<CascaderLabels>
): CascaderLabels {
if (!labels) return CASCADER_LABELS
return { ...CASCADER_LABELS, ...labels }
}
/** Resolves the search placeholder, which may be a string or a function. */
export function resolveCascaderSearchLabel(
labels: CascaderLabels,
parentLabel?: string
): string {
return typeof labels.search === "function"
? labels.search(parentLabel)
: labels.search
}