@saasicat/ui-vue 0.16.0 → 0.18.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 (46) hide show
  1. package/README.md +12 -11
  2. package/dist/{messages-DetB75lX.d.ts → catalog-JXdKdIqG.d.cts} +104 -40
  3. package/dist/{messages-DetB75lX.d.cts → catalog-JXdKdIqG.d.ts} +104 -40
  4. package/dist/{chunk-TZ2NGQXK.js → chunk-CC4WK3DC.js} +40 -8
  5. package/dist/{chunk-CRUUZMMN.js → chunk-VRCVJTY4.js} +30 -4
  6. package/dist/{chunk-MGKJCVDU.js → chunk-Y5MWMWMD.js} +18 -18
  7. package/dist/client/index.cjs +70 -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 +10 -4
  11. package/dist/index.cjs +85 -22
  12. package/dist/index.d.cts +5 -5
  13. package/dist/index.d.ts +5 -5
  14. package/dist/index.js +11 -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-1nCOkvgi.d.cts} +29 -9
  20. package/dist/{use-super-admin-i18n-BPC-H9jz.d.ts → use-super-admin-i18n-BlqcD0N7.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 +23 -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/BundleVersionStrip.vue +1 -1
  36. package/src/components/bundle-editor/bundle-version-status.ts +18 -11
  37. package/src/pages-standard/AdminLayout.vue +7 -3
  38. package/src/pages-standard/DiscoveryPage.vue +1 -1
  39. package/src/pages-standard/SuperAdminLoginPage.vue +8 -9
  40. package/src/pages-standard/bundles-page/BundleAccordionList.vue +2 -3
  41. package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +4 -4
  42. package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +7 -7
  43. package/src/pages-standard/discovery-page/discovery-ui.ts +24 -15
  44. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +2 -3
  45. package/src/pages-standard/plan-versions/format.ts +7 -8
  46. package/src/vue/use-super-admin-i18n.ts +59 -28
package/dist/index.cjs CHANGED
@@ -72,6 +72,7 @@ __export(src_exports, {
72
72
  createManifestStore: () => createManifestStore,
73
73
  createPlatformLoaders: () => createPlatformLoaders,
74
74
  createProjectPageHostRoute: () => createProjectPageHostRoute,
75
+ createSaCatalog: () => createSaCatalog,
75
76
  createSuperAdminI18n: () => createSuperAdminI18n,
76
77
  defaultHttpClient: () => defaultHttpClient,
77
78
  defaultIconForActionKey: () => defaultIconForActionKey,
@@ -83,11 +84,13 @@ __export(src_exports, {
83
84
  formatCurrency: () => formatCurrency,
84
85
  formatMessage: () => formatMessage,
85
86
  getJson: () => getJson,
86
- isSaLocale: () => isSaLocale,
87
+ isProductionBoot: () => isProductionBoot,
88
+ isSaBuiltinLocale: () => isSaBuiltinLocale,
87
89
  mergeMessages: () => mergeMessages,
88
90
  postJson: () => postJson,
89
91
  provideEntitlement: () => provideEntitlement,
90
92
  resolveExtension: () => resolveExtension,
93
+ resolveLoginBranding: () => resolveLoginBranding,
91
94
  resolveMessages: () => resolveMessages,
92
95
  trimTrailingSlashes: () => trimTrailingSlashes,
93
96
  useActions: () => useActions,
@@ -345,7 +348,7 @@ var SA_LOCALE_LABELS = {
345
348
  de: "Deutsch",
346
349
  en: "English"
347
350
  };
348
- function isSaLocale(value) {
351
+ function isSaBuiltinLocale(value) {
349
352
  return typeof value === "string" && SA_LOCALES.includes(value);
350
353
  }
351
354
 
@@ -469,15 +472,21 @@ var PAGE_SECTIONS = {
469
472
  platformEmail: "system",
470
473
  platformEmailHistory: "system"
471
474
  };
475
+ function resolveNav(options) {
476
+ return options.nav ?? builtinNav(options.locale);
477
+ }
478
+ function builtinNav(locale) {
479
+ return navMessages[isSaBuiltinLocale(locale) ? locale : DEFAULT_SA_LOCALE];
480
+ }
472
481
  function defaultSectionOrder(locale = DEFAULT_SA_LOCALE) {
473
- const sections = navMessages[locale].sections;
482
+ const sections = typeof locale === "string" ? builtinNav(locale).sections : locale.sections;
474
483
  return [sections.overview, sections.catalog, sections.customers, sections.system];
475
484
  }
476
485
  function buildRoutes(manifest, options = {}) {
477
486
  const routes = [];
478
487
  const capabilities = manifest.capabilities ?? {};
479
488
  const standard = manifest.navigation?.standardPages ?? {};
480
- const nav = navMessages[options.locale ?? DEFAULT_SA_LOCALE];
489
+ const nav = resolveNav(options);
481
490
  const defaultNavSections = Object.fromEntries(
482
491
  Object.entries(PAGE_SECTIONS).map(([page, section]) => [page, nav.sections[section]])
483
492
  );
@@ -812,7 +821,7 @@ function formatterFor(locale, currency, decimals) {
812
821
  const key = `${locale}:${currency}:${decimals}`;
813
822
  let formatter = formatters.get(key);
814
823
  if (!formatter) {
815
- formatter = new Intl.NumberFormat(SA_INTL_LOCALES[locale], {
824
+ formatter = new Intl.NumberFormat(SA_INTL_LOCALES[locale] ?? locale, {
816
825
  style: "currency",
817
826
  currency,
818
827
  minimumFractionDigits: decimals,
@@ -978,7 +987,7 @@ var bundlesMessages = defineMessages(
978
987
  bundleKeyHint: "API-stabil \xB7 wird aus dem Label erzeugt",
979
988
  errorKeyFormat: "Nur A-Z, 0-9, Underscore; muss mit Buchstabe beginnen.",
980
989
  errorKeyExists: "Dieser Bundle-Key existiert bereits.",
981
- descriptionPlaceholder: "z. B. Kampagnen, WhatsApp und Korrespondenz f\xFCr aktive Vereine.",
990
+ descriptionPlaceholder: "z. B. Kampagnen, WhatsApp und Korrespondenz.",
982
991
  sectionPricing: "v1 \xB7 Pricing & G\xFCltigkeit",
983
992
  pricingHint: "Monats- & Jahrespreis sowie Datum, ab dem das Bundle verkaufbar wird.",
984
993
  validFromImmediate: "\u2713 Bundle ist nach Anlage sofort live und verkaufbar.",
@@ -1190,7 +1199,7 @@ var bundlesMessages = defineMessages(
1190
1199
  bundleKeyHint: "API-stable \xB7 derived from the label",
1191
1200
  errorKeyFormat: "Only A-Z, 0-9, underscore; must start with a letter.",
1192
1201
  errorKeyExists: "This bundle key already exists.",
1193
- descriptionPlaceholder: "e.g. Campaigns, WhatsApp and correspondence for active clubs.",
1202
+ descriptionPlaceholder: "e.g. Campaigns, WhatsApp and correspondence.",
1194
1203
  sectionPricing: "v1 \xB7 pricing & validity",
1195
1204
  pricingHint: "Monthly & yearly price plus the date from which the bundle becomes sellable.",
1196
1205
  validFromImmediate: "\u2713 The bundle is live and sellable right after creation.",
@@ -1864,7 +1873,7 @@ var marketingMessages = defineMessages(
1864
1873
  ctaOverrideLabel: "CTA-Text \xFCberschreiben (optional)",
1865
1874
  ctaOverrideHint: "leer lassen f\xFCr Auto-Text",
1866
1875
  topFeatureCount: "{count} Eintr\xE4ge",
1867
- featureLabelPlaceholder: "z. B. Beitragsverwaltung",
1876
+ featureLabelPlaceholder: "z. B. Erweiterte Berichte",
1868
1877
  featureStrongPlaceholder: "Highlight, z. B. bis 100",
1869
1878
  moveUp: "Nach oben",
1870
1879
  moveDown: "Nach unten",
@@ -1992,7 +2001,7 @@ var marketingMessages = defineMessages(
1992
2001
  ctaOverrideLabel: "Override CTA text (optional)",
1993
2002
  ctaOverrideHint: "leave empty for the auto text",
1994
2003
  topFeatureCount: "{count} entries",
1995
- featureLabelPlaceholder: "e.g. Membership management",
2004
+ featureLabelPlaceholder: "e.g. Advanced reporting",
1996
2005
  featureStrongPlaceholder: "Highlight, e.g. up to 100",
1997
2006
  moveUp: "Move up",
1998
2007
  moveDown: "Move down",
@@ -3778,6 +3787,54 @@ function resolveMessages(locale, overrides) {
3778
3787
  return mergeMessages(SA_MESSAGES[locale], overrides);
3779
3788
  }
3780
3789
 
3790
+ // src/client/i18n/catalog.ts
3791
+ function createSaCatalog(options = {}) {
3792
+ const additional = options.additionalLocales ?? {};
3793
+ const known = /* @__PURE__ */ new Set([...SA_LOCALES, ...Object.keys(additional)]);
3794
+ const requested = options.locales?.filter((code) => known.has(code));
3795
+ const offered = requested && requested.length > 0 ? requested : [...SA_LOCALES, ...Object.keys(additional)];
3796
+ const labelFor = (locale) => additional[locale]?.label ?? (isSaBuiltinLocale(locale) ? SA_LOCALE_LABELS[locale] : locale);
3797
+ const cache = /* @__PURE__ */ new Map();
3798
+ const messagesFor = (locale) => {
3799
+ const hit = cache.get(locale);
3800
+ if (hit) return hit;
3801
+ const definition = additional[locale];
3802
+ const base = SA_MESSAGES[definition?.basedOn ?? (isSaBuiltinLocale(locale) ? locale : "en")];
3803
+ const translated = definition ? mergeMessages(base, definition.messages) : base;
3804
+ const resolved = mergeMessages(translated, options.overrides?.[locale]);
3805
+ cache.set(locale, resolved);
3806
+ return resolved;
3807
+ };
3808
+ const offeredSet = new Set(offered);
3809
+ return {
3810
+ locales: offered.map((code) => ({ code, label: labelFor(code) })),
3811
+ has: (locale) => offeredSet.has(locale),
3812
+ // The app's first offered locale beats the platform default: an app
3813
+ // shipping English only must not start in German.
3814
+ defaultLocale: offered.includes(DEFAULT_SA_LOCALE) ? DEFAULT_SA_LOCALE : offered[0],
3815
+ messagesFor,
3816
+ intlLocaleFor: (locale) => additional[locale]?.intlLocale ?? (isSaBuiltinLocale(locale) ? SA_INTL_LOCALES[locale] : locale),
3817
+ labelFor
3818
+ };
3819
+ }
3820
+
3821
+ // src/client/login-branding.ts
3822
+ var DEFAULT_TAG = "SuperAdmin";
3823
+ function resolveLoginBranding(boot, fallback = {}) {
3824
+ const project = boot?.project ?? null;
3825
+ const environment = project?.environment ?? null;
3826
+ return {
3827
+ name: project?.displayName || fallback.name || "",
3828
+ tag: project?.label || fallback.tag || DEFAULT_TAG,
3829
+ icon: project?.icon || fallback.logoText || "",
3830
+ logoUrl: project?.logoUrl || null,
3831
+ environment: environment && environment !== "production" ? environment : null
3832
+ };
3833
+ }
3834
+ function isProductionBoot(boot) {
3835
+ return boot?.project?.environment === "production";
3836
+ }
3837
+
3781
3838
  // src/vue/super-admin-context.ts
3782
3839
  var SUPER_ADMIN_BRAND_KEY = /* @__PURE__ */ Symbol.for(
3783
3840
  "@saasicat/ui-vue/SUPER_ADMIN_BRAND"
@@ -3880,27 +3937,30 @@ var SUPER_ADMIN_I18N_KEY = /* @__PURE__ */ Symbol.for(
3880
3937
  "@saasicat/ui-vue/SUPER_ADMIN_I18N"
3881
3938
  );
3882
3939
  function createSuperAdminI18n(options = {}) {
3883
- const locale = (0, import_vue2.isRef)(options.locale) ? options.locale : createOwnedLocale(options);
3884
- const messages = (0, import_vue2.computed)(
3885
- () => resolveMessages(locale.value, options.overrides?.[locale.value])
3940
+ const catalog = createSaCatalog(options);
3941
+ const locale = (0, import_vue2.isRef)(options.locale) ? options.locale : createOwnedLocale(options, catalog);
3942
+ const active = (0, import_vue2.computed)(
3943
+ () => catalog.has(locale.value) ? locale.value : catalog.defaultLocale
3886
3944
  );
3887
- const intlLocale = (0, import_vue2.computed)(() => SA_INTL_LOCALES[locale.value]);
3945
+ const messages = (0, import_vue2.computed)(() => catalog.messagesFor(active.value));
3946
+ const intlLocale = (0, import_vue2.computed)(() => catalog.intlLocaleFor(active.value));
3888
3947
  const writable = !(0, import_vue2.isReadonly)(locale);
3889
3948
  return {
3890
3949
  locale,
3891
3950
  messages,
3892
3951
  intlLocale,
3893
- switcherEnabled: (options.switcher ?? true) && writable
3952
+ switcherEnabled: (options.switcher ?? true) && writable && catalog.locales.length > 1,
3953
+ availableLocales: catalog.locales
3894
3954
  };
3895
3955
  }
3896
- function createOwnedLocale(options) {
3897
- if (options.persist === false) {
3898
- return (0, import_vue2.ref)(options.locale ?? DEFAULT_SA_LOCALE);
3899
- }
3956
+ function createOwnedLocale(options, catalog) {
3957
+ const initial = options.locale ?? catalog.defaultLocale;
3958
+ if (options.persist === false) return (0, import_vue2.ref)(initial);
3900
3959
  const storage = options.storage ?? defaultKvStore();
3901
- const stored = storage.get(SA_LOCALE_STORAGE_KEY);
3902
- const locale = (0, import_vue2.ref)(isSaLocale(stored) ? stored : options.locale ?? DEFAULT_SA_LOCALE);
3903
- (0, import_vue2.watch)(locale, (next) => storage.set(SA_LOCALE_STORAGE_KEY, next));
3960
+ const key = `${options.storageKeyPrefix ?? ""}${SA_LOCALE_STORAGE_KEY}`;
3961
+ const stored = storage.get(key);
3962
+ const locale = (0, import_vue2.ref)(stored && catalog.has(stored) ? stored : initial);
3963
+ (0, import_vue2.watch)(locale, (next) => storage.set(key, next));
3904
3964
  return locale;
3905
3965
  }
3906
3966
  var fallbackI18n = null;
@@ -6661,6 +6721,7 @@ function defaultTenantPlanSectionI18n(locale) {
6661
6721
  createManifestStore,
6662
6722
  createPlatformLoaders,
6663
6723
  createProjectPageHostRoute,
6724
+ createSaCatalog,
6664
6725
  createSuperAdminI18n,
6665
6726
  defaultHttpClient,
6666
6727
  defaultIconForActionKey,
@@ -6672,11 +6733,13 @@ function defaultTenantPlanSectionI18n(locale) {
6672
6733
  formatCurrency,
6673
6734
  formatMessage,
6674
6735
  getJson,
6675
- isSaLocale,
6736
+ isProductionBoot,
6737
+ isSaBuiltinLocale,
6676
6738
  mergeMessages,
6677
6739
  postJson,
6678
6740
  provideEntitlement,
6679
6741
  resolveExtension,
6742
+ resolveLoginBranding,
6680
6743
  resolveMessages,
6681
6744
  trimTrailingSlashes,
6682
6745
  useActions,
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { BootLoaderOptions, ManifestLoaderOptions, BuildRouteEntry, SidebarSection, NavBuilderOptions, BatchColumnData, BatchColumnFetcherOptions, BootLoader, ManifestLoader } from './client/index.cjs';
2
- export { ADMIN_UI_VERSION, AdminPromoListFilter, AdminPromoListRow, AdminResourceClientOptions, BatchColumnDriftError, BatchColumnFetcher, BatchColumnRow, BatchColumnValue, BootLoadError, CachedManifestEntry, DEFAULT_STANDARD_PAGE_ROUTES, HttpJsonError, ManifestLoadError, MessageParams, ParamStyle, SidebarItem, buildRoutes, buildSidebar, createAdminResourceClient, defaultSectionOrder, formatCurrency, formatMessage, getJson, postJson, resolveExtension, trimTrailingSlashes } from './client/index.cjs';
3
- import { H as HttpClient, c as ActionRegistry, A as ActionHandler, K as KvStore, S as SaLocale } from './messages-DetB75lX.cjs';
4
- export { d as ActionDefNotInManifestError, D as DEFAULT_SA_LOCALE, e as HttpResponse, M as MessageTree, f as MissingHandlerError, P as PartialMessages, R as ResolvedAction, g as SA_INTL_LOCALES, h as SA_LOCALES, i as SA_LOCALE_LABELS, j as SA_MESSAGES, a as SaMessages, b as SaMessagesOverrides, T as TranslationOf, k as defaultHttpClient, l as defaultKvStore, m as defineMessages, n as isSaLocale, o as mergeMessages, r as resolveMessages } from './messages-DetB75lX.cjs';
5
- import { A as ActionsMap, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, c as SuperAdminLoginAdapter } from './use-super-admin-i18n-DokHhkS_.cjs';
6
- export { f as ExtensionLoader, I as InstallPlugin, g as SA_LOCALE_STORAGE_KEY, h as SUPER_ADMIN_ACTIONS_KEY, i as SUPER_ADMIN_BRAND_KEY, j as SUPER_ADMIN_ENDPOINTS_KEY, k as SUPER_ADMIN_EXTENSIONS_KEY, l as SUPER_ADMIN_HTTP_KEY, m as SUPER_ADMIN_I18N_KEY, n as SUPER_ADMIN_LOGIN_ADAPTER_KEY, o as SUPER_ADMIN_MANIFEST_KEY, p as SUPER_ADMIN_NOTIFY_KEY, q as SuperAdminAuthGuardOptions, S as SuperAdminGuardOptions, e as SuperAdminI18n, d as SuperAdminI18nOptions, r as SuperAdminLoginResult, s as SuperAdminManifestGuardOptions, U as UiNotify, t as UiNotifyKind, u as UiNotifyOptions, v as buildNavigationGuard, w as createSuperAdminI18n, x as useSaMessages, y as useSuperAdminI18n } from './use-super-admin-i18n-DokHhkS_.cjs';
2
+ export { ADMIN_UI_VERSION, AdminPromoListFilter, AdminPromoListRow, AdminResourceClientOptions, BatchColumnDriftError, BatchColumnFetcher, BatchColumnRow, BatchColumnValue, BootLoadError, CachedManifestEntry, DEFAULT_STANDARD_PAGE_ROUTES, HttpJsonError, LoginBootProject, LoginBrandFallback, LoginBranding, ManifestLoadError, MessageParams, ParamStyle, SidebarItem, buildRoutes, buildSidebar, createAdminResourceClient, defaultSectionOrder, formatCurrency, formatMessage, getJson, isProductionBoot, postJson, resolveExtension, resolveLoginBranding, trimTrailingSlashes } from './client/index.cjs';
3
+ import { H as HttpClient, e as ActionRegistry, A as ActionHandler, K as KvStore, S as SaLocale } from './catalog-JXdKdIqG.cjs';
4
+ export { f as ActionDefNotInManifestError, D as DEFAULT_SA_LOCALE, g as HttpResponse, M as MessageTree, h as MissingHandlerError, P as PartialMessages, R as ResolvedAction, i as SA_INTL_LOCALES, j as SA_LOCALES, k as SA_LOCALE_LABELS, l as SA_MESSAGES, m as SaBuiltinLocale, n as SaCatalog, o as SaCatalogOptions, c as SaLocaleDefinition, b as SaLocaleOption, a as SaMessages, d as SaMessagesOverrides, T as TranslationOf, p as createSaCatalog, q as defaultHttpClient, r as defaultKvStore, s as defineMessages, t as isSaBuiltinLocale, u as mergeMessages, v as resolveMessages } from './catalog-JXdKdIqG.cjs';
5
+ import { A as ActionsMap, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, c as SuperAdminLoginAdapter } from './use-super-admin-i18n-1nCOkvgi.cjs';
6
+ export { f as ExtensionLoader, I as InstallPlugin, g as SA_LOCALE_STORAGE_KEY, h as SUPER_ADMIN_ACTIONS_KEY, i as SUPER_ADMIN_BRAND_KEY, j as SUPER_ADMIN_ENDPOINTS_KEY, k as SUPER_ADMIN_EXTENSIONS_KEY, l as SUPER_ADMIN_HTTP_KEY, m as SUPER_ADMIN_I18N_KEY, n as SUPER_ADMIN_LOGIN_ADAPTER_KEY, o as SUPER_ADMIN_MANIFEST_KEY, p as SUPER_ADMIN_NOTIFY_KEY, q as SuperAdminAuthGuardOptions, S as SuperAdminGuardOptions, e as SuperAdminI18n, d as SuperAdminI18nOptions, r as SuperAdminLoginResult, s as SuperAdminManifestGuardOptions, U as UiNotify, t as UiNotifyKind, u as UiNotifyOptions, v as buildNavigationGuard, w as createSuperAdminI18n, x as useSaMessages, y as useSuperAdminI18n } from './use-super-admin-i18n-1nCOkvgi.cjs';
7
7
  import { AdminManifest, TenantListFilter, TenantDto, AuditQuery, AuditEntry, FeatureUiRegistry, PromoPreviewResponse, OnboardingSelectionRequest, PublicMarketingBundle, FeatureDef, FeatureKey, PublicBootResponse, DiscoverySnapshot, CapabilityCatalogEntryRow, FeatureCatalogEntryRow, QuotaCatalogEntryRow, ReviewCatalogEntryData, CatalogEntryI18n, UpdateCatalogEntryBaseData, SyncDiscoveryResult, BundleVersionRow, CreateBundleVersionDraftData, BundleVersionMutationResult, UpdateBundleVersionDraftData, BundleRow, CreateBundleData, UpdateBundleData, SubscriptionBundleRecord, MarketingProjectionFilter, MarketingProjectionRow, CreateMarketingProjectionData, UpdateMarketingProjectionData, PromotionRow, CreatePromotionData, UpdatePromotionData, PlanVersionRow, CreatePlanVersionDraftData, PlanVersionMutationResult, UpdatePlanVersionDraftData, PlanRow, CreatePlanData, UpdatePlanData, TenantActionDef } from '@saasicat/types';
8
8
  import { RouteRecordRaw, NavigationGuardWithThis } from 'vue-router';
9
9
  import * as vue from 'vue';
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { BootLoaderOptions, ManifestLoaderOptions, BuildRouteEntry, SidebarSection, NavBuilderOptions, BatchColumnData, BatchColumnFetcherOptions, BootLoader, ManifestLoader } from './client/index.js';
2
- export { ADMIN_UI_VERSION, AdminPromoListFilter, AdminPromoListRow, AdminResourceClientOptions, BatchColumnDriftError, BatchColumnFetcher, BatchColumnRow, BatchColumnValue, BootLoadError, CachedManifestEntry, DEFAULT_STANDARD_PAGE_ROUTES, HttpJsonError, ManifestLoadError, MessageParams, ParamStyle, SidebarItem, buildRoutes, buildSidebar, createAdminResourceClient, defaultSectionOrder, formatCurrency, formatMessage, getJson, postJson, resolveExtension, trimTrailingSlashes } from './client/index.js';
3
- import { H as HttpClient, c as ActionRegistry, A as ActionHandler, K as KvStore, S as SaLocale } from './messages-DetB75lX.js';
4
- export { d as ActionDefNotInManifestError, D as DEFAULT_SA_LOCALE, e as HttpResponse, M as MessageTree, f as MissingHandlerError, P as PartialMessages, R as ResolvedAction, g as SA_INTL_LOCALES, h as SA_LOCALES, i as SA_LOCALE_LABELS, j as SA_MESSAGES, a as SaMessages, b as SaMessagesOverrides, T as TranslationOf, k as defaultHttpClient, l as defaultKvStore, m as defineMessages, n as isSaLocale, o as mergeMessages, r as resolveMessages } from './messages-DetB75lX.js';
5
- import { A as ActionsMap, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, c as SuperAdminLoginAdapter } from './use-super-admin-i18n-BPC-H9jz.js';
6
- export { f as ExtensionLoader, I as InstallPlugin, g as SA_LOCALE_STORAGE_KEY, h as SUPER_ADMIN_ACTIONS_KEY, i as SUPER_ADMIN_BRAND_KEY, j as SUPER_ADMIN_ENDPOINTS_KEY, k as SUPER_ADMIN_EXTENSIONS_KEY, l as SUPER_ADMIN_HTTP_KEY, m as SUPER_ADMIN_I18N_KEY, n as SUPER_ADMIN_LOGIN_ADAPTER_KEY, o as SUPER_ADMIN_MANIFEST_KEY, p as SUPER_ADMIN_NOTIFY_KEY, q as SuperAdminAuthGuardOptions, S as SuperAdminGuardOptions, e as SuperAdminI18n, d as SuperAdminI18nOptions, r as SuperAdminLoginResult, s as SuperAdminManifestGuardOptions, U as UiNotify, t as UiNotifyKind, u as UiNotifyOptions, v as buildNavigationGuard, w as createSuperAdminI18n, x as useSaMessages, y as useSuperAdminI18n } from './use-super-admin-i18n-BPC-H9jz.js';
2
+ export { ADMIN_UI_VERSION, AdminPromoListFilter, AdminPromoListRow, AdminResourceClientOptions, BatchColumnDriftError, BatchColumnFetcher, BatchColumnRow, BatchColumnValue, BootLoadError, CachedManifestEntry, DEFAULT_STANDARD_PAGE_ROUTES, HttpJsonError, LoginBootProject, LoginBrandFallback, LoginBranding, ManifestLoadError, MessageParams, ParamStyle, SidebarItem, buildRoutes, buildSidebar, createAdminResourceClient, defaultSectionOrder, formatCurrency, formatMessage, getJson, isProductionBoot, postJson, resolveExtension, resolveLoginBranding, trimTrailingSlashes } from './client/index.js';
3
+ import { H as HttpClient, e as ActionRegistry, A as ActionHandler, K as KvStore, S as SaLocale } from './catalog-JXdKdIqG.js';
4
+ export { f as ActionDefNotInManifestError, D as DEFAULT_SA_LOCALE, g as HttpResponse, M as MessageTree, h as MissingHandlerError, P as PartialMessages, R as ResolvedAction, i as SA_INTL_LOCALES, j as SA_LOCALES, k as SA_LOCALE_LABELS, l as SA_MESSAGES, m as SaBuiltinLocale, n as SaCatalog, o as SaCatalogOptions, c as SaLocaleDefinition, b as SaLocaleOption, a as SaMessages, d as SaMessagesOverrides, T as TranslationOf, p as createSaCatalog, q as defaultHttpClient, r as defaultKvStore, s as defineMessages, t as isSaBuiltinLocale, u as mergeMessages, v as resolveMessages } from './catalog-JXdKdIqG.js';
5
+ import { A as ActionsMap, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, c as SuperAdminLoginAdapter } from './use-super-admin-i18n-BlqcD0N7.js';
6
+ export { f as ExtensionLoader, I as InstallPlugin, g as SA_LOCALE_STORAGE_KEY, h as SUPER_ADMIN_ACTIONS_KEY, i as SUPER_ADMIN_BRAND_KEY, j as SUPER_ADMIN_ENDPOINTS_KEY, k as SUPER_ADMIN_EXTENSIONS_KEY, l as SUPER_ADMIN_HTTP_KEY, m as SUPER_ADMIN_I18N_KEY, n as SUPER_ADMIN_LOGIN_ADAPTER_KEY, o as SUPER_ADMIN_MANIFEST_KEY, p as SUPER_ADMIN_NOTIFY_KEY, q as SuperAdminAuthGuardOptions, S as SuperAdminGuardOptions, e as SuperAdminI18n, d as SuperAdminI18nOptions, r as SuperAdminLoginResult, s as SuperAdminManifestGuardOptions, U as UiNotify, t as UiNotifyKind, u as UiNotifyOptions, v as buildNavigationGuard, w as createSuperAdminI18n, x as useSaMessages, y as useSuperAdminI18n } from './use-super-admin-i18n-BlqcD0N7.js';
7
7
  import { AdminManifest, TenantListFilter, TenantDto, AuditQuery, AuditEntry, FeatureUiRegistry, PromoPreviewResponse, OnboardingSelectionRequest, PublicMarketingBundle, FeatureDef, FeatureKey, PublicBootResponse, DiscoverySnapshot, CapabilityCatalogEntryRow, FeatureCatalogEntryRow, QuotaCatalogEntryRow, ReviewCatalogEntryData, CatalogEntryI18n, UpdateCatalogEntryBaseData, SyncDiscoveryResult, BundleVersionRow, CreateBundleVersionDraftData, BundleVersionMutationResult, UpdateBundleVersionDraftData, BundleRow, CreateBundleData, UpdateBundleData, SubscriptionBundleRecord, MarketingProjectionFilter, MarketingProjectionRow, CreateMarketingProjectionData, UpdateMarketingProjectionData, PromotionRow, CreatePromotionData, UpdatePromotionData, PlanVersionRow, CreatePlanVersionDraftData, PlanVersionMutationResult, UpdatePlanVersionDraftData, PlanRow, CreatePlanData, UpdatePlanData, TenantActionDef } from '@saasicat/types';
8
8
  import { RouteRecordRaw, NavigationGuardWithThis } from 'vue-router';
9
9
  import * as vue from 'vue';
package/dist/index.js CHANGED
@@ -16,10 +16,12 @@ import {
16
16
  createAdminResourceClient,
17
17
  defaultSectionOrder,
18
18
  getJson,
19
+ isProductionBoot,
19
20
  postJson,
20
21
  resolveExtension,
22
+ resolveLoginBranding,
21
23
  trimTrailingSlashes
22
- } from "./chunk-CRUUZMMN.js";
24
+ } from "./chunk-VRCVJTY4.js";
23
25
  import {
24
26
  SA_LOCALE_STORAGE_KEY,
25
27
  SUPER_ADMIN_ACTIONS_KEY,
@@ -35,22 +37,23 @@ import {
35
37
  createSuperAdminI18n,
36
38
  useSaMessages,
37
39
  useSuperAdminI18n
38
- } from "./chunk-MGKJCVDU.js";
40
+ } from "./chunk-Y5MWMWMD.js";
39
41
  import {
40
42
  DEFAULT_SA_LOCALE,
41
43
  SA_INTL_LOCALES,
42
44
  SA_LOCALES,
43
45
  SA_LOCALE_LABELS,
44
46
  SA_MESSAGES,
47
+ createSaCatalog,
45
48
  defaultHttpClient,
46
49
  defaultKvStore,
47
50
  defineMessages,
48
51
  formatCurrency,
49
52
  formatMessage,
50
- isSaLocale,
53
+ isSaBuiltinLocale,
51
54
  mergeMessages,
52
55
  resolveMessages
53
- } from "./chunk-TZ2NGQXK.js";
56
+ } from "./chunk-CC4WK3DC.js";
54
57
 
55
58
  // src/vue/use-super-admin-context.ts
56
59
  import { inject } from "vue";
@@ -2842,6 +2845,7 @@ export {
2842
2845
  createManifestStore,
2843
2846
  createPlatformLoaders,
2844
2847
  createProjectPageHostRoute,
2848
+ createSaCatalog,
2845
2849
  createSuperAdminI18n,
2846
2850
  defaultHttpClient,
2847
2851
  defaultIconForActionKey,
@@ -2853,11 +2857,13 @@ export {
2853
2857
  formatCurrency,
2854
2858
  formatMessage,
2855
2859
  getJson,
2856
- isSaLocale,
2860
+ isProductionBoot,
2861
+ isSaBuiltinLocale,
2857
2862
  mergeMessages,
2858
2863
  postJson,
2859
2864
  provideEntitlement,
2860
2865
  resolveExtension,
2866
+ resolveLoginBranding,
2861
2867
  resolveMessages,
2862
2868
  trimTrailingSlashes,
2863
2869
  useActions,
@@ -138,7 +138,11 @@ var SA_INTL_LOCALES = {
138
138
  de: "de-DE",
139
139
  en: "en-US"
140
140
  };
141
- function isSaLocale(value) {
141
+ var SA_LOCALE_LABELS = {
142
+ de: "Deutsch",
143
+ en: "English"
144
+ };
145
+ function isSaBuiltinLocale(value) {
142
146
  return typeof value === "string" && SA_LOCALES.includes(value);
143
147
  }
144
148
 
@@ -313,7 +317,7 @@ var bundlesMessages = defineMessages(
313
317
  bundleKeyHint: "API-stabil \xB7 wird aus dem Label erzeugt",
314
318
  errorKeyFormat: "Nur A-Z, 0-9, Underscore; muss mit Buchstabe beginnen.",
315
319
  errorKeyExists: "Dieser Bundle-Key existiert bereits.",
316
- descriptionPlaceholder: "z. B. Kampagnen, WhatsApp und Korrespondenz f\xFCr aktive Vereine.",
320
+ descriptionPlaceholder: "z. B. Kampagnen, WhatsApp und Korrespondenz.",
317
321
  sectionPricing: "v1 \xB7 Pricing & G\xFCltigkeit",
318
322
  pricingHint: "Monats- & Jahrespreis sowie Datum, ab dem das Bundle verkaufbar wird.",
319
323
  validFromImmediate: "\u2713 Bundle ist nach Anlage sofort live und verkaufbar.",
@@ -525,7 +529,7 @@ var bundlesMessages = defineMessages(
525
529
  bundleKeyHint: "API-stable \xB7 derived from the label",
526
530
  errorKeyFormat: "Only A-Z, 0-9, underscore; must start with a letter.",
527
531
  errorKeyExists: "This bundle key already exists.",
528
- descriptionPlaceholder: "e.g. Campaigns, WhatsApp and correspondence for active clubs.",
532
+ descriptionPlaceholder: "e.g. Campaigns, WhatsApp and correspondence.",
529
533
  sectionPricing: "v1 \xB7 pricing & validity",
530
534
  pricingHint: "Monthly & yearly price plus the date from which the bundle becomes sellable.",
531
535
  validFromImmediate: "\u2713 The bundle is live and sellable right after creation.",
@@ -1199,7 +1203,7 @@ var marketingMessages = defineMessages(
1199
1203
  ctaOverrideLabel: "CTA-Text \xFCberschreiben (optional)",
1200
1204
  ctaOverrideHint: "leer lassen f\xFCr Auto-Text",
1201
1205
  topFeatureCount: "{count} Eintr\xE4ge",
1202
- featureLabelPlaceholder: "z. B. Beitragsverwaltung",
1206
+ featureLabelPlaceholder: "z. B. Erweiterte Berichte",
1203
1207
  featureStrongPlaceholder: "Highlight, z. B. bis 100",
1204
1208
  moveUp: "Nach oben",
1205
1209
  moveDown: "Nach unten",
@@ -1327,7 +1331,7 @@ var marketingMessages = defineMessages(
1327
1331
  ctaOverrideLabel: "Override CTA text (optional)",
1328
1332
  ctaOverrideHint: "leave empty for the auto text",
1329
1333
  topFeatureCount: "{count} entries",
1330
- featureLabelPlaceholder: "e.g. Membership management",
1334
+ featureLabelPlaceholder: "e.g. Advanced reporting",
1331
1335
  featureStrongPlaceholder: "Highlight, e.g. up to 100",
1332
1336
  moveUp: "Move up",
1333
1337
  moveDown: "Move down",
@@ -3158,9 +3162,36 @@ var SA_MESSAGES = {
3158
3162
  de: catalogFor("de"),
3159
3163
  en: catalogFor("en")
3160
3164
  };
3161
- function resolveMessages(locale, overrides) {
3162
- if (!overrides) return SA_MESSAGES[locale];
3163
- return mergeMessages(SA_MESSAGES[locale], overrides);
3165
+
3166
+ // src/client/i18n/catalog.ts
3167
+ function createSaCatalog(options = {}) {
3168
+ const additional = options.additionalLocales ?? {};
3169
+ const known = /* @__PURE__ */ new Set([...SA_LOCALES, ...Object.keys(additional)]);
3170
+ const requested = options.locales?.filter((code) => known.has(code));
3171
+ const offered = requested && requested.length > 0 ? requested : [...SA_LOCALES, ...Object.keys(additional)];
3172
+ const labelFor = (locale) => additional[locale]?.label ?? (isSaBuiltinLocale(locale) ? SA_LOCALE_LABELS[locale] : locale);
3173
+ const cache = /* @__PURE__ */ new Map();
3174
+ const messagesFor = (locale) => {
3175
+ const hit = cache.get(locale);
3176
+ if (hit) return hit;
3177
+ const definition = additional[locale];
3178
+ const base = SA_MESSAGES[definition?.basedOn ?? (isSaBuiltinLocale(locale) ? locale : "en")];
3179
+ const translated = definition ? mergeMessages(base, definition.messages) : base;
3180
+ const resolved = mergeMessages(translated, options.overrides?.[locale]);
3181
+ cache.set(locale, resolved);
3182
+ return resolved;
3183
+ };
3184
+ const offeredSet = new Set(offered);
3185
+ return {
3186
+ locales: offered.map((code) => ({ code, label: labelFor(code) })),
3187
+ has: (locale) => offeredSet.has(locale),
3188
+ // The app's first offered locale beats the platform default: an app
3189
+ // shipping English only must not start in German.
3190
+ defaultLocale: offered.includes(DEFAULT_SA_LOCALE) ? DEFAULT_SA_LOCALE : offered[0],
3191
+ messagesFor,
3192
+ intlLocaleFor: (locale) => additional[locale]?.intlLocale ?? (isSaBuiltinLocale(locale) ? SA_INTL_LOCALES[locale] : locale),
3193
+ labelFor
3194
+ };
3164
3195
  }
3165
3196
 
3166
3197
  // src/vue/use-super-admin-i18n.ts
@@ -3169,27 +3200,30 @@ var SUPER_ADMIN_I18N_KEY = /* @__PURE__ */ Symbol.for(
3169
3200
  "@saasicat/ui-vue/SUPER_ADMIN_I18N"
3170
3201
  );
3171
3202
  function createSuperAdminI18n(options = {}) {
3172
- const locale = (0, import_vue.isRef)(options.locale) ? options.locale : createOwnedLocale(options);
3173
- const messages = (0, import_vue.computed)(
3174
- () => resolveMessages(locale.value, options.overrides?.[locale.value])
3203
+ const catalog = createSaCatalog(options);
3204
+ const locale = (0, import_vue.isRef)(options.locale) ? options.locale : createOwnedLocale(options, catalog);
3205
+ const active = (0, import_vue.computed)(
3206
+ () => catalog.has(locale.value) ? locale.value : catalog.defaultLocale
3175
3207
  );
3176
- const intlLocale = (0, import_vue.computed)(() => SA_INTL_LOCALES[locale.value]);
3208
+ const messages = (0, import_vue.computed)(() => catalog.messagesFor(active.value));
3209
+ const intlLocale = (0, import_vue.computed)(() => catalog.intlLocaleFor(active.value));
3177
3210
  const writable = !(0, import_vue.isReadonly)(locale);
3178
3211
  return {
3179
3212
  locale,
3180
3213
  messages,
3181
3214
  intlLocale,
3182
- switcherEnabled: (options.switcher ?? true) && writable
3215
+ switcherEnabled: (options.switcher ?? true) && writable && catalog.locales.length > 1,
3216
+ availableLocales: catalog.locales
3183
3217
  };
3184
3218
  }
3185
- function createOwnedLocale(options) {
3186
- if (options.persist === false) {
3187
- return (0, import_vue.ref)(options.locale ?? DEFAULT_SA_LOCALE);
3188
- }
3219
+ function createOwnedLocale(options, catalog) {
3220
+ const initial = options.locale ?? catalog.defaultLocale;
3221
+ if (options.persist === false) return (0, import_vue.ref)(initial);
3189
3222
  const storage = options.storage ?? defaultKvStore();
3190
- const stored = storage.get(SA_LOCALE_STORAGE_KEY);
3191
- const locale = (0, import_vue.ref)(isSaLocale(stored) ? stored : options.locale ?? DEFAULT_SA_LOCALE);
3192
- (0, import_vue.watch)(locale, (next) => storage.set(SA_LOCALE_STORAGE_KEY, next));
3223
+ const key = `${options.storageKeyPrefix ?? ""}${SA_LOCALE_STORAGE_KEY}`;
3224
+ const stored = storage.get(key);
3225
+ const locale = (0, import_vue.ref)(stored && catalog.has(stored) ? stored : initial);
3226
+ (0, import_vue.watch)(locale, (next) => storage.set(key, next));
3193
3227
  return locale;
3194
3228
  }
3195
3229
 
@@ -2,8 +2,8 @@ import { Component, App } from 'vue';
2
2
  import { QuasarPluginOptions } from 'quasar';
3
3
  import { Pinia } from 'pinia';
4
4
  import { RouteRecordRaw, RouterHistory, Router } from 'vue-router';
5
- import { H as HttpClient } from '../messages-DetB75lX.cjs';
6
- import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, I as InstallPlugin, d as SuperAdminI18nOptions, e as SuperAdminI18n } from '../use-super-admin-i18n-DokHhkS_.cjs';
5
+ import { H as HttpClient } from '../catalog-JXdKdIqG.cjs';
6
+ import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, I as InstallPlugin, d as SuperAdminI18nOptions, e as SuperAdminI18n } from '../use-super-admin-i18n-1nCOkvgi.cjs';
7
7
  import '@saasicat/types';
8
8
 
9
9
  interface CreateSuperAdminAppOptions extends SuperAdminGuardOptions {
@@ -2,8 +2,8 @@ import { Component, App } from 'vue';
2
2
  import { QuasarPluginOptions } from 'quasar';
3
3
  import { Pinia } from 'pinia';
4
4
  import { RouteRecordRaw, RouterHistory, Router } from 'vue-router';
5
- import { H as HttpClient } from '../messages-DetB75lX.js';
6
- import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, I as InstallPlugin, d as SuperAdminI18nOptions, e as SuperAdminI18n } from '../use-super-admin-i18n-BPC-H9jz.js';
5
+ import { H as HttpClient } from '../catalog-JXdKdIqG.js';
6
+ import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, I as InstallPlugin, d as SuperAdminI18nOptions, e as SuperAdminI18n } from '../use-super-admin-i18n-BlqcD0N7.js';
7
7
  import '@saasicat/types';
8
8
 
9
9
  interface CreateSuperAdminAppOptions extends SuperAdminGuardOptions {
@@ -10,10 +10,10 @@ import {
10
10
  SUPER_ADMIN_NOTIFY_KEY,
11
11
  buildNavigationGuard,
12
12
  createSuperAdminI18n
13
- } from "../chunk-MGKJCVDU.js";
13
+ } from "../chunk-Y5MWMWMD.js";
14
14
  import {
15
15
  defaultHttpClient
16
- } from "../chunk-TZ2NGQXK.js";
16
+ } from "../chunk-CC4WK3DC.js";
17
17
 
18
18
  // src/quasar/create-super-admin-app.ts
19
19
  import { createApp } from "vue";
@@ -1,7 +1,7 @@
1
1
  import { Component, App, InjectionKey, Ref, ComputedRef } from 'vue';
2
2
  import { NavigationGuardWithThis } from 'vue-router';
3
3
  import { ActionKey, ComponentKey, AdminManifest } from '@saasicat/types';
4
- import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaMessagesOverrides, K as KvStore } from './messages-DetB75lX.cjs';
4
+ import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaLocaleOption, c as SaLocaleDefinition, d as SaMessagesOverrides, K as KvStore } from './catalog-JXdKdIqG.cjs';
5
5
 
6
6
  /**
7
7
  * App-specific branding data that the platform `AdminLayout` and other
@@ -184,19 +184,34 @@ interface SuperAdminI18n {
184
184
  intlLocale: ComputedRef<string>;
185
185
  /**
186
186
  * Whether the shell renders its language switcher. `false` when the app
187
- * opted out or when `locale` cannot be written to a switcher that cannot
188
- * change anything is worse than none.
187
+ * opted out, when `locale` cannot be written to, or when only one language
188
+ * is on offer — a switcher that cannot change anything is worse than none.
189
189
  */
190
190
  switcherEnabled: boolean;
191
+ /** Locales the switcher offers, in order, with their display names. */
192
+ availableLocales: readonly SaLocaleOption[];
191
193
  }
192
194
  interface SuperAdminI18nOptions {
193
195
  /**
194
- * Starting UI locale, default `'de'` — the shell's switcher lets the user
195
- * pick another one, and a stored pick outranks this. Pass a `Ref` when the
196
- * app wants to keep control (e.g. a user-profile setting).
196
+ * Starting UI locale — the shell's switcher lets the user pick another one,
197
+ * and a stored pick outranks this. Defaults to `'de'` when offered,
198
+ * otherwise to the first entry of `locales`. Pass a `Ref` when the app wants
199
+ * to keep control (e.g. a user-profile setting).
197
200
  */
198
201
  locale?: SaLocale | Ref<SaLocale>;
199
- /** Per-locale string overrides layered over the platform catalog. */
202
+ /**
203
+ * Languages to offer, in switcher order. Default: German, English and
204
+ * everything in `additionalLocales`. Narrow it to ship a single language —
205
+ * the switcher then hides itself.
206
+ */
207
+ locales?: readonly SaLocale[];
208
+ /**
209
+ * Languages the app adds on top of the two the platform ships, keyed by
210
+ * locale code. Untranslated keys fall back to `basedOn`, so a partial
211
+ * catalog is usable from the first key onwards.
212
+ */
213
+ additionalLocales?: Readonly<Record<string, SaLocaleDefinition>>;
214
+ /** Per-locale string overrides layered over whatever catalog applies. */
200
215
  overrides?: Partial<Record<SaLocale, SaMessagesOverrides>>;
201
216
  /**
202
217
  * Remembers the locale picked in the header switcher across reloads,
@@ -205,10 +220,15 @@ interface SuperAdminI18nOptions {
205
220
  */
206
221
  persist?: boolean;
207
222
  /**
208
- * Storage adapter for `persist`. Defaults to `defaultKvStore()`. Pass a
209
- * key-prefixing wrapper when several apps share one origin.
223
+ * Storage adapter for `persist`. Defaults to `defaultKvStore()`.
210
224
  */
211
225
  storage?: KvStore;
226
+ /**
227
+ * Prefix for the persisted key, mirroring `createPlatformLoaders`. Set it
228
+ * when several apps share one origin and must not inherit each other's
229
+ * language pick.
230
+ */
231
+ storageKeyPrefix?: string;
212
232
  /**
213
233
  * Renders the language switcher in the shell chrome, default `true`. Set
214
234
  * `false` for a deployment that ships one language. A readonly `locale`
@@ -1,7 +1,7 @@
1
1
  import { Component, App, InjectionKey, Ref, ComputedRef } from 'vue';
2
2
  import { NavigationGuardWithThis } from 'vue-router';
3
3
  import { ActionKey, ComponentKey, AdminManifest } from '@saasicat/types';
4
- import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaMessagesOverrides, K as KvStore } from './messages-DetB75lX.js';
4
+ import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaLocaleOption, c as SaLocaleDefinition, d as SaMessagesOverrides, K as KvStore } from './catalog-JXdKdIqG.js';
5
5
 
6
6
  /**
7
7
  * App-specific branding data that the platform `AdminLayout` and other
@@ -184,19 +184,34 @@ interface SuperAdminI18n {
184
184
  intlLocale: ComputedRef<string>;
185
185
  /**
186
186
  * Whether the shell renders its language switcher. `false` when the app
187
- * opted out or when `locale` cannot be written to a switcher that cannot
188
- * change anything is worse than none.
187
+ * opted out, when `locale` cannot be written to, or when only one language
188
+ * is on offer — a switcher that cannot change anything is worse than none.
189
189
  */
190
190
  switcherEnabled: boolean;
191
+ /** Locales the switcher offers, in order, with their display names. */
192
+ availableLocales: readonly SaLocaleOption[];
191
193
  }
192
194
  interface SuperAdminI18nOptions {
193
195
  /**
194
- * Starting UI locale, default `'de'` — the shell's switcher lets the user
195
- * pick another one, and a stored pick outranks this. Pass a `Ref` when the
196
- * app wants to keep control (e.g. a user-profile setting).
196
+ * Starting UI locale — the shell's switcher lets the user pick another one,
197
+ * and a stored pick outranks this. Defaults to `'de'` when offered,
198
+ * otherwise to the first entry of `locales`. Pass a `Ref` when the app wants
199
+ * to keep control (e.g. a user-profile setting).
197
200
  */
198
201
  locale?: SaLocale | Ref<SaLocale>;
199
- /** Per-locale string overrides layered over the platform catalog. */
202
+ /**
203
+ * Languages to offer, in switcher order. Default: German, English and
204
+ * everything in `additionalLocales`. Narrow it to ship a single language —
205
+ * the switcher then hides itself.
206
+ */
207
+ locales?: readonly SaLocale[];
208
+ /**
209
+ * Languages the app adds on top of the two the platform ships, keyed by
210
+ * locale code. Untranslated keys fall back to `basedOn`, so a partial
211
+ * catalog is usable from the first key onwards.
212
+ */
213
+ additionalLocales?: Readonly<Record<string, SaLocaleDefinition>>;
214
+ /** Per-locale string overrides layered over whatever catalog applies. */
200
215
  overrides?: Partial<Record<SaLocale, SaMessagesOverrides>>;
201
216
  /**
202
217
  * Remembers the locale picked in the header switcher across reloads,
@@ -205,10 +220,15 @@ interface SuperAdminI18nOptions {
205
220
  */
206
221
  persist?: boolean;
207
222
  /**
208
- * Storage adapter for `persist`. Defaults to `defaultKvStore()`. Pass a
209
- * key-prefixing wrapper when several apps share one origin.
223
+ * Storage adapter for `persist`. Defaults to `defaultKvStore()`.
210
224
  */
211
225
  storage?: KvStore;
226
+ /**
227
+ * Prefix for the persisted key, mirroring `createPlatformLoaders`. Set it
228
+ * when several apps share one origin and must not inherit each other's
229
+ * language pick.
230
+ */
231
+ storageKeyPrefix?: string;
212
232
  /**
213
233
  * Renders the language switcher in the shell chrome, default `true`. Set
214
234
  * `false` for a deployment that ships one language. A readonly `locale`