@pie-players/pie-i18n-generated 0.0.0-stage → 1.0.0-beta.4

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
@@ -1,3 +1,179 @@
1
- # Temporary Holding Version
1
+ # @pie-players/pie-i18n-generated
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Machine-generated, **unreviewed** interface locale catalogs for PIE players —
4
+ the optional tier beneath the reviewed catalogs that
5
+ [`@pie-players/pie-players-shared/i18n`](../players-shared/src/i18n/README.md)
6
+ ships. Every catalog here is generated from the English source against a
7
+ per-locale terminology brief, is provenance-marked, and is **not** presented
8
+ as reviewed: the coverage number certifies structure (keys, placeholders,
9
+ plural shape, freshness), never translation quality.
10
+
11
+ Installing this package changes nothing on its own. A host opts in per
12
+ deployment by wiring the loaders into the provider; a host that never sets a
13
+ generated locale fetches nothing, because every catalog is a lazy chunk
14
+ behind a static `import()` map and the default locale (`en-US`) is statically
15
+ resident in `players-shared` and never hits a loader at all.
16
+
17
+ ## Wiring
18
+
19
+ ```ts
20
+ import {
21
+ BUNDLED_LOCALES,
22
+ createPieI18n,
23
+ loadBundledCatalog,
24
+ } from "@pie-players/pie-players-shared/i18n";
25
+ import {
26
+ GENERATED_LOCALES,
27
+ loadGeneratedCatalog,
28
+ } from "@pie-players/pie-i18n-generated";
29
+
30
+ const i18n = createPieI18n({
31
+ availableLocales: [...BUNDLED_LOCALES, ...GENERATED_LOCALES],
32
+ // Bundled first: a reviewed catalog silently wins over a generated one for
33
+ // the same tag, so promoting a locale out of this package into the
34
+ // reviewed set needs no host action.
35
+ loadCatalog: (locale) =>
36
+ loadBundledCatalog(locale).catch(() => loadGeneratedCatalog(locale)),
37
+ });
38
+
39
+ // Hand the provider to the player; the element's `locale` names the locale
40
+ // and is the only thing that drives it. Built without `locale` on purpose:
41
+ // the factory names a tag without loading its catalog.
42
+ document.querySelector("pie-section-player-splitpane").runtime = { i18n };
43
+ ```
44
+
45
+ ```html
46
+ <pie-section-player-splitpane locale="th-TH" …></pie-section-player-splitpane>
47
+ ```
48
+
49
+ Hosts may also override individual strings of a generated catalog the same
50
+ way they override reviewed ones: `customMessages` is consulted ahead of any
51
+ loaded catalog.
52
+
53
+ ## Locales
54
+
55
+ | Tag | Status | Brief | Generated |
56
+ | --- | --- | --- | --- |
57
+ | `th-TH` | generated, unreviewed | [`briefs/th-TH.md`](./briefs/th-TH.md) | 2026-10-19 |
58
+ | `en-GB` | generated, unreviewed | [`briefs/en-GB.md`](./briefs/en-GB.md) | 2026-10-19 |
59
+ | `en-AU` | generated, unreviewed | [`briefs/en-AU.md`](./briefs/en-AU.md) | 2026-10-19 |
60
+ | `es` | generated, unreviewed (Latin-American neutral; serves `es*`) | [`briefs/es.md`](./briefs/es.md) | 2026-10-19 |
61
+ | `fr` | generated, unreviewed (France flavour; serves `fr*`) | [`briefs/fr.md`](./briefs/fr.md) | 2026-10-19 |
62
+ | `ar` | generated, unreviewed (MSA; serves `ar*`; RTL — first RTL locale, layout not yet audited) | [`briefs/ar.md`](./briefs/ar.md) | 2026-10-19 |
63
+ | `pt` | generated, unreviewed (Brazilian; serves `pt*`) | [`briefs/pt.md`](./briefs/pt.md) | 2026-10-19 |
64
+ | `ko` | generated, unreviewed | [`briefs/ko.md`](./briefs/ko.md) | 2026-10-19 |
65
+ | `ja` | generated, unreviewed | [`briefs/ja.md`](./briefs/ja.md) | 2026-10-19 |
66
+ | `zh` | generated, unreviewed (Simplified; serves `zh`, `zh-CN`, `zh-SG`; Traditional hosts supply `customMessages`) | [`briefs/zh.md`](./briefs/zh.md) | 2026-10-19 |
67
+ | `cy` | generated, unreviewed (serves `cy`, `cy-GB`; six CLDR plural categories) | [`briefs/cy.md`](./briefs/cy.md) | 2026-10-19 |
68
+ | `sv` | generated, unreviewed (serves `sv`, `sv-SE`, `sv-FI`) | [`briefs/sv.md`](./briefs/sv.md) | 2026-10-19 |
69
+ | `ro` | generated, unreviewed (serves `ro`, `ro-RO`, `ro-MD`; three CLDR plural categories, `other` takes `de`) | [`briefs/ro.md`](./briefs/ro.md) | 2026-10-19 |
70
+
71
+ Locale tags are chosen for variant reach: bare tags (`es`, `fr`, `ar`) let
72
+ `es-MX`, `fr-CA` and `ar-SA` widen onto them through RFC 4647 lookup, while
73
+ `en-GB`/`en-AU` are regional on purpose so bare `en` keeps resolving to the
74
+ bundled English.
75
+
76
+ Provenance — the exact `en-US` source hash each catalog was generated from,
77
+ the generating model and date — is recorded in
78
+ [`catalog-manifest.json`](./catalog-manifest.json).
79
+
80
+ The `calibration/` directory holds a pipeline measurement against the one
81
+ locale that has audited ground truth (Dutch), not a shippable catalog. See
82
+ [`calibration/nl-NL-calibration.md`](./calibration/nl-NL-calibration.md) for
83
+ what the pipeline does and does not deliver, and what that means for reviewing
84
+ a generated locale.
85
+
86
+ ## Checks
87
+
88
+ ```bash
89
+ bun run check:i18n-generated
90
+ ```
91
+
92
+ runs `scripts/check-catalogs.ts`, which fails on:
93
+
94
+ - **staleness** — per the locale's status: a `generated` locale whose recorded
95
+ `en-US` hash no longer matches the current English source (its untranslated
96
+ keys would silently fall back to English); a `reviewed` locale whose English
97
+ snapshot no longer matches current English, listing exactly the keys whose
98
+ English moved;
99
+ - **key drift** — a missing key, or a key English no longer defines;
100
+ - **placeholder drift** — a lost `{name}` slot or an invented one, in string
101
+ or plural-group leaves;
102
+ - **plural shape** — a missing plural group, or a group without `other`;
103
+ - **manifest disagreement** — a locale present in the loader map but not the
104
+ manifest, or the reverse.
105
+
106
+ The check runs in the repository's pre-commit and CI gates: changing English
107
+ fails the gate until the affected catalogs are regenerated — wholesale for
108
+ `generated` locales, surgically for `reviewed` ones.
109
+
110
+ ## The review lifecycle
111
+
112
+ A locale moves through two statuses, recorded in `catalog-manifest.json`, and
113
+ what regeneration may touch follows from the status:
114
+
115
+ **`generated` — machine output, no reader behind it.** Regeneration is
116
+ wholesale, and losing nothing makes that safe:
117
+
118
+ 1. Read `briefs/<locale>.md`, then regenerate `src/messages/<locale>.ts` from
119
+ the current `packages/players-shared/src/i18n/messages/en-US.ts`, keeping
120
+ every `{placeholder}` slot and the plural-group shape.
121
+ 2. Update `catalog-manifest.json`: the new `source.hash` (sha256 over the
122
+ JSON serialization of the `en-US` default export), and the locale's
123
+ `generatedAt` and `model`.
124
+ 3. Run `bun run check:i18n-generated` until it passes.
125
+
126
+ **Taking a review pass** promotes the locale in place, and this is the step
127
+ that makes iteration to audited quality a ratchet instead of a treadmill:
128
+
129
+ 1. A native reader goes through the catalog — the calibration report's
130
+ finding list is the checklist — correcting the file in place.
131
+ 2. Flip the locale's `status` to `reviewed` in the manifest, add `reviewedAt`
132
+ (and a `reviewedBy` note if wanted), and keep `sourceHash` as the
133
+ generation it started from.
134
+ 3. Run `bun run scripts/write-english-snapshot.ts <locale>`, which anchors
135
+ the corrections to the English they were reviewed against
136
+ (`src/messages/<locale>.english-snapshot.json`).
137
+ 4. `bun run check:i18n-generated` passes again — and from now on, changing a
138
+ translated string is preserved work: only keys whose *English* changed
139
+ ever need regenerating.
140
+
141
+ **`reviewed` — regeneration is surgical, never wholesale.** When the gate
142
+ lists keys whose English moved:
143
+
144
+ 1. Regenerate **only the listed keys**, against the brief and the new English
145
+ strings. Every translation the gate does not name stays exactly as the
146
+ review left it.
147
+ 2. Refresh the anchor:
148
+ `bun run scripts/write-english-snapshot.ts <locale>`.
149
+ 3. `bun run check:i18n-generated` until it passes.
150
+
151
+ Wholesale regeneration of a `reviewed` locale discards review work; do it
152
+ only deliberately, by flipping the status back to `generated` first.
153
+
154
+ ## Adding a locale
155
+
156
+ Add `briefs/<locale>.md` (register, glossary, untranslated product names —
157
+ the th-TH brief is the template), translate `en-US` under it into
158
+ `src/messages/<locale>.ts`, then register the tag in both `GENERATED_LOCALES`
159
+ (`src/index.ts`) and `catalog-manifest.json`. The static `import()` map keeps
160
+ each locale its own lazy chunk, exactly as `players-shared`'s `catalogs.ts`
161
+ does.
162
+
163
+ ## Promoting a locale to the reviewed set
164
+
165
+ A locale that has been through a review pass above can go further — into the
166
+ audited set `players-shared` itself ships — and hosts need no migration
167
+ either way:
168
+
169
+ 1. A native speaker with domain context reviews the catalog — the
170
+ calibration report's finding list is the review checklist.
171
+ 2. Move the corrected catalog into
172
+ `packages/players-shared/src/i18n/messages/<locale>.ts`, add the tag to
173
+ `BundledLocaleCode`, `catalogs.ts` and `COMPLETE_LOCALES` per that
174
+ package's README, and give it a changeset.
175
+ 3. Delete the catalog from this package and the tag from the loader map and
176
+ manifest.
177
+
178
+ Because hosts compose the loaders bundled-first, the reviewed catalog wins
179
+ from the moment it ships, whether or not the host has removed this package.
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Generated interface catalogs for PIE Players — the optional locale tier.
3
+ *
4
+ * Every catalog here is machine-generated against the terminology brief named
5
+ * in `catalog-manifest.json` and is **not reviewed** by a native speaker. The
6
+ * reviewed catalogs ship from `@pie-players/pie-players-shared/i18n`; this
7
+ * package is pure data a host opts into by installing it and wiring it into
8
+ * the provider alongside the bundled loaders:
9
+ *
10
+ * ```ts
11
+ * import {
12
+ * BUNDLED_LOCALES,
13
+ * createPieI18n,
14
+ * loadBundledCatalog,
15
+ * } from "@pie-players/pie-players-shared/i18n";
16
+ * import {
17
+ * GENERATED_LOCALES,
18
+ * loadGeneratedCatalog,
19
+ * } from "@pie-players/pie-i18n-generated";
20
+ *
21
+ * const i18n = createPieI18n({
22
+ * locale: "th-TH",
23
+ * availableLocales: [...BUNDLED_LOCALES, ...GENERATED_LOCALES],
24
+ * // Bundled first: a reviewed catalog silently wins over a generated one
25
+ * // for the same tag, which is what makes promotion from generated to
26
+ * // reviewed a no-op for a wired host.
27
+ * loadCatalog: (locale) =>
28
+ * loadBundledCatalog(locale).catch(() => loadGeneratedCatalog(locale)),
29
+ * });
30
+ * ```
31
+ *
32
+ * Nothing here loads unless a host asks for a generated locale: the loaders
33
+ * are never invoked for the default `en-US` (statically resident in
34
+ * `players-shared`), and each locale stays its own lazy chunk because `tsc`
35
+ * emits these `import()` calls verbatim — the same property
36
+ * `players-shared`'s `catalogs.ts` relies on.
37
+ *
38
+ * Provenance per locale — source hash, brief, model, generation date — lives
39
+ * in `catalog-manifest.json`. `scripts/check-catalogs.ts` fails when a catalog
40
+ * drifts from the English source or from its brief's structural rules.
41
+ *
42
+ * @module @pie-players/pie-i18n-generated
43
+ */
44
+ import type { MessageCatalog } from "@pie-players/pie-players-shared/i18n/types";
45
+ /**
46
+ * Every locale tag this package ships a generated catalog for.
47
+ *
48
+ * `en-GB` and `en-AU` are regional on purpose: bare `en` must keep resolving
49
+ * to the statically bundled English, never to a regional variant.
50
+ */
51
+ export declare const GENERATED_LOCALES: readonly string[];
52
+ /**
53
+ * Load one generated catalog.
54
+ *
55
+ * Throws for a locale with no generated catalog; the provider treats that as
56
+ * "resolve through the fallback chain" rather than an error, mirroring
57
+ * `loadBundledCatalog`.
58
+ */
59
+ export declare function loadGeneratedCatalog(locale: string): Promise<MessageCatalog>;
package/dist/index.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Generated interface catalogs for PIE Players — the optional locale tier.
3
+ *
4
+ * Every catalog here is machine-generated against the terminology brief named
5
+ * in `catalog-manifest.json` and is **not reviewed** by a native speaker. The
6
+ * reviewed catalogs ship from `@pie-players/pie-players-shared/i18n`; this
7
+ * package is pure data a host opts into by installing it and wiring it into
8
+ * the provider alongside the bundled loaders:
9
+ *
10
+ * ```ts
11
+ * import {
12
+ * BUNDLED_LOCALES,
13
+ * createPieI18n,
14
+ * loadBundledCatalog,
15
+ * } from "@pie-players/pie-players-shared/i18n";
16
+ * import {
17
+ * GENERATED_LOCALES,
18
+ * loadGeneratedCatalog,
19
+ * } from "@pie-players/pie-i18n-generated";
20
+ *
21
+ * const i18n = createPieI18n({
22
+ * locale: "th-TH",
23
+ * availableLocales: [...BUNDLED_LOCALES, ...GENERATED_LOCALES],
24
+ * // Bundled first: a reviewed catalog silently wins over a generated one
25
+ * // for the same tag, which is what makes promotion from generated to
26
+ * // reviewed a no-op for a wired host.
27
+ * loadCatalog: (locale) =>
28
+ * loadBundledCatalog(locale).catch(() => loadGeneratedCatalog(locale)),
29
+ * });
30
+ * ```
31
+ *
32
+ * Nothing here loads unless a host asks for a generated locale: the loaders
33
+ * are never invoked for the default `en-US` (statically resident in
34
+ * `players-shared`), and each locale stays its own lazy chunk because `tsc`
35
+ * emits these `import()` calls verbatim — the same property
36
+ * `players-shared`'s `catalogs.ts` relies on.
37
+ *
38
+ * Provenance per locale — source hash, brief, model, generation date — lives
39
+ * in `catalog-manifest.json`. `scripts/check-catalogs.ts` fails when a catalog
40
+ * drifts from the English source or from its brief's structural rules.
41
+ *
42
+ * @module @pie-players/pie-i18n-generated
43
+ */
44
+ /**
45
+ * An explicit static map rather than a bundler macro, for the same reason
46
+ * `catalogs.ts` gives: `tsc` emits the `import()` calls verbatim, so `dist`
47
+ * evaluates under every ESM runtime, and every bundler keeps each locale its
48
+ * own chunk. String-keyed: the generated set is open, and a host adding a
49
+ * locale adds one line here alongside the file.
50
+ */
51
+ const GENERATED_CATALOG_LOADERS = {
52
+ "th-TH": () => import("./messages/th-TH.js"),
53
+ "en-GB": () => import("./messages/en-GB.js"),
54
+ "en-AU": () => import("./messages/en-AU.js"),
55
+ // Bare tags on purpose: the provider's RFC 4647 lookup plus
56
+ // primary-subtag widening lands `es-MX`, `es-419`, `fr-CA`, `pt-BR` and
57
+ // `ar-SA` on these, and a host wanting a different national flavour
58
+ // overrides per key through `customMessages`. `zh` is Simplified Chinese
59
+ // and catches `zh-CN`/`zh-SG`; Traditional-Chinese hosts supply their own
60
+ // messages.
61
+ es: () => import("./messages/es.js"),
62
+ fr: () => import("./messages/fr.js"),
63
+ ar: () => import("./messages/ar.js"),
64
+ pt: () => import("./messages/pt.js"),
65
+ ko: () => import("./messages/ko.js"),
66
+ ja: () => import("./messages/ja.js"),
67
+ zh: () => import("./messages/zh.js"),
68
+ // Bare tags: `cy-GB` widens to `cy`, `sv-SE`/`sv-FI` to `sv`, and
69
+ // `ro-RO`/`ro-MD` to `ro`.
70
+ cy: () => import("./messages/cy.js"),
71
+ sv: () => import("./messages/sv.js"),
72
+ ro: () => import("./messages/ro.js"),
73
+ };
74
+ /**
75
+ * Every locale tag this package ships a generated catalog for.
76
+ *
77
+ * `en-GB` and `en-AU` are regional on purpose: bare `en` must keep resolving
78
+ * to the statically bundled English, never to a regional variant.
79
+ */
80
+ export const GENERATED_LOCALES = [
81
+ "th-TH",
82
+ "en-GB",
83
+ "en-AU",
84
+ "es",
85
+ "fr",
86
+ "ar",
87
+ "pt",
88
+ "ko",
89
+ "ja",
90
+ "zh",
91
+ "cy",
92
+ "sv",
93
+ "ro",
94
+ ];
95
+ /**
96
+ * Load one generated catalog.
97
+ *
98
+ * Throws for a locale with no generated catalog; the provider treats that as
99
+ * "resolve through the fallback chain" rather than an error, mirroring
100
+ * `loadBundledCatalog`.
101
+ */
102
+ export async function loadGeneratedCatalog(locale) {
103
+ const loader = GENERATED_CATALOG_LOADERS[locale];
104
+ if (!loader) {
105
+ throw new Error(`No generated i18n catalog for locale: ${locale}`);
106
+ }
107
+ const module = await loader();
108
+ return module.default;
109
+ }