@pie-players/pie-players-shared 0.3.75 → 1.0.0-beta.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 (59) hide show
  1. package/README.md +88 -77
  2. package/dist/components/vendor/nds/README.md +4 -5
  3. package/dist/config/profile.d.ts +1 -1
  4. package/dist/config/profile.js +1 -1
  5. package/dist/i18n/index.d.ts +1 -1
  6. package/dist/i18n/index.js +1 -1
  7. package/dist/i18n/language-tags.d.ts +9 -1
  8. package/dist/i18n/language-tags.js +11 -1
  9. package/dist/i18n/messages/en-US.d.ts +33 -5
  10. package/dist/i18n/messages/en-US.js +33 -5
  11. package/dist/i18n/messages/nl-NL.d.ts +33 -5
  12. package/dist/i18n/messages/nl-NL.js +33 -5
  13. package/dist/i18n/provider.d.ts +14 -7
  14. package/dist/i18n/provider.js +26 -29
  15. package/dist/index.js +0 -1
  16. package/dist/instrumentation/providers/BaseInstrumentationProvider.js +0 -14
  17. package/dist/loader-config.d.ts +0 -2
  18. package/dist/loader-config.js +0 -2
  19. package/dist/loaders/element-loader.js +13 -6
  20. package/dist/loaders/esm-adapter.js +48 -37
  21. package/dist/loaders/iife-adapter.d.ts +3 -3
  22. package/dist/loaders/iife-adapter.js +36 -35
  23. package/dist/media/index.d.ts +4 -6
  24. package/dist/media/index.js +6 -8
  25. package/dist/object/index.d.ts +8 -3
  26. package/dist/object/index.js +8 -6
  27. package/dist/pie/asset-handler.d.ts +0 -17
  28. package/dist/pie/asset-handler.js +1 -1
  29. package/dist/pie/authoring.js +5 -7
  30. package/dist/pie/elements-rendered.d.ts +0 -2
  31. package/dist/pie/elements-rendered.js +1 -1
  32. package/dist/pie/initialization.d.ts +13 -14
  33. package/dist/pie/initialization.js +61 -75
  34. package/dist/pie/item-session-contract.js +7 -9
  35. package/dist/pie/resource-monitor.d.ts +34 -32
  36. package/dist/pie/resource-monitor.js +152 -231
  37. package/dist/pie/stages.d.ts +4 -3
  38. package/dist/pie/stages.js +4 -3
  39. package/dist/pie/updates.js +5 -4
  40. package/dist/pie/utils.d.ts +0 -2
  41. package/dist/pie/utils.js +0 -2
  42. package/dist/security/sanitize-item-markup.d.ts +4 -4
  43. package/dist/security/sanitize-item-markup.js +4 -4
  44. package/dist/security/sanitize-svg-icon.js +2 -2
  45. package/dist/security/wrap-overwide-images.d.ts +1 -1
  46. package/dist/security/wrap-overwide-images.js +1 -1
  47. package/dist/timed-media/media-element-source.js +1 -2
  48. package/dist/types/index.d.ts +62 -52
  49. package/dist/types/index.js +2 -2
  50. package/dist/ui/attribute-coercion.d.ts +5 -0
  51. package/dist/ui/attribute-coercion.js +6 -4
  52. package/dist/ui/content-styles.d.ts +6 -10
  53. package/dist/ui/content-styles.js +8 -13
  54. package/dist/ui/first-focusable.d.ts +0 -1
  55. package/dist/ui/first-focusable.js +0 -1
  56. package/dist/ui/focus-trap.js +2 -3
  57. package/dist/ui/scope-css.d.ts +23 -26
  58. package/dist/ui/scope-css.js +23 -26
  59. package/package.json +3 -2
package/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # PIE Players Shared
2
2
 
3
- Shared runtime utilities for the `@pie-players/*` player suite. This package is
4
- for framework code and host integrations that need the same loader, PIE config,
5
- security, object, i18n, or type helpers used by the player packages.
3
+ Shared runtime utilities for the `@pie-players/*` player suite: the element
4
+ loader, PIE config and session helpers, markup security, i18n, and the
5
+ formative, timed-media and media contracts. For framework code and host
6
+ integrations that need the helpers the player packages use.
6
7
 
7
8
  ## Public Exports
8
9
 
@@ -12,28 +13,38 @@ import { makeUniqueTags } from "@pie-players/pie-players-shared/pie";
12
13
  import { normalizeItemPlayerStrategy } from "@pie-players/pie-players-shared";
13
14
  ```
14
15
 
15
- Supported subpaths are declared in `package.json`:
16
-
17
- - `@pie-players/pie-players-shared`
18
- - `@pie-players/pie-players-shared/loader-config`
19
- - `@pie-players/pie-players-shared/security`
20
- - `@pie-players/pie-players-shared/object`
21
- - `@pie-players/pie-players-shared/types`
22
- - `@pie-players/pie-players-shared/formative`
23
- - `@pie-players/pie-players-shared/timed-media`
24
- - `@pie-players/pie-players-shared/media`
25
- - `@pie-players/pie-players-shared/pie`
26
- - `@pie-players/pie-players-shared/pie/tag-names`
27
- - `@pie-players/pie-players-shared/loaders`
28
- - `@pie-players/pie-players-shared/i18n`
16
+ The subpaths below are the package's `exports`; nothing else is importable.
17
+
18
+ | Subpath | Contents |
19
+ | --- | --- |
20
+ | `@pie-players/pie-players-shared` | The root: the loader, PIE, security and object helpers, loader config, instrumentation providers, the item-player strategy helpers, the default bundle host URL (`BUILDER_BUNDLE_URL`), a subset of the shared types, and the UI helpers (attribute coercion, content styles, pointer gestures, overlay placement, focus trap, safe storage, scoped CSS) |
21
+ | `/loader-config` | `LoaderConfig` and its defaults: instrumentation (`trackPageActions`, `instrumentationProvider`), resource retry, and IIFE bundle retry |
22
+ | `/security` | The item-markup sanitizer (`sanitizeItemMarkup`, `createDefaultItemMarkupSanitizer`, `buildAuthoringAllowList`), external-stylesheet URL validation, the SVG icon and `style` attribute sanitizers, and the wrappers that put overwide images and tables in a scrollable region |
23
+ | `/object` | `mergeObjectsIgnoringNullUndefined`, `cloneDeep`, `isPlainRecord` |
24
+ | `/types` | The shared types: item, passage, section and session entities, PIE models and controllers, accessibility catalogs, and the [media vocabulary](#media-validation) |
25
+ | `/formative` | [Formative delivery](#formative-delivery) |
26
+ | `/timed-media` | [Timed media](#timed-media) |
27
+ | `/media` | [Media validation](#media-validation) |
28
+ | `/pie` | PIE runtime utilities: config helpers, bundle initialization, `ItemController` and the item session contract, session commit and snapshots, authoring helpers, math renderer setup, instrumentation event maps ([PIE utilities](src/pie/README.md)) |
29
+ | `/pie/tag-names` | Custom element tag helpers: `validateCustomElementTag`, `toViewTag`, `toPrintHashedTag`, `parseVersionedTagName` |
30
+ | `/loaders` | The `ElementLoader` primitive and its IIFE and ESM adapters, `registerPreloadedElements`, the element package policy, and the MathJax event names ([Element loading](#element-loading)) |
31
+ | `/i18n` | `createPieI18n`, a provider for every bundled locale ([i18n](src/i18n/README.md)) |
32
+ | `/i18n/types` | The i18n contract, types only |
33
+ | `/i18n/provider` | `SimpleI18n`, `getDefaultI18n` and `resolveInterfaceI18n`, with the English catalog only |
34
+ | `/i18n/catalogs` | `BUNDLED_LOCALES` and lazy loaders for the bundled locales other than English |
35
+ | `/i18n/language-tags` | BCP 47 language tag normalization and matching |
36
+ | `/nds-icon-button` | `<nds-icon-button>`, an icon button vendored as a prebuilt bundle with Lit inlined; importing it registers the tag. The assessment toolkit and `@pie-players/pie-tool-tts-inline` render it |
37
+ | `/ui/attribute-coercion` | Boolean attribute coercion for custom elements |
38
+ | `/ui/content-styles` | `installContentStyles`, `auditContentStyles` and the opt-out checks behind the item player's content styles |
39
+ | `/tools/term-lookup` | Term normalization and the lookup session the dictionary tools share; the host supplies the endpoint or a resolver |
29
40
 
30
41
  ## Formative Delivery
31
42
 
32
43
  `@pie-players/pie-players-shared/formative` holds the policy resolution, outcome
33
44
  aggregation, Try-state reducer, item view, and mastery rollup that
34
- `pie-section-player` drives. It is pure — no DOM, no timers, no element registry
35
- — so it is testable without a browser and importable by an adapter that has no
36
- player.
45
+ `pie-section-player` drives. It is pure (no DOM, no timers, no element
46
+ registry), so it is testable without a browser and importable by an adapter that
47
+ has no player.
37
48
 
38
49
  ```ts
39
50
  import {
@@ -44,8 +55,8 @@ import {
44
55
  } from "@pie-players/pie-players-shared/formative";
45
56
  ```
46
57
 
47
- The authored half — `FormativeDeliveryPolicy` on `AssessmentSection` and
48
- `FormativeItemPolicy` on `AssessmentItemRef` — is re-exported from the package
58
+ The authored half, `FormativeDeliveryPolicy` on `AssessmentSection` and
59
+ `FormativeItemPolicy` on `AssessmentItemRef`, is re-exported from the package
49
60
  root and from `/types`, beside the section types it annotates.
50
61
 
51
62
  The contract is [`docs/prds/formative-delivery-contract.md`](../../docs/prds/formative-delivery-contract.md),
@@ -55,9 +66,9 @@ including the QTI 3 mapping and why PIE says **Try** rather than "attempt".
55
66
 
56
67
  `@pie-players/pie-players-shared/timed-media` holds the timed-media section
57
68
  vocabulary, its validation, the cue reduction, the session slice, and the **Media
58
- Time Source** port that a section reaches media through. Pure apart from
59
- `createMediaElementTimeSource`, which is inert until called, so the contract stays
60
- importable in Node beside the rest.
69
+ Time Source** port that a section reaches media through. It is pure apart from
70
+ `createMediaElementTimeSource`, which is inert until called, so the contract
71
+ imports in Node.js beside the rest.
61
72
 
62
73
  ```ts
63
74
  import {
@@ -69,14 +80,14 @@ import {
69
80
  } from "@pie-players/pie-players-shared/timed-media";
70
81
  ```
71
82
 
72
- The port is shaped after `HTMLMediaElement` on purpose — a native `<video>`
73
- satisfies it through the adapter above — with two departures: seeking is
74
- `seekTo(seconds)` rather than a writable `currentTime`, and `capabilities` declares
75
- `canPause` / `canRestrictSeeking`, because a source that cannot control playback
76
- needs a way to say so. A host implementing the port directly needs no PIE element to
77
- deliver timed media.
83
+ The port is shaped after `HTMLMediaElement`, so a native `<video>` satisfies it
84
+ through `createMediaElementTimeSource`, with two departures: seeking is
85
+ `seekTo(seconds)` in place of a writable `currentTime`, and `capabilities`
86
+ declares `canPause` and `canRestrictSeeking`, because a source that cannot
87
+ control playback needs a way to say so. A host implementing the port directly
88
+ needs no PIE element to deliver timed media.
78
89
 
79
- The authored half — `sectionType` and `timedMedia` on `AssessmentSection` — is
90
+ The authored half, `sectionType` and `timedMedia` on `AssessmentSection`, is
80
91
  re-exported from the package root and from `/types`, beside the section types it
81
92
  annotates.
82
93
 
@@ -87,14 +98,22 @@ unenforceable policy degrades to advisory rather than failing closed.
87
98
  ## Media Validation
88
99
 
89
100
  `@pie-players/pie-players-shared/media` validates authored, wire-facing media
90
- references before they reach a media element: the source-scheme allow-list
91
- (`isSafeMediaSrc`), source normalization with dedupe by `src`
92
- (`normalizeMediaSources`), fragment-range normalization, and
93
- unknown-`MediaAssetRef.version` rejection. `applyMediaFragment` writes a
94
- fragment range as a Media Fragments URI, and `enforceMediaFragment` holds a
95
- media element to that range because browsers honour the URI inconsistently.
101
+ references before they reach a media element:
102
+
103
+ - `isSafeMediaSrc` is the source-scheme allow-list: relative and
104
+ protocol-relative URLs, `http:`, `https:`, `data:` and `blob:`.
105
+ - `normalizeMediaSources` drops unsafe and malformed sources and deduplicates by
106
+ `src`, keeping the first.
107
+ - `normalizeMediaFragment` normalizes a fragment range; an end at or before the
108
+ start becomes an open end.
109
+ - `isUnsupportedMediaAssetVersion` rejects a `MediaAssetRef.version` other than
110
+ `SUPPORTED_MEDIA_ASSET_VERSION` (1); an absent version passes.
111
+ - `applyMediaFragment` writes a fragment range as a Media Fragments URI (`#t=`),
112
+ and `enforceMediaFragment` holds a media element to that range, because
113
+ browsers honor the URI inconsistently.
114
+
96
115
  Nothing runs at import time, so an element imports it without the assessment
97
- toolkit; the toolkit root re-exports the same names.
116
+ toolkit.
98
117
 
99
118
  ```ts
100
119
  import {
@@ -104,46 +123,38 @@ import {
104
123
  } from "@pie-players/pie-players-shared/media";
105
124
  ```
106
125
 
107
- The media vocabulary it validates is [`docs/prds/shared-contracts/media-asset-contract.md`](../../docs/prds/shared-contracts/media-asset-contract.md).
108
-
109
- ## Browser ESM Element Contract
110
-
111
- The canonical producer-side contract lives with the `pie-elements-ng` package
112
- contract. The loader assumptions here must stay aligned with that producer
113
- surface.
114
-
115
- The ESM element loader consumes static `@pie-element/*` browser entries such as
116
- `dist/browser/delivery/index.js`; it does not transform element packages through
117
- CDN `+esm` entry points. Browser ESM elements must publish exact shared runtime
118
- metadata in `package.json` under `pie.browserSharedDependencies`.
119
- Under URL resolution it loads the variant an element declares in
120
- `pie.browserEditorRuntime` and maps that runtime's `pie.browserModules`, one
121
- runtime version per page; see
122
- [Shared editor runtime](../../docs/item-player/loading-strategies.md#shared-editor-runtime).
123
-
124
- jsDelivr is the default npm CDN provider. Hosts can opt into `esm.sh` with
125
- `loaderOptions.esmCdnProvider = "esm.sh"` and `loaderOptions.esmCdnUrl =
126
- "https://esm.sh"`. For `esm.sh`, PIE package artifacts are loaded from
127
- `raw.esm.sh` while shared browser dependencies are loaded from `esm.sh`.
128
- Provider names are open-ended: custom/internal providers can pass their own name
129
- when they follow the jsDelivr-compatible package-file URL layout, or pass a
130
- provider object when package artifacts and shared dependencies need different
131
- route builders. Under `esm` the item player's own MathJax takes the provider's
132
- package-file root as its asset root when the page sets none; see
133
- [MathJax assets](../../docs/item-player/loading-strategies.md#mathjax-assets).
134
-
135
- `dependencies` and `peerDependencies` are not used as fallback runtime contracts.
136
- If multiple elements request different minor or patch versions of a shared
137
- singleton such as React, the loader chooses the highest same-major version and
138
- emits console plus instrumentation warnings. Different major versions fail the
139
- load and are also reported through console and instrumentation.
140
-
141
- Preloaded mode means the host has already registered the expected custom element
142
- tag; it is not a separate package format. IIFE mode remains supported through the
143
- same package exports and controller compatibility shim used by legacy builders.
126
+ The media vocabulary it validates ships in `/types`:
127
+
128
+ | Type | Shape |
129
+ | --- | --- |
130
+ | `MediaAssetRef` | `{ version: 1, id, kind, sources, poster?, thumbnail?, durationSeconds?, tracks?, transcript?, label?, description?, lang? }` |
131
+ | `MediaKind` | `"image" \| "audio" \| "video" \| "other"` |
132
+ | `MediaSource` | `{ src, type?, width?, height?, bitrate? }` |
133
+ | `TextTrackRef` | `{ src, kind, lang, label, default? }`, where `kind` is `captions`, `subtitles`, `descriptions`, `chapters` or `metadata` |
134
+ | `TranscriptRef` | `{ src?, html?, plainText?, lang? }` |
135
+ | `MediaFragmentRange` | `{ startSeconds, endSeconds? }`: a slice of a longer recording, so one file serves several content nodes |
136
+ | `SignLanguageCardPayload` | `{ signLang?, media, fragment? }`, the payload of a `sign-language` catalog card |
137
+ | `SpokenAudioCardPayload` | `{ media, fragment? }`, the payload of a recorded `spoken` catalog card |
138
+ | `CatalogCardPayload` | Either payload; the card's `catalog` decides which |
139
+
140
+ The design record is [`docs/prds/shared-contracts/media-asset-contract.md`](../../docs/prds/shared-contracts/media-asset-contract.md).
141
+
142
+ ## Element Loading
143
+
144
+ `@pie-players/pie-players-shared/loaders` holds the `ElementLoader` primitive and
145
+ the IIFE and ESM adapters `<pie-item-player>` loads elements through.
146
+ [Loading strategies](../../docs/item-player/loading-strategies.md) documents
147
+ their behavior, including the ESM adapter's file layout and CDN providers
148
+ ([`strategy="esm"`](../../docs/item-player/loading-strategies.md#strategyesm))
149
+ and the one-version rule for page singletons such as React
150
+ ([Shared dependencies](../../docs/item-player/loading-strategies.md#shared-dependencies)).
151
+ The producer side is the
152
+ [PIE element contract](https://github.com/pie-framework/pie-elements-ng/blob/develop/docs/PIE_ELEMENT_CONTRACT.md)
153
+ in pie-elements-ng.
144
154
 
145
155
  ## Related Documentation
146
156
 
147
157
  - [PIE utilities README](src/pie/README.md)
148
158
  - [i18n README](src/i18n/README.md)
149
- - [PIE element tag/id contract](../../docs/architecture/types-and-utilities-contract.md)
159
+ - [Loading strategies](../../docs/item-player/loading-strategies.md)
160
+ - [Internationalization](../../docs/architecture/internationalization.md)
@@ -22,9 +22,9 @@ lands, so it never leaks into a consumer's own dependency set.
22
22
 
23
23
  | Field | Value |
24
24
  | --- | --- |
25
- | Upstream repo | `git@github.com:RenaissancePlace/nextComponentLibrary.git` |
25
+ | Upstream | NDS component library |
26
26
  | Source file | `web-components/src/components/icon-button/nds-icon-button.ts` |
27
- | Last sync commit | `1c728d3` (`feat(UX-2319): add new components…`) |
27
+ | Last sync commit | `1c728d3` |
28
28
  | Bundler | Vite 7 in lib mode, ES output, single-file (Lit inlined) |
29
29
 
30
30
  ## Why a vendored bundle, not an npm dep
@@ -79,9 +79,8 @@ Consuming bundlers treat this module per their own externals config:
79
79
 
80
80
  When a newer `nds-icon-button` is needed:
81
81
 
82
- 1. Pull the latest `nextComponentLibrary` next to this repo (sibling checkout:
83
- `../../nextComponentLibrary`).
84
- 2. From `nextComponentLibrary/web-components/`, `bun install`.
82
+ 1. Check out the latest NDS component library source.
83
+ 2. From its `web-components/` directory, `bun install`.
85
84
  3. Build a self-contained ESM bundle (no shared chunks) — the upstream
86
85
  `vite.bundle.config.js` splits Lit into a `property.js` chunk, which would
87
86
  break the single-file copy here. Use this one-shot config instead:
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Minimal, frontend-safe defaults for PIE build service URLs.
3
3
  *
4
- * In PIEOneer, these come from `$env/dynamic/public`. In `pie-players` we avoid
4
+ * A SvelteKit host reads these from `$env/dynamic/public`. `pie-players` avoids
5
5
  * SvelteKit env dependencies so this can be consumed by any bundler.
6
6
  */
7
7
  declare global {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Minimal, frontend-safe defaults for PIE build service URLs.
3
3
  *
4
- * In PIEOneer, these come from `$env/dynamic/public`. In `pie-players` we avoid
4
+ * A SvelteKit host reads these from `$env/dynamic/public`. `pie-players` avoids
5
5
  * SvelteKit env dependencies so this can be consumed by any bundler.
6
6
  */
7
7
  import { DEFAULT_BUNDLE_HOST } from "../loaders/defaults.js";
@@ -28,6 +28,6 @@ import type { I18nConfig, I18nServiceApi } from "./types.js";
28
28
  */
29
29
  export declare function createPieI18n(config?: I18nConfig): I18nServiceApi;
30
30
  export { BUNDLED_LOCALES, loadBundledCatalog } from "./catalogs.js";
31
- export { findBestLanguageMatch, languageTagLookupSequence, languageTagsEqual, normalizeLanguageTag, } from "./language-tags.js";
31
+ export { findBestLanguageMatch, languageTagLookupSequence, languageTagsEqual, normalizeLanguageTag, toLanguageTag, } from "./language-tags.js";
32
32
  export { DEFAULT_LOCALE, detectBrowserLocale, dynamicMessageKey, getDefaultI18n, localeDirection, resolveInterfaceI18n, SimpleI18n, } from "./provider.js";
33
33
  export type { BundledLocaleCode, DynamicMessageKey, I18nConfig, I18nProvider, I18nServiceApi, InterpolationValues, LocaleCode, MessageCatalog, MessageKey, MessageKeyInput, MessageNode, PluralOptions, PluralTranslation, TextDirection, TranslationBundle, } from "./types.js";
@@ -35,5 +35,5 @@ export function createPieI18n(config = {}) {
35
35
  });
36
36
  }
37
37
  export { BUNDLED_LOCALES, loadBundledCatalog } from "./catalogs.js";
38
- export { findBestLanguageMatch, languageTagLookupSequence, languageTagsEqual, normalizeLanguageTag, } from "./language-tags.js";
38
+ export { findBestLanguageMatch, languageTagLookupSequence, languageTagsEqual, normalizeLanguageTag, toLanguageTag, } from "./language-tags.js";
39
39
  export { DEFAULT_LOCALE, detectBrowserLocale, dynamicMessageKey, getDefaultI18n, localeDirection, resolveInterfaceI18n, SimpleI18n, } from "./provider.js";
@@ -22,12 +22,20 @@
22
22
  * Converts POSIX underscores to hyphens, strips a POSIX charset or modifier
23
23
  * suffix (`es_ES.UTF-8`, `es_ES@euro`), and lowercases. Lowercasing rather than
24
24
  * applying BCP-47 display casing is intentional — this output is a comparison
25
- * key, not something to render.
25
+ * key, not something to render. `toLanguageTag` is the form to render.
26
26
  *
27
27
  * Returns an empty string for nullish or blank input, which callers read as
28
28
  * "this card declares no language".
29
29
  */
30
30
  export declare function normalizeLanguageTag(tag: string | null | undefined): string;
31
+ /**
32
+ * A tag in BCP-47 syntax with its authored casing kept: POSIX underscores become
33
+ * hyphens, so `es_ES` renders as `es-ES`. The form for a `lang` attribute or a
34
+ * platform locale API.
35
+ *
36
+ * Returns an empty string for nullish or blank input.
37
+ */
38
+ export declare function toLanguageTag(tag: string | null | undefined): string;
31
39
  /**
32
40
  * Whether two language tags denote the same locale, ignoring separator style and
33
41
  * case. Two tags that both declare no language are not a match.
@@ -22,7 +22,7 @@
22
22
  * Converts POSIX underscores to hyphens, strips a POSIX charset or modifier
23
23
  * suffix (`es_ES.UTF-8`, `es_ES@euro`), and lowercases. Lowercasing rather than
24
24
  * applying BCP-47 display casing is intentional — this output is a comparison
25
- * key, not something to render.
25
+ * key, not something to render. `toLanguageTag` is the form to render.
26
26
  *
27
27
  * Returns an empty string for nullish or blank input, which callers read as
28
28
  * "this card declares no language".
@@ -34,6 +34,16 @@ export function normalizeLanguageTag(tag) {
34
34
  const withoutPosixSuffix = separatorsUnified.split(/[.@]/)[0];
35
35
  return withoutPosixSuffix.toLowerCase();
36
36
  }
37
+ /**
38
+ * A tag in BCP-47 syntax with its authored casing kept: POSIX underscores become
39
+ * hyphens, so `es_ES` renders as `es-ES`. The form for a `lang` attribute or a
40
+ * platform locale API.
41
+ *
42
+ * Returns an empty string for nullish or blank input.
43
+ */
44
+ export function toLanguageTag(tag) {
45
+ return tag ? tag.trim().replace(/_/g, "-") : "";
46
+ }
37
47
  /**
38
48
  * Whether two language tags denote the same locale, ignoring separator style and
39
49
  * case. Two tags that both declare no language are not a match.
@@ -393,6 +393,7 @@ declare const enUS: {
393
393
  textToSpeech: {
394
394
  name: string;
395
395
  description: string;
396
+ initFailed: string;
396
397
  /** Inline reading controls and their live-region announcements. */
397
398
  inline: {
398
399
  controlsA11y: string;
@@ -400,14 +401,13 @@ declare const enUS: {
400
401
  rewindA11y: string;
401
402
  fastForwardA11y: string;
402
403
  stopA11y: string;
403
- initializing: string;
404
- initFailed: string;
405
404
  starting: string;
406
405
  started: string;
407
406
  resumed: string;
408
407
  pausedAnnouncement: string;
409
408
  stopped: string;
410
409
  switchedSection: string;
410
+ nothingToRead: string;
411
411
  startFailed: string;
412
412
  skippedForward: string;
413
413
  skipForwardFailed: string;
@@ -502,6 +502,7 @@ declare const enUS: {
502
502
  pnp: {
503
503
  title: string;
504
504
  noAssessmentBound: string;
505
+ noAssessmentBoundBody: string;
505
506
  determinationReadOnly: string;
506
507
  toolsEditor: string;
507
508
  enforcement: string;
@@ -578,12 +579,39 @@ declare const enUS: {
578
579
  previewEnterText: string;
579
580
  previewUnavailable: string;
580
581
  browserSynthesisUnavailable: string;
581
- browserSynthesisEndedEarly: string;
582
- browserSynthesisQueueFailed: string;
583
- browserPreviewFailed: string;
584
582
  ssmlWordTrackingDisabled: string;
585
583
  applyUnavailable: string;
586
584
  coordinatorUnavailable: string;
585
+ customProviderNotRegistered: string;
586
+ customProviderNoConfig: string;
587
+ httpError: string;
588
+ unknownError: string;
589
+ previewRequestFailed: string;
590
+ notAvailable: string;
591
+ voiceLocal: string;
592
+ voiceRemote: string;
593
+ voiceBrowserDefault: string;
594
+ browserAvailable: {
595
+ one: string;
596
+ other: string;
597
+ };
598
+ pollyAvailable: {
599
+ one: string;
600
+ other: string;
601
+ };
602
+ googleAvailable: {
603
+ one: string;
604
+ other: string;
605
+ };
606
+ headerRowReservation: string;
607
+ speedOptionsHelp: string;
608
+ usingSelectedVoice: string;
609
+ autoVoice: string;
610
+ autoVoiceWaiting: string;
611
+ previewHintBrowserSsml: string;
612
+ previewHintGoogleSsml: string;
613
+ previewHintCustom: string;
614
+ previewHintTracking: string;
587
615
  };
588
616
  };
589
617
  };
@@ -407,6 +407,7 @@ const enUS = {
407
407
  textToSpeech: {
408
408
  name: "Text to Speech",
409
409
  description: "Read content aloud",
410
+ initFailed: "Unable to initialize text-to-speech. Try again.",
410
411
  /** Inline reading controls and their live-region announcements. */
411
412
  inline: {
412
413
  controlsA11y: "Reading controls",
@@ -414,14 +415,13 @@ const enUS = {
414
415
  rewindA11y: "Rewind",
415
416
  fastForwardA11y: "Fast-forward",
416
417
  stopA11y: "Stop reading",
417
- initializing: "Initializing text-to-speech",
418
- initFailed: "Unable to initialize text-to-speech. Try again.",
419
418
  starting: "Starting reading",
420
419
  started: "Reading started",
421
420
  resumed: "Reading resumed",
422
421
  pausedAnnouncement: "Reading paused",
423
422
  stopped: "Reading stopped",
424
423
  switchedSection: "Reading switched to another section",
424
+ nothingToRead: "Nothing to read aloud here",
425
425
  startFailed: "Unable to start reading",
426
426
  skippedForward: "Skipped forward",
427
427
  skipForwardFailed: "Unable to skip forward",
@@ -516,6 +516,7 @@ const enUS = {
516
516
  pnp: {
517
517
  title: "PNP Profile",
518
518
  noAssessmentBound: "No assessment bound",
519
+ noAssessmentBoundBody: "Profile, district and test-administration policy have nothing to read, so only an item's requiredTools can grant an accommodation. Pass the assessment to the toolkit's assessment property, or call updateAssessment(...) on the toolkit coordinator.",
519
520
  determinationReadOnly: "Determination (read-only)",
520
521
  toolsEditor: "Tools Editor",
521
522
  enforcement: "PNP enforcement",
@@ -594,12 +595,39 @@ const enUS = {
594
595
  previewEnterText: "Enter preview text before starting playback.",
595
596
  previewUnavailable: "Cannot preview while this service is unavailable.",
596
597
  browserSynthesisUnavailable: "Browser speech synthesis is unavailable.",
597
- browserSynthesisEndedEarly: "Browser speech synthesis ended before audio started. Restart the browser and try again.",
598
- browserSynthesisQueueFailed: "Browser speech synthesis failed to queue preview audio.",
599
- browserPreviewFailed: "Failed to play browser voice preview.",
600
598
  ssmlWordTrackingDisabled: "Google SSML preview preserves authored SSML, so word tracking is disabled.",
601
599
  applyUnavailable: "Cannot apply settings while this service is unavailable.",
602
600
  coordinatorUnavailable: "Toolkit coordinator is not available for text-to-speech updates.",
601
+ customProviderNotRegistered: "Custom provider '{id}' is not registered.",
602
+ customProviderNoConfig: "Custom provider '{id}' did not return an apply config.",
603
+ httpError: "HTTP {status}: {message}",
604
+ unknownError: "Unknown error",
605
+ previewRequestFailed: "Preview request failed ({status}).",
606
+ notAvailable: "n/a",
607
+ voiceLocal: "local",
608
+ voiceRemote: "remote",
609
+ voiceBrowserDefault: "browser default",
610
+ browserAvailable: {
611
+ one: "Browser TTS available ({count} voice detected).",
612
+ other: "Browser TTS available ({count} voices detected).",
613
+ },
614
+ pollyAvailable: {
615
+ one: "AWS Polly available ({count} matching voice, {engine} engine).",
616
+ other: "AWS Polly available ({count} matching voices, {engine} engine).",
617
+ },
618
+ googleAvailable: {
619
+ one: "Google Cloud TTS available ({count} matching voice).",
620
+ other: "Google Cloud TTS available ({count} matching voices).",
621
+ },
622
+ headerRowReservation: "Item header row reservation: {state}",
623
+ speedOptionsHelp: "Comma or semicolon-separated multipliers. Include 1 for Normal; leave empty to hide speed controls.",
624
+ usingSelectedVoice: "Using selected voice: {voice}",
625
+ autoVoice: "Best available voice (auto): {voice}",
626
+ autoVoiceWaiting: "Best available voice (auto): waiting for browser voices.",
627
+ previewHintBrowserSsml: "SSML is unsupported in Browser preview.",
628
+ previewHintGoogleSsml: "SSML preserved. Tracking disabled.",
629
+ previewHintCustom: "Tracking enabled when provider returns speech marks.",
630
+ previewHintTracking: "Tracking enabled while preview plays.",
603
631
  },
604
632
  },
605
633
  };
@@ -321,20 +321,20 @@ declare const nlNL: {
321
321
  textToSpeech: {
322
322
  name: string;
323
323
  description: string;
324
+ initFailed: string;
324
325
  inline: {
325
326
  controlsA11y: string;
326
327
  playbackSpeedA11y: string;
327
328
  rewindA11y: string;
328
329
  fastForwardA11y: string;
329
330
  stopA11y: string;
330
- initializing: string;
331
- initFailed: string;
332
331
  starting: string;
333
332
  started: string;
334
333
  resumed: string;
335
334
  pausedAnnouncement: string;
336
335
  stopped: string;
337
336
  switchedSection: string;
337
+ nothingToRead: string;
338
338
  startFailed: string;
339
339
  skippedForward: string;
340
340
  skipForwardFailed: string;
@@ -414,6 +414,7 @@ declare const nlNL: {
414
414
  pnp: {
415
415
  title: string;
416
416
  noAssessmentBound: string;
417
+ noAssessmentBoundBody: string;
417
418
  determinationReadOnly: string;
418
419
  toolsEditor: string;
419
420
  enforcement: string;
@@ -489,12 +490,39 @@ declare const nlNL: {
489
490
  previewEnterText: string;
490
491
  previewUnavailable: string;
491
492
  browserSynthesisUnavailable: string;
492
- browserSynthesisEndedEarly: string;
493
- browserSynthesisQueueFailed: string;
494
- browserPreviewFailed: string;
495
493
  ssmlWordTrackingDisabled: string;
496
494
  applyUnavailable: string;
497
495
  coordinatorUnavailable: string;
496
+ customProviderNotRegistered: string;
497
+ customProviderNoConfig: string;
498
+ httpError: string;
499
+ unknownError: string;
500
+ previewRequestFailed: string;
501
+ notAvailable: string;
502
+ voiceLocal: string;
503
+ voiceRemote: string;
504
+ voiceBrowserDefault: string;
505
+ browserAvailable: {
506
+ one: string;
507
+ other: string;
508
+ };
509
+ pollyAvailable: {
510
+ one: string;
511
+ other: string;
512
+ };
513
+ googleAvailable: {
514
+ one: string;
515
+ other: string;
516
+ };
517
+ headerRowReservation: string;
518
+ speedOptionsHelp: string;
519
+ usingSelectedVoice: string;
520
+ autoVoice: string;
521
+ autoVoiceWaiting: string;
522
+ previewHintBrowserSsml: string;
523
+ previewHintGoogleSsml: string;
524
+ previewHintCustom: string;
525
+ previewHintTracking: string;
498
526
  };
499
527
  };
500
528
  };
@@ -321,20 +321,20 @@ const nlNL = {
321
321
  textToSpeech: {
322
322
  name: "Voorlezen",
323
323
  description: "Inhoud laten voorlezen",
324
+ initFailed: "Voorlezen kon niet worden gestart. Probeer het opnieuw.",
324
325
  inline: {
325
326
  controlsA11y: "Voorleesknoppen",
326
327
  playbackSpeedA11y: "Voorleessnelheid",
327
328
  rewindA11y: "Terugspoelen",
328
329
  fastForwardA11y: "Vooruitspoelen",
329
330
  stopA11y: "Voorlezen stoppen",
330
- initializing: "Voorlezen wordt gestart",
331
- initFailed: "Voorlezen kon niet worden gestart. Probeer het opnieuw.",
332
331
  starting: "Voorlezen wordt gestart",
333
332
  started: "Voorlezen gestart",
334
333
  resumed: "Voorlezen hervat",
335
334
  pausedAnnouncement: "Voorlezen gepauzeerd",
336
335
  stopped: "Voorlezen gestopt",
337
336
  switchedSection: "Het voorlezen is naar een ander onderdeel gegaan",
337
+ nothingToRead: "Hier is niets om voor te lezen",
338
338
  startFailed: "Voorlezen kon niet worden gestart",
339
339
  skippedForward: "Vooruitgespoeld",
340
340
  skipForwardFailed: "Vooruitspoelen is niet gelukt",
@@ -414,6 +414,7 @@ const nlNL = {
414
414
  pnp: {
415
415
  title: "PNP-profiel",
416
416
  noAssessmentBound: "Geen toets gekoppeld",
417
+ noAssessmentBoundBody: "Profiel-, district- en afnamebeleid hebben niets om te lezen, dus alleen de requiredTools van een item kunnen een voorziening toekennen. Geef de toets door aan de assessment-eigenschap van de toolkit, of roep updateAssessment(...) aan op de toolkit-coordinator.",
417
418
  determinationReadOnly: "Vaststelling (alleen lezen)",
418
419
  toolsEditor: "Hulpmiddelen bewerken",
419
420
  enforcement: "PNP-handhaving",
@@ -489,12 +490,39 @@ const nlNL = {
489
490
  previewEnterText: "Voer eerst voorbeeldtekst in.",
490
491
  previewUnavailable: "Een voorbeeld is niet mogelijk zolang deze dienst niet beschikbaar is.",
491
492
  browserSynthesisUnavailable: "Spraaksynthese van de browser is niet beschikbaar.",
492
- browserSynthesisEndedEarly: "De spraaksynthese van de browser stopte voordat de audio begon. Start de browser opnieuw en probeer het nog eens.",
493
- browserSynthesisQueueFailed: "De spraaksynthese van de browser kon het voorbeeldfragment niet in de wachtrij zetten.",
494
- browserPreviewFailed: "Het voorbeeld van de browserstem kon niet worden afgespeeld.",
495
493
  ssmlWordTrackingDisabled: "Bij een Google SSML-voorbeeld blijft de SSML uit de bron intact, waardoor woordmarkering is uitgeschakeld.",
496
494
  applyUnavailable: "Instellingen toepassen is niet mogelijk zolang deze dienst niet beschikbaar is.",
497
495
  coordinatorUnavailable: "De toolkitcoördinator is niet beschikbaar voor voorleesinstellingen.",
496
+ customProviderNotRegistered: "Aangepaste provider '{id}' is niet geregistreerd.",
497
+ customProviderNoConfig: "Aangepaste provider '{id}' gaf geen configuratie om toe te passen.",
498
+ httpError: "HTTP {status}: {message}",
499
+ unknownError: "Onbekende fout",
500
+ previewRequestFailed: "Het voorbeeldverzoek is mislukt ({status}).",
501
+ notAvailable: "n.v.t.",
502
+ voiceLocal: "lokaal",
503
+ voiceRemote: "extern",
504
+ voiceBrowserDefault: "browserstandaard",
505
+ browserAvailable: {
506
+ one: "Browserspraak beschikbaar ({count} stem gevonden).",
507
+ other: "Browserspraak beschikbaar ({count} stemmen gevonden).",
508
+ },
509
+ pollyAvailable: {
510
+ one: "AWS Polly beschikbaar ({count} passende stem, engine {engine}).",
511
+ other: "AWS Polly beschikbaar ({count} passende stemmen, engine {engine}).",
512
+ },
513
+ googleAvailable: {
514
+ one: "Google Cloud TTS beschikbaar ({count} passende stem).",
515
+ other: "Google Cloud TTS beschikbaar ({count} passende stemmen).",
516
+ },
517
+ headerRowReservation: "Rij in de itemkop gereserveerd: {state}",
518
+ speedOptionsHelp: "Vermenigvuldigers, gescheiden door komma's of puntkomma's. Neem 1 op voor Normaal; laat leeg om de snelheidsknoppen te verbergen.",
519
+ usingSelectedVoice: "Gekozen stem: {voice}",
520
+ autoVoice: "Beste beschikbare stem (automatisch): {voice}",
521
+ autoVoiceWaiting: "Beste beschikbare stem (automatisch): wachten op de stemmen van de browser.",
522
+ previewHintBrowserSsml: "SSML wordt niet ondersteund in het browservoorbeeld.",
523
+ previewHintGoogleSsml: "SSML blijft intact. Markering uitgeschakeld.",
524
+ previewHintCustom: "Markering actief wanneer de provider spraakmarkeringen teruggeeft.",
525
+ previewHintTracking: "Markering actief tijdens het afspelen van het voorbeeld.",
498
526
  },
499
527
  },
500
528
  };