@saasicat/ui-vue 0.16.0 → 0.17.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 (40) hide show
  1. package/README.md +12 -11
  2. package/dist/{messages-DetB75lX.d.ts → catalog-C4Gv2_KM.d.cts} +115 -40
  3. package/dist/{messages-DetB75lX.d.cts → catalog-C4Gv2_KM.d.ts} +115 -40
  4. package/dist/{chunk-MGKJCVDU.js → chunk-247RJPU4.js} +18 -18
  5. package/dist/{chunk-TZ2NGQXK.js → chunk-IZGFS5RS.js} +44 -8
  6. package/dist/{chunk-CRUUZMMN.js → chunk-XPSD5HKG.js} +30 -4
  7. package/dist/client/index.cjs +75 -10
  8. package/dist/client/index.d.cts +51 -5
  9. package/dist/client/index.d.ts +51 -5
  10. package/dist/client/index.js +12 -4
  11. package/dist/index.cjs +90 -22
  12. package/dist/index.d.cts +5 -5
  13. package/dist/index.d.ts +5 -5
  14. package/dist/index.js +13 -5
  15. package/dist/quasar/index.cjs +54 -20
  16. package/dist/quasar/index.d.cts +2 -2
  17. package/dist/quasar/index.d.ts +2 -2
  18. package/dist/quasar/index.js +2 -2
  19. package/dist/{use-super-admin-i18n-DokHhkS_.d.cts → use-super-admin-i18n-BdPqeTHl.d.cts} +29 -9
  20. package/dist/{use-super-admin-i18n-BPC-H9jz.d.ts → use-super-admin-i18n-uteRCL9q.d.ts} +29 -9
  21. package/package.json +8 -3
  22. package/src/client/i18n/catalog.ts +120 -0
  23. package/src/client/i18n/currency.ts +7 -2
  24. package/src/client/i18n/define.ts +2 -2
  25. package/src/client/i18n/index.ts +1 -0
  26. package/src/client/i18n/locale.ts +40 -11
  27. package/src/client/i18n/messages/bundles.ts +2 -3
  28. package/src/client/i18n/messages/marketing.ts +2 -2
  29. package/src/client/i18n/messages/nav.ts +3 -3
  30. package/src/client/i18n/messages.ts +7 -4
  31. package/src/client/index.ts +1 -0
  32. package/src/client/login-branding.ts +61 -0
  33. package/src/client/nav-builder.ts +33 -6
  34. package/src/components/LocaleSwitcher.vue +21 -12
  35. package/src/components/bundle-editor/bundle-version-status.ts +16 -8
  36. package/src/pages-standard/AdminLayout.vue +7 -3
  37. package/src/pages-standard/SuperAdminLoginPage.vue +8 -9
  38. package/src/pages-standard/discovery-page/discovery-ui.ts +8 -8
  39. package/src/pages-standard/plan-versions/format.ts +2 -2
  40. package/src/vue/use-super-admin-i18n.ts +59 -28
package/README.md CHANGED
@@ -15,12 +15,12 @@ The package is layered so that each entry only loads what it names.
15
15
  Lower layers never import upward; ESLint (`no-restricted-imports`, repo
16
16
  root config) enforces the boundaries in CI.
17
17
 
18
- | Entry | Source | May import | Contents |
19
- | --- | --- | --- | --- |
20
- | `@saasicat/ui-vue/client` | `src/client/` | `@saasicat/types` only | Framework-free core: `BootLoader`, `ManifestLoader` (ETag cache), nav builder, action registry, batch column fetcher, `HttpClient`/`KvStore` contract. Usable from any framework or plain Node/TypeScript. |
21
- | `@saasicat/ui-vue` (main) | `src/vue/` (+ client re-exports) | Vue, `vue-router`, Pinia — **no Quasar** | Composables (`useTenants`, `usePlanEditor`, …), router guards, injection keys + shell contract, notify-port type, optional Pinia store factory. |
22
- | `@saasicat/ui-vue/quasar` | `src/quasar/` | everything above + Quasar | `createSuperAdminApp()` bootstrap and the Quasar notify-port implementation. |
23
- | `@saasicat/ui-vue/pages-standard/*`, `/pages-tenant/*`, `/components/*` | SFC directories | everything | The Quasar reference UI, shipped as raw `.vue` from `src/` (compiled by the consumer's Vite). |
18
+ | Entry | Source | May import | Contents |
19
+ | ----------------------------------------------------------------------- | -------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
+ | `@saasicat/ui-vue/client` | `src/client/` | `@saasicat/types` only | Framework-free core: `BootLoader`, `ManifestLoader` (ETag cache), nav builder, action registry, batch column fetcher, `HttpClient`/`KvStore` contract. Usable from any framework or plain Node/TypeScript. |
21
+ | `@saasicat/ui-vue` (main) | `src/vue/` (+ client re-exports) | Vue, `vue-router`, Pinia — **no Quasar** | Composables (`useTenants`, `usePlanEditor`, …), router guards, injection keys + shell contract, notify-port type, optional Pinia store factory. |
22
+ | `@saasicat/ui-vue/quasar` | `src/quasar/` | everything above + Quasar | `createSuperAdminApp()` bootstrap and the Quasar notify-port implementation. |
23
+ | `@saasicat/ui-vue/pages-standard/*`, `/pages-tenant/*`, `/components/*` | SFC directories | everything | The Quasar reference UI, shipped as raw `.vue` from `src/` (compiled by the consumer's Vite). |
24
24
 
25
25
  Rules of thumb when contributing:
26
26
 
@@ -49,7 +49,7 @@ createSuperAdminApp({
49
49
  ```
50
50
 
51
51
  Confirm dialogs inside the reference pages intentionally keep using Quasar's
52
- `Dialog` — the pages *are* the Quasar layer.
52
+ `Dialog` — the pages _are_ the Quasar layer.
53
53
 
54
54
  ## Usage
55
55
 
@@ -93,10 +93,11 @@ createSuperAdminApp({
93
93
  ```
94
94
 
95
95
  Users switch languages themselves through the shell's `LocaleSwitcher` (header
96
- and login page); the pick is remembered. `i18n.switcher: false` removes it for a
97
- single-language deployment, a `Ref<SaLocale>` hands the value to the app, and
98
- `i18n.overrides` replaces individual strings per locale. Components read the
99
- catalog via `useSaMessages('<namespace>')` / `useSuperAdminI18n()`.
96
+ and login page); the pick is remembered. Which languages an app offers is its
97
+ own call: `i18n.locales` narrows the set, `i18n.additionalLocales` adds
98
+ languages the platform does not ship, `i18n.overrides` replaces individual
99
+ strings, and `i18n.switcher: false` drops the control entirely. Components read
100
+ the catalog via `useSaMessages('<namespace>')` / `useSuperAdminI18n()`.
100
101
  See [handbook §8.6](https://github.com/uelker70/saasicat/blob/main/docs/handbook.md#86-ui-language-i18n).
101
102
 
102
103
  ## Build
@@ -1,4 +1,4 @@
1
- import { AdminManifest, TenantActionDef, StandardPageKey } from '@saasicat/types';
1
+ import { StandardPageKey, AdminManifest, TenantActionDef } from '@saasicat/types';
2
2
 
3
3
  /**
4
4
  * Minimal abstraction over `fetch`. Consumers may pass their own
@@ -38,19 +38,49 @@ declare function defaultKvStore(): KvStore;
38
38
  */
39
39
  declare function defaultHttpClient(): HttpClient;
40
40
 
41
- type SaLocale = 'de' | 'en';
42
- declare const SA_LOCALES: readonly SaLocale[];
43
- declare const DEFAULT_SA_LOCALE: SaLocale;
44
- /** BCP-47 tags used for `Intl`/`toLocaleString` formatting per UI locale. */
45
- declare const SA_INTL_LOCALES: Record<SaLocale, string>;
41
+ /** Locales the platform itself ships a catalog for. */
42
+ type SaBuiltinLocale = 'de' | 'en';
43
+ /**
44
+ * Any locale the shell can run in: the built-ins plus whatever an app adds via
45
+ * `i18n.additionalLocales`. The `string & {}` arm keeps autocompletion for the
46
+ * built-ins while leaving the set open.
47
+ */
48
+ type SaLocale = SaBuiltinLocale | (string & {});
49
+ /** The locales the platform ships. Apps narrow or extend this, see `i18n`. */
50
+ declare const SA_LOCALES: readonly SaBuiltinLocale[];
51
+ declare const DEFAULT_SA_LOCALE: SaBuiltinLocale;
52
+ /** BCP-47 tags used for `Intl`/`toLocaleString` formatting per built-in locale. */
53
+ declare const SA_INTL_LOCALES: Record<SaBuiltinLocale, string>;
46
54
  /**
47
55
  * Names for the language switcher. Endonyms — a language is listed in its own
48
56
  * language, so the entries stay readable no matter which locale is active and
49
57
  * never need translating.
50
58
  */
51
- declare const SA_LOCALE_LABELS: Record<SaLocale, string>;
52
- /** Narrows untrusted input (persisted preference, query parameter) to a locale. */
53
- declare function isSaLocale(value: unknown): value is SaLocale;
59
+ declare const SA_LOCALE_LABELS: Record<SaBuiltinLocale, string>;
60
+ /** Narrows untrusted input to a locale the platform ships a catalog for. */
61
+ declare function isSaBuiltinLocale(value: unknown): value is SaBuiltinLocale;
62
+ /**
63
+ * The built-in catalog a locale reads from. Helper modules that index the raw
64
+ * catalogs (rather than the resolved one from `useSaMessages`) need this: a
65
+ * language an app added has no entry there, and indexing it would yield
66
+ * `undefined` and take the page down.
67
+ *
68
+ * Strings resolved this way stay in the fallback language for an app-supplied
69
+ * locale — migrating those helpers to the resolved catalog is tracked
70
+ * separately.
71
+ */
72
+ declare function builtinLocaleOf(locale: SaLocale | undefined, fallback?: SaBuiltinLocale): SaBuiltinLocale;
73
+
74
+ type SaNavMessages = {
75
+ readonly pages: Record<StandardPageKey, string>;
76
+ readonly sections: {
77
+ readonly overview: string;
78
+ readonly catalog: string;
79
+ readonly customers: string;
80
+ readonly system: string;
81
+ };
82
+ };
83
+ declare const navMessages: Record<SaBuiltinLocale, SaNavMessages>;
54
84
 
55
85
  /** Consumer implementation; receives the action inputs as a generic object. */
56
86
  type ActionHandler<TInput = unknown, TResult = unknown> = (input: TInput) => Promise<TResult>;
@@ -115,7 +145,7 @@ type PartialMessages<T> = {
115
145
  * Couples the German reference catalog with its English translation. Missing
116
146
  * or extra keys in the English object are compile errors.
117
147
  */
118
- declare function defineMessages<T extends MessageTree>(de: T, en: TranslationOf<T>): Record<SaLocale, T>;
148
+ declare function defineMessages<T extends MessageTree>(de: T, en: TranslationOf<T>): Record<SaBuiltinLocale, T>;
119
149
  /**
120
150
  * Recursively overlays `overrides` onto `base` without mutating either side.
121
151
  * Only string leaves are replaced; keys not present in `base` are ignored —
@@ -123,7 +153,7 @@ declare function defineMessages<T extends MessageTree>(de: T, en: TranslationOf<
123
153
  */
124
154
  declare function mergeMessages<T extends MessageTree>(base: T, overrides: PartialMessages<T> | undefined): T;
125
155
 
126
- declare const auditMessages: Record<SaLocale, {
156
+ declare const auditMessages: Record<SaBuiltinLocale, {
127
157
  title: string;
128
158
  subtitle: string;
129
159
  filterButton: string;
@@ -143,7 +173,7 @@ declare const auditMessages: Record<SaLocale, {
143
173
  };
144
174
  }>;
145
175
 
146
- declare const bundlesMessages: Record<SaLocale, {
176
+ declare const bundlesMessages: Record<SaBuiltinLocale, {
147
177
  fields: {
148
178
  masterData: string;
149
179
  label: string;
@@ -349,7 +379,7 @@ declare const bundlesMessages: Record<SaLocale, {
349
379
  };
350
380
  }>;
351
381
 
352
- declare const commonMessages: Record<SaLocale, {
382
+ declare const commonMessages: Record<SaBuiltinLocale, {
353
383
  save: string;
354
384
  cancel: string;
355
385
  close: string;
@@ -420,14 +450,14 @@ declare const commonMessages: Record<SaLocale, {
420
450
  noChanges: string;
421
451
  }>;
422
452
 
423
- declare const dashboardMessages: Record<SaLocale, {
453
+ declare const dashboardMessages: Record<SaBuiltinLocale, {
424
454
  title: string;
425
455
  emptyKpiCards: string;
426
456
  shortcutsTitle: string;
427
457
  deltaVsPreviousPeriod: string;
428
458
  }>;
429
459
 
430
- declare const discoveryMessages: Record<SaLocale, {
460
+ declare const discoveryMessages: Record<SaBuiltinLocale, {
431
461
  title: string;
432
462
  subtitleLead: string;
433
463
  subtitleEmphasis: string;
@@ -532,7 +562,7 @@ declare const discoveryMessages: Record<SaLocale, {
532
562
  };
533
563
  }>;
534
564
 
535
- declare const emailMessages: Record<SaLocale, {
565
+ declare const emailMessages: Record<SaBuiltinLocale, {
536
566
  sender: string;
537
567
  recipient: string;
538
568
  errorAction: string;
@@ -592,7 +622,7 @@ declare const emailMessages: Record<SaLocale, {
592
622
  };
593
623
  }>;
594
624
 
595
- declare const marketingMessages: Record<SaLocale, {
625
+ declare const marketingMessages: Record<SaBuiltinLocale, {
596
626
  topFeatures: string;
597
627
  tabs: {
598
628
  preview: string;
@@ -721,18 +751,7 @@ declare const marketingMessages: Record<SaLocale, {
721
751
  };
722
752
  }>;
723
753
 
724
- type NavNamespace = {
725
- readonly pages: Record<StandardPageKey, string>;
726
- readonly sections: {
727
- readonly overview: string;
728
- readonly catalog: string;
729
- readonly customers: string;
730
- readonly system: string;
731
- };
732
- };
733
- declare const navMessages: Record<SaLocale, NavNamespace>;
734
-
735
- declare const pilotsMessages: Record<SaLocale, {
754
+ declare const pilotsMessages: Record<SaBuiltinLocale, {
736
755
  title: string;
737
756
  subtitleCount: string;
738
757
  createAction: string;
@@ -826,7 +845,7 @@ declare const pilotsMessages: Record<SaLocale, {
826
845
  };
827
846
  }>;
828
847
 
829
- declare const planDetailMessages: Record<SaLocale, {
848
+ declare const planDetailMessages: Record<SaBuiltinLocale, {
830
849
  header: {
831
850
  noDescription: string;
832
851
  nameEditHint: string;
@@ -954,7 +973,7 @@ declare const planDetailMessages: Record<SaLocale, {
954
973
  };
955
974
  }>;
956
975
 
957
- declare const planEditorMessages: Record<SaLocale, {
976
+ declare const planEditorMessages: Record<SaBuiltinLocale, {
958
977
  perMonthShort: string;
959
978
  perYearShort: string;
960
979
  sections: {
@@ -1039,7 +1058,7 @@ declare const planEditorMessages: Record<SaLocale, {
1039
1058
  };
1040
1059
  }>;
1041
1060
 
1042
- declare const planVersionsMessages: Record<SaLocale, {
1061
+ declare const planVersionsMessages: Record<SaBuiltinLocale, {
1043
1062
  compareEnd: string;
1044
1063
  status: {
1045
1064
  draft: string;
@@ -1092,7 +1111,7 @@ declare const planVersionsMessages: Record<SaLocale, {
1092
1111
  };
1093
1112
  }>;
1094
1113
 
1095
- declare const plansMessages: Record<SaLocale, {
1114
+ declare const plansMessages: Record<SaBuiltinLocale, {
1096
1115
  page: {
1097
1116
  loading: string;
1098
1117
  toastPlanCreated: string;
@@ -1308,7 +1327,7 @@ declare const plansMessages: Record<SaLocale, {
1308
1327
  };
1309
1328
  }>;
1310
1329
 
1311
- declare const promosMessages: Record<SaLocale, {
1330
+ declare const promosMessages: Record<SaBuiltinLocale, {
1312
1331
  title: string;
1313
1332
  createAction: string;
1314
1333
  apiErrorHttpStatus: string;
@@ -1407,7 +1426,7 @@ declare const promosMessages: Record<SaLocale, {
1407
1426
  };
1408
1427
  }>;
1409
1428
 
1410
- declare const shellMessages: Record<SaLocale, {
1429
+ declare const shellMessages: Record<SaBuiltinLocale, {
1411
1430
  emailLabel: string;
1412
1431
  passwordLabel: string;
1413
1432
  invalidEmail: string;
@@ -1476,7 +1495,7 @@ declare const shellMessages: Record<SaLocale, {
1476
1495
  };
1477
1496
  }>;
1478
1497
 
1479
- declare const tenantsMessages: Record<SaLocale, {
1498
+ declare const tenantsMessages: Record<SaBuiltinLocale, {
1480
1499
  tenant: string;
1481
1500
  plan: string;
1482
1501
  list: {
@@ -1526,7 +1545,7 @@ declare const tenantsMessages: Record<SaLocale, {
1526
1545
  };
1527
1546
  }>;
1528
1547
 
1529
- declare const usersMessages: Record<SaLocale, {
1548
+ declare const usersMessages: Record<SaBuiltinLocale, {
1530
1549
  title: string;
1531
1550
  filterQuery: string;
1532
1551
  filterTenant: string;
@@ -1587,11 +1606,67 @@ type SaMessages = {
1587
1606
  /** Shape of app-side string overrides (deep partial of the catalog). */
1588
1607
  type SaMessagesOverrides = PartialMessages<SaMessages>;
1589
1608
  /** Complete, immutable message catalogs per locale. */
1590
- declare const SA_MESSAGES: Record<SaLocale, SaMessages>;
1609
+ declare const SA_MESSAGES: Record<SaBuiltinLocale, SaMessages>;
1591
1610
  /**
1592
1611
  * Returns the platform catalog for `locale`, optionally overlaid with
1593
1612
  * app-side overrides.
1594
1613
  */
1595
- declare function resolveMessages(locale: SaLocale, overrides?: SaMessagesOverrides): SaMessages;
1614
+ declare function resolveMessages(locale: SaBuiltinLocale, overrides?: SaMessagesOverrides): SaMessages;
1615
+
1616
+ /** A language an app brings along, on top of the two the platform ships. */
1617
+ interface SaLocaleDefinition {
1618
+ /**
1619
+ * Name for the switcher, written in the language itself (`'Français'`) —
1620
+ * that way the entry stays readable whatever locale is currently active.
1621
+ */
1622
+ label: string;
1623
+ /** BCP-47 tag for `Intl`/`toLocaleString`, e.g. `'fr-FR'`. */
1624
+ intlLocale: string;
1625
+ /**
1626
+ * Built-in catalog that fills whatever `messages` leaves out, default
1627
+ * `'en'`. A partial translation therefore renders — untranslated keys fall
1628
+ * back instead of showing blanks.
1629
+ */
1630
+ basedOn?: SaBuiltinLocale;
1631
+ /** The translation. Every key is optional; see `basedOn`. */
1632
+ messages: SaMessagesOverrides;
1633
+ }
1634
+ interface SaCatalogOptions {
1635
+ /**
1636
+ * Locales offered in the switcher, in the order given. Default: the
1637
+ * built-ins plus everything in `additionalLocales`. Narrow it to ship a
1638
+ * single language; entries without a catalog are ignored.
1639
+ */
1640
+ locales?: readonly SaLocale[];
1641
+ /** Languages the app adds, keyed by locale code (`'fr'`, `'pt-BR'`, …). */
1642
+ additionalLocales?: Readonly<Record<string, SaLocaleDefinition>>;
1643
+ /** Per-locale wording changes layered on top of whatever catalog applies. */
1644
+ overrides?: Readonly<Partial<Record<SaLocale, SaMessagesOverrides>>>;
1645
+ }
1646
+ /** One entry of the switcher. */
1647
+ interface SaLocaleOption {
1648
+ code: SaLocale;
1649
+ label: string;
1650
+ }
1651
+ interface SaCatalog {
1652
+ /** Locales to offer, in switcher order. Never empty. */
1653
+ readonly locales: readonly SaLocaleOption[];
1654
+ /**
1655
+ * Whether the app offers this locale. Deliberately not "can it be
1656
+ * rendered": an app that narrowed to English must not be pushed back into
1657
+ * German by a stored pick from before, even though German is still shipped.
1658
+ */
1659
+ has(locale: SaLocale): boolean;
1660
+ /** Locale to start from when nothing else is known. */
1661
+ readonly defaultLocale: SaLocale;
1662
+ messagesFor(locale: SaLocale): SaMessages;
1663
+ intlLocaleFor(locale: SaLocale): string;
1664
+ labelFor(locale: SaLocale): string;
1665
+ }
1666
+ /**
1667
+ * Builds the catalog an app runs on. Resolution is cached per locale, so
1668
+ * switching back and forth does not re-merge the message tree.
1669
+ */
1670
+ declare function createSaCatalog(options?: SaCatalogOptions): SaCatalog;
1596
1671
 
1597
- export { type ActionHandler as A, DEFAULT_SA_LOCALE as D, type HttpClient as H, type KvStore as K, type MessageTree as M, type PartialMessages as P, type ResolvedAction as R, type SaLocale as S, type TranslationOf as T, type SaMessages as a, type SaMessagesOverrides as b, ActionRegistry as c, ActionDefNotInManifestError as d, type HttpResponse as e, MissingHandlerError as f, SA_INTL_LOCALES as g, SA_LOCALES as h, SA_LOCALE_LABELS as i, SA_MESSAGES as j, defaultHttpClient as k, defaultKvStore as l, defineMessages as m, isSaLocale as n, mergeMessages as o, resolveMessages as r };
1672
+ export { type ActionHandler as A, DEFAULT_SA_LOCALE as D, type HttpClient as H, type KvStore as K, type MessageTree as M, type PartialMessages as P, type ResolvedAction as R, type SaLocale as S, type TranslationOf as T, type SaMessages as a, type SaLocaleOption as b, type SaLocaleDefinition as c, type SaMessagesOverrides as d, ActionRegistry as e, ActionDefNotInManifestError as f, type HttpResponse as g, MissingHandlerError as h, SA_INTL_LOCALES as i, SA_LOCALES as j, SA_LOCALE_LABELS as k, SA_MESSAGES as l, type SaBuiltinLocale as m, type SaCatalog as n, type SaCatalogOptions as o, builtinLocaleOf as p, createSaCatalog as q, defaultHttpClient as r, defaultKvStore as s, defineMessages as t, isSaBuiltinLocale as u, mergeMessages as v, resolveMessages as w, type SaNavMessages as x };
@@ -1,4 +1,4 @@
1
- import { AdminManifest, TenantActionDef, StandardPageKey } from '@saasicat/types';
1
+ import { StandardPageKey, AdminManifest, TenantActionDef } from '@saasicat/types';
2
2
 
3
3
  /**
4
4
  * Minimal abstraction over `fetch`. Consumers may pass their own
@@ -38,19 +38,49 @@ declare function defaultKvStore(): KvStore;
38
38
  */
39
39
  declare function defaultHttpClient(): HttpClient;
40
40
 
41
- type SaLocale = 'de' | 'en';
42
- declare const SA_LOCALES: readonly SaLocale[];
43
- declare const DEFAULT_SA_LOCALE: SaLocale;
44
- /** BCP-47 tags used for `Intl`/`toLocaleString` formatting per UI locale. */
45
- declare const SA_INTL_LOCALES: Record<SaLocale, string>;
41
+ /** Locales the platform itself ships a catalog for. */
42
+ type SaBuiltinLocale = 'de' | 'en';
43
+ /**
44
+ * Any locale the shell can run in: the built-ins plus whatever an app adds via
45
+ * `i18n.additionalLocales`. The `string & {}` arm keeps autocompletion for the
46
+ * built-ins while leaving the set open.
47
+ */
48
+ type SaLocale = SaBuiltinLocale | (string & {});
49
+ /** The locales the platform ships. Apps narrow or extend this, see `i18n`. */
50
+ declare const SA_LOCALES: readonly SaBuiltinLocale[];
51
+ declare const DEFAULT_SA_LOCALE: SaBuiltinLocale;
52
+ /** BCP-47 tags used for `Intl`/`toLocaleString` formatting per built-in locale. */
53
+ declare const SA_INTL_LOCALES: Record<SaBuiltinLocale, string>;
46
54
  /**
47
55
  * Names for the language switcher. Endonyms — a language is listed in its own
48
56
  * language, so the entries stay readable no matter which locale is active and
49
57
  * never need translating.
50
58
  */
51
- declare const SA_LOCALE_LABELS: Record<SaLocale, string>;
52
- /** Narrows untrusted input (persisted preference, query parameter) to a locale. */
53
- declare function isSaLocale(value: unknown): value is SaLocale;
59
+ declare const SA_LOCALE_LABELS: Record<SaBuiltinLocale, string>;
60
+ /** Narrows untrusted input to a locale the platform ships a catalog for. */
61
+ declare function isSaBuiltinLocale(value: unknown): value is SaBuiltinLocale;
62
+ /**
63
+ * The built-in catalog a locale reads from. Helper modules that index the raw
64
+ * catalogs (rather than the resolved one from `useSaMessages`) need this: a
65
+ * language an app added has no entry there, and indexing it would yield
66
+ * `undefined` and take the page down.
67
+ *
68
+ * Strings resolved this way stay in the fallback language for an app-supplied
69
+ * locale — migrating those helpers to the resolved catalog is tracked
70
+ * separately.
71
+ */
72
+ declare function builtinLocaleOf(locale: SaLocale | undefined, fallback?: SaBuiltinLocale): SaBuiltinLocale;
73
+
74
+ type SaNavMessages = {
75
+ readonly pages: Record<StandardPageKey, string>;
76
+ readonly sections: {
77
+ readonly overview: string;
78
+ readonly catalog: string;
79
+ readonly customers: string;
80
+ readonly system: string;
81
+ };
82
+ };
83
+ declare const navMessages: Record<SaBuiltinLocale, SaNavMessages>;
54
84
 
55
85
  /** Consumer implementation; receives the action inputs as a generic object. */
56
86
  type ActionHandler<TInput = unknown, TResult = unknown> = (input: TInput) => Promise<TResult>;
@@ -115,7 +145,7 @@ type PartialMessages<T> = {
115
145
  * Couples the German reference catalog with its English translation. Missing
116
146
  * or extra keys in the English object are compile errors.
117
147
  */
118
- declare function defineMessages<T extends MessageTree>(de: T, en: TranslationOf<T>): Record<SaLocale, T>;
148
+ declare function defineMessages<T extends MessageTree>(de: T, en: TranslationOf<T>): Record<SaBuiltinLocale, T>;
119
149
  /**
120
150
  * Recursively overlays `overrides` onto `base` without mutating either side.
121
151
  * Only string leaves are replaced; keys not present in `base` are ignored —
@@ -123,7 +153,7 @@ declare function defineMessages<T extends MessageTree>(de: T, en: TranslationOf<
123
153
  */
124
154
  declare function mergeMessages<T extends MessageTree>(base: T, overrides: PartialMessages<T> | undefined): T;
125
155
 
126
- declare const auditMessages: Record<SaLocale, {
156
+ declare const auditMessages: Record<SaBuiltinLocale, {
127
157
  title: string;
128
158
  subtitle: string;
129
159
  filterButton: string;
@@ -143,7 +173,7 @@ declare const auditMessages: Record<SaLocale, {
143
173
  };
144
174
  }>;
145
175
 
146
- declare const bundlesMessages: Record<SaLocale, {
176
+ declare const bundlesMessages: Record<SaBuiltinLocale, {
147
177
  fields: {
148
178
  masterData: string;
149
179
  label: string;
@@ -349,7 +379,7 @@ declare const bundlesMessages: Record<SaLocale, {
349
379
  };
350
380
  }>;
351
381
 
352
- declare const commonMessages: Record<SaLocale, {
382
+ declare const commonMessages: Record<SaBuiltinLocale, {
353
383
  save: string;
354
384
  cancel: string;
355
385
  close: string;
@@ -420,14 +450,14 @@ declare const commonMessages: Record<SaLocale, {
420
450
  noChanges: string;
421
451
  }>;
422
452
 
423
- declare const dashboardMessages: Record<SaLocale, {
453
+ declare const dashboardMessages: Record<SaBuiltinLocale, {
424
454
  title: string;
425
455
  emptyKpiCards: string;
426
456
  shortcutsTitle: string;
427
457
  deltaVsPreviousPeriod: string;
428
458
  }>;
429
459
 
430
- declare const discoveryMessages: Record<SaLocale, {
460
+ declare const discoveryMessages: Record<SaBuiltinLocale, {
431
461
  title: string;
432
462
  subtitleLead: string;
433
463
  subtitleEmphasis: string;
@@ -532,7 +562,7 @@ declare const discoveryMessages: Record<SaLocale, {
532
562
  };
533
563
  }>;
534
564
 
535
- declare const emailMessages: Record<SaLocale, {
565
+ declare const emailMessages: Record<SaBuiltinLocale, {
536
566
  sender: string;
537
567
  recipient: string;
538
568
  errorAction: string;
@@ -592,7 +622,7 @@ declare const emailMessages: Record<SaLocale, {
592
622
  };
593
623
  }>;
594
624
 
595
- declare const marketingMessages: Record<SaLocale, {
625
+ declare const marketingMessages: Record<SaBuiltinLocale, {
596
626
  topFeatures: string;
597
627
  tabs: {
598
628
  preview: string;
@@ -721,18 +751,7 @@ declare const marketingMessages: Record<SaLocale, {
721
751
  };
722
752
  }>;
723
753
 
724
- type NavNamespace = {
725
- readonly pages: Record<StandardPageKey, string>;
726
- readonly sections: {
727
- readonly overview: string;
728
- readonly catalog: string;
729
- readonly customers: string;
730
- readonly system: string;
731
- };
732
- };
733
- declare const navMessages: Record<SaLocale, NavNamespace>;
734
-
735
- declare const pilotsMessages: Record<SaLocale, {
754
+ declare const pilotsMessages: Record<SaBuiltinLocale, {
736
755
  title: string;
737
756
  subtitleCount: string;
738
757
  createAction: string;
@@ -826,7 +845,7 @@ declare const pilotsMessages: Record<SaLocale, {
826
845
  };
827
846
  }>;
828
847
 
829
- declare const planDetailMessages: Record<SaLocale, {
848
+ declare const planDetailMessages: Record<SaBuiltinLocale, {
830
849
  header: {
831
850
  noDescription: string;
832
851
  nameEditHint: string;
@@ -954,7 +973,7 @@ declare const planDetailMessages: Record<SaLocale, {
954
973
  };
955
974
  }>;
956
975
 
957
- declare const planEditorMessages: Record<SaLocale, {
976
+ declare const planEditorMessages: Record<SaBuiltinLocale, {
958
977
  perMonthShort: string;
959
978
  perYearShort: string;
960
979
  sections: {
@@ -1039,7 +1058,7 @@ declare const planEditorMessages: Record<SaLocale, {
1039
1058
  };
1040
1059
  }>;
1041
1060
 
1042
- declare const planVersionsMessages: Record<SaLocale, {
1061
+ declare const planVersionsMessages: Record<SaBuiltinLocale, {
1043
1062
  compareEnd: string;
1044
1063
  status: {
1045
1064
  draft: string;
@@ -1092,7 +1111,7 @@ declare const planVersionsMessages: Record<SaLocale, {
1092
1111
  };
1093
1112
  }>;
1094
1113
 
1095
- declare const plansMessages: Record<SaLocale, {
1114
+ declare const plansMessages: Record<SaBuiltinLocale, {
1096
1115
  page: {
1097
1116
  loading: string;
1098
1117
  toastPlanCreated: string;
@@ -1308,7 +1327,7 @@ declare const plansMessages: Record<SaLocale, {
1308
1327
  };
1309
1328
  }>;
1310
1329
 
1311
- declare const promosMessages: Record<SaLocale, {
1330
+ declare const promosMessages: Record<SaBuiltinLocale, {
1312
1331
  title: string;
1313
1332
  createAction: string;
1314
1333
  apiErrorHttpStatus: string;
@@ -1407,7 +1426,7 @@ declare const promosMessages: Record<SaLocale, {
1407
1426
  };
1408
1427
  }>;
1409
1428
 
1410
- declare const shellMessages: Record<SaLocale, {
1429
+ declare const shellMessages: Record<SaBuiltinLocale, {
1411
1430
  emailLabel: string;
1412
1431
  passwordLabel: string;
1413
1432
  invalidEmail: string;
@@ -1476,7 +1495,7 @@ declare const shellMessages: Record<SaLocale, {
1476
1495
  };
1477
1496
  }>;
1478
1497
 
1479
- declare const tenantsMessages: Record<SaLocale, {
1498
+ declare const tenantsMessages: Record<SaBuiltinLocale, {
1480
1499
  tenant: string;
1481
1500
  plan: string;
1482
1501
  list: {
@@ -1526,7 +1545,7 @@ declare const tenantsMessages: Record<SaLocale, {
1526
1545
  };
1527
1546
  }>;
1528
1547
 
1529
- declare const usersMessages: Record<SaLocale, {
1548
+ declare const usersMessages: Record<SaBuiltinLocale, {
1530
1549
  title: string;
1531
1550
  filterQuery: string;
1532
1551
  filterTenant: string;
@@ -1587,11 +1606,67 @@ type SaMessages = {
1587
1606
  /** Shape of app-side string overrides (deep partial of the catalog). */
1588
1607
  type SaMessagesOverrides = PartialMessages<SaMessages>;
1589
1608
  /** Complete, immutable message catalogs per locale. */
1590
- declare const SA_MESSAGES: Record<SaLocale, SaMessages>;
1609
+ declare const SA_MESSAGES: Record<SaBuiltinLocale, SaMessages>;
1591
1610
  /**
1592
1611
  * Returns the platform catalog for `locale`, optionally overlaid with
1593
1612
  * app-side overrides.
1594
1613
  */
1595
- declare function resolveMessages(locale: SaLocale, overrides?: SaMessagesOverrides): SaMessages;
1614
+ declare function resolveMessages(locale: SaBuiltinLocale, overrides?: SaMessagesOverrides): SaMessages;
1615
+
1616
+ /** A language an app brings along, on top of the two the platform ships. */
1617
+ interface SaLocaleDefinition {
1618
+ /**
1619
+ * Name for the switcher, written in the language itself (`'Français'`) —
1620
+ * that way the entry stays readable whatever locale is currently active.
1621
+ */
1622
+ label: string;
1623
+ /** BCP-47 tag for `Intl`/`toLocaleString`, e.g. `'fr-FR'`. */
1624
+ intlLocale: string;
1625
+ /**
1626
+ * Built-in catalog that fills whatever `messages` leaves out, default
1627
+ * `'en'`. A partial translation therefore renders — untranslated keys fall
1628
+ * back instead of showing blanks.
1629
+ */
1630
+ basedOn?: SaBuiltinLocale;
1631
+ /** The translation. Every key is optional; see `basedOn`. */
1632
+ messages: SaMessagesOverrides;
1633
+ }
1634
+ interface SaCatalogOptions {
1635
+ /**
1636
+ * Locales offered in the switcher, in the order given. Default: the
1637
+ * built-ins plus everything in `additionalLocales`. Narrow it to ship a
1638
+ * single language; entries without a catalog are ignored.
1639
+ */
1640
+ locales?: readonly SaLocale[];
1641
+ /** Languages the app adds, keyed by locale code (`'fr'`, `'pt-BR'`, …). */
1642
+ additionalLocales?: Readonly<Record<string, SaLocaleDefinition>>;
1643
+ /** Per-locale wording changes layered on top of whatever catalog applies. */
1644
+ overrides?: Readonly<Partial<Record<SaLocale, SaMessagesOverrides>>>;
1645
+ }
1646
+ /** One entry of the switcher. */
1647
+ interface SaLocaleOption {
1648
+ code: SaLocale;
1649
+ label: string;
1650
+ }
1651
+ interface SaCatalog {
1652
+ /** Locales to offer, in switcher order. Never empty. */
1653
+ readonly locales: readonly SaLocaleOption[];
1654
+ /**
1655
+ * Whether the app offers this locale. Deliberately not "can it be
1656
+ * rendered": an app that narrowed to English must not be pushed back into
1657
+ * German by a stored pick from before, even though German is still shipped.
1658
+ */
1659
+ has(locale: SaLocale): boolean;
1660
+ /** Locale to start from when nothing else is known. */
1661
+ readonly defaultLocale: SaLocale;
1662
+ messagesFor(locale: SaLocale): SaMessages;
1663
+ intlLocaleFor(locale: SaLocale): string;
1664
+ labelFor(locale: SaLocale): string;
1665
+ }
1666
+ /**
1667
+ * Builds the catalog an app runs on. Resolution is cached per locale, so
1668
+ * switching back and forth does not re-merge the message tree.
1669
+ */
1670
+ declare function createSaCatalog(options?: SaCatalogOptions): SaCatalog;
1596
1671
 
1597
- export { type ActionHandler as A, DEFAULT_SA_LOCALE as D, type HttpClient as H, type KvStore as K, type MessageTree as M, type PartialMessages as P, type ResolvedAction as R, type SaLocale as S, type TranslationOf as T, type SaMessages as a, type SaMessagesOverrides as b, ActionRegistry as c, ActionDefNotInManifestError as d, type HttpResponse as e, MissingHandlerError as f, SA_INTL_LOCALES as g, SA_LOCALES as h, SA_LOCALE_LABELS as i, SA_MESSAGES as j, defaultHttpClient as k, defaultKvStore as l, defineMessages as m, isSaLocale as n, mergeMessages as o, resolveMessages as r };
1672
+ export { type ActionHandler as A, DEFAULT_SA_LOCALE as D, type HttpClient as H, type KvStore as K, type MessageTree as M, type PartialMessages as P, type ResolvedAction as R, type SaLocale as S, type TranslationOf as T, type SaMessages as a, type SaLocaleOption as b, type SaLocaleDefinition as c, type SaMessagesOverrides as d, ActionRegistry as e, ActionDefNotInManifestError as f, type HttpResponse as g, MissingHandlerError as h, SA_INTL_LOCALES as i, SA_LOCALES as j, SA_LOCALE_LABELS as k, SA_MESSAGES as l, type SaBuiltinLocale as m, type SaCatalog as n, type SaCatalogOptions as o, builtinLocaleOf as p, createSaCatalog as q, defaultHttpClient as r, defaultKvStore as s, defineMessages as t, isSaBuiltinLocale as u, mergeMessages as v, resolveMessages as w, type SaNavMessages as x };