@eifi1/ui-kit 0.22.0 → 0.23.0

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 (205) hide show
  1. package/README.md +42 -25
  2. package/dist/chart.d.ts +11 -8
  3. package/dist/components/amount-input.d.ts +28 -8
  4. package/dist/components/amount-input.js +3 -1
  5. package/dist/components/amount-input.js.map +1 -1
  6. package/dist/components/autocomplete.d.ts +21 -0
  7. package/dist/components/autocomplete.js +128 -103
  8. package/dist/components/autocomplete.js.map +1 -1
  9. package/dist/components/button-group.d.ts +11 -8
  10. package/dist/components/calculator.d.ts +11 -8
  11. package/dist/components/column-mapper.d.ts +182 -0
  12. package/dist/components/column-mapper.js +375 -0
  13. package/dist/components/column-mapper.js.map +1 -0
  14. package/dist/components/combobox-core.d.ts +39 -2
  15. package/dist/components/combobox-core.js +29 -6
  16. package/dist/components/combobox-core.js.map +1 -1
  17. package/dist/components/combobox.d.ts +80 -2
  18. package/dist/components/combobox.js +448 -311
  19. package/dist/components/combobox.js.map +1 -1
  20. package/dist/components/confirm-dialog.d.ts +11 -8
  21. package/dist/components/confirm-dialog.js +7 -8
  22. package/dist/components/confirm-dialog.js.map +1 -1
  23. package/dist/components/copy-button.d.ts +8 -5
  24. package/dist/components/country-select.d.ts +50 -7
  25. package/dist/components/country-select.js +65 -9
  26. package/dist/components/country-select.js.map +1 -1
  27. package/dist/components/danger-confirm.d.ts +11 -8
  28. package/dist/components/danger-confirm.js +13 -3
  29. package/dist/components/danger-confirm.js.map +1 -1
  30. package/dist/components/date-picker.d.ts +41 -0
  31. package/dist/components/date-picker.js +269 -8
  32. package/dist/components/date-picker.js.map +1 -1
  33. package/dist/components/entity-combobox.d.ts +35 -1
  34. package/dist/components/entity-combobox.js +148 -112
  35. package/dist/components/entity-combobox.js.map +1 -1
  36. package/dist/components/facing-pair.d.ts +11 -8
  37. package/dist/components/field-parts.d.ts +116 -6
  38. package/dist/components/field-parts.js +84 -0
  39. package/dist/components/field-parts.js.map +1 -1
  40. package/dist/components/field-strip.d.ts +105 -0
  41. package/dist/components/field-strip.js +46 -0
  42. package/dist/components/field-strip.js.map +1 -0
  43. package/dist/components/file-button.d.ts +11 -8
  44. package/dist/components/file-dropzone.d.ts +11 -8
  45. package/dist/components/form-actions.d.ts +10 -7
  46. package/dist/components/form-actions.js +8 -2
  47. package/dist/components/form-actions.js.map +1 -1
  48. package/dist/components/iban-input.d.ts +11 -8
  49. package/dist/components/icon-picker.d.ts +10 -1
  50. package/dist/components/icon-picker.js +12 -4
  51. package/dist/components/icon-picker.js.map +1 -1
  52. package/dist/components/language-select.d.ts +11 -8
  53. package/dist/components/money-field.d.ts +14 -8
  54. package/dist/components/money-field.js.map +1 -1
  55. package/dist/components/month-picker.js +2 -1
  56. package/dist/components/month-picker.js.map +1 -1
  57. package/dist/components/multi-entity-combobox.d.ts +26 -1
  58. package/dist/components/multi-entity-combobox.js +143 -102
  59. package/dist/components/multi-entity-combobox.js.map +1 -1
  60. package/dist/components/number-field.d.ts +11 -8
  61. package/dist/components/number-input.d.ts +11 -8
  62. package/dist/components/numpad-sheet.d.ts +11 -8
  63. package/dist/components/phone-input.d.ts +11 -8
  64. package/dist/components/reauth-dialog.d.ts +2 -2
  65. package/dist/components/reauth-dialog.js +6 -8
  66. package/dist/components/reauth-dialog.js.map +1 -1
  67. package/dist/components/series-chart.d.ts +11 -8
  68. package/dist/components/settings-fields.d.ts +11 -8
  69. package/dist/components/share-card.d.ts +10 -7
  70. package/dist/components/swatch-picker.d.ts +17 -1
  71. package/dist/components/swatch-picker.js +10 -4
  72. package/dist/components/swatch-picker.js.map +1 -1
  73. package/dist/components/text-link.d.ts +10 -7
  74. package/dist/components/tile-radio.d.ts +20 -0
  75. package/dist/components/tile-radio.js +25 -8
  76. package/dist/components/tile-radio.js.map +1 -1
  77. package/dist/components/time-input.d.ts +11 -8
  78. package/dist/components/toggle-group.js +2 -2
  79. package/dist/components/toggle-group.js.map +1 -1
  80. package/dist/components/trigger-aria.d.ts +3 -0
  81. package/dist/components/trigger-aria.js +3 -1
  82. package/dist/components/trigger-aria.js.map +1 -1
  83. package/dist/components/ui.d.ts +8 -5
  84. package/dist/components/ui.js +5 -11
  85. package/dist/components/ui.js.map +1 -1
  86. package/dist/feedback/feedback-attachment.d.ts +1 -1
  87. package/dist/feedback/feedback-attachment.js +156 -56
  88. package/dist/feedback/feedback-attachment.js.map +1 -1
  89. package/dist/feedback/feedback-dialog.d.ts +1 -1
  90. package/dist/feedback/feedback-dialog.js.map +1 -1
  91. package/dist/feedback/feedback-inbox.d.ts +3 -2
  92. package/dist/feedback/feedback-inbox.js.map +1 -1
  93. package/dist/feedback/feedback-thread.d.ts +68 -287
  94. package/dist/feedback/feedback-thread.js +8 -1
  95. package/dist/feedback/feedback-thread.js.map +1 -1
  96. package/dist/{kit-labels-R8mIAc9N.d.ts → feedback-BxeQVzwq.d.ts} +405 -22
  97. package/dist/{feedback-attachment-NhUm7Zga.d.ts → feedback-attachment-fGAzZPf0.d.ts} +112 -10
  98. package/dist/feedback.d.ts +66 -2
  99. package/dist/hooks/use-file-drop.d.ts +11 -8
  100. package/dist/i18n/defaults.d.ts +11 -8
  101. package/dist/i18n/defaults.js +3 -1
  102. package/dist/i18n/defaults.js.map +1 -1
  103. package/dist/i18n/german.d.ts +11 -8
  104. package/dist/i18n/german.js +36 -2
  105. package/dist/i18n/german.js.map +1 -1
  106. package/dist/i18n/kit-labels.d.ts +7 -4
  107. package/dist/i18n/kit-labels.js.map +1 -1
  108. package/dist/i18n/languages.d.ts +11 -8
  109. package/dist/i18n/locales/de-CH.d.ts +11 -8
  110. package/dist/i18n/locales/en.d.ts +11 -8
  111. package/dist/i18n/locales/en.js +7 -0
  112. package/dist/i18n/locales/en.js.map +1 -1
  113. package/dist/i18n/locales/es.d.ts +11 -8
  114. package/dist/i18n/locales/es.js +37 -2
  115. package/dist/i18n/locales/es.js.map +1 -1
  116. package/dist/i18n/locales/fr.d.ts +11 -8
  117. package/dist/i18n/locales/fr.js +36 -2
  118. package/dist/i18n/locales/fr.js.map +1 -1
  119. package/dist/i18n/locales/hu.d.ts +11 -8
  120. package/dist/i18n/locales/hu.js +38 -2
  121. package/dist/i18n/locales/hu.js.map +1 -1
  122. package/dist/i18n/locales/it.d.ts +11 -8
  123. package/dist/i18n/locales/it.js +37 -2
  124. package/dist/i18n/locales/it.js.map +1 -1
  125. package/dist/i18n/locales/zh.d.ts +11 -8
  126. package/dist/i18n/locales/zh.js +34 -2
  127. package/dist/i18n/locales/zh.js.map +1 -1
  128. package/dist/i18n/review.d.ts +11 -8
  129. package/dist/i18n/review.js +20 -1
  130. package/dist/i18n/review.js.map +1 -1
  131. package/dist/index.d.ts +7 -3
  132. package/dist/index.js +16 -0
  133. package/dist/index.js.map +1 -1
  134. package/dist/lib/column-mapping.d.ts +81 -0
  135. package/dist/lib/column-mapping.js +108 -0
  136. package/dist/lib/column-mapping.js.map +1 -0
  137. package/dist/lib/table-text.d.ts +109 -1
  138. package/dist/lib/table-text.js +122 -1
  139. package/dist/lib/table-text.js.map +1 -1
  140. package/dist/rhf/fields.d.ts +89 -11
  141. package/dist/rhf/fields.js +124 -0
  142. package/dist/rhf/fields.js.map +1 -1
  143. package/dist/rhf/form.d.ts +11 -8
  144. package/dist/rhf.d.ts +12 -9
  145. package/dist/rhf.js.map +1 -1
  146. package/dist/shell/app-shell.d.ts +10 -7
  147. package/dist/shell/top-bar-brand.d.ts +11 -8
  148. package/dist/shell/topbar-action-menu.d.ts +36 -3
  149. package/dist/shell/topbar-action-menu.js +74 -33
  150. package/dist/shell/topbar-action-menu.js.map +1 -1
  151. package/dist/shell.d.ts +10 -7
  152. package/dist/table-text.d.ts +1 -1
  153. package/dist/wizard/stepper-nav.d.ts +41 -10
  154. package/dist/wizard/stepper-nav.js +4 -0
  155. package/dist/wizard/stepper-nav.js.map +1 -1
  156. package/dist/wizard.d.ts +11 -8
  157. package/package.json +1 -1
  158. package/src/components/amount-input.tsx +20 -1
  159. package/src/components/autocomplete.tsx +172 -111
  160. package/src/components/column-mapper.tsx +666 -0
  161. package/src/components/combobox-core.tsx +82 -9
  162. package/src/components/combobox.tsx +614 -332
  163. package/src/components/confirm-dialog.tsx +12 -8
  164. package/src/components/country-select.tsx +135 -22
  165. package/src/components/danger-confirm.tsx +91 -12
  166. package/src/components/date-picker.tsx +423 -10
  167. package/src/components/entity-combobox.tsx +217 -126
  168. package/src/components/field-parts.tsx +224 -5
  169. package/src/components/field-strip.tsx +149 -0
  170. package/src/components/form-actions.tsx +32 -4
  171. package/src/components/icon-picker.tsx +23 -4
  172. package/src/components/money-field.tsx +3 -0
  173. package/src/components/month-picker.tsx +2 -1
  174. package/src/components/multi-entity-combobox.tsx +207 -120
  175. package/src/components/reauth-dialog.tsx +17 -18
  176. package/src/components/swatch-picker.tsx +29 -5
  177. package/src/components/tile-radio.tsx +74 -13
  178. package/src/components/toggle-group.tsx +2 -2
  179. package/src/components/trigger-aria.ts +5 -0
  180. package/src/components/ui.tsx +7 -33
  181. package/src/feedback/feedback-attachment.tsx +308 -61
  182. package/src/feedback/feedback-dialog.tsx +7 -3
  183. package/src/feedback/feedback-inbox.tsx +3 -2
  184. package/src/feedback/feedback-thread.tsx +47 -4
  185. package/src/i18n/defaults.ts +2 -0
  186. package/src/i18n/german.ts +42 -0
  187. package/src/i18n/kit-labels.tsx +4 -0
  188. package/src/i18n/locales/en.ts +27 -5
  189. package/src/i18n/locales/es.ts +41 -0
  190. package/src/i18n/locales/fr.ts +42 -0
  191. package/src/i18n/locales/hu.ts +37 -0
  192. package/src/i18n/locales/it.ts +41 -0
  193. package/src/i18n/locales/zh.ts +32 -0
  194. package/src/i18n/review.ts +19 -0
  195. package/src/index.ts +18 -0
  196. package/src/lib/column-mapping.ts +234 -0
  197. package/src/lib/table-text.ts +271 -0
  198. package/src/rhf/fields.tsx +254 -0
  199. package/src/rhf.ts +2 -1
  200. package/src/shell/topbar-action-menu.tsx +134 -38
  201. package/src/wizard/stepper-nav.tsx +35 -1
  202. package/dist/components/field-anatomy.d.ts +0 -95
  203. package/dist/components/field-anatomy.js +0 -84
  204. package/dist/components/field-anatomy.js.map +0 -1
  205. package/src/components/field-anatomy.tsx +0 -190
@@ -10,6 +10,7 @@ import {
10
10
  DEFAULT_MAX_ATTACHMENT_BYTES,
11
11
  FeedbackAttachmentField,
12
12
  type FeedbackAttachmentError,
13
+ type FeedbackAttachmentErrorInfo,
13
14
  } from "./feedback-attachment";
14
15
 
15
16
  export interface FeedbackCategoryOption {
@@ -157,7 +158,9 @@ interface FeedbackDialogBaseProps {
157
158
  export interface FeedbackDialogSingleProps extends FeedbackDialogBaseProps {
158
159
  attachments?: "single";
159
160
  onSubmit: (data: FeedbackSubmission) => void | Promise<void>;
160
- onAttachmentError?: (kind: "type" | "size") => void;
161
+ /** `info` (0.23.0) names the refused file and the limit — see
162
+ * {@link FeedbackAttachmentErrorInfo}. */
163
+ onAttachmentError?: (kind: "type" | "size", info: FeedbackAttachmentErrorInfo) => void;
161
164
  /** Multiple mode only — refused here rather than silently ignored. */
162
165
  maxAttachments?: never;
163
166
  }
@@ -172,8 +175,9 @@ export interface FeedbackDialogMultipleProps extends FeedbackDialogBaseProps {
172
175
  /** Default `DEFAULT_MAX_ATTACHMENTS` (5). The screenshot does not count. */
173
176
  maxAttachments?: number;
174
177
  onSubmit: (data: FeedbackMultipleSubmission) => void | Promise<void>;
175
- /** `"count"`: more files arrived at once than there was room for; the surplus was dropped. */
176
- onAttachmentError?: (kind: FeedbackAttachmentError) => void;
178
+ /** `"count"`: more files arrived at once than there was room for; the surplus was
179
+ * dropped. `info` (0.23.0) names the refused file(s) and the limit. */
180
+ onAttachmentError?: (kind: FeedbackAttachmentError, info: FeedbackAttachmentErrorInfo) => void;
177
181
  }
178
182
 
179
183
  export type FeedbackDialogProps = FeedbackDialogSingleProps | FeedbackDialogMultipleProps;
@@ -18,7 +18,7 @@ import type { LucideIcon } from "lucide-react";
18
18
  import { cn } from "../lib/cn";
19
19
  import { Button, Textarea } from "../components/ui";
20
20
  import { Tooltip } from "../components/tooltip";
21
- import { FeedbackAttachmentField } from "./feedback-attachment";
21
+ import { FeedbackAttachmentField, type FeedbackAttachmentErrorInfo } from "./feedback-attachment";
22
22
  import type { FeedbackAttachmentLabels } from "./feedback-dialog";
23
23
 
24
24
  /**
@@ -566,7 +566,8 @@ export interface FeedbackNoteAttachment {
566
566
  labels: FeedbackAttachmentLabels;
567
567
  accept?: string[];
568
568
  maxBytes?: number;
569
- onError?: (kind: "type" | "size") => void;
569
+ /** `info` (0.23.0) names the refused file and the limit. */
570
+ onError?: (kind: "type" | "size", info: FeedbackAttachmentErrorInfo) => void;
570
571
  onCaptureScreenshot?: () => Promise<File | null>;
571
572
  }
572
573
 
@@ -4,10 +4,12 @@ import { ImageIcon, ImageOff, Paperclip, Send } from "lucide-react";
4
4
  import { cn } from "../lib/cn";
5
5
  import { formatDate, formatRelativeTime } from "../lib/format";
6
6
  import { Button, EmptyState, Spinner, Textarea } from "../components/ui";
7
+ import type { CharacterCountLabels } from "../components/ui";
7
8
  import { Skeleton } from "../components/skeleton";
8
9
  import { Tooltip } from "../components/tooltip";
9
10
  import { useKitFileLabels, useKitLabels, useKitLocale } from "../i18n/kit-labels";
10
11
  import { FeedbackAttachmentField } from "./feedback-attachment";
12
+ import type { FeedbackAttachmentFieldSingleProps } from "./feedback-attachment";
11
13
  import type { FeedbackNoteAttachment } from "./feedback-inbox";
12
14
  import type { FeedbackAttachmentLabels } from "./feedback-dialog";
13
15
 
@@ -463,9 +465,12 @@ function toDate(value: Date | string | number): Date | null {
463
465
  /* ── Composer ─────────────────────────────────────────────────────────────── */
464
466
 
465
467
  /** What the composer's attach control takes — {@link FeedbackNoteAttachment} with the
466
- * labels optional, since the field reads `feedbackAttachment` from the provider. */
467
- export type FeedbackComposerAttachment = Omit<FeedbackNoteAttachment, "labels"> & {
468
+ * labels optional, since the field reads `feedbackAttachment` from the provider, and
469
+ * `onError` the field's own (0.23.0): it is handed the refused file and the limit too
470
+ * (`FeedbackAttachmentErrorInfo`), and a `(kind) => …` handler still fits. */
471
+ export type FeedbackComposerAttachment = Omit<FeedbackNoteAttachment, "labels" | "onError"> & {
468
472
  labels?: Partial<FeedbackAttachmentLabels>;
473
+ onError?: FeedbackAttachmentFieldSingleProps["onError"];
469
474
  };
470
475
 
471
476
  export interface FeedbackComposerProps {
@@ -498,7 +503,7 @@ export interface FeedbackComposerProps {
498
503
  * for state the composer cannot see, i.e. `attachmentSlot`'s (keksdose G5): `true`
499
504
  * with files ready makes an empty draft sendable (`onSend("", null)`), `false` while
500
505
  * an upload is still running holds even a written reply back. Left out, the text
501
- * decides. `pending` blocks a send either way.
506
+ * decides. `pending` blocks a send either way, as does a draft past `maxLength`.
502
507
  */
503
508
  canSend?: boolean;
504
509
  /**
@@ -513,6 +518,31 @@ export interface FeedbackComposerProps {
513
518
  * reader. */
514
519
  disabledReason?: ReactNode;
515
520
  rows?: number;
521
+ /**
522
+ * The longest draft the box takes, in UTF-16 code units as the browser counts — the
523
+ * server's limit, so a reply too long for it is cut off while it is typed rather than
524
+ * refused by a 422 after the press (keksdose G6: its support replies are capped at
525
+ * 4,000 on the server, and its other text fields already count against that with
526
+ * Textarea's `maxLength` + `showCount` since 0.22; the reply box, a composer, could
527
+ * not). The box's own `maxLength`, so typing and pasting stop at it.
528
+ *
529
+ * {@link FeedbackComposerHandle.insertText} is not stopped by it — the browser limits
530
+ * typing, not a script — and a canned reply is not cut mid-sentence to fit. A draft
531
+ * past the limit therefore holds Send back (whatever `canSend` says, as `pending`
532
+ * does) until it is trimmed; with `showCount` the counter, red at the limit, says by
533
+ * how much.
534
+ */
535
+ maxLength?: number;
536
+ /**
537
+ * A live "12/4000" under the box, with `maxLength` — Textarea's `showCount`, the same
538
+ * counter (keksdose G6): the count is part of the box's description, read with it on
539
+ * focus, and a polite live region speaks only on entering the last stretch and at the
540
+ * limit, never per keystroke. Under `size="sm"` the hidden send hint stays the box's
541
+ * description too, before the count. Ignored without `maxLength`.
542
+ */
543
+ showCount?: boolean;
544
+ /** The counter's words — the `characterCount` namespace, as on Textarea. */
545
+ countLabels?: Partial<CharacterCountLabels>;
516
546
  /**
517
547
  * The box's hint, over the `placeholder` label — keksdose F9: an admin answering a
518
548
  * support ticket is asked something else ("Reply to Anna…") than a member adding to
@@ -594,6 +624,9 @@ export function FeedbackComposer({
594
624
  sendOn = "mod-enter",
595
625
  disabledReason,
596
626
  rows = 3,
627
+ maxLength,
628
+ showCount,
629
+ countLabels,
597
630
  placeholder,
598
631
  value,
599
632
  onValueChange,
@@ -668,7 +701,10 @@ export function FeedbackComposer({
668
701
  );
669
702
  }
670
703
 
671
- const canSend = (canSendProp ?? draft.trim().length > 0) && !pending;
704
+ // Longer than `maxLength` only through `insertText` or a controlled value (typing
705
+ // stops at it): the server would refuse it, so it does not go.
706
+ const tooLong = maxLength !== undefined && draft.length > maxLength;
707
+ const canSend = (canSendProp ?? draft.trim().length > 0) && !pending && !tooLong;
672
708
  const send = () => {
673
709
  if (!canSend) return;
674
710
  const sentDraft = draft;
@@ -689,7 +725,14 @@ export function FeedbackComposer({
689
725
  rows={rows}
690
726
  dir="auto"
691
727
  aria-label={labels.field}
728
+ // Textarea puts the counter's description after this one, so under "sm" the box
729
+ // reads "Enter to send… 12 of 4000 characters".
692
730
  aria-describedby={size === "sm" ? hintId : undefined}
731
+ maxLength={maxLength}
732
+ // Passed only when given: Textarea keeps a wrapper for a `showCount` it was
733
+ // handed at all, and a composer without one keeps the DOM it had.
734
+ {...(showCount !== undefined ? { showCount } : null)}
735
+ countLabels={countLabels}
693
736
  ref={box}
694
737
  placeholder={placeholder ?? labels.placeholder}
695
738
  value={draft}
@@ -62,6 +62,7 @@ import { DEFAULT_INLINE_EDIT_LABELS } from "../components/inline-edit-field";
62
62
  import { DEFAULT_IBAN_INPUT_LABELS } from "../components/iban-input";
63
63
  import { DEFAULT_PHONE_INPUT_LABELS } from "../components/phone-input";
64
64
  import { DEFAULT_SIGN_CHIP_LABELS } from "../components/sign-chip";
65
+ import { DEFAULT_COLUMN_MAPPER_LABELS } from "../components/column-mapper";
65
66
  import {
66
67
  DEFAULT_APP_SHELL_LABELS,
67
68
  DEFAULT_CALCULATOR_LABELS,
@@ -157,4 +158,5 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
157
158
  ibanInput: DEFAULT_IBAN_INPUT_LABELS,
158
159
  phoneInput: DEFAULT_PHONE_INPUT_LABELS,
159
160
  signChip: DEFAULT_SIGN_CHIP_LABELS,
161
+ columnMapper: DEFAULT_COLUMN_MAPPER_LABELS,
160
162
  };
@@ -29,6 +29,7 @@ export function germanLabels(numberLocale: string): UiKitLabels {
29
29
  attachmentRemoveFile: (name) => `${name} entfernen`,
30
30
  attachmentLimit: (max) => `Maximal ${n(max)} ${max === 1 ? "Anhang" : "Anhänge"} – zum Hinzufügen erst einen entfernen.`,
31
31
  attachmentUploading: "Wird hochgeladen…",
32
+ attachmentUploadFailed: (name) => `„${name}“ konnte nicht hochgeladen werden`,
32
33
  },
33
34
  feedbackDialog: {
34
35
  title: "Feedback senden",
@@ -340,6 +341,9 @@ export function germanLabels(numberLocale: string): UiKitLabels {
340
341
  password: "Passwort",
341
342
  phrase: (phrase) => `Geben Sie zur Bestätigung „${phrase}“ ein`,
342
343
  acknowledge: "Ich habe gelesen, was dies bewirkt, und möchte fortfahren.",
344
+ needsPhrase: (phrase) => `Geben Sie zur Bestätigung „${phrase}“ ein`,
345
+ needsAcknowledge: "Setzen Sie zur Bestätigung das Häkchen",
346
+ needsPassword: "Geben Sie zur Bestätigung Ihr Passwort ein",
343
347
  },
344
348
  tabs: {
345
349
  // "Tab" is what German UIs say; "Registerkarte" reads like a 1990s manual.
@@ -698,6 +702,44 @@ export function germanLabels(numberLocale: string): UiKitLabels {
698
702
  inflow: "Einnahme",
699
703
  direction: (current, next) => `Richtung: ${current} – zu ${next} wechseln`,
700
704
  },
705
+ columnMapper: {
706
+ paste: "Tabelle einfügen",
707
+ pasteHint:
708
+ "Die Zeilen aus einer Tabellenkalkulation kopieren und hier einfügen – oder eine CSV- oder Textdatei hier ablegen.",
709
+ chooseFile: "Datei auswählen",
710
+ readError: (name) => `„${name}“ konnte nicht gelesen werden`,
711
+ headerRow: "Die erste Zeile enthält die Spaltennamen",
712
+ summary: (columns, rows) =>
713
+ `${n(columns)} ${columns === 1 ? "Spalte" : "Spalten"}, ${n(rows)} ${rows === 1 ? "Zeile" : "Zeilen"}`,
714
+ // Fragments of the summary line ("3 Spalten, 120 Zeilen · durch Semikolons getrennt").
715
+ separatorSemicolon: "durch Semikolons getrennt",
716
+ separatorComma: "durch Kommas getrennt",
717
+ separatorTab: "durch Tabulatoren getrennt",
718
+ separatorSpace: "durch Leerzeichen getrennt",
719
+ // The examples stay as written: they show the convention, not the reader's locale.
720
+ decimalComma: "Dezimalkomma (1,5)",
721
+ decimalPoint: "Dezimalpunkt (1.5)",
722
+ unreadCount: (count) =>
723
+ count === 1
724
+ ? "1 Zeile konnte nicht gelesen werden"
725
+ : `${n(count)} Zeilen konnten nicht gelesen werden`,
726
+ // A line number is a position, not a count: unformatted, as `measuredGrid.lineError`.
727
+ unreadLine: (line) => `Zeile ${line} konnte nicht gelesen werden`,
728
+ unreadMore: (count) => `…und ${n(count)} weitere`,
729
+ noRows: "Keine Zeile dieses Textes lässt sich als Tabellenzeile lesen.",
730
+ table: "Spaltenzuordnung",
731
+ columnN: (column) => `Spalte ${column}`,
732
+ roleOf: (column) => `Was enthält die Spalte „${column}“?`,
733
+ ignore: "Ignorieren",
734
+ requiredRole: (role) => `${role} (erforderlich)`,
735
+ previewOf: (shown, total) =>
736
+ shown === 1
737
+ ? `Die erste von ${n(total)} Zeilen`
738
+ : `Die ersten ${n(shown)} von ${n(total)} Zeilen`,
739
+ // `roles` arrives joined with "oder" ("Soll oder Haben").
740
+ oneOf: (roles) => `entweder ${roles}`,
741
+ missing: (roles) => `Noch erforderlich: ${roles}.`,
742
+ },
701
743
  translationReview: {
702
744
  statusMissing: "Fehlt",
703
745
  statusUnreviewed: "Ungeprüft",
@@ -58,6 +58,7 @@ import type { InlineEditLabels } from "../components/inline-edit-field";
58
58
  import type { IbanInputLabels } from "../components/iban-input";
59
59
  import type { PhoneInputLabels } from "../components/phone-input";
60
60
  import type { SignChipLabels } from "../components/sign-chip";
61
+ import type { ColumnMapperLabels } from "../components/column-mapper";
61
62
 
62
63
  /**
63
64
  * EVERY string the kit renders, as one typed tree — and an optional provider that
@@ -311,6 +312,9 @@ export interface UiKitLabels {
311
312
  phoneInput: PhoneInputLabels;
312
313
  /** 0.22.0: `SignChip` — the outflow / inflow toggle beside an amount. */
313
314
  signChip: SignChipLabels;
315
+ /** 0.23.0: `ColumnMapper` / `ColumnRoleTable` — a pasted or dropped table, a role per
316
+ * column. */
317
+ columnMapper: ColumnMapperLabels;
314
318
  }
315
319
 
316
320
  /**
@@ -33,7 +33,9 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
33
33
  ...d.measuredGrid,
34
34
  points: (count) => (count === 1 ? "1 point" : `${n(count)} points`),
35
35
  problems: (count) =>
36
- count === 1 ? "1 cell is not a number" : `${n(count)} cells are not numbers`,
36
+ count === 1
37
+ ? "1 cell is not a number"
38
+ : `${n(count)} cells are not numbers`,
37
39
  },
38
40
  dataTable: {
39
41
  ...d.dataTable,
@@ -51,9 +53,12 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
51
53
  combobox: {
52
54
  ...d.combobox,
53
55
  selectedCount: (count) => `${n(count)} selected`,
54
- resultCount: (count) => (count === 1 ? "1 result" : `${n(count)} results`),
56
+ resultCount: (count) =>
57
+ count === 1 ? "1 result" : `${n(count)} results`,
55
58
  minChars: (count) =>
56
- count === 1 ? "Type at least 1 character" : `Type at least ${n(count)} characters`,
59
+ count === 1
60
+ ? "Type at least 1 character"
61
+ : `Type at least ${n(count)} characters`,
57
62
  },
58
63
  multiSelect: {
59
64
  ...d.multiSelect,
@@ -81,7 +86,9 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
81
86
  `“${name}” was not added: at most ${n(maxFiles)} ${maxFiles === 1 ? "file" : "files"}`,
82
87
  rejectedMany: (count) => `${n(count)} files were not added`,
83
88
  rejectedPick: (count) =>
84
- count === 1 ? "The file was not added" : `None of the ${n(count)} files were added`,
89
+ count === 1
90
+ ? "The file was not added"
91
+ : `None of the ${n(count)} files were added`,
85
92
  selected: (count, firstName) =>
86
93
  count === 1 ? `“${firstName}” selected` : `${n(count)} files selected`,
87
94
  },
@@ -123,12 +130,27 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
123
130
  characterCount: {
124
131
  ...d.characterCount,
125
132
  count: (used, max) => `${n(used)} of ${n(max)} characters`,
126
- remaining: (left) => (left === 1 ? "1 character left" : `${n(left)} characters left`),
133
+ remaining: (left) =>
134
+ left === 1 ? "1 character left" : `${n(left)} characters left`,
127
135
  },
128
136
  ibanInput: {
129
137
  ...d.ibanInput,
130
138
  length: (actual, expected) =>
131
139
  `An IBAN from this country has ${n(expected)} characters — this one has ${n(actual)}.`,
132
140
  },
141
+ columnMapper: {
142
+ ...d.columnMapper,
143
+ summary: (columns, rows) =>
144
+ `${n(columns)} ${columns === 1 ? "column" : "columns"}, ${n(rows)} ${rows === 1 ? "row" : "rows"}`,
145
+ unreadCount: (count) =>
146
+ count === 1
147
+ ? "1 line could not be read"
148
+ : `${n(count)} lines could not be read`,
149
+ unreadMore: (count) => `…and ${n(count)} more`,
150
+ previewOf: (shown, total) =>
151
+ shown === 1
152
+ ? `The first of ${n(total)} rows`
153
+ : `The first ${n(shown)} of ${n(total)} rows`,
154
+ },
133
155
  };
134
156
  }
@@ -26,6 +26,7 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
26
26
  attachmentRemoveFile: (name) => `Quitar ${name}`,
27
27
  attachmentLimit: (max) => `Máximo ${n(max)} ${max === 1 ? "adjunto" : "adjuntos"}: quite uno para añadir otro.`,
28
28
  attachmentUploading: "Subiendo…",
29
+ attachmentUploadFailed: (name) => `No se ha podido subir «${name}»`,
29
30
  },
30
31
  feedbackDialog: {
31
32
  title: "Enviar comentarios",
@@ -330,6 +331,9 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
330
331
  password: "Contraseña",
331
332
  phrase: (phrase) => `Escriba «${phrase}» para confirmar`,
332
333
  acknowledge: "He leído lo que hace esta acción y deseo continuar.",
334
+ needsPhrase: (phrase) => `Escriba «${phrase}» para confirmar`,
335
+ needsAcknowledge: "Marque la casilla para confirmar",
336
+ needsPassword: "Introduzca su contraseña para confirmar",
333
337
  },
334
338
  tabs: {
335
339
  add: "Añadir pestaña",
@@ -680,6 +684,43 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
680
684
  inflow: "Entrada",
681
685
  direction: (current, next) => `Dirección: ${current} — cambiar a ${next}`,
682
686
  },
687
+ columnMapper: {
688
+ paste: "Pegar una tabla",
689
+ pasteHint:
690
+ "Copie las filas de una hoja de cálculo y péguelas aquí, o suelte un archivo CSV o de texto.",
691
+ chooseFile: "Elegir un archivo",
692
+ readError: (name) => `No se ha podido leer «${name}»`,
693
+ headerRow: "La primera línea contiene los nombres de las columnas",
694
+ summary: (columns, rows) =>
695
+ `${n(columns)} ${plural(columns, "columna", "columnas")}, ${n(rows)} ${plural(rows, "fila", "filas")}`,
696
+ // Fragments of the summary line. "Separado por…" would have to agree with a noun the
697
+ // line does not name, so the separator is stated as a value.
698
+ separatorSemicolon: "separador: punto y coma",
699
+ separatorComma: "separador: coma",
700
+ separatorTab: "separador: tabulación",
701
+ separatorSpace: "separador: espacio",
702
+ // The examples stay as written: they show the convention, not the reader's locale.
703
+ decimalComma: "coma decimal (1,5)",
704
+ decimalPoint: "punto decimal (1.5)",
705
+ unreadCount: (count) =>
706
+ count === 1 ? "No se ha podido leer 1 línea" : `No se han podido leer ${n(count)} líneas`,
707
+ unreadLine: (line) => `No se pudo leer la línea ${line}`,
708
+ unreadMore: (count) => `…y ${n(count)} más`,
709
+ noRows: "Ninguna línea de este texto se puede leer como una fila de la tabla.",
710
+ table: "Columnas y sus funciones",
711
+ columnN: (column) => `Columna ${column}`,
712
+ roleOf: (column) => `¿Qué contiene la columna «${column}»?`,
713
+ ignore: "Ignorar",
714
+ requiredRole: (role) => `${role} (obligatorio)`,
715
+ previewOf: (shown, total) =>
716
+ shown === 1
717
+ ? `La primera de ${n(total)} filas`
718
+ : `Las primeras ${n(shown)} de ${n(total)} filas`,
719
+ // `roles` arrives joined with "o" ("Debe o Haber").
720
+ oneOf: (roles) => `${roles} (a elegir)`,
721
+ // "Falta" + infinitive: no verb to agree with however many roles follow.
722
+ missing: (roles) => `Falta asignar: ${roles}.`,
723
+ },
683
724
  translationReview: {
684
725
  statusMissing: "Falta",
685
726
  statusUnreviewed: "Sin revisar",
@@ -26,6 +26,7 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
26
26
  attachmentRemoveFile: (name) => `Retirer ${name}`,
27
27
  attachmentLimit: (max) => `${n(max)} ${max === 1 ? "pièce jointe" : "pièces jointes"} au maximum — retirez-en une pour en ajouter une autre.`,
28
28
  attachmentUploading: "Envoi en cours…",
29
+ attachmentUploadFailed: (name) => `«\u202f${name}\u202f» n’a pas pu être envoyé`,
29
30
  },
30
31
  feedbackDialog: {
31
32
  title: "Envoyer un retour",
@@ -339,6 +340,9 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
339
340
  password: "Mot de passe",
340
341
  phrase: (phrase) => `Saisissez «\u202f${phrase}\u202f» pour confirmer`,
341
342
  acknowledge: "J’ai lu ce que fait cette action et je souhaite continuer.",
343
+ needsPhrase: (phrase) => `Saisissez «\u202f${phrase}\u202f» pour confirmer`,
344
+ needsAcknowledge: "Cochez la case pour confirmer",
345
+ needsPassword: "Saisissez votre mot de passe pour confirmer",
342
346
  },
343
347
  tabs: {
344
348
  add: "Ajouter un onglet",
@@ -696,6 +700,44 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
696
700
  inflow: "Entrée",
697
701
  direction: (current, next) => `Sens\u00a0: ${current} — passer à ${next}`,
698
702
  },
703
+ columnMapper: {
704
+ paste: "Coller un tableau",
705
+ pasteHint:
706
+ "Copiez les lignes d’un tableur et collez-les ici, ou déposez un fichier CSV ou texte.",
707
+ chooseFile: "Choisir un fichier",
708
+ readError: (name) => `«\u202f${name}\u202f» n’a pas pu être lu`,
709
+ headerRow: "La première ligne contient les noms des colonnes",
710
+ summary: (columns, rows) =>
711
+ `${n(columns)} ${plural(columns, "colonne", "colonnes")}, ${n(rows)} ${plural(rows, "ligne", "lignes")}`,
712
+ // Fragments of the summary line. "Séparé par…" would have to agree with a noun the
713
+ // line does not name, so the separator is stated as a value.
714
+ separatorSemicolon: "séparateur\u00a0: point-virgule",
715
+ separatorComma: "séparateur\u00a0: virgule",
716
+ separatorTab: "séparateur\u00a0: tabulation",
717
+ separatorSpace: "séparateur\u00a0: espace",
718
+ // The examples stay as written: they show the convention, not the reader's locale.
719
+ decimalComma: "virgule décimale (1,5)",
720
+ decimalPoint: "point décimal (1.5)",
721
+ unreadCount: (count) =>
722
+ count < 2
723
+ ? `${n(count)} ligne n’a pas pu être lue`
724
+ : `${n(count)} lignes n’ont pas pu être lues`,
725
+ unreadLine: (line) => `La ligne ${line} n\u2019a pas pu être lue`,
726
+ unreadMore: (count) => `…et ${n(count)} ${plural(count, "autre", "autres")}`,
727
+ noRows: "Aucune ligne de ce texte ne se lit comme une ligne du tableau.",
728
+ table: "Colonnes et leurs rôles",
729
+ columnN: (column) => `Colonne ${column}`,
730
+ roleOf: (column) => `Que contient la colonne «\u202f${column}\u202f»\u202f?`,
731
+ ignore: "Ignorer",
732
+ requiredRole: (role) => `${role} (obligatoire)`,
733
+ previewOf: (shown, total) =>
734
+ shown < 2
735
+ ? `La première ligne sur ${n(total)}`
736
+ : `Les ${n(shown)} premières lignes sur ${n(total)}`,
737
+ // `roles` arrives joined with "ou" ("Débit ou Crédit").
738
+ oneOf: (roles) => `${roles} (au choix)`,
739
+ missing: (roles) => `Il manque encore\u00a0: ${roles}.`,
740
+ },
699
741
  translationReview: {
700
742
  statusMissing: "Manquant",
701
743
  statusUnreviewed: "Non relu",
@@ -25,6 +25,8 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
25
25
  attachmentRemoveFile: (name) => `${name} eltávolítása`,
26
26
  attachmentLimit: (max) => `Legfeljebb ${n(max)} melléklet – egy újabbhoz távolítson el egyet.`,
27
27
  attachmentUploading: "Feltöltés…",
28
+ // Opens with the quoted name, as `filePicker`'s refusals: no a/az has to agree with it.
29
+ attachmentUploadFailed: (name) => `„${name}”: a feltöltés nem sikerült`,
28
30
  },
29
31
  feedbackDialog: {
30
32
  title: "Visszajelzés küldése",
@@ -330,6 +332,9 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
330
332
  // Phrase after a colon, so no article has to agree with it.
331
333
  phrase: (phrase) => `A megerősítéshez írja be: „${phrase}”`,
332
334
  acknowledge: "Elolvastam, mit eredményez ez a művelet, és folytatni szeretném.",
335
+ needsPhrase: (phrase) => `A megerősítéshez írja be: „${phrase}”`,
336
+ needsAcknowledge: "A megerősítéshez jelölje be a négyzetet",
337
+ needsPassword: "A megerősítéshez adja meg a jelszavát",
333
338
  },
334
339
  tabs: {
335
340
  add: "Lap hozzáadása",
@@ -686,6 +691,38 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
686
691
  inflow: "Bevétel",
687
692
  direction: (current, next) => `Irány: ${current} – váltás erre: ${next}`,
688
693
  },
694
+ columnMapper: {
695
+ paste: "Táblázat beillesztése",
696
+ pasteHint:
697
+ "Másolja ki a sorokat egy táblázatkezelőből, és illessze be ide, vagy húzzon ide egy CSV- vagy szövegfájlt.",
698
+ chooseFile: "Fájl kiválasztása",
699
+ readError: (name) => `„${name}”: a fájl nem olvasható`,
700
+ headerRow: "Az első sor az oszlopok nevét tartalmazza",
701
+ // No plural after a numeral (convention 2).
702
+ summary: (columns, rows) => `${n(columns)} oszlop, ${n(rows)} sor`,
703
+ separatorSemicolon: "pontosvesszővel elválasztva",
704
+ separatorComma: "vesszővel elválasztva",
705
+ separatorTab: "tabulátorral elválasztva",
706
+ separatorSpace: "szóközzel elválasztva",
707
+ // The examples stay as written: they show the convention, not the reader's locale.
708
+ decimalComma: "tizedesvessző (1,5)",
709
+ decimalPoint: "tizedespont (1.5)",
710
+ unreadCount: (count) => `${n(count)} sor nem olvasható`,
711
+ unreadLine: (line) => `A(z) ${line}. sor nem olvasható`,
712
+ unreadMore: (count) => `…és még ${n(count)}`,
713
+ noRows: "A szöveg egyetlen sora sem olvasható a táblázat soraként.",
714
+ table: "Oszlopok és szerepük",
715
+ columnN: (column) => `${column}. oszlop`,
716
+ // The name after a colon, so no article or case ending has to agree with it.
717
+ roleOf: (column) => `Mit tartalmaz ez az oszlop: „${column}”?`,
718
+ ignore: "Kihagyás",
719
+ requiredRole: (role) => `${role} (kötelező)`,
720
+ // "(összesen N)", as `dataTable.pageChanged`: both numerals stay bare (convention 3).
721
+ previewOf: (shown, total) => `Az első ${n(shown)} sor (összesen ${n(total)})`,
722
+ // `roles` arrives joined with "vagy" ("Tartozik vagy Követel").
723
+ oneOf: (roles) => `${roles} (valamelyik)`,
724
+ missing: (roles) => `Még hiányzik: ${roles}.`,
725
+ },
689
726
  translationReview: {
690
727
  statusMissing: "Hiányzik",
691
728
  statusUnreviewed: "Nincs ellenőrizve",
@@ -26,6 +26,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
26
26
  attachmentRemoveFile: (name) => `Rimuovi ${name}`,
27
27
  attachmentLimit: (max) => `Massimo ${n(max)} ${max === 1 ? "allegato" : "allegati"}: ne rimuova uno per aggiungerne un altro.`,
28
28
  attachmentUploading: "Caricamento…",
29
+ attachmentUploadFailed: (name) => `Impossibile caricare «${name}»`,
29
30
  },
30
31
  feedbackDialog: {
31
32
  title: "Invia feedback",
@@ -332,6 +333,9 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
332
333
  password: "Password",
333
334
  phrase: (phrase) => `Digiti «${phrase}» per confermare`,
334
335
  acknowledge: "Ho letto cosa comporta questa operazione e desidero continuare.",
336
+ needsPhrase: (phrase) => `Digiti «${phrase}» per confermare`,
337
+ needsAcknowledge: "Spunti la casella per confermare",
338
+ needsPassword: "Inserisca la password per confermare",
335
339
  },
336
340
  tabs: {
337
341
  add: "Aggiungi scheda",
@@ -684,6 +688,43 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
684
688
  // "premere per…": an instruction without a tu imperative, as the kit's Italian is formal.
685
689
  direction: (current, next) => `Direzione: ${current} – premere per passare a ${next}`,
686
690
  },
691
+ columnMapper: {
692
+ paste: "Incolla una tabella",
693
+ pasteHint:
694
+ "Copi le righe da un foglio di calcolo e le incolli qui, oppure trascini un file CSV o di testo.",
695
+ chooseFile: "Scegli un file",
696
+ readError: (name) => `Impossibile leggere «${name}»`,
697
+ headerRow: "La prima riga contiene i nomi delle colonne",
698
+ summary: (columns, rows) =>
699
+ `${n(columns)} ${plural(columns, "colonna", "colonne")}, ${n(rows)} ${plural(rows, "riga", "righe")}`,
700
+ // Fragments of the summary line. "Separato da…" would have to agree with a noun the
701
+ // line does not name, so the separator is stated as a value.
702
+ separatorSemicolon: "separatore: punto e virgola",
703
+ separatorComma: "separatore: virgola",
704
+ separatorTab: "separatore: tabulazione",
705
+ separatorSpace: "separatore: spazio",
706
+ // The examples stay as written: they show the convention, not the reader's locale.
707
+ decimalComma: "virgola decimale (1,5)",
708
+ decimalPoint: "punto decimale (1.5)",
709
+ unreadCount: (count) =>
710
+ count === 1 ? "Impossibile leggere 1 riga" : `Impossibile leggere ${n(count)} righe`,
711
+ unreadLine: (line) => `Impossibile leggere la riga ${line}`,
712
+ unreadMore: (count) => (count === 1 ? "…e un’altra" : `…e altre ${n(count)}`),
713
+ noRows: "Nessuna riga di questo testo si può leggere come riga della tabella.",
714
+ table: "Colonne e relativi ruoli",
715
+ columnN: (column) => `Colonna ${column}`,
716
+ roleOf: (column) => `Che cosa contiene la colonna «${column}»?`,
717
+ ignore: "Ignora",
718
+ requiredRole: (role) => `${role} (obbligatorio)`,
719
+ previewOf: (shown, total) =>
720
+ shown === 1
721
+ ? `La prima di ${n(total)} righe`
722
+ : `Le prime ${n(shown)} di ${n(total)} righe`,
723
+ // `roles` arrives joined with "o" ("Dare o Avere").
724
+ oneOf: (roles) => `${roles} (a scelta)`,
725
+ // A participle phrase: no verb to agree with however many roles follow.
726
+ missing: (roles) => `Ancora da assegnare: ${roles}.`,
727
+ },
687
728
  translationReview: {
688
729
  statusMissing: "Mancante",
689
730
  statusUnreviewed: "Non rivisto",
@@ -25,6 +25,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
25
25
  attachmentRemoveFile: (name) => `移除 ${name}`,
26
26
  attachmentLimit: (max) => `最多 ${n(max)} 个附件,请先移除一个再添加。`,
27
27
  attachmentUploading: "正在上传…",
28
+ attachmentUploadFailed: (name) => `无法上传“${name}”`,
28
29
  },
29
30
  feedbackDialog: {
30
31
  title: "发送反馈",
@@ -324,6 +325,9 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
324
325
  password: "密码",
325
326
  phrase: (phrase) => `输入“${phrase}”以确认`,
326
327
  acknowledge: "我已了解此操作的作用,并希望继续。",
328
+ needsPhrase: (phrase) => `输入“${phrase}”以确认`,
329
+ needsAcknowledge: "勾选复选框以确认",
330
+ needsPassword: "输入密码以确认",
327
331
  },
328
332
  tabs: {
329
333
  add: "添加标签页",
@@ -667,6 +671,34 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
667
671
  inflow: "收入",
668
672
  direction: (current, next) => `方向:${current},切换为${next}`,
669
673
  },
674
+ columnMapper: {
675
+ paste: "粘贴表格",
676
+ pasteHint: "从电子表格中复制行并粘贴到此处,或拖放 CSV 或文本文件。",
677
+ chooseFile: "选择文件",
678
+ readError: (name) => `无法读取“${name}”`,
679
+ headerRow: "第一行为列名",
680
+ summary: (columns, rows) => `${n(columns)} 列,${n(rows)} 行`,
681
+ separatorSemicolon: "以分号分隔",
682
+ separatorComma: "以逗号分隔",
683
+ separatorTab: "以制表符分隔",
684
+ separatorSpace: "以空格分隔",
685
+ // The examples stay as written: they show the convention, not the reader's locale.
686
+ decimalComma: "小数点为逗号(1,5)",
687
+ decimalPoint: "小数点为句点(1.5)",
688
+ unreadCount: (count) => `${n(count)} 行无法读取`,
689
+ unreadLine: (line) => `无法读取第 ${line} 行`,
690
+ unreadMore: (count) => `……另有 ${n(count)} 行`,
691
+ noRows: "此文本中没有任何一行能读作表格的一行。",
692
+ table: "列及其用途",
693
+ columnN: (column) => `第 ${column} 列`,
694
+ roleOf: (column) => `“${column}”列的内容是什么?`,
695
+ ignore: "忽略",
696
+ requiredRole: (role) => `${role}(必填)`,
697
+ previewOf: (shown, total) => `显示前 ${n(shown)} 行,共 ${n(total)} 行`,
698
+ // `roles` arrives joined with "或" ("借方或贷方").
699
+ oneOf: (roles) => `${roles}(任选其一)`,
700
+ missing: (roles) => `仍需指定:${roles}。`,
701
+ },
670
702
  translationReview: {
671
703
  statusMissing: "缺失",
672
704
  statusUnreviewed: "未审校",
@@ -122,6 +122,7 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
122
122
  "passwordStrength.optional": [["{{rule}}"]],
123
123
  "passwordStrength.tooLong": [[72]],
124
124
  "dangerConfirm.phrase": [["{{phrase}}"]],
125
+ "dangerConfirm.needsPhrase": [["{{phrase}}"]],
125
126
  "iconPicker.resultCount": one,
126
127
  "filePicker.rejectedType": [["{{name}}"]],
127
128
  "filePicker.rejectedTypeOnly": [["{{accept}}", "{{name}}"]],
@@ -147,6 +148,7 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
147
148
  "measuredGrid.problems": one,
148
149
  "feedbackAttachment.attachmentRemoveFile": [["{{name}}"]],
149
150
  "feedbackAttachment.attachmentLimit": one,
151
+ "feedbackAttachment.attachmentUploadFailed": [["{{name}}"]],
150
152
  "feedbackComposer.sendHint": [["{{modifier}}"]],
151
153
  "accountSettings.passkeys.renameItem": [["{{name}}"]],
152
154
  "accountSettings.passkeys.renameField": [["{{name}}"]],
@@ -197,4 +199,21 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
197
199
  "ibanInput.country": [["{{code}}"]],
198
200
  "ibanInput.length": [[20, 21]],
199
201
  "signChip.direction": [["{{current}}", "{{next}}"]],
202
+ "columnMapper.readError": [["{{name}}"]],
203
+ "columnMapper.summary": [
204
+ [1, 1],
205
+ [3, 12],
206
+ ],
207
+ "columnMapper.unreadCount": one,
208
+ "columnMapper.unreadLine": [[3]],
209
+ "columnMapper.unreadMore": one,
210
+ "columnMapper.columnN": [[3]],
211
+ "columnMapper.roleOf": [["{{column}}"]],
212
+ "columnMapper.requiredRole": [["{{role}}"]],
213
+ "columnMapper.previewOf": [
214
+ [1, 12],
215
+ [10, 250],
216
+ ],
217
+ "columnMapper.oneOf": [["{{roles}}"]],
218
+ "columnMapper.missing": [["{{roles}}"]],
200
219
  };