@sonenta/react-i18next 2.1.0 → 2.3.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.
package/README.md CHANGED
@@ -497,6 +497,84 @@ Plurals work the same in overlays (single key + CLDR plural forms); an overlay
497
497
  plural set fully replaces the base key's. Surface overlays are served from the
498
498
  CDN; `env: "dev"` is base-only for now.
499
499
 
500
+ ## Accessibility surfaces
501
+
502
+ A11y variants attach SEMANTIC accessibility text to a key — `aria_label`,
503
+ `alt_text`, `screen_reader`, `plain_language` — delivered through the same
504
+ sparse-overlay engine as device surfaces, but applied **orthogonally** to the
505
+ visible text (an element has both its visible label AND an accessible name).
506
+ Opt in per surface; they load alongside the base bundles.
507
+
508
+ ```tsx
509
+ <SonentaProvider {...config} a11ySurfaces={["aria_label", "alt_text"]}>
510
+ <App />
511
+ </SonentaProvider>
512
+
513
+ function SaveButton() {
514
+ const { t } = useTranslation("common");
515
+ return (
516
+ <button aria-label={t.aria("save")}>{t("save")}</button>
517
+ );
518
+ // t("save") → visible text ("Save")
519
+ // t.aria("save") → aria_label overlay, or the visible text if no override
520
+ }
521
+
522
+ function Hero() {
523
+ const { i18n } = useTranslation();
524
+ return <img src={i18n.a11yAsset("hero")?.ref} alt={i18n.alt("hero")} />;
525
+ }
526
+ ```
527
+
528
+ - `t.aria(key)` / `t.alt(key)` — overlay value, **falling back to the visible
529
+ text** when no a11y override exists. Also on the instance: `i18n.aria()` /
530
+ `i18n.alt()`.
531
+ - `t.a11y(key, surface)` / `i18n.a11y(key, surface)` — the raw resolver:
532
+ returns `undefined` when there's no override (use for `screen_reader` /
533
+ `plain_language`, which should be **omitted** rather than fall back).
534
+ - `i18n.a11yAsset(key)` — the `alt_text` overlay's localized-image `$asset`.
535
+ - **Plain language (cognitive) toggle:** include `plain_language` (or pass
536
+ `plainLanguage`) and call `i18n.setPlainLanguage(true)` — `t()` then returns
537
+ the `plain_language` overlay for keys that have one (else the base text).
538
+
539
+ Resolution is locale-outer / surface-inner (same chain as `t()`): `(fr-CA,
540
+ aria_label) > (fr-CA, base) > (fr, aria_label) > (fr, base)`. Overlays are
541
+ CDN-only (`env: "dev"` is base-only).
542
+
543
+ ## Available languages (runtime switcher)
544
+
545
+ List the languages **published for the active version** at runtime — so adding
546
+ a language in Sonenta makes it appear in your switcher **without recompiling
547
+ the app**. The set comes from a per-version CDN manifest (public, cacheable,
548
+ no auth); each code is enriched from the language catalog (`native_name`,
549
+ `rtl`, …).
550
+
551
+ ```tsx
552
+ import { useTranslation, useAvailableLanguages } from "@sonenta/react-i18next";
553
+
554
+ function LanguageSwitcher() {
555
+ const { i18n } = useTranslation();
556
+ const languages = useAvailableLanguages(); // [{ code, native_name, rtl, is_default, published_at? }]
557
+ return (
558
+ <select value={i18n.language} onChange={(e) => i18n.setLocale(e.target.value)}>
559
+ {languages.map((l) => (
560
+ <option key={l.code} value={l.code}>
561
+ {l.native_name ?? l.code}
562
+ </option>
563
+ ))}
564
+ </select>
565
+ );
566
+ }
567
+ ```
568
+
569
+ - A newly-published language appears on the **next load** (the manifest is
570
+ CDN-served), or immediately after `i18n.reload()` — no rebuild, no redeploy
571
+ of your app.
572
+ - `is_default` marks the project's default locale; `published_at` (when the
573
+ manifest provides it) lets you flag recently-added languages.
574
+ - Falls back to `defaultLocale` + `fallbackLng` when no manifest is available
575
+ (or `env: "dev"`, which is CDN-only). Opt out with `disableLanguageManifest`.
576
+ - Also imperative: `i18n.availableLanguages`.
577
+
500
578
  ## Recipes
501
579
 
502
580
  ### Next.js (App Router)
package/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ A11Y_SURFACES: () => A11Y_SURFACES,
23
24
  DEFAULT_SURFACE_BREAKPOINTS: () => DEFAULT_SURFACE_BREAKPOINTS,
24
25
  SonentaProvider: () => SonentaProvider,
25
26
  Trans: () => Trans,
@@ -31,6 +32,7 @@ __export(index_exports, {
31
32
  logTransport: () => logTransport,
32
33
  surfaceForWidth: () => surfaceForWidth,
33
34
  t: () => t,
35
+ useAvailableLanguages: () => useAvailableLanguages,
34
36
  useTranslation: () => useTranslation
35
37
  });
36
38
  module.exports = __toCommonJS(index_exports);
@@ -236,7 +238,7 @@ function flattenPlurals(tree, locale) {
236
238
 
237
239
  // src/transport.ts
238
240
  var SDK_LIB = "@sonenta/react-i18next";
239
- var SDK_VER = true ? "2.1.0" : "0.0.0-dev";
241
+ var SDK_VER = true ? "2.3.0" : "0.0.0-dev";
240
242
  function defaultTransport(opts) {
241
243
  return async (batch) => {
242
244
  if (!batch.length) return;
@@ -428,6 +430,14 @@ var SonentaI18n = class {
428
430
  // config.languageCatalog and/or the public GET /v1/languages in start().
429
431
  _catalog = new LanguageCatalog();
430
432
  _catalogDisabled = false;
433
+ // Published-languages manifest (available-languages feature). Per-version,
434
+ // public CDN, no auth: the SET of locales published for the active version.
435
+ // Codes are enriched from `_catalog` (#803) for native_name/rtl/etc.
436
+ _manifestDisabled = false;
437
+ _manifest = null;
438
+ // The configured default locale (the initial `this.locale`, which is mutable
439
+ // via setLocale) — used as the available-languages fallback + default marker.
440
+ _defaultLocale;
431
441
  _config;
432
442
  // Surface variant (#911). When set, each loaded (locale,ns) is composed as
433
443
  // base ⊕ sparse `{ns}.{surface}.json` overlay (overlay wins per key). We
@@ -442,6 +452,14 @@ var SonentaI18n = class {
442
452
  // Resolved asset refs (#911 minimal v1), keyed `${locale}/${ns}/${keyPath}`
443
453
  // for the CURRENT composition (base assets, then overlay assets override).
444
454
  _assets = /* @__PURE__ */ new Map();
455
+ // A11y surfaces (#989 / task 994). Each configured a11y surface S is loaded
456
+ // as its OWN i18next namespace `${ns}__${S}` (sparse `{ns}.{S}.json`), so the
457
+ // accessors resolve through i18next's native locale-fallback + nested-key
458
+ // logic without touching the visible-text bundle. `_a11yAssets` carries the
459
+ // `alt_text` `$asset` refs, keyed `${locale}/${ns}/${keyPath}#${surface}`.
460
+ _a11ySurfaces = /* @__PURE__ */ new Set();
461
+ _plainLanguage = false;
462
+ _a11yAssets = /* @__PURE__ */ new Map();
445
463
  _missing;
446
464
  _listeners = /* @__PURE__ */ new Set();
447
465
  // Stable snapshot reference for useSyncExternalStore. Rebuilt ONLY in _notify
@@ -467,8 +485,12 @@ var SonentaI18n = class {
467
485
  );
468
486
  }
469
487
  this.locale = config.defaultLocale;
488
+ this._defaultLocale = config.defaultLocale;
470
489
  this.fallbackLng = config.fallbackLng;
471
490
  this._surface = config.surface;
491
+ for (const s of config.a11ySurfaces ?? []) this._a11ySurfaces.add(s);
492
+ this._plainLanguage = config.plainLanguage === true;
493
+ if (this._plainLanguage) this._a11ySurfaces.add("plain_language");
472
494
  let keySeparator = ".";
473
495
  if (config.keySeparator !== void 0) {
474
496
  keySeparator = config.keySeparator;
@@ -566,6 +588,7 @@ var SonentaI18n = class {
566
588
  this._rebindFormat();
567
589
  this._catalogDisabled = config.disableLanguageCatalog === true;
568
590
  this._catalog.merge(config.languageCatalog);
591
+ this._manifestDisabled = config.disableLanguageManifest === true;
569
592
  const active = config.initialBundles?.[this.locale];
570
593
  if (active && this._config.namespaces.every(
571
594
  (n) => active[n] && Object.keys(active[n]).length > 0
@@ -653,9 +676,16 @@ var SonentaI18n = class {
653
676
  surface: this._surface,
654
677
  setSurface: this.setSurface,
655
678
  asset: this.asset,
679
+ aria: this.aria,
680
+ alt: this.alt,
681
+ a11y: this.a11y,
682
+ a11yAsset: this.a11yAsset,
683
+ plainLanguage: this._plainLanguage,
684
+ setPlainLanguage: this.setPlainLanguage,
656
685
  dir: this.dir,
657
686
  nativeName: this.nativeName,
658
687
  languageMeta: this.languageMeta,
688
+ availableLanguages: this.availableLanguages,
659
689
  i18next: this._i18next
660
690
  };
661
691
  }
@@ -717,7 +747,9 @@ var SonentaI18n = class {
717
747
  // Best-effort: align the key separator with the version's key_style (#754).
718
748
  this._loadKeyStyle(fetchImpl),
719
749
  // Best-effort: load the public language catalog for dir()/nativeName().
720
- this._loadCatalog(fetchImpl)
750
+ this._loadCatalog(fetchImpl),
751
+ // Best-effort: load the per-version published-languages manifest.
752
+ this._loadManifest(fetchImpl)
721
753
  ]);
722
754
  await this._syncLanguage();
723
755
  this.ready = true;
@@ -741,6 +773,71 @@ var SonentaI18n = class {
741
773
  }
742
774
  /** Best-effort: fetch the PUBLIC language catalog and merge it in (#803).
743
775
  * Skipped when disabled; a failure keeps any embedded catalog. */
776
+ /**
777
+ * Best-effort: fetch the per-version published-languages manifest
778
+ * (`{cdnBase}/p/{project}/{version}/latest/languages.json`, public, no auth,
779
+ * CDN-cached) — the SET of locales published for the active version. Codes
780
+ * only; enriched from the catalog. A 404/offline keeps any prior manifest
781
+ * (`availableLanguages` then falls back to default + fallback). CDN-only:
782
+ * `env: "dev"` skips it (the dev runtime has no manifest yet).
783
+ */
784
+ async _loadManifest(fetchImpl, opts = {}) {
785
+ if (this._manifestDisabled) return;
786
+ if (this._config.env === "dev") return;
787
+ const url = `${this._config.cdnBase.replace(/\/+$/, "")}/p/${this._config.projectUuid}/${this._config.version}/latest/languages.json`;
788
+ const init = { method: "GET", credentials: "omit" };
789
+ if (opts.bust) init.cache = "reload";
790
+ try {
791
+ const r = await fetchImpl(url, init);
792
+ if (!r.ok) return;
793
+ const data = await r.json();
794
+ const raw = Array.isArray(data.languages) ? data.languages : [];
795
+ const languages = [];
796
+ for (const e of raw) {
797
+ if (typeof e === "string") {
798
+ languages.push({ code: e });
799
+ } else if (e && typeof e === "object" && typeof e.code === "string") {
800
+ const obj = e;
801
+ languages.push({
802
+ code: obj.code,
803
+ published_at: typeof obj.published_at === "string" ? obj.published_at : void 0
804
+ });
805
+ }
806
+ }
807
+ this._manifest = {
808
+ default_locale: typeof data.default_locale === "string" ? data.default_locale : void 0,
809
+ languages
810
+ };
811
+ } catch {
812
+ }
813
+ }
814
+ /** Languages PUBLISHED for the active version (available-languages feature):
815
+ * the manifest's codes enriched with catalog metadata, marked with the
816
+ * default locale + any `published_at`. Falls back to the configured
817
+ * `defaultLocale` + `fallbackLng` when no manifest is available. */
818
+ get availableLanguages() {
819
+ const def = this._manifest?.default_locale ?? this._defaultLocale;
820
+ let entries = this._manifest?.languages;
821
+ if (!entries || entries.length === 0) {
822
+ const seen = /* @__PURE__ */ new Set();
823
+ entries = [];
824
+ for (const c of [this._defaultLocale, ...asArray(this.fallbackLng)]) {
825
+ if (c && !seen.has(c)) {
826
+ seen.add(c);
827
+ entries.push({ code: c });
828
+ }
829
+ }
830
+ }
831
+ return entries.map(({ code, published_at }) => {
832
+ const meta = this.languageMeta(code);
833
+ return {
834
+ ...meta ?? {},
835
+ code,
836
+ ...published_at ? { published_at } : {},
837
+ is_default: code === def
838
+ };
839
+ });
840
+ }
744
841
  async _loadCatalog(fetchImpl) {
745
842
  if (this._catalogDisabled) return;
746
843
  this._catalog.merge(await loadCatalog(this._config.apiBase, fetchImpl));
@@ -831,12 +928,14 @@ var SonentaI18n = class {
831
928
  if (opts.namespace && opts.namespace !== ns) continue;
832
929
  targets.push({ locale, ns });
833
930
  }
834
- if (targets.length === 0) return;
835
- await Promise.all(
836
- targets.map(
931
+ if (targets.length === 0 && (opts.locale || opts.namespace)) return;
932
+ const manifest = this._loadManifest(fetch, { bust: true });
933
+ await Promise.all([
934
+ manifest,
935
+ ...targets.map(
837
936
  (t2) => this._loadBundle(t2.locale, t2.ns, fetch, { bust: true })
838
937
  )
839
- );
938
+ ]);
840
939
  this._notify();
841
940
  };
842
941
  /**
@@ -894,9 +993,96 @@ var SonentaI18n = class {
894
993
  }
895
994
  return void 0;
896
995
  };
996
+ // ---- A11y (#989 / task 994) ----
997
+ /** Split a possibly-`ns:key` string into `{ ns, bareKey }`, honoring the
998
+ * configured nsSeparator (mirrors {@link asset}). */
999
+ _splitKey(key, namespace) {
1000
+ let ns = namespace ?? this.defaultNamespace;
1001
+ let bareKey = key;
1002
+ if (typeof this._nsSeparator === "string" && this._nsSeparator) {
1003
+ const idx = key.indexOf(this._nsSeparator);
1004
+ if (idx > 0) {
1005
+ ns = key.slice(0, idx);
1006
+ bareKey = key.slice(idx + this._nsSeparator.length);
1007
+ }
1008
+ }
1009
+ return { ns, bareKey };
1010
+ }
1011
+ /** Resolve an a11y `surface` override for `key`, or `undefined` when none
1012
+ * exists (the namespace is sparse). Goes through i18next so the locale
1013
+ * fallback chain + nested keys + plurals apply. */
1014
+ a11y = (key, surface, namespace) => {
1015
+ if (!this._a11ySurfaces.has(surface)) return void 0;
1016
+ const { ns, bareKey } = this._splitKey(key, namespace);
1017
+ const a11yNs = this._a11yNs(ns, surface);
1018
+ if (!this._i18next.exists(bareKey, { ns: a11yNs })) return void 0;
1019
+ return this._i18next.t(bareKey, { ns: a11yNs });
1020
+ };
1021
+ /** Accessible name for `key` (`aria_label` overlay) — falls back to the
1022
+ * visible `t(key)` text when no override exists. Spread as `aria-label`. */
1023
+ aria = (key, namespace) => {
1024
+ const v = this.a11y(key, "aria_label", namespace);
1025
+ if (v !== void 0) return v;
1026
+ const { ns, bareKey } = this._splitKey(key, namespace);
1027
+ return this._i18next.t(bareKey, { ns });
1028
+ };
1029
+ /** Image alt text for `key` (`alt_text` overlay) — falls back to the visible
1030
+ * `t(key)` text. Pair with {@link a11yAsset} for a localized image. */
1031
+ alt = (key, namespace) => {
1032
+ const v = this.a11y(key, "alt_text", namespace);
1033
+ if (v !== void 0) return v;
1034
+ const { ns, bareKey } = this._splitKey(key, namespace);
1035
+ return this._i18next.t(bareKey, { ns });
1036
+ };
1037
+ /** Localized-image `$asset` ref from a key's `alt_text` overlay, or
1038
+ * `undefined`. Walks the locale fallback chain like `t()` would. */
1039
+ a11yAsset = (key, namespace) => {
1040
+ const { ns, bareKey } = this._splitKey(key, namespace);
1041
+ for (const loc of this._resolutionChain(this.locale)) {
1042
+ const ref = this._a11yAssets.get(`${loc}/${ns}/${bareKey}#alt_text`);
1043
+ if (ref) return ref;
1044
+ }
1045
+ return void 0;
1046
+ };
1047
+ get plainLanguage() {
1048
+ return this._plainLanguage;
1049
+ }
1050
+ /** Toggle simplified-language ("plain language" / FALC) mode (#989). When
1051
+ * on, `t()` returns the `plain_language` overlay value for keys that have
1052
+ * one. Loads the `plain_language` overlays on first enable if they were not
1053
+ * configured, then re-renders. No-op when unchanged. */
1054
+ setPlainLanguage = async (on) => {
1055
+ if (on === this._plainLanguage) return;
1056
+ this._plainLanguage = on;
1057
+ if (on && !this._a11ySurfaces.has("plain_language")) {
1058
+ this._a11ySurfaces.add("plain_language");
1059
+ const targets = [];
1060
+ for (const k of this._attempted) {
1061
+ const parts = k.split("/");
1062
+ const locale = parts[1];
1063
+ const ns = parts[2];
1064
+ if (locale && ns) targets.push({ locale, ns });
1065
+ }
1066
+ await Promise.all(
1067
+ targets.map((t2) => this._loadA11yOverlays(t2.locale, t2.ns))
1068
+ );
1069
+ }
1070
+ this._notify();
1071
+ this._signalLoaded();
1072
+ };
897
1073
  // ---- Translation ----
898
1074
  t = (key, optionsOrDefault, maybeOptions) => {
899
1075
  const options = typeof optionsOrDefault === "string" ? { ...maybeOptions ?? {}, defaultValue: optionsOrDefault } : optionsOrDefault;
1076
+ if (this._plainLanguage && this._a11ySurfaces.has("plain_language")) {
1077
+ const { ns, bareKey } = this._splitKey(key);
1078
+ const plainNs = this._a11yNs(ns, "plain_language");
1079
+ if (this._i18next.exists(bareKey, { ...options ?? {}, ns: plainNs })) {
1080
+ return this._i18next.t(bareKey, {
1081
+ ...options ?? {},
1082
+ ns: plainNs
1083
+ });
1084
+ }
1085
+ }
900
1086
  const literal = this._probeLiteral(key);
901
1087
  if (literal !== void 0) {
902
1088
  const interpolator = this._i18next.services?.interpolator;
@@ -1047,6 +1233,67 @@ var SonentaI18n = class {
1047
1233
  this._attempted.add(cacheKey);
1048
1234
  }
1049
1235
  await this._composeBundle(locale, ns, fetchImpl, opts.bust);
1236
+ await this._loadA11yOverlays(locale, ns, fetchImpl, opts.bust);
1237
+ }
1238
+ /**
1239
+ * Load every configured a11y surface overlay (#989 / task 994) for
1240
+ * (locale, ns) into a DEDICATED i18next namespace `${ns}__${surface}`, so
1241
+ * `aria()` / `alt()` / `a11y()` resolve them through i18next (locale
1242
+ * fallback + nested keys + plurals) WITHOUT polluting the visible-text
1243
+ * bundle. Sparse + best-effort: an absent overlay registers `{}` and the
1244
+ * accessor reports "no override".
1245
+ */
1246
+ async _loadA11yOverlays(locale, ns, fetchImpl = fetch, bust = false) {
1247
+ if (this._a11ySurfaces.size === 0) return;
1248
+ await Promise.all(
1249
+ [...this._a11ySurfaces].map(async (surface) => {
1250
+ const overlay = await this._loadOverlay(
1251
+ locale,
1252
+ ns,
1253
+ surface,
1254
+ fetchImpl,
1255
+ bust
1256
+ );
1257
+ const tree = this._unwrapA11y(overlay, locale, ns, surface);
1258
+ this._i18next.addResourceBundle(
1259
+ locale,
1260
+ this._a11yNs(ns, surface),
1261
+ flattenPlurals(tree, locale),
1262
+ false,
1263
+ true
1264
+ );
1265
+ })
1266
+ );
1267
+ }
1268
+ /** i18next namespace that backs an a11y surface overlay for `ns`. */
1269
+ _a11yNs(ns, surface) {
1270
+ return `${ns}__${surface}`;
1271
+ }
1272
+ /** Like {@link _unwrapAssets} but records `$asset` refs into `_a11yAssets`
1273
+ * (keyed with the `#${surface}` suffix) instead of the visible-text
1274
+ * `_assets`, so `alt_text` images don't collide with device-surface assets. */
1275
+ _unwrapA11y(tree, locale, ns, surface) {
1276
+ const sep = typeof this._i18next.options.keySeparator === "string" ? this._i18next.options.keySeparator : ".";
1277
+ const walk = (node, path) => {
1278
+ if (!node || typeof node !== "object") return node;
1279
+ const obj = node;
1280
+ if (Object.prototype.hasOwnProperty.call(obj, "$value")) {
1281
+ const a = obj.$asset;
1282
+ if (a && typeof a.kind === "string" && typeof a.ref === "string") {
1283
+ this._a11yAssets.set(
1284
+ `${locale}/${ns}/${path.join(sep)}#${surface}`,
1285
+ { kind: a.kind, ref: a.ref }
1286
+ );
1287
+ }
1288
+ return obj.$value;
1289
+ }
1290
+ const out = {};
1291
+ for (const [k, v] of Object.entries(obj)) {
1292
+ out[k] = walk(v, [...path, k]);
1293
+ }
1294
+ return out;
1295
+ };
1296
+ return walk(tree, []);
1050
1297
  }
1051
1298
  /**
1052
1299
  * Compose the i18next bundle for (locale, ns) as base ⊕ surface overlay
@@ -1174,6 +1421,12 @@ function t(key, optionsOrDefault, maybeOptions) {
1174
1421
  }
1175
1422
 
1176
1423
  // src/surface.ts
1424
+ var A11Y_SURFACES = [
1425
+ "aria_label",
1426
+ "alt_text",
1427
+ "screen_reader",
1428
+ "plain_language"
1429
+ ];
1177
1430
  var DEFAULT_SURFACE_BREAKPOINTS = {
1178
1431
  mobile: 640,
1179
1432
  tablet: 1024
@@ -1257,7 +1510,12 @@ function useTranslation(defaultNamespace) {
1257
1510
  );
1258
1511
  return i18n.t(fullKey, optionsOrDefault, maybeOptions);
1259
1512
  };
1260
- return fn;
1513
+ const withNs = (key) => defaultNamespace && !key.includes(":") ? `${defaultNamespace}:${key}` : key;
1514
+ const aug = fn;
1515
+ aug.aria = (key, namespace) => i18n.aria(withNs(key), namespace);
1516
+ aug.alt = (key, namespace) => i18n.alt(withNs(key), namespace);
1517
+ aug.a11y = (key, surface, namespace) => i18n.a11y(withNs(key), surface, namespace);
1518
+ return aug;
1261
1519
  }, [i18n, defaultNamespace]);
1262
1520
  (0, import_react2.useEffect)(() => {
1263
1521
  keyRegistry._set(tokenRef.current, renderedRef.current);
@@ -1268,6 +1526,9 @@ function useTranslation(defaultNamespace) {
1268
1526
  }, []);
1269
1527
  return { t: t2, i18n: snapshot };
1270
1528
  }
1529
+ function useAvailableLanguages() {
1530
+ return useI18nSnapshot().availableLanguages;
1531
+ }
1271
1532
 
1272
1533
  // src/trans.tsx
1273
1534
  var import_react3 = require("react");
@@ -1310,6 +1571,7 @@ function splitOnComponents(text, components) {
1310
1571
  }
1311
1572
  // Annotate the CommonJS export names for ESM import in node:
1312
1573
  0 && (module.exports = {
1574
+ A11Y_SURFACES,
1313
1575
  DEFAULT_SURFACE_BREAKPOINTS,
1314
1576
  SonentaProvider,
1315
1577
  Trans,
@@ -1321,6 +1583,7 @@ function splitOnComponents(text, components) {
1321
1583
  logTransport,
1322
1584
  surfaceForWidth,
1323
1585
  t,
1586
+ useAvailableLanguages,
1324
1587
  useTranslation
1325
1588
  });
1326
1589
  //# sourceMappingURL=index.cjs.map