@protobase/ui 0.25.2 → 0.25.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/dist/account/account-pages.js +3 -2
  2. package/dist/account/account-security-page.js +9 -4
  3. package/dist/account/backup-codes.js +7 -6
  4. package/dist/account/messages.js +214 -0
  5. package/dist/account/passkeys-section.js +23 -22
  6. package/dist/account/password-dialog.js +3 -2
  7. package/dist/account/sign-in-policy-form.js +11 -10
  8. package/dist/account/sign-in-policy-labels.js +30 -29
  9. package/dist/account/sign-in-policy-page.js +11 -6
  10. package/dist/account/sign-in-providers-section.js +6 -5
  11. package/dist/account/staff-sign-in-log.js +11 -10
  12. package/dist/account/totp-qr.js +2 -1
  13. package/dist/account/two-factor-section.js +24 -23
  14. package/dist/account/two-factor-setup.js +18 -17
  15. package/dist/action-card/action-card.js +2 -1
  16. package/dist/action-card/messages.js +8 -0
  17. package/dist/activity-chart/activity-chart.js +3 -2
  18. package/dist/activity-chart/messages.js +14 -0
  19. package/dist/app/app.d.ts +1 -1
  20. package/dist/app/app.js +20 -21
  21. package/dist/app/assistant-shell.js +2 -1
  22. package/dist/app/batch-failure.js +2 -1
  23. package/dist/app/breadcrumb-for.js +4 -3
  24. package/dist/app/charts/chart-groups.js +3 -2
  25. package/dist/app/charts/chart-view.js +3 -2
  26. package/dist/app/charts/group-bars.js +2 -1
  27. package/dist/app/charts/messages.js +26 -0
  28. package/dist/app/charts/period-chart.js +3 -2
  29. package/dist/app/charts/split-points.js +2 -1
  30. package/dist/app/comments/comment-composer.js +19 -18
  31. package/dist/app/comments/comments-section.js +4 -3
  32. package/dist/app/comments/messages.js +26 -0
  33. package/dist/app/create/create-page.js +11 -18
  34. package/dist/app/custom-section.js +4 -6
  35. package/dist/app/displays/board-column.js +6 -5
  36. package/dist/app/displays/board-columns.js +3 -2
  37. package/dist/app/displays/board-display.js +13 -15
  38. package/dist/app/displays/calendar-controls.js +3 -2
  39. package/dist/app/displays/calendar-display.js +12 -17
  40. package/dist/app/displays/card-carousel.js +4 -3
  41. package/dist/app/displays/cards-display.js +2 -1
  42. package/dist/app/displays/custom-display.js +4 -6
  43. package/dist/app/displays/display-frame.js +3 -2
  44. package/dist/app/displays/list-cards.js +5 -8
  45. package/dist/app/displays/list-displays.js +3 -3
  46. package/dist/app/displays/map-display.js +15 -14
  47. package/dist/app/displays/messages.js +128 -2
  48. package/dist/app/displays/period-controls.js +4 -3
  49. package/dist/app/displays/record-card.js +7 -6
  50. package/dist/app/displays/timeline-display.js +17 -26
  51. package/dist/app/displays/timeline-range.js +2 -1
  52. package/dist/app/displays/use-board-move.js +2 -2
  53. package/dist/app/displays/use-calendar-move.js +2 -1
  54. package/dist/app/document-title.js +2 -1
  55. package/dist/app/export/builder-port.js +2 -1
  56. package/dist/app/export/export-dialog.js +36 -40
  57. package/dist/app/export/export-worker.js +2 -0
  58. package/dist/app/export/messages.js +78 -0
  59. package/dist/app/export/run-export.js +5 -2
  60. package/dist/app/export/sink.js +4 -3
  61. package/dist/app/export/use-export-flow.js +4 -3
  62. package/dist/app/field-editor.js +4 -3
  63. package/dist/app/field-types/messages.js +8 -0
  64. package/dist/app/field-types/widget-part.js +2 -1
  65. package/dist/app/file-field.js +11 -14
  66. package/dist/app/gallery/gallery-section.js +4 -3
  67. package/dist/app/gallery/lightbox.js +5 -4
  68. package/dist/app/gallery/messages.js +30 -0
  69. package/dist/app/gallery/photo-details.js +3 -2
  70. package/dist/app/hand-order/hand-sorted-list.js +3 -6
  71. package/dist/app/hand-order/messages.js +4 -2
  72. package/dist/app/history/describe-change.js +3 -8
  73. package/dist/app/history/messages.js +32 -0
  74. package/dist/app/import/convert.js +20 -19
  75. package/dist/app/import/failed-rows.js +3 -2
  76. package/dist/app/import/import-button.js +2 -1
  77. package/dist/app/import/import-check-step.js +19 -35
  78. package/dist/app/import/import-dialog.js +13 -17
  79. package/dist/app/import/import-file-step.js +5 -9
  80. package/dist/app/import/import-mapping-step.js +32 -35
  81. package/dist/app/import/import-result-step.js +13 -12
  82. package/dist/app/import/messages.js +200 -0
  83. package/dist/app/import/parse-worker.js +2 -0
  84. package/dist/app/import/prepare-rows.js +5 -4
  85. package/dist/app/import/sheet-cell.js +2 -1
  86. package/dist/app/import/sheet-file.js +3 -2
  87. package/dist/app/import/sheet-source.js +7 -3
  88. package/dist/app/import/xlsx-reader.js +4 -3
  89. package/dist/app/import/xlsx-xml.js +2 -1
  90. package/dist/app/import/zip-reader.js +4 -3
  91. package/dist/app/list-edit/use-list-edits.js +1 -1
  92. package/dist/app/list-group-chart.js +2 -1
  93. package/dist/app/list-page.js +1 -1
  94. package/dist/app/map/map-layer-switch.js +3 -2
  95. package/dist/app/map/messages.js +14 -0
  96. package/dist/app/messages.js +116 -2
  97. package/dist/app/meta-gate.js +1 -1
  98. package/dist/app/nav-from-meta.js +11 -10
  99. package/dist/app/pages/action-inputs/action-inputs-dialog.js +3 -2
  100. package/dist/app/pages/action-inputs/input-answers.js +6 -5
  101. package/dist/app/pages/blocks/card-row.js +3 -2
  102. package/dist/app/pages/blocks/modal-form.js +5 -4
  103. package/dist/app/pages/blocks/record-card.js +3 -2
  104. package/dist/app/pages/blocks/stat.js +2 -1
  105. package/dist/app/pages/composed-page.js +6 -8
  106. package/dist/app/pages/confirm-action.js +2 -1
  107. package/dist/app/pages/custom-boundary.js +4 -2
  108. package/dist/app/pages/messages.js +64 -0
  109. package/dist/app/pages/render-node.js +5 -7
  110. package/dist/app/pages/use-run-action.js +11 -10
  111. package/dist/app/record-page.js +1 -1
  112. package/dist/app/record-row/messages.js +26 -0
  113. package/dist/app/record-row/record-row-body.js +12 -7
  114. package/dist/app/record-row/record-row.js +4 -3
  115. package/dist/app/record-row/saved-record-row.js +3 -2
  116. package/dist/app/related-dots/dots-summary.js +2 -1
  117. package/dist/app/related-dots/messages.js +8 -0
  118. package/dist/app/relation-picker.js +9 -8
  119. package/dist/app/router.js +5 -4
  120. package/dist/app/runtime/messages.js +58 -0
  121. package/dist/app/runtime/runtime-status.js +10 -23
  122. package/dist/app/runtime/runtime-update-dialog.js +7 -6
  123. package/dist/app/runtime/runtime-update.js +4 -3
  124. package/dist/app/saved/favorite-button.js +2 -1
  125. package/dist/app/saved/messages.js +22 -0
  126. package/dist/app/saved/saved-list.js +4 -7
  127. package/dist/app/saved/use-favorite.js +3 -2
  128. package/dist/app/search/messages.js +6 -2
  129. package/dist/app/search/navigation-hits.js +9 -7
  130. package/dist/app/searches/filter-chip-list.js +2 -1
  131. package/dist/app/searches/messages.js +96 -0
  132. package/dist/app/searches/save-search-button.js +5 -4
  133. package/dist/app/searches/saved-search-rows.js +3 -2
  134. package/dist/app/searches/saved-search-section.js +6 -5
  135. package/dist/app/searches/saved-search.js +4 -3
  136. package/dist/app/searches/saved-searches.js +15 -25
  137. package/dist/app/searches/search-controls.js +5 -4
  138. package/dist/app/searches/search-diff.js +4 -2
  139. package/dist/app/searches/search-name-dialog.js +4 -3
  140. package/dist/app/searches/update-search-dialog.js +15 -30
  141. package/dist/app/searches/use-save-search.js +8 -7
  142. package/dist/app/searches/use-search-row.js +4 -3
  143. package/dist/app/sensitive-field.js +5 -4
  144. package/dist/app/source-notice.js +13 -8
  145. package/dist/app/staff-banner.js +12 -19
  146. package/dist/app-shell/global-search.js +2 -2
  147. package/dist/app-shell/messages.js +4 -2
  148. package/dist/assistant-dock/assistant-dock.js +5 -5
  149. package/dist/assistant-dock/messages.js +26 -0
  150. package/dist/assistant-dock/use-assistant.js +2 -1
  151. package/dist/assistant-dock/use-backend-data.js +2 -1
  152. package/dist/assistant-dock/widget-view.js +3 -2
  153. package/dist/auth/auth-gate.js +10 -5
  154. package/dist/auth/auth-locale.js +14 -0
  155. package/dist/auth/auth-messages.js +122 -43
  156. package/dist/auth/auth-provider.js +6 -1
  157. package/dist/auth/code-form.js +2 -1
  158. package/dist/auth/email-code-form.js +11 -13
  159. package/dist/auth/email-code-page.js +2 -1
  160. package/dist/auth/first-run-page.js +6 -5
  161. package/dist/auth/forgot-password-form.js +14 -16
  162. package/dist/auth/forgot-password-page.js +2 -1
  163. package/dist/auth/messages.js +200 -0
  164. package/dist/auth/provider-sign-in-page.js +5 -5
  165. package/dist/auth/resend-code-button.js +2 -1
  166. package/dist/auth/reset-password-form.js +11 -10
  167. package/dist/auth/reset-password-page.js +4 -3
  168. package/dist/auth/setup-required-form.js +7 -9
  169. package/dist/auth/setup-required-page.js +7 -2
  170. package/dist/auth/sign-in-form.js +15 -18
  171. package/dist/auth/sign-in-prompt.js +4 -3
  172. package/dist/auth/sign-up-code-form.js +11 -13
  173. package/dist/auth/sign-up-details-form.js +14 -21
  174. package/dist/auth/sign-up-email-form.js +8 -7
  175. package/dist/auth/sign-up-page.js +2 -1
  176. package/dist/auth/staff-sign-in-form.js +14 -17
  177. package/dist/auth/two-factor-form.js +17 -16
  178. package/dist/auth/user-to-shell.js +2 -1
  179. package/dist/chat/composer.js +8 -7
  180. package/dist/chat/messages.js +14 -0
  181. package/dist/compact-table/compact-table.js +4 -8
  182. package/dist/compact-table/messages.js +18 -0
  183. package/dist/data/meta-data.d.ts +1 -1
  184. package/dist/data/use-update-record.js +1 -1
  185. package/dist/inline-child-table/inline-child-table.js +12 -12
  186. package/dist/inline-child-table/line-row.js +10 -9
  187. package/dist/inline-child-table/messages.js +52 -0
  188. package/dist/inline-child-table/totals-footer.js +5 -8
  189. package/dist/invitations/invite-form.js +3 -2
  190. package/dist/invitations/link-notice.js +6 -4
  191. package/dist/invitations/messages.js +24 -0
  192. package/dist/invitations/pending-invitation-row.js +4 -3
  193. package/dist/lists/messages.js +8 -0
  194. package/dist/lists/paged-search-list.js +2 -1
  195. package/dist/locale/slotted.js +14 -0
  196. package/dist/markdown/markdown-editor.js +6 -5
  197. package/dist/markdown/messages.js +16 -0
  198. package/dist/organizations/all-organizations.js +6 -5
  199. package/dist/organizations/app-roles-picker.js +2 -1
  200. package/dist/organizations/invitation-page.js +39 -49
  201. package/dist/organizations/invitations-page.js +11 -10
  202. package/dist/organizations/members-page.js +15 -10
  203. package/dist/organizations/messages.js +180 -0
  204. package/dist/organizations/organization-page.js +27 -26
  205. package/dist/organizations/organization-paths.js +16 -13
  206. package/dist/organizations/organization-row.js +3 -2
  207. package/dist/organizations/organizations-page.js +17 -16
  208. package/dist/organizations/use-organizations.js +2 -1
  209. package/dist/package.js +1 -1
  210. package/dist/plugins/before-sign-in.js +6 -1
  211. package/dist/plugins/load-plugin.d.ts +1 -1
  212. package/dist/plugins/load-plugin.js +3 -2
  213. package/dist/plugins/messages.js +36 -0
  214. package/dist/plugins/plugin-gate.js +6 -1
  215. package/dist/plugins/plugin-manifest.js +8 -7
  216. package/dist/plugins/plugin-notice.js +8 -12
  217. package/dist/primitives/messages.js +16 -0
  218. package/dist/primitives/sheet.js +2 -1
  219. package/dist/primitives/spinner.js +2 -1
  220. package/dist/primitives/token.js +2 -1
  221. package/dist/record-view/messages.js +4 -2
  222. package/dist/record-view/record-tabs.js +2 -1
  223. package/dist/roles/messages.js +14 -0
  224. package/dist/roles/use-active-role.js +3 -2
  225. package/dist/shell/assets/app-CW3U56yM.js +69 -0
  226. package/dist/shell/assets/cn-Bi0ZEjD9.js +1 -0
  227. package/dist/shell/assets/export-worker-ClACk00b.js +2 -0
  228. package/dist/shell/assets/{index-xY0Mvv6V.js → index-B7cIjCuz.js} +2 -2
  229. package/dist/shell/assets/maplibre-CVG5L9FD.js +814 -0
  230. package/dist/shell/assets/{markdown-content-ukDTCu6u.js → markdown-content-CtGUP4W6.js} +1 -1
  231. package/dist/shell/assets/parse-worker-UhbwEjS0.js +5 -0
  232. package/dist/shell/assets/preload-helper-BX9CcZkY.js +1 -0
  233. package/dist/shell/assets/{record-map-BJmYZ7kQ.js → record-map-Cw3_NcUl.js} +1 -1
  234. package/dist/shell/assets/records-map-D4f3ZeDL.js +1 -0
  235. package/dist/shell/assets/shared-protobase-ui-CB_i50NP.js +1 -0
  236. package/dist/shell/index.html +5 -3
  237. package/dist/shell/protobase.shell.json +1 -1
  238. package/dist/shell/protobase.sw.js +2 -2
  239. package/dist/side-panel/messages.js +8 -0
  240. package/dist/side-panel/side-panel.js +2 -1
  241. package/dist/toasts/messages.js +8 -0
  242. package/dist/toasts/toast.js +2 -1
  243. package/dist/users/app-invitations.js +5 -4
  244. package/dist/users/create-user-form.js +9 -8
  245. package/dist/users/messages.js +52 -0
  246. package/dist/users/use-user-roles.js +2 -1
  247. package/dist/users/user-list.js +6 -5
  248. package/dist/users/user-row.js +5 -4
  249. package/package.json +4 -4
  250. package/dist/shell/assets/app-C-OLrsRR.js +0 -69
  251. package/dist/shell/assets/export-worker-Bz57Pf5g.js +0 -2
  252. package/dist/shell/assets/maplibre-Dov6nhPX.js +0 -814
  253. package/dist/shell/assets/parse-worker-BtcY0dQw.js +0 -5
  254. package/dist/shell/assets/records-map-C1_gDKHR.js +0 -1
  255. package/dist/shell/assets/shared-protobase-ui-CFYJAbMR.js +0 -1
@@ -3,6 +3,7 @@ import { viewerZone } from "../../format/zone.js";
3
3
  import { labelFieldOf } from "../../live/model-helpers.js";
4
4
  import { useAdminMeta } from "../meta-gate.js";
5
5
  import { useAggregate } from "../../data/use-aggregates.js";
6
+ import { text } from "./messages.js";
6
7
  import { periodPoints, valueSlices } from "./chart-groups.js";
7
8
  import { GroupBars } from "./group-bars.js";
8
9
  import { GroupDonut } from "./group-donut.js";
@@ -28,7 +29,7 @@ const labelOf = (field, resources, views) => {
28
29
  * Loads a chart's groups through `:aggregate` and draws them: a date field's buckets as bars or a line, a field's
29
30
  * values as ranked bars or a donut, each value linking to its records. The server checked the chart at startup.
30
31
  */
31
- const ChartView = ({ model, view, groupBy, splitBy, type = "bar", sum, average, filter = "", granularity = "day", range = "30d", limit = 10, empty = "Nothing to chart yet." }) => {
32
+ const ChartView = ({ model, view, groupBy, splitBy, type = "bar", sum, average, filter = "", granularity = "day", range = "30d", limit = 10, empty = text("nothingYet") }) => {
32
33
  const { resources, views } = useAdminMeta();
33
34
  const field = model.fields[groupBy];
34
35
  const dated = field?.type === "date" || field?.type === "timestamp";
@@ -61,7 +62,7 @@ const ChartView = ({ model, view, groupBy, splitBy, type = "bar", sum, average,
61
62
  if (result.isPending) return /* @__PURE__ */ jsx(Skeleton, { className: "h-36 w-full" });
62
63
  if (result.error) return /* @__PURE__ */ jsx("p", {
63
64
  className: "text-danger-text",
64
- children: "Could not load the chart."
65
+ children: text("couldNotLoad")
65
66
  });
66
67
  const { groups } = result.data;
67
68
  if (groups.every((group) => group.count === 0)) return /* @__PURE__ */ jsx("p", {
@@ -1,4 +1,5 @@
1
1
  import { AppLink } from "../use-app-address.js";
2
+ import { text } from "./messages.js";
2
3
  import { measureText } from "./chart-groups.js";
3
4
  import { filteredListAddress } from "../list-link.js";
4
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -16,7 +17,7 @@ const GroupBars = ({ resource, view, slices, measure }) => {
16
17
  return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(AppLink, {
17
18
  to: filteredListAddress(view, resource, slice.filter),
18
19
  className: "group grid grid-cols-[minmax(6rem,35%)_1fr_auto] items-center gap-3 rounded-md px-1 py-1 hover:bg-muted",
19
- title: measure.kind === "count" ? void 0 : `${measureText(slice.count, { kind: "count" })} records`,
20
+ title: measure.kind === "count" ? void 0 : text("records", measureText(slice.count, { kind: "count" })),
20
21
  children: [
21
22
  /* @__PURE__ */ jsx("span", {
22
23
  className: "truncate group-hover:text-primary-text",
@@ -0,0 +1,26 @@
1
+ import { defineMessages } from "../../locale/messages.js";
2
+ //#region src/app/charts/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ notSet: "Not set",
6
+ yes: "Yes",
7
+ no: "No",
8
+ nothingYet: "Nothing to chart yet.",
9
+ couldNotLoad: "Could not load the chart.",
10
+ weekOf: (day) => `Week of ${day}`,
11
+ other: "Other",
12
+ records: (count) => `${count} records`
13
+ },
14
+ nl: {
15
+ notSet: "Niet ingevuld",
16
+ yes: "Ja",
17
+ no: "Nee",
18
+ nothingYet: "Nog niets om in een grafiek te zetten.",
19
+ couldNotLoad: "Kon de grafiek niet laden.",
20
+ weekOf: (day) => `Week van ${day}`,
21
+ other: "Overig",
22
+ records: (count) => `${count} records`
23
+ }
24
+ });
25
+ //#endregion
26
+ export { text };
@@ -1,5 +1,6 @@
1
- import { useMediaQuery } from "../../lib/use-media-query.js";
2
1
  import { formatTag } from "../../locale/active-locale.js";
2
+ import { useMediaQuery } from "../../lib/use-media-query.js";
3
+ import { text } from "./messages.js";
3
4
  import { measureText } from "./chart-groups.js";
4
5
  import { LegendItem } from "./legend-item.js";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -68,7 +69,7 @@ const PointTooltip = ({ active, payload, measure, bucket, series }) => {
68
69
  children: [
69
70
  /* @__PURE__ */ jsx("div", {
70
71
  className: "text-muted-foreground",
71
- children: (bucket === "week" ? "Week of " : "") + formatterOf(tooltipFormats[bucket])(point.at)
72
+ children: bucket === "week" ? text("weekOf", formatterOf(tooltipFormats[bucket])(point.at)) : formatterOf(tooltipFormats[bucket])(point.at)
72
73
  }),
73
74
  series?.map((part) => /* @__PURE__ */ jsxs("div", {
74
75
  className: "flex items-center gap-2",
@@ -1,3 +1,4 @@
1
+ import { text } from "./messages.js";
1
2
  import { colorOf, groupFilter, labelOf, periodPoints } from "./chart-groups.js";
2
3
  //#region src/app/charts/split-points.ts
3
4
  const noise = 1e-6;
@@ -42,7 +43,7 @@ const splitPoints = (groups, splits, field, hints, measure, filter = "") => {
42
43
  return {
43
44
  series: [...series, {
44
45
  key: "other",
45
- label: "Other",
46
+ label: text("other"),
46
47
  color: "var(--border-strong)"
47
48
  }],
48
49
  points
@@ -7,6 +7,7 @@ import { menuKeyTarget } from "../../primitives/menu-keys.js";
7
7
  import { activeMention, insertMention, mentionedIds } from "./mention-text.js";
8
8
  import { useMentionable } from "./use-mentionable.js";
9
9
  import { usePostComment } from "./use-post-comment.js";
10
+ import { text } from "./messages.js";
10
11
  import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
11
12
  import { jsx, jsxs } from "react/jsx-runtime";
12
13
  //#region src/app/comments/comment-composer.tsx
@@ -17,7 +18,7 @@ const shownPeople = 8;
17
18
  * the arrows move, Enter or Tab picks, Escape closes. ⌘/Ctrl+Enter or the button posts.
18
19
  */
19
20
  const CommentComposer = ({ resource, recordKey }) => {
20
- const [text, setText] = useState("");
21
+ const [draft, setDraft] = useState("");
21
22
  const [caret, setCaret] = useState(0);
22
23
  const [focused, setFocused] = useState(false);
23
24
  const [picked, setPicked] = useState([]);
@@ -28,7 +29,7 @@ const CommentComposer = ({ resource, recordKey }) => {
28
29
  const listId = useId();
29
30
  const toast = useToast();
30
31
  const post = usePostComment(resource, recordKey);
31
- const mention = activeMention(text, caret);
32
+ const mention = activeMention(draft, caret);
32
33
  const open = focused && mention !== void 0 && mention.start !== closed;
33
34
  const people = useMentionable(resource, recordKey, open ? mention.query : void 0);
34
35
  const options = open ? (people.data ?? []).slice(0, shownPeople) : [];
@@ -44,27 +45,27 @@ const CommentComposer = ({ resource, recordKey }) => {
44
45
  box.current.setSelectionRange(nextCaret.current, nextCaret.current);
45
46
  setCaret(nextCaret.current);
46
47
  nextCaret.current = void 0;
47
- }, [text]);
48
+ }, [draft]);
48
49
  const pick = (user) => {
49
50
  if (!mention) return;
50
- const next = insertMention(text, mention, user.name);
51
+ const next = insertMention(draft, mention, user.name);
51
52
  nextCaret.current = next.caret;
52
- setText(next.text);
53
+ setDraft(next.text);
53
54
  setPicked((current) => [...current, user]);
54
55
  };
55
56
  const submit = () => {
56
- if (text.trim() === "" || post.isPending) return;
57
+ if (draft.trim() === "" || post.isPending) return;
57
58
  post.mutate({
58
- body: text,
59
- mentions: mentionedIds(text, picked)
59
+ body: draft,
60
+ mentions: mentionedIds(draft, picked)
60
61
  }, {
61
62
  onSuccess: () => {
62
- setText("");
63
+ setDraft("");
63
64
  setPicked([]);
64
65
  },
65
66
  onError: (error) => toast.show({
66
67
  state: "error",
67
- title: "Could not post the comment",
68
+ title: text("couldNotPost"),
68
69
  description: refusalReason(error)
69
70
  })
70
71
  });
@@ -100,16 +101,16 @@ const CommentComposer = ({ resource, recordKey }) => {
100
101
  children: [/* @__PURE__ */ jsx(Textarea, {
101
102
  ref: box,
102
103
  role: "combobox",
103
- "aria-label": "Comment",
104
+ "aria-label": text("comment"),
104
105
  "aria-multiline": true,
105
106
  "aria-autocomplete": "list",
106
107
  "aria-expanded": listed,
107
108
  "aria-controls": listId,
108
109
  "aria-activedescendant": listed && options[active] ? optionId(active) : void 0,
109
- placeholder: "Write a comment, @ to mention someone",
110
- value: text,
110
+ placeholder: text("placeholder"),
111
+ value: draft,
111
112
  onChange: (event) => {
112
- setText(event.target.value);
113
+ setDraft(event.target.value);
113
114
  setCaret(event.target.selectionStart);
114
115
  },
115
116
  onSelect: (event) => setCaret(event.currentTarget.selectionStart),
@@ -119,7 +120,7 @@ const CommentComposer = ({ resource, recordKey }) => {
119
120
  }), listed && /* @__PURE__ */ jsxs("ul", {
120
121
  id: listId,
121
122
  role: "listbox",
122
- "aria-label": "People to mention",
123
+ "aria-label": text("peopleToMention"),
123
124
  className: "absolute z-30 mt-1 w-full max-w-sm rounded-md border bg-background py-1 shadow-pop",
124
125
  onMouseDown: (event) => event.preventDefault(),
125
126
  children: [options.map((user, index) => /* @__PURE__ */ jsx("li", {
@@ -132,16 +133,16 @@ const CommentComposer = ({ resource, recordKey }) => {
132
133
  children: user.name
133
134
  }, user.id)), options.length === 0 && /* @__PURE__ */ jsx("li", {
134
135
  className: "px-3 py-1.5 text-muted-foreground",
135
- children: "No one found"
136
+ children: text("noOneFound")
136
137
  })]
137
138
  })]
138
139
  }), /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, {
139
140
  variant: "primary",
140
141
  size: "sm",
141
- disabled: text.trim() === "",
142
+ disabled: draft.trim() === "",
142
143
  loading: post.isPending,
143
144
  onClick: submit,
144
- children: "Comment"
145
+ children: text("comment")
145
146
  }) })]
146
147
  });
147
148
  };
@@ -6,6 +6,7 @@ import { hueOf, initialsOf } from "../../auth/user-to-shell.js";
6
6
  import { Section } from "../../record-view/section.js";
7
7
  import { ErrorBanner } from "../error-banner.js";
8
8
  import { CommentBody } from "./comment-body.js";
9
+ import { text } from "./messages.js";
9
10
  import { CommentComposer } from "./comment-composer.js";
10
11
  import { useRecordComments } from "./use-record-comments.js";
11
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -51,17 +52,17 @@ const CommentsSection = ({ item, resource, recordKey, canWrite }) => {
51
52
  return /* @__PURE__ */ jsxs(Section, {
52
53
  title: item.title,
53
54
  children: [comments.isPending ? /* @__PURE__ */ jsx(Loading, {}) : comments.error ? /* @__PURE__ */ jsx(ErrorBanner, {
54
- title: "Could not load the comments",
55
+ title: text("couldNotLoad"),
55
56
  error: comments.error
56
57
  }) : /* @__PURE__ */ jsxs(Fragment, { children: [comments.hasNextPage && /* @__PURE__ */ jsx(Button, {
57
58
  size: "sm",
58
59
  className: "mb-4",
59
60
  disabled: comments.isFetchingNextPage,
60
61
  onClick: () => void comments.fetchNextPage(),
61
- children: "Show earlier comments"
62
+ children: text("showEarlier")
62
63
  }), comments.items.length === 0 ? /* @__PURE__ */ jsx("p", {
63
64
  className: "text-muted-foreground",
64
- children: "No comments yet."
65
+ children: text("noComments")
65
66
  }) : /* @__PURE__ */ jsx("ol", {
66
67
  className: "divide-y divide-border",
67
68
  children: comments.items.map((comment) => /* @__PURE__ */ jsx(Comment, { comment }, comment.id))
@@ -0,0 +1,26 @@
1
+ import { defineMessages } from "../../locale/messages.js";
2
+ //#region src/app/comments/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ couldNotPost: "Could not post the comment",
6
+ comment: "Comment",
7
+ placeholder: "Write a comment, @ to mention someone",
8
+ peopleToMention: "People to mention",
9
+ noOneFound: "No one found",
10
+ couldNotLoad: "Could not load the comments",
11
+ showEarlier: "Show earlier comments",
12
+ noComments: "No comments yet."
13
+ },
14
+ nl: {
15
+ couldNotPost: "Kon de opmerking niet plaatsen",
16
+ comment: "Opmerking",
17
+ placeholder: "Schrijf een opmerking, @ om iemand te noemen",
18
+ peopleToMention: "Mensen om te noemen",
19
+ noOneFound: "Niemand gevonden",
20
+ couldNotLoad: "Kon de opmerkingen niet laden",
21
+ showEarlier: "Eerdere opmerkingen tonen",
22
+ noComments: "Nog geen opmerkingen."
23
+ }
24
+ });
25
+ //#endregion
26
+ export { text };
@@ -1,9 +1,10 @@
1
1
  import { useClient } from "../../data/api-provider.js";
2
+ import { text } from "../messages.js";
2
3
  import { humanize } from "../../live/naming.js";
3
4
  import { recordKey } from "../../live/model-helpers.js";
4
5
  import { Notice, useAdminMeta, usePermissions } from "../meta-gate.js";
5
6
  import { useAppAddress } from "../use-app-address.js";
6
- import { text } from "./messages.js";
7
+ import { text as text$1 } from "./messages.js";
7
8
  import { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isRequired } from "./create-values.js";
8
9
  import { FieldEditor } from "../field-editor.js";
9
10
  import { fieldErrors, fieldMessage } from "../field-errors.js";
@@ -13,7 +14,7 @@ import { fetchLabels } from "../../live/relation-labels.js";
13
14
  import { useLiveSave } from "../use-live-save.js";
14
15
  import { CreateRecordForm } from "./create-record-form.js";
15
16
  import { useEffect, useState } from "react";
16
- import { jsx, jsxs } from "react/jsx-runtime";
17
+ import { jsx } from "react/jsx-runtime";
17
18
  import { useQueryClient } from "@tanstack/react-query";
18
19
  //#region src/app/create/create-page.tsx
19
20
  const without = (source, name) => Object.fromEntries(Object.entries(source).filter(([key]) => key !== name));
@@ -22,21 +23,13 @@ const CreatePage = ({ resource, values }) => {
22
23
  const meta = useAdminMeta();
23
24
  const model = meta.resources[resource];
24
25
  const permissions = usePermissions(resource);
25
- if (!model) return /* @__PURE__ */ jsxs(Notice, {
26
- title: "Not found",
27
- children: [
28
- "There is no resource called ",
29
- resource,
30
- "."
31
- ]
26
+ if (!model) return /* @__PURE__ */ jsx(Notice, {
27
+ title: text("notFound"),
28
+ children: text("noResource", resource)
32
29
  });
33
- if (!can(permissions, "create")) return /* @__PURE__ */ jsxs(Notice, {
34
- title: "Not allowed",
35
- children: [
36
- "You cannot create ",
37
- meta.views[resource]?.names?.plural.toLowerCase() ?? resource,
38
- "."
39
- ]
30
+ if (!can(permissions, "create")) return /* @__PURE__ */ jsx(Notice, {
31
+ title: text$1("notAllowed"),
32
+ children: text$1("cannotCreate", meta.views[resource]?.names?.plural.toLowerCase() ?? resource)
40
33
  });
41
34
  return /* @__PURE__ */ jsx(LoadedCreate, {
42
35
  resource,
@@ -51,7 +44,7 @@ const LoadedCreate = ({ resource, values }) => {
51
44
  const queryClient = useQueryClient();
52
45
  const { go } = useAppAddress();
53
46
  const noun = view?.names?.singular ?? humanize(resource);
54
- const feedback = useLiveSave(view?.saveFeedback ?? "toast", text("subject", noun));
47
+ const feedback = useLiveSave(view?.saveFeedback ?? "toast", text$1("subject", noun));
55
48
  const sections = createSections(model, view);
56
49
  const [draft, setDraft] = useState(() => initialDraft(model, sections, values));
57
50
  const [errors, setErrors] = useState({});
@@ -132,7 +125,7 @@ const LoadedCreate = ({ resource, values }) => {
132
125
  value: isChosen(value) ? value.key : value ?? (field.type === "boolean" ? false : ""),
133
126
  stored: void 0,
134
127
  relationLabel: isChosen(value) ? value.label : void 0,
135
- placeholder: hasDatabaseDefault(field) ? text("setByDatabase") : void 0,
128
+ placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : void 0,
136
129
  relationPicker: target ? {
137
130
  target,
138
131
  view: meta.views[target.name]
@@ -1,3 +1,5 @@
1
+ import { text } from "./messages.js";
2
+ import { slotted } from "../locale/slotted.js";
1
3
  import { Problem } from "./pages/problem.js";
2
4
  import { CustomBoundary } from "./pages/custom-boundary.js";
3
5
  import { Section } from "../record-view/section.js";
@@ -5,7 +7,7 @@ import { SidebarCard } from "../record-view/record-sidebar.js";
5
7
  import { useProjectUi } from "./pages/project-ui.js";
6
8
  import { RecordScope } from "./pages/record-scope.js";
7
9
  import { unmarkFieldRefs } from "@protobase/schema/internal";
8
- import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { jsx } from "react/jsx-runtime";
9
11
  //#region src/app/custom-section.tsx
10
12
  /**
11
13
  * A component of the record layout (`l.custom`), drawn with the record around it, so `useLayoutRecord()` gives it, in a
@@ -20,11 +22,7 @@ const CustomSection = ({ item, scope }) => {
20
22
  name: item.component,
21
23
  children: /* @__PURE__ */ jsx(Component, { ...unmarkFieldRefs(item.props ?? {}) })
22
24
  })
23
- }) : /* @__PURE__ */ jsxs(Problem, { children: [
24
- "No component is registered as ",
25
- item.component,
26
- ". Export it from protobase.ui.tsx, or load the plugin that provides it."
27
- ] });
25
+ }) : /* @__PURE__ */ jsx(Problem, { children: slotted(text("noComponent"), item.component) });
28
26
  if (item.sidebar) return item.title ? /* @__PURE__ */ jsx(SidebarCard, {
29
27
  title: item.title,
30
28
  children: body
@@ -1,8 +1,9 @@
1
- import { cn } from "../../lib/cn.js";
2
1
  import { formatTag } from "../../locale/active-locale.js";
2
+ import { cn } from "../../lib/cn.js";
3
3
  import { Skeleton } from "../../primitives/skeleton.js";
4
4
  import { Button } from "../../primitives/button.js";
5
5
  import { humanize } from "../../live/naming.js";
6
+ import { text } from "./messages.js";
6
7
  import { boardCard } from "./board-columns.js";
7
8
  import { ErrorBanner } from "../error-banner.js";
8
9
  import { rowAllows } from "../permissions.js";
@@ -75,11 +76,11 @@ const BoardColumnView = ({ model, view, display, column, fields, filter, orderBy
75
76
  })]
76
77
  }),
77
78
  list.loading ? Array.from({ length: 3 }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "h-20" }, index)) : list.error ? /* @__PURE__ */ jsx(ErrorBanner, {
78
- title: "Could not load rows",
79
+ title: text("couldNotLoadRows"),
79
80
  error: list.error
80
- }) : rows.length === 0 ? /* @__PURE__ */ jsxs("p", {
81
+ }) : rows.length === 0 ? /* @__PURE__ */ jsx("p", {
81
82
  className: "px-1 py-4 text-center text-xs text-muted-foreground",
82
- children: ["No ", plural]
83
+ children: text("noPlural", plural)
83
84
  }) : /* @__PURE__ */ jsx(SortableContext, {
84
85
  id: columnId(column.value),
85
86
  items: rows.map((row) => row.key),
@@ -103,7 +104,7 @@ const BoardColumnView = ({ model, view, display, column, fields, filter, orderBy
103
104
  size: "sm",
104
105
  variant: "ghost",
105
106
  onClick: list.loadMore,
106
- children: "Show more"
107
+ children: text("showMore")
107
108
  })
108
109
  ]
109
110
  });
@@ -1,5 +1,6 @@
1
1
  import { humanize } from "../../live/naming.js";
2
2
  import { recordTitleField } from "../../live/model-helpers.js";
3
+ import { text } from "./messages.js";
3
4
  import { isApiError } from "@protobase/client";
4
5
  import { enumLabel, printFilter, where } from "@protobase/schema";
5
6
  //#region src/app/displays/board-columns.ts
@@ -18,7 +19,7 @@ const boardColumns = (model, view, display) => {
18
19
  if (!field.nullable) return values;
19
20
  return [...values, {
20
21
  value: null,
21
- label: `No ${(hints?.label ?? humanize(display.by)).toLowerCase()}`
22
+ label: text("noValue", hints?.label ?? humanize(display.by))
22
23
  }];
23
24
  };
24
25
  /** The field a board orders each column by: the resource's `.position()`, when its records are ordered as one rather than within a parent. */
@@ -39,7 +40,7 @@ const boardCard = (model, view, display) => ({
39
40
  });
40
41
  /** Why the server refused a move: the reasons it gives per field, such as a `validate` message, else its detail. */
41
42
  const refusalReason = (error) => {
42
- if (!isApiError(error)) return error instanceof Error ? error.message : "Something went wrong";
43
+ if (!isApiError(error)) return error instanceof Error ? error.message : text("somethingWrong");
43
44
  const reasons = error.errors.map((entry) => entry.message);
44
45
  return reasons.length > 0 ? reasons.join("; ") : error.message;
45
46
  };
@@ -1,4 +1,6 @@
1
+ import { slotted } from "../../locale/slotted.js";
1
2
  import { Problem } from "../pages/problem.js";
3
+ import { text } from "./messages.js";
2
4
  import { boardCard, boardColumns, boardRank } from "./board-columns.js";
3
5
  import { usePermissions } from "../meta-gate.js";
4
6
  import { useProjectUi } from "../pages/project-ui.js";
@@ -73,23 +75,19 @@ const BoardDisplay = ({ model, view, display, fields, filter, orderBy, limit })
73
75
  },
74
76
  coordinateGetter: cardCoordinates
75
77
  }));
76
- if (dropName !== void 0 && !drop) return /* @__PURE__ */ jsxs(Problem, { children: [
77
- "No board drop is registered as ",
78
- dropName,
79
- ". Give it in boardDrops in protobase.ui.tsx, or load the plugin that provides it."
80
- ] });
78
+ if (dropName !== void 0 && !drop) return /* @__PURE__ */ jsx(Problem, { children: slotted(text("noDrop"), dropName) });
81
79
  const title = (row) => String(row.labels[card.title] ?? row.record[card.title] ?? row.key);
82
80
  const columnOf = (id) => columns.find((column) => columnId(column.value) === id);
83
81
  const valueOf = (row) => row.record[display.by] ?? null;
84
82
  const crosses = (row, to) => takes(valueOf(row)) && takes(to.value);
85
83
  const announced = (row, over) => {
86
- if (!over) return { over: "no column" };
84
+ if (!over) return { over: text("noColumn") };
87
85
  const place = placeable ? placeOver(over, row) : void 0;
88
- if (!place) return { over: columnOf(over.id)?.label ?? "no column" };
86
+ if (!place) return { over: columnOf(over.id)?.label ?? text("noColumn") };
89
87
  const count = place.rows.some((entry) => entry.key === row.key) ? place.rows.length : place.rows.length + 1;
90
88
  return {
91
- over: `${place.column.label}, place ${place.index + 1} of ${count}`,
92
- place: `${place.column.label}, place ${place.index + 1}`
89
+ over: text("placeOf", place.column.label, place.index + 1, count),
90
+ place: text("place", place.column.label, place.index + 1)
93
91
  };
94
92
  };
95
93
  const refused = (row, over) => {
@@ -97,15 +95,15 @@ const BoardDisplay = ({ model, view, display, fields, filter, orderBy, limit })
97
95
  return target !== void 0 && target.value !== valueOf(row) && !crosses(row, target);
98
96
  };
99
97
  const announcements = {
100
- onDragStart: ({ active: picked }) => `Picked up ${title(picked.data.current.row)}.`,
101
- onDragOver: ({ active: picked, over }) => `${title(picked.data.current.row)} is over ${announced(picked.data.current.row, over).over}.`,
98
+ onDragStart: ({ active: picked }) => text("pickedUp", title(picked.data.current.row)),
99
+ onDragOver: ({ active: picked, over }) => text("isOver", title(picked.data.current.row), announced(picked.data.current.row, over).over),
102
100
  onDragEnd: ({ active: picked, over }) => {
103
101
  const row = picked.data.current.row;
104
- if (!over || refused(row, over)) return `${title(row)} was put back.`;
102
+ if (!over || refused(row, over)) return text("putBack", title(row));
105
103
  const said = announced(row, over);
106
- return `${title(row)} was moved to ${said.place ?? said.over}.`;
104
+ return text("wasMovedTo", title(row), said.place ?? said.over);
107
105
  },
108
- onDragCancel: ({ active: picked }) => `Moving ${title(picked.data.current.row)} was cancelled.`
106
+ onDragCancel: ({ active: picked }) => text("cancelled", title(picked.data.current.row))
109
107
  };
110
108
  const start = ({ active: picked }) => {
111
109
  dragged.current = true;
@@ -144,7 +142,7 @@ const BoardDisplay = ({ model, view, display, fields, filter, orderBy, limit })
144
142
  },
145
143
  accessibility: {
146
144
  announcements,
147
- screenReaderInstructions: { draggable: !placeable ? "To move a card to another column, press Space, move it with the left and right arrow keys, and press Space again to drop it there. Escape puts it back." : movable ? "To move a card, press Space, move it with the up and down arrow keys within its column and the left and right arrow keys to another column, and press Space again to drop it there. Escape puts it back." : "To move a card, press Space, move it with the up and down arrow keys within its column, and press Space again to drop it there. Escape puts it back." }
145
+ screenReaderInstructions: { draggable: !placeable ? text("boardAcross") : movable ? text("boardAnywhere") : text("boardWithin") }
148
146
  },
149
147
  children: [/* @__PURE__ */ jsx("div", {
150
148
  className: "relative min-w-0 flex-1 overflow-x-auto",
@@ -1,5 +1,6 @@
1
1
  import { dayIn, viewerZone } from "../../format/zone.js";
2
2
  import { formatDayRange, formatMonth } from "../../format/date.js";
3
+ import { text } from "./messages.js";
3
4
  import { calendarView, gridDays, stepView, todayView } from "./calendar-range.js";
4
5
  import { PeriodControls } from "./period-controls.js";
5
6
  import { jsx } from "react/jsx-runtime";
@@ -28,8 +29,8 @@ const CalendarControls = ({ view, now, linkTo, onView }) => {
28
29
  previous: stepView(view, -1),
29
30
  today: todayView(view.scale, now),
30
31
  next: stepView(view, 1),
31
- scales: [scale("month", "Month"), scale("week", "Week")],
32
- scalesLabel: "Calendar scale",
32
+ scales: [scale("month", text("month")), scale("week", text("week"))],
33
+ scalesLabel: text("calendarScale"),
33
34
  linkTo,
34
35
  onView
35
36
  });
@@ -2,6 +2,7 @@ import { cn } from "../../lib/cn.js";
2
2
  import { Skeleton } from "../../primitives/skeleton.js";
3
3
  import { dayIn, viewerZone } from "../../format/zone.js";
4
4
  import { formatDate, formatWeekday } from "../../format/date.js";
5
+ import { text } from "./messages.js";
5
6
  import { usePermissions } from "../meta-gate.js";
6
7
  import { AppLink } from "../use-app-address.js";
7
8
  import { ErrorBanner } from "../error-banner.js";
@@ -57,10 +58,10 @@ const CalendarDisplay = ({ model, view, display, fields, filter, at, linkTo, now
57
58
  const dayText = (id) => formatDate(Date.parse(`${String(id).slice(dayId("").length)}T00:00:00Z`));
58
59
  const title = (picked) => eventTitle(model, view, display, picked.row);
59
60
  const announcements = {
60
- onDragStart: ({ active: picked }) => `Picked up ${title(picked.data.current)}.`,
61
- onDragOver: ({ active: picked, over }) => `${title(picked.data.current)} is over ${over ? dayText(over.id) : "no day"}.`,
62
- onDragEnd: ({ active: picked, over }) => over ? `${title(picked.data.current)} was moved to ${dayText(over.id)}.` : `${title(picked.data.current)} was put back.`,
63
- onDragCancel: ({ active: picked }) => `Moving ${title(picked.data.current)} was cancelled.`
61
+ onDragStart: ({ active: picked }) => text("pickedUp", title(picked.data.current)),
62
+ onDragOver: ({ active: picked, over }) => text("isOver", title(picked.data.current), over ? dayText(over.id) : text("noDay")),
63
+ onDragEnd: ({ active: picked, over }) => over ? text("wasMovedTo", title(picked.data.current), dayText(over.id)) : text("putBack", title(picked.data.current)),
64
+ onDragCancel: ({ active: picked }) => text("cancelled", title(picked.data.current))
64
65
  };
65
66
  const release = () => {
66
67
  setActive(void 0);
@@ -81,17 +82,17 @@ const CalendarDisplay = ({ model, view, display, fields, filter, at, linkTo, now
81
82
  scale: "week",
82
83
  day
83
84
  }, now);
84
- const text = `+${hidden} more`;
85
+ const label = text("moreHidden", hidden);
85
86
  const className = "self-start rounded px-1 text-xs text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-primary";
86
87
  return linkTo ? /* @__PURE__ */ jsx(AppLink, {
87
88
  to: linkTo(to),
88
89
  className,
89
- children: text
90
+ children: label
90
91
  }) : /* @__PURE__ */ jsx("button", {
91
92
  type: "button",
92
93
  className,
93
94
  onClick: () => setLocal(to),
94
- children: text
95
+ children: label
95
96
  });
96
97
  };
97
98
  return /* @__PURE__ */ jsxs("div", {
@@ -104,18 +105,12 @@ const CalendarDisplay = ({ model, view, display, fields, filter, at, linkTo, now
104
105
  onView: setLocal
105
106
  }),
106
107
  list.error && /* @__PURE__ */ jsx(ErrorBanner, {
107
- title: "Could not load rows",
108
+ title: text("couldNotLoadRows"),
108
109
  error: list.error
109
110
  }),
110
- list.capped && /* @__PURE__ */ jsxs("p", {
111
+ list.capped && /* @__PURE__ */ jsx("p", {
111
112
  className: "text-xs text-muted-foreground",
112
- children: [
113
- "Showing the first ",
114
- 500,
115
- " records of this ",
116
- shown.scale,
117
- ". Narrow the filter to see the rest."
118
- ]
113
+ children: text("capped", 500, shown.scale)
119
114
  }),
120
115
  /* @__PURE__ */ jsxs(DndContext, {
121
116
  sensors,
@@ -125,7 +120,7 @@ const CalendarDisplay = ({ model, view, display, fields, filter, at, linkTo, now
125
120
  onDragCancel: release,
126
121
  accessibility: {
127
122
  announcements,
128
- screenReaderInstructions: { draggable: "To move an event to another day, press Space, move it a day with the left and right arrow keys or a week with the up and down arrow keys, and press Space again to drop it there. Escape puts it back." }
123
+ screenReaderInstructions: { draggable: text("calendarInstructions") }
129
124
  },
130
125
  children: [/* @__PURE__ */ jsxs("div", {
131
126
  className: "overflow-hidden rounded-lg border border-border",
@@ -1,4 +1,5 @@
1
1
  import { cn } from "../../lib/cn.js";
2
+ import { text } from "./messages.js";
2
3
  import { CardImageFrame, CardPhoto } from "./card-image.js";
3
4
  import { cardPhotoList, scrolledPhoto, shownPhoto } from "./card-photos.js";
4
5
  import { useRef, useState } from "react";
@@ -51,7 +52,7 @@ const CardCarousel = ({ photos, cover, swipe = true, children }) => {
51
52
  children: [
52
53
  /* @__PURE__ */ jsx("button", {
53
54
  type: "button",
54
- "aria-label": "Previous photo",
55
+ "aria-label": text("previousPhoto"),
55
56
  className: cn(arrow, !swipe && touchArrow, "left-2"),
56
57
  onClick: () => press(-1),
57
58
  children: /* @__PURE__ */ jsx(ChevronLeft, {
@@ -61,7 +62,7 @@ const CardCarousel = ({ photos, cover, swipe = true, children }) => {
61
62
  }),
62
63
  /* @__PURE__ */ jsx("button", {
63
64
  type: "button",
64
- "aria-label": "Next photo",
65
+ "aria-label": text("nextPhoto"),
65
66
  className: cn(arrow, !swipe && touchArrow, "right-2"),
66
67
  onClick: () => press(1),
67
68
  children: /* @__PURE__ */ jsx(ChevronRight, {
@@ -74,7 +75,7 @@ const CardCarousel = ({ photos, cover, swipe = true, children }) => {
74
75
  "aria-live": "polite",
75
76
  children: [/* @__PURE__ */ jsx("span", {
76
77
  className: "sr-only",
77
- children: `Photo ${index + 1} of ${list.length}`
78
+ children: text("photoOf", index + 1, list.length)
78
79
  }), /* @__PURE__ */ jsx("span", {
79
80
  className: "flex items-center gap-1 rounded-full bg-background/70 px-1.5 py-1 leading-none",
80
81
  "aria-hidden": true,
@@ -1,6 +1,7 @@
1
1
  import { cn } from "../../lib/cn.js";
2
2
  import { Skeleton } from "../../primitives/skeleton.js";
3
3
  import { humanize } from "../../live/naming.js";
4
+ import { text } from "./messages.js";
4
5
  import { RecordCard } from "./record-card.js";
5
6
  import { DisplayFooter, DisplayState } from "./display-frame.js";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -12,7 +13,7 @@ const CardsDisplay = ({ model, view, display, list, row = false, footer = true }
12
13
  const plural = (view?.names?.plural ?? humanize(model.name)).toLowerCase();
13
14
  return /* @__PURE__ */ jsxs(DisplayState, {
14
15
  list,
15
- empty: `No ${plural} match.`,
16
+ empty: text("noneMatch", plural),
16
17
  loading: /* @__PURE__ */ jsx("div", {
17
18
  className: grid,
18
19
  children: Array.from({ length: 6 }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "h-24" }, index))