@pie-players/pie-players-shared 0.3.75 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -77
- package/dist/components/vendor/nds/README.md +4 -5
- package/dist/config/profile.d.ts +1 -1
- package/dist/config/profile.js +1 -1
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/language-tags.d.ts +9 -1
- package/dist/i18n/language-tags.js +11 -1
- package/dist/i18n/messages/en-US.d.ts +33 -5
- package/dist/i18n/messages/en-US.js +33 -5
- package/dist/i18n/messages/nl-NL.d.ts +33 -5
- package/dist/i18n/messages/nl-NL.js +33 -5
- package/dist/i18n/provider.d.ts +14 -7
- package/dist/i18n/provider.js +26 -29
- package/dist/index.js +0 -1
- package/dist/instrumentation/providers/BaseInstrumentationProvider.js +0 -14
- package/dist/loader-config.d.ts +0 -2
- package/dist/loader-config.js +0 -2
- package/dist/loaders/element-loader.js +13 -6
- package/dist/loaders/esm-adapter.js +48 -37
- package/dist/loaders/iife-adapter.d.ts +3 -3
- package/dist/loaders/iife-adapter.js +36 -35
- package/dist/media/index.d.ts +4 -6
- package/dist/media/index.js +6 -8
- package/dist/object/index.d.ts +8 -3
- package/dist/object/index.js +8 -6
- package/dist/pie/asset-handler.d.ts +0 -17
- package/dist/pie/asset-handler.js +1 -1
- package/dist/pie/authoring.js +5 -7
- package/dist/pie/elements-rendered.d.ts +0 -2
- package/dist/pie/elements-rendered.js +1 -1
- package/dist/pie/index.d.ts +1 -0
- package/dist/pie/index.js +1 -0
- package/dist/pie/initialization.d.ts +13 -14
- package/dist/pie/initialization.js +61 -75
- package/dist/pie/item-session-contract.js +7 -9
- package/dist/pie/resource-monitor.d.ts +34 -32
- package/dist/pie/resource-monitor.js +152 -231
- package/dist/pie/stages.d.ts +4 -3
- package/dist/pie/stages.js +4 -3
- package/dist/pie/updates.js +5 -4
- package/dist/pie/utils.d.ts +0 -2
- package/dist/pie/utils.js +0 -2
- package/dist/pie/versioned-tag.d.ts +10 -0
- package/dist/pie/versioned-tag.js +27 -0
- package/dist/security/sanitize-item-markup.d.ts +4 -4
- package/dist/security/sanitize-item-markup.js +4 -4
- package/dist/security/sanitize-svg-icon.js +2 -2
- package/dist/security/wrap-overwide-images.d.ts +1 -1
- package/dist/security/wrap-overwide-images.js +1 -1
- package/dist/timed-media/media-element-source.js +1 -2
- package/dist/types/index.d.ts +88 -52
- package/dist/types/index.js +2 -2
- package/dist/ui/attribute-coercion.d.ts +5 -0
- package/dist/ui/attribute-coercion.js +6 -4
- package/dist/ui/content-styles.d.ts +6 -10
- package/dist/ui/content-styles.js +8 -13
- package/dist/ui/first-focusable.d.ts +0 -1
- package/dist/ui/first-focusable.js +0 -1
- package/dist/ui/focus-trap.js +2 -3
- package/dist/ui/scope-css.d.ts +23 -26
- package/dist/ui/scope-css.js +23 -26
- 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
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
35
|
-
|
|
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
|
|
48
|
-
`FormativeItemPolicy` on `AssessmentItemRef
|
|
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.
|
|
59
|
-
`createMediaElementTimeSource`, which is inert until called, so the contract
|
|
60
|
-
|
|
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
|
|
73
|
-
|
|
74
|
-
`seekTo(seconds)`
|
|
75
|
-
`canPause`
|
|
76
|
-
needs a way to say so. A host implementing the port directly
|
|
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
|
|
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:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
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
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
`
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
`
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
[
|
|
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
|
-
- [
|
|
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
|
|
25
|
+
| Upstream | NDS component library |
|
|
26
26
|
| Source file | `web-components/src/components/icon-button/nds-icon-button.ts` |
|
|
27
|
-
| Last sync commit | `1c728d3`
|
|
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.
|
|
83
|
-
|
|
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:
|
package/dist/config/profile.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Minimal, frontend-safe defaults for PIE build service URLs.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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 {
|
package/dist/config/profile.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Minimal, frontend-safe defaults for PIE build service URLs.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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";
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/i18n/index.js
CHANGED
|
@@ -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
|
};
|