@eifi1/ui-kit 0.28.0 → 0.28.1

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 (96) hide show
  1. package/README.md +3 -3
  2. package/dist/chart.d.ts +1 -1
  3. package/dist/components/amount-input.d.ts +1 -1
  4. package/dist/components/button-group.d.ts +1 -1
  5. package/dist/components/calculator.d.ts +1 -1
  6. package/dist/components/confirm-dialog.d.ts +1 -1
  7. package/dist/components/copy-button.d.ts +1 -1
  8. package/dist/components/danger-confirm.d.ts +1 -1
  9. package/dist/components/facing-pair.d.ts +1 -1
  10. package/dist/components/file-button.d.ts +1 -1
  11. package/dist/components/file-dropzone.d.ts +1 -1
  12. package/dist/components/form-actions.d.ts +1 -1
  13. package/dist/components/iban-input.d.ts +1 -1
  14. package/dist/components/language-select.d.ts +1 -1
  15. package/dist/components/legal-page.d.ts +9 -3
  16. package/dist/components/legal-page.js +24 -5
  17. package/dist/components/legal-page.js.map +1 -1
  18. package/dist/components/legal.d.ts +1 -1
  19. package/dist/components/legal.js +49 -5
  20. package/dist/components/legal.js.map +1 -1
  21. package/dist/components/money-field.d.ts +1 -1
  22. package/dist/components/number-field.d.ts +1 -1
  23. package/dist/components/number-input.d.ts +1 -1
  24. package/dist/components/numpad-sheet.d.ts +1 -1
  25. package/dist/components/phone-input.d.ts +1 -1
  26. package/dist/components/series-chart.d.ts +1 -1
  27. package/dist/components/settings-fields.d.ts +1 -1
  28. package/dist/components/share-card.d.ts +1 -1
  29. package/dist/components/text-link.d.ts +1 -1
  30. package/dist/components/time-input.d.ts +1 -1
  31. package/dist/components/ui.d.ts +1 -1
  32. package/dist/feedback/feedback-attachment.d.ts +1 -1
  33. package/dist/feedback/feedback-context.d.ts +1 -1
  34. package/dist/feedback/feedback-crash.d.ts +1 -1
  35. package/dist/feedback/feedback-dialog.d.ts +1 -1
  36. package/dist/feedback/feedback-inbox.d.ts +1 -1
  37. package/dist/feedback/feedback-labels.d.ts +1 -1
  38. package/dist/feedback/feedback-menu.d.ts +1 -1
  39. package/dist/feedback/feedback-record.d.ts +1 -1
  40. package/dist/feedback/feedback-row-detail.d.ts +1 -1
  41. package/dist/feedback/feedback-status-undo.d.ts +1 -1
  42. package/dist/feedback/feedback-submit.d.ts +1 -1
  43. package/dist/feedback/feedback-swipe.d.ts +1 -1
  44. package/dist/feedback/feedback-table.d.ts +1 -1
  45. package/dist/feedback/feedback-thread.d.ts +1 -1
  46. package/dist/feedback.d.ts +1 -1
  47. package/dist/hooks/use-file-drop.d.ts +1 -1
  48. package/dist/i18n/defaults.d.ts +1 -1
  49. package/dist/i18n/german.d.ts +1 -1
  50. package/dist/i18n/german.js +8 -0
  51. package/dist/i18n/german.js.map +1 -1
  52. package/dist/i18n/kit-labels.d.ts +1 -1
  53. package/dist/i18n/languages.d.ts +1 -1
  54. package/dist/i18n/locales/de-CH.d.ts +1 -1
  55. package/dist/i18n/locales/en.d.ts +1 -1
  56. package/dist/i18n/locales/es.d.ts +1 -1
  57. package/dist/i18n/locales/es.js +11 -3
  58. package/dist/i18n/locales/es.js.map +1 -1
  59. package/dist/i18n/locales/fr.d.ts +1 -1
  60. package/dist/i18n/locales/fr.js +8 -0
  61. package/dist/i18n/locales/fr.js.map +1 -1
  62. package/dist/i18n/locales/hu.d.ts +1 -1
  63. package/dist/i18n/locales/hu.js +11 -3
  64. package/dist/i18n/locales/hu.js.map +1 -1
  65. package/dist/i18n/locales/it.d.ts +1 -1
  66. package/dist/i18n/locales/it.js +9 -1
  67. package/dist/i18n/locales/it.js.map +1 -1
  68. package/dist/i18n/locales/zh.d.ts +1 -1
  69. package/dist/i18n/locales/zh.js +9 -1
  70. package/dist/i18n/locales/zh.js.map +1 -1
  71. package/dist/i18n/review.d.ts +1 -1
  72. package/dist/i18n/review.js +9 -7
  73. package/dist/i18n/review.js.map +1 -1
  74. package/dist/index.d.ts +1 -1
  75. package/dist/index.js +13 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/{kit-labels-SBN0biPl.d.ts → kit-labels-Du0uHBq4.d.ts} +41 -5
  78. package/dist/rhf/fields.d.ts +1 -1
  79. package/dist/rhf/form.d.ts +1 -1
  80. package/dist/rhf.d.ts +1 -1
  81. package/dist/shell/app-shell.d.ts +1 -1
  82. package/dist/shell/top-bar-brand.d.ts +1 -1
  83. package/dist/shell.d.ts +1 -1
  84. package/dist/wizard/stepper-nav.d.ts +1 -1
  85. package/dist/wizard.d.ts +1 -1
  86. package/package.json +1 -1
  87. package/src/components/legal-page.tsx +35 -0
  88. package/src/components/legal.tsx +93 -7
  89. package/src/i18n/german.ts +10 -0
  90. package/src/i18n/locales/es.ts +13 -3
  91. package/src/i18n/locales/fr.ts +10 -0
  92. package/src/i18n/locales/hu.ts +13 -3
  93. package/src/i18n/locales/it.ts +11 -1
  94. package/src/i18n/locales/zh.ts +11 -1
  95. package/src/i18n/review.ts +7 -6
  96. package/src/index.ts +10 -1
@@ -1,5 +1,5 @@
1
1
  import { createContext, useContext } from "react";
2
- import type { ReactNode } from "react";
2
+ import type { MouseEvent, ReactNode } from "react";
3
3
 
4
4
  import { useKitLabels } from "../i18n/kit-labels";
5
5
  import type { LabelOverride } from "../i18n/kit-labels";
@@ -66,6 +66,14 @@ export interface LegalOperator {
66
66
  country: string;
67
67
  /** The contact address the imprint and the privacy policy print. */
68
68
  email: string;
69
+ /**
70
+ * The language whose version of the legal texts is BINDING ("de-CH"), a BCP 47 code
71
+ * (0.28.1, Marcel 2026-10-05). The lawyer reviews that one; the others are translations
72
+ * for convenience. With it, {@link LegalPage} shows a note on every page read in
73
+ * another language, and the terms' `language` section says which version prevails.
74
+ * Only the language subtag counts: "de-CH" and "de" are the same language here.
75
+ */
76
+ bindingLanguage?: string;
69
77
  }
70
78
 
71
79
  /**
@@ -78,10 +86,42 @@ export interface LegalOperator {
78
86
  */
79
87
  export type LegalOperatorText = Readonly<Record<keyof LegalOperator, string>>;
80
88
 
89
+ /** The language subtag of a BCP 47 code, lower-cased: "de-CH" → "de". */
90
+ export function baseLanguage(code: string): string {
91
+ return code.split(/[-_]/)[0]!.toLowerCase();
92
+ }
93
+
94
+ const languageNamers = new Map<string, Intl.DisplayNames | null>();
95
+
96
+ /** The name of a language in `locale` — "Deutsch", "German", "allemand", "德语" — from its
97
+ * language subtag, so "de-CH" is plain "German", not "Swiss High German". The code itself
98
+ * when the runtime cannot name it. */
99
+ function languageName(code: string, locale: string): string {
100
+ let namer = languageNamers.get(locale);
101
+ if (namer === undefined) {
102
+ try {
103
+ namer = new Intl.DisplayNames([locale], { type: "language" });
104
+ } catch {
105
+ namer = null;
106
+ }
107
+ languageNamers.set(locale, namer);
108
+ }
109
+ const base = baseLanguage(code);
110
+ try {
111
+ return namer?.of(base) ?? base;
112
+ } catch {
113
+ return base;
114
+ }
115
+ }
116
+
81
117
  /** @internal The operator as the texts read it: its country named in `locale` (English
82
118
  * without one — `countryName`'s rule, so an app that set no locale reads English). */
83
119
  export function legalOperatorText(operator: LegalOperator, locale: string = "en"): LegalOperatorText {
84
- return { ...operator, country: countryName(operator.country, locale) };
120
+ return {
121
+ ...operator,
122
+ country: countryName(operator.country, locale),
123
+ bindingLanguage: operator.bindingLanguage ? languageName(operator.bindingLanguage, locale) : "",
124
+ };
85
125
  }
86
126
 
87
127
  // ── Labels ────────────────────────────────────────────────────────────────────
@@ -142,6 +182,16 @@ export interface LegalLabels {
142
182
  beta: string;
143
183
  privacy: string;
144
184
  };
185
+ /**
186
+ * 0.28.1: on a page read in another language than the operator's `bindingLanguage` —
187
+ * `note` under the title, `show` on the button that switches to the binding version
188
+ * (when the app passes `onShowBindingLanguage`). `o.bindingLanguage` is the binding
189
+ * language's NAME in the reader's language ("German", "allemand").
190
+ */
191
+ translation: {
192
+ note: (o: LegalOperatorText) => string;
193
+ show: (o: LegalOperatorText) => string;
194
+ };
145
195
  /** The kit's sections (§4.3), by page and section key. */
146
196
  sections: {
147
197
  impressum: {
@@ -164,6 +214,8 @@ export interface LegalLabels {
164
214
  warranty: LegalTextSectionLabels;
165
215
  liability: LegalTextSectionLabels;
166
216
  changes: LegalTextSectionLabels;
217
+ /** 0.28.1: which language's version prevails — `o.bindingLanguage`, named. */
218
+ language: LegalOperatorSectionLabels;
167
219
  /** The place of jurisdiction is the operator's `{city} ({region}), {country}`. */
168
220
  law: LegalOperatorSectionLabels;
169
221
  };
@@ -189,6 +241,11 @@ export const DEFAULT_LEGAL_LABELS: LegalLabels = {
189
241
  privacy:
190
242
  "Closed beta. The legal wording below has not been reviewed by a lawyer yet and will be before a public launch. The technical descriptions — what is stored, where, and who can read it — describe what the software actually does today, and are meant to be checked against it.",
191
243
  },
244
+ translation: {
245
+ note: (o) =>
246
+ `This is a translation for your convenience. The ${o.bindingLanguage} version is binding.`,
247
+ show: (o) => `Show the ${o.bindingLanguage} version`,
248
+ },
192
249
  sections: {
193
250
  impressum: {
194
251
  operator: {
@@ -243,6 +300,11 @@ export const DEFAULT_LEGAL_LABELS: LegalLabels = {
243
300
  title: "Changes to these terms",
244
301
  body: "These terms may be updated as the service evolves. Material changes will be announced by email or in the app; continued use after a change means you accept it.",
245
302
  },
303
+ language: {
304
+ title: "Language",
305
+ body: (o) =>
306
+ `These terms are written in ${o.bindingLanguage}. Translations into other languages are provided for convenience only; where a translation differs, the ${o.bindingLanguage} version prevails.`,
307
+ },
246
308
  law: {
247
309
  title: "Governing law",
248
310
  body: (o) =>
@@ -375,6 +437,9 @@ export interface LegalLinksProps {
375
437
  * links, so in a plain container — Kurvenschmiede's account-menu `<li>` — they ran
376
438
  * together as "ImpressumDatenschutzerklärungNutz…" and overflowed). Default true. */
377
439
  nav?: boolean;
440
+ /** Called on a click on any of the links, before it navigates — an account menu closes
441
+ * itself here (0.28.1, keksdose: it had to catch the click on a wrapping `<div>`). */
442
+ onLinkClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
378
443
  labels?: LabelOverride<LegalLabels>;
379
444
  className?: string;
380
445
  }
@@ -396,12 +461,19 @@ function defaultLinks(labels: LegalLabels): LegalLink[] {
396
461
  * already hears three links, and "middle dot" between them is noise. They are flex items
397
462
  * of the row like the links, so the row's gap spaces them evenly.
398
463
  */
399
- export function LegalLinks({ links, currentHref, nav = true, labels: labelsProp, className }: LegalLinksProps) {
464
+ export function LegalLinks({
465
+ links,
466
+ currentHref,
467
+ nav = true,
468
+ onLinkClick,
469
+ labels: labelsProp,
470
+ className,
471
+ }: LegalLinksProps) {
400
472
  const labels = useLegalLabels(labelsProp);
401
473
  const shown = links ?? defaultLinks(labels);
402
474
  const items = shown.flatMap((link, i) => [
403
475
  ...(i > 0 ? [<span key={`sep-${link.href}`} aria-hidden="true">·</span>] : []),
404
- <TextLink key={link.href} href={link.href} tone="muted" current={link.href === currentHref}>
476
+ <TextLink key={link.href} href={link.href} tone="muted" current={link.href === currentHref} onClick={onLinkClick}>
405
477
  {link.label}
406
478
  </TextLink>,
407
479
  ]);
@@ -425,6 +497,14 @@ export interface LegalFooterProps {
425
497
  /** Marks that link `aria-current="page"`. {@link LegalPage}'s default footer passes
426
498
  * its own page. */
427
499
  currentHref?: string;
500
+ /**
501
+ * Default true: the footer is a `<footer>`. `false` draws a `<div>` — for a host that is
502
+ * a footer already: `AuthLayout`'s `footer` slot is one, and HTML forbids a footer inside
503
+ * a footer (0.28.1, kastlan). On an `AuthLayout` page the usual choice is simpler still:
504
+ * `footer={<LegalLinks nav={false} />}` with `footerLabel` set to the kit's `navLabel`
505
+ * ({@link useLegalLabels}).
506
+ */
507
+ landmark?: boolean;
428
508
  className?: string;
429
509
  labels?: LabelOverride<LegalLabels>;
430
510
  }
@@ -440,9 +520,10 @@ export interface LegalFooterProps {
440
520
  * only. Its palette classes (`border-slate-200 bg-white dark:…`) became the surface and
441
521
  * border tokens, so it follows the theme like the rest of the kit.
442
522
  */
443
- export function LegalFooter({ children, links, currentHref, className, labels }: LegalFooterProps) {
523
+ export function LegalFooter({ children, links, currentHref, landmark = true, className, labels }: LegalFooterProps) {
524
+ const Root = landmark ? "footer" : "div";
444
525
  return (
445
- <footer className={cn("border-t border-[var(--border)] bg-[var(--bg-surface)]", className)}>
526
+ <Root className={cn("border-t border-[var(--border)] bg-[var(--bg-surface)]", className)}>
446
527
  <div className="mx-auto max-w-5xl px-4 py-8 text-sm text-[var(--text-muted)]">
447
528
  <LegalLinks
448
529
  links={links}
@@ -452,7 +533,7 @@ export function LegalFooter({ children, links, currentHref, className, labels }:
452
533
  />
453
534
  {children !== undefined && children !== null && <p className="mt-4 text-center">{children}</p>}
454
535
  </div>
455
- </footer>
536
+ </Root>
456
537
  );
457
538
  }
458
539
 
@@ -526,6 +607,9 @@ export interface LegalLayoutProps {
526
607
  * than the others (its technical sections describe running code).
527
608
  */
528
609
  notice?: ReactNode;
610
+ /** 0.28.1: under the title, before the notice — {@link LegalPage}'s note that this
611
+ * page is a translation and which language is binding. */
612
+ translationNote?: ReactNode;
529
613
  /** The sections — {@link LegalSection}s. */
530
614
  children: ReactNode;
531
615
  /** The three pages, linked at the end of the column with the current one marked.
@@ -543,6 +627,7 @@ export function LegalLayout({
543
627
  title,
544
628
  back,
545
629
  notice,
630
+ translationNote,
546
631
  children,
547
632
  links,
548
633
  currentHref,
@@ -554,6 +639,7 @@ export function LegalLayout({
554
639
  <article className={cn("mx-auto w-full max-w-3xl px-4 py-12", className)}>
555
640
  {back !== undefined && <div className="mb-4 text-sm">{back}</div>}
556
641
  <Heading className="text-3xl font-bold tracking-tight break-words text-[var(--text-primary)]">{title}</Heading>
642
+ {translationNote !== undefined && translationNote !== null && <div className="mt-3">{translationNote}</div>}
557
643
  {notice !== undefined && notice !== null && (
558
644
  <AlertBanner tone="warning" role="note" className="mt-4">
559
645
  {notice}
@@ -779,6 +779,11 @@ export function germanLabels(numberLocale: string): UiKitLabels {
779
779
  privacy:
780
780
  "Geschlossene Beta. Die rechtlichen Formulierungen unten sind noch nicht anwaltlich geprüft; das geschieht vor einem öffentlichen Start. Die technischen Angaben — was gespeichert wird, wo, und wer es lesen kann — beschreiben, was die Software heute tatsächlich tut, und sind ausdrücklich dazu da, daran überprüft zu werden.",
781
781
  },
782
+ translation: {
783
+ note: (o) =>
784
+ `Dies ist eine Übersetzung zu Ihrer Information. Massgebend ist die Fassung auf ${o.bindingLanguage}.`,
785
+ show: (o) => `Fassung auf ${o.bindingLanguage} anzeigen`,
786
+ },
782
787
  sections: {
783
788
  impressum: {
784
789
  operator: {
@@ -833,6 +838,11 @@ export function germanLabels(numberLocale: string): UiKitLabels {
833
838
  title: "Änderungen dieser Bedingungen",
834
839
  body: "Diese Bedingungen können im Zuge der Weiterentwicklung des Dienstes angepasst werden. Wesentliche Änderungen werden per E-Mail oder in der App angekündigt; die weitere Nutzung nach einer Änderung gilt als Zustimmung.",
835
840
  },
841
+ language: {
842
+ title: "Sprache",
843
+ body: (o) =>
844
+ `Diese Bedingungen sind auf ${o.bindingLanguage} verfasst. Übersetzungen in andere Sprachen dienen nur der Information; weicht eine Übersetzung ab, ist die Fassung auf ${o.bindingLanguage} massgebend.`,
845
+ },
836
846
  law: {
837
847
  title: "Anwendbares Recht",
838
848
  body: (o) =>
@@ -757,7 +757,12 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
757
757
  notice: {
758
758
  beta: "Beta cerrada. Estos textos aún no han sido revisados por un abogado y lo serán antes de un lanzamiento público.",
759
759
  privacy:
760
- "Beta cerrada. La redacción jurídica que sigue aún no ha sido revisada por un abogado y lo será antes de un lanzamiento público. Las descripciones técnicas —qué se guarda, dónde y quién puede leerlo— describen lo que el software hace realmente hoy y están pensadas para que se contrasten con él.",
760
+ "Beta cerrada. La redacción jurídica que sigue aún no ha sido revisada por un abogado y lo será antes de un lanzamiento público. Las descripciones técnicas —qué se guarda, dónde y quién puede leerlo— describen lo que el software hace realmente hoy y deben poder contrastarse con él.",
761
+ },
762
+ translation: {
763
+ note: (o) =>
764
+ `Esta es una traducción ofrecida para su comodidad. La versión vinculante es la versión en ${o.bindingLanguage}.`,
765
+ show: (o) => `Ver la versión en ${o.bindingLanguage}`,
761
766
  },
762
767
  sections: {
763
768
  impressum: {
@@ -783,12 +788,12 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
783
788
  },
784
789
  legal_basis: {
785
790
  title: "Base jurídica",
786
- body: "Como el responsable tiene su sede en Suiza, el tratamiento se rige por la Ley Federal suiza de Protección de Datos (LPD). Cuando le sea aplicable el Reglamento General de Protección de Datos de la UE (RGPD), nos basamos en la ejecución de un contrato para prestar el servicio (art. 6.1.b RGPD) y en nuestro interés legítimo en operarlo y protegerlo (art. 6.1.f RGPD).",
791
+ body: "Como el responsable reside en Suiza, el tratamiento se rige por la Ley Federal suiza de Protección de Datos (LPD). Cuando le sea aplicable el Reglamento General de Protección de Datos de la UE (RGPD), nos basamos en la ejecución de un contrato para prestar el servicio (art. 6.1.b RGPD) y en nuestro interés legítimo en operarlo y protegerlo (art. 6.1.f RGPD).",
787
792
  },
788
793
  browser: {
789
794
  title: "Qué guarda su navegador",
790
795
  lead: "Ninguna cookie. La aplicación guarda lo siguiente en el almacenamiento de su navegador:",
791
- tail: "Nada de ello se usa para seguirle. Las entradas pendientes de envío se nos envían en cuanto es posible; todo lo demás permanece en su dispositivo. Todo ello desaparece cuando borra los datos del sitio, y al cerrar sesión se eliminan los tokens de inicio de sesión.",
796
+ tail: "Nada de ello se usa para rastrearle. Las entradas pendientes de envío se nos envían en cuanto sea posible; todo lo demás permanece en su dispositivo. Todo ello desaparece cuando borra los datos del sitio, y al cerrar sesión se eliminan los tokens de inicio de sesión.",
792
797
  },
793
798
  rights: {
794
799
  title: "Sus derechos",
@@ -813,6 +818,11 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
813
818
  title: "Cambios en estas condiciones",
814
819
  body: "Estas condiciones pueden actualizarse a medida que evolucione el servicio. Los cambios importantes se anunciarán por correo electrónico o en la aplicación; si sigue usando el servicio después de un cambio, lo acepta.",
815
820
  },
821
+ language: {
822
+ title: "Idioma",
823
+ body: (o) =>
824
+ `Estas condiciones están redactadas en ${o.bindingLanguage}. Las traducciones a otros idiomas se ofrecen solo por comodidad; en caso de discrepancia, prevalece la versión en ${o.bindingLanguage}.`,
825
+ },
816
826
  law: {
817
827
  title: "Legislación aplicable",
818
828
  body: (o) =>
@@ -777,6 +777,11 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
777
777
  privacy:
778
778
  "Bêta fermée. Les formulations juridiques ci-dessous n’ont pas encore été relues par un avocat\u202f; cela sera fait avant un lancement public. Les descriptions techniques — ce qui est stocké, où, et qui peut le lire — décrivent ce que fait réellement le logiciel aujourd’hui et sont faites pour être vérifiées par rapport à lui.",
779
779
  },
780
+ translation: {
781
+ note: (o) =>
782
+ `Ceci est une traduction fournie pour votre commodité. Seule la version en ${o.bindingLanguage} fait foi.`,
783
+ show: (o) => `Afficher la version en ${o.bindingLanguage}`,
784
+ },
780
785
  sections: {
781
786
  impressum: {
782
787
  operator: {
@@ -831,6 +836,11 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
831
836
  title: "Modifications des présentes conditions",
832
837
  body: "Ces conditions peuvent être mises à jour au fil de l’évolution du service. Les modifications importantes seront annoncées par e-mail ou dans l’app\u202f; continuer à utiliser le service après une modification vaut acceptation.",
833
838
  },
839
+ language: {
840
+ title: "Langue",
841
+ body: (o) =>
842
+ `Les présentes conditions sont rédigées en ${o.bindingLanguage}. Les traductions dans d’autres langues sont fournies à titre indicatif uniquement\u202f; en cas de divergence, la version en ${o.bindingLanguage} prévaut.`,
843
+ },
834
844
  law: {
835
845
  title: "Droit applicable",
836
846
  body: (o) =>
@@ -761,12 +761,17 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
761
761
  // The accusative ending is glued to the link, as Italian glues its article: "a
762
762
  // Felhasználási feltételeket és az Adatvédelmi tájékoztatót". Nothing is added around
763
763
  // a placeholder, and the articles fit these two titles (a F…, az A…).
764
- accept: "Elfogadom a {terms}et és az {privacy}t",
764
+ accept: "Elfogadom a következőket: {terms} és {privacy}",
765
765
  notice: {
766
766
  beta: "Zárt béta. Ezeket a szövegeket ügyvéd még nem ellenőrizte; erre a nyilvános indulás előtt sor kerül.",
767
767
  privacy:
768
768
  "Zárt béta. Az alábbi jogi szövegezést ügyvéd még nem ellenőrizte; erre a nyilvános indulás előtt sor kerül. A műszaki leírások – mit tárolunk, hol és ki olvashatja – azt írják le, amit a szoftver ma ténylegesen tesz, és arra valók, hogy a szoftverrel össze lehessen vetni őket.",
769
769
  },
770
+ translation: {
771
+ note: (o) =>
772
+ `Ez a szöveg tájékoztató jellegű fordítás; az irányadó változat nyelve: ${o.bindingLanguage}.`,
773
+ show: (o) => `Megjelenítés ezen a nyelven: ${o.bindingLanguage}`,
774
+ },
770
775
  sections: {
771
776
  impressum: {
772
777
  operator: {
@@ -791,11 +796,11 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
791
796
  },
792
797
  legal_basis: {
793
798
  title: "Jogalap",
794
- body: "Mivel az üzemeltető székhelye Svájcban van, az adatkezelésre a svájci szövetségi adatvédelmi törvény (DSG/FADP) az irányadó. Ha Önre az EU általános adatvédelmi rendelete (GDPR) vonatkozik, a szolgáltatás nyújtására irányuló szerződés teljesítésére (GDPR 6. cikk (1) b) pont) és a szolgáltatás működtetéséhez és védelméhez fűződő jogos érdekünkre (GDPR 6. cikk (1) f) pont) támaszkodunk.",
799
+ body: "Mivel az üzemeltető lakóhelye Svájcban van, az adatkezelésre a svájci szövetségi adatvédelmi törvény (DSG/FADP) az irányadó. Ha Önre az EU általános adatvédelmi rendelete (GDPR) vonatkozik, a szolgáltatás nyújtására irányuló szerződés teljesítésére (GDPR 6. cikk (1) b) pont) és a szolgáltatás működtetéséhez és védelméhez fűződő jogos érdekünkre (GDPR 6. cikk (1) f) pont) támaszkodunk.",
795
800
  },
796
801
  browser: {
797
802
  title: "Mit tárol az Ön böngészője",
798
- lead: "Nincsenek sütik. Az alkalmazás a következőket tárolja a böngészője tárhelyén:",
803
+ lead: "Nincsenek sütik. Az alkalmazás a következőket tárolja az Ön böngészőjének tárhelyén:",
799
804
  tail: "Egyiket sem használjuk az Ön követésére. A küldésre váró bejegyzéseket a böngésző elküldi nekünk, amint lehetséges; minden más az Ön eszközén marad. Mindez eltűnik, amikor törli a webhely adatait, kijelentkezéskor pedig a bejelentkezési tokenek törlődnek.",
800
805
  },
801
806
  rights: {
@@ -821,6 +826,11 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
821
826
  title: "A feltételek módosítása",
822
827
  body: "Ezek a feltételek a szolgáltatás fejlődésével módosulhatnak. A lényeges változásokat e-mailben vagy az alkalmazásban jelentjük be; aki a változás után tovább használja a szolgáltatást, elfogadja azt.",
823
828
  },
829
+ language: {
830
+ title: "Nyelv",
831
+ body: (o) =>
832
+ `Ezek a feltételek ${o.bindingLanguage} nyelven készültek. A más nyelvű fordítások csak tájékoztató jellegűek; eltérés esetén az eredeti, ${o.bindingLanguage} nyelvű változat az irányadó.`,
833
+ },
824
834
  law: {
825
835
  title: "Irányadó jog",
826
836
  body: (o) =>
@@ -764,6 +764,11 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
764
764
  privacy:
765
765
  "Beta chiusa. Le formulazioni legali qui sotto non sono ancora state verificate da un avvocato; lo saranno prima di un lancio pubblico. Le descrizioni tecniche — cosa viene memorizzato, dove e chi può leggerlo — descrivono ciò che il software fa davvero oggi e sono pensate per essere verificate rispetto a esso.",
766
766
  },
767
+ translation: {
768
+ note: (o) =>
769
+ `Questa è una traduzione fornita per Sua comodità. Fa fede la versione in ${o.bindingLanguage}.`,
770
+ show: (o) => `Mostra la versione in ${o.bindingLanguage}`,
771
+ },
767
772
  sections: {
768
773
  impressum: {
769
774
  operator: {
@@ -790,7 +795,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
790
795
  },
791
796
  legal_basis: {
792
797
  title: "Base giuridica",
793
- body: "Poiché il gestore ha sede in Svizzera, il trattamento è disciplinato dalla Legge federale svizzera sulla protezione dei dati (LPD). Dove il Regolamento generale sulla protezione dei dati dell’UE (GDPR) si applica a Lei, ci basiamo sull’esecuzione di un contratto per fornire il servizio (art. 6, par. 1, lett. b GDPR) e sul nostro legittimo interesse a gestirlo e proteggerlo (art. 6, par. 1, lett. f GDPR).",
798
+ body: "Poiché il gestore risiede in Svizzera, il trattamento è disciplinato dalla Legge federale svizzera sulla protezione dei dati (LPD). Dove il Regolamento generale sulla protezione dei dati dell’UE (GDPR) si applica a Lei, ci basiamo sull’esecuzione di un contratto per fornire il servizio (art. 6, par. 1, lett. b GDPR) e sul nostro legittimo interesse a gestirlo e proteggerlo (art. 6, par. 1, lett. f GDPR).",
794
799
  },
795
800
  browser: {
796
801
  title: "Che cosa salva il Suo browser",
@@ -820,6 +825,11 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
820
825
  title: "Modifiche a queste condizioni",
821
826
  body: "Queste condizioni possono essere aggiornate con l’evoluzione del servizio. Le modifiche sostanziali saranno annunciate via email o nell’app; l’uso continuato dopo una modifica ne implica l’accettazione.",
822
827
  },
828
+ language: {
829
+ title: "Lingua",
830
+ body: (o) =>
831
+ `Le presenti condizioni sono redatte in ${o.bindingLanguage}. Le traduzioni in altre lingue sono fornite solo per comodità; in caso di divergenza prevale la versione in ${o.bindingLanguage}.`,
832
+ },
823
833
  law: {
824
834
  title: "Legge applicabile",
825
835
  body: (o) =>
@@ -736,12 +736,17 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
736
736
  },
737
737
  backHome: "返回首页",
738
738
  // Book-title marks around the two documents, outside the links.
739
- accept: "我接受《{terms}》和《{privacy}》",
739
+ accept: "我已阅读并同意《{terms}》和《{privacy}》",
740
740
  notice: {
741
741
  beta: "封闭测试阶段。这些文本尚未经律师审阅,将在公开发布之前完成审阅。",
742
742
  privacy:
743
743
  "封闭测试阶段。以下法律措辞尚未经律师审阅,将在公开发布之前完成审阅。其中的技术说明——存储了什么、存储在哪里以及谁可以读取——描述的是软件目前的实际行为,旨在供人对照软件加以核实。",
744
744
  },
745
+ translation: {
746
+ note: (o) =>
747
+ `本页为方便阅读而提供的译文,以${o.bindingLanguage}版本为准。`,
748
+ show: (o) => `查看${o.bindingLanguage}版本`,
749
+ },
745
750
  sections: {
746
751
  impressum: {
747
752
  operator: {
@@ -796,6 +801,11 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
796
801
  title: "条款变更",
797
802
  body: "本条款可能会随着服务的发展而更新。重大变更将通过电子邮件或在应用程序中公布;变更后继续使用本服务即表示您接受变更。",
798
803
  },
804
+ language: {
805
+ title: "语言",
806
+ body: (o) =>
807
+ `本条款以${o.bindingLanguage}撰写。其他语言的译文仅供参考;如译文与原文不一致,以${o.bindingLanguage}版本为准。`,
808
+ },
799
809
  law: {
800
810
  title: "适用法律",
801
811
  body: (o) =>
@@ -1,5 +1,5 @@
1
+ import { legalOperatorText } from "../components/legal";
1
2
  import type { LegalOperator, LegalOperatorText } from "../components/legal";
2
- import { countryName } from "../lib/countries";
3
3
  import type { UiKitLabels } from "./kit-labels";
4
4
 
5
5
  /**
@@ -40,7 +40,7 @@ import type { UiKitLabels } from "./kit-labels";
40
40
  */
41
41
  export function kitLabelStrings(labels: UiKitLabels, options: KitLabelStringsOptions = {}): Record<string, string> {
42
42
  const out: Record<string, string> = {};
43
- const operator = options.operator && operatorText(options.operator, options.locale);
43
+ const operator = options.operator && legalOperatorText(options.operator, options.locale);
44
44
  const walk = (node: unknown, path: string) => {
45
45
  if (typeof node === "string") {
46
46
  out[path] = node;
@@ -76,10 +76,6 @@ export interface KitLabelStringsOptions {
76
76
  locale?: string;
77
77
  }
78
78
 
79
- function operatorText(operator: LegalOperator, locale = "en"): LegalOperatorText {
80
- return { ...operator, country: countryName(operator.country, locale) };
81
- }
82
-
83
79
  function render(label: (...args: unknown[]) => unknown, args: readonly unknown[]): string {
84
80
  try {
85
81
  return String(label(...args));
@@ -112,6 +108,7 @@ const LEGAL_OPERATOR_SAMPLE: LegalOperatorText = Object.freeze({
112
108
  region: "{{region}}",
113
109
  country: "{{country}}",
114
110
  email: "{{email}}",
111
+ bindingLanguage: "{{bindingLanguage}}",
115
112
  });
116
113
  const operatorSample = [[LEGAL_OPERATOR_SAMPLE]] as const;
117
114
 
@@ -293,4 +290,8 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
293
290
  "legal.sections.privacy.controller.body": operatorSample,
294
291
  "legal.sections.privacy.contact.body": operatorSample,
295
292
  "legal.sections.terms.law.body": operatorSample,
293
+ // 0.28.1: the binding language, named in the reader's language.
294
+ "legal.sections.terms.language.body": operatorSample,
295
+ "legal.translation.note": operatorSample,
296
+ "legal.translation.show": operatorSample,
296
297
  };
package/src/index.ts CHANGED
@@ -462,7 +462,16 @@ export type {
462
462
  // The legal pages' shell (0.19, H10): Imprint, Privacy Policy, Terms in every app.
463
463
  // 0.28 (docs/legal-harmonization.md): the shared wording, the page frame, the kit's
464
464
  // sections, the public footer and the terms checkbox.
465
- export { LegalLayout, LegalSection, LegalLinks, LegalFooter, DEFAULT_LEGAL_LABELS } from "./components/legal";
465
+ export {
466
+ LegalLayout,
467
+ LegalSection,
468
+ LegalLinks,
469
+ LegalFooter,
470
+ DEFAULT_LEGAL_LABELS,
471
+ LEGAL_HREFS,
472
+ legalOperatorText,
473
+ useLegalLabels,
474
+ } from "./components/legal";
466
475
  export type {
467
476
  LegalLabels,
468
477
  LegalLink,