@adminiumjs/i18n 0.1.2 → 0.2.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.
Files changed (135) hide show
  1. package/dist/a11y-keys.d.ts +17 -0
  2. package/dist/a11y-keys.d.ts.map +1 -0
  3. package/dist/a11y-keys.js +478 -0
  4. package/dist/a11y-keys.js.map +1 -0
  5. package/dist/create-i18n.d.ts.map +1 -1
  6. package/dist/create-i18n.js +37 -4
  7. package/dist/create-i18n.js.map +1 -1
  8. package/dist/editing.d.ts +17 -0
  9. package/dist/editing.d.ts.map +1 -0
  10. package/dist/editing.js +16 -0
  11. package/dist/editing.js.map +1 -0
  12. package/dist/fallback.d.ts +24 -0
  13. package/dist/fallback.d.ts.map +1 -0
  14. package/dist/fallback.js +40 -0
  15. package/dist/fallback.js.map +1 -0
  16. package/dist/format-errors.d.ts +41 -0
  17. package/dist/format-errors.d.ts.map +1 -0
  18. package/dist/format-errors.js +63 -0
  19. package/dist/format-errors.js.map +1 -0
  20. package/dist/icu-format.d.ts +4 -0
  21. package/dist/icu-format.d.ts.map +1 -1
  22. package/dist/icu-format.js +43 -3
  23. package/dist/icu-format.js.map +1 -1
  24. package/dist/index.d.ts +10 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +14 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/keys.d.ts +33 -0
  29. package/dist/keys.d.ts.map +1 -0
  30. package/dist/keys.js +77 -0
  31. package/dist/keys.js.map +1 -0
  32. package/dist/locales.d.ts +102 -13
  33. package/dist/locales.d.ts.map +1 -1
  34. package/dist/locales.js +142 -17
  35. package/dist/locales.js.map +1 -1
  36. package/dist/overrides.d.ts +62 -0
  37. package/dist/overrides.d.ts.map +1 -0
  38. package/dist/overrides.js +112 -0
  39. package/dist/overrides.js.map +1 -0
  40. package/dist/react.d.ts +6 -0
  41. package/dist/react.d.ts.map +1 -1
  42. package/dist/react.js +17 -26
  43. package/dist/react.js.map +1 -1
  44. package/dist/resources/ar-eg/common.d.ts +96 -0
  45. package/dist/resources/ar-eg/common.d.ts.map +1 -1
  46. package/dist/resources/ar-eg/common.js +103 -7
  47. package/dist/resources/ar-eg/common.js.map +1 -1
  48. package/dist/resources/ar-eg/ui.d.ts +586 -586
  49. package/dist/resources/ar-eg/ui.js +586 -586
  50. package/dist/resources/ar-eg/ui.js.map +1 -1
  51. package/dist/resources/cs-cz/common.d.ts +96 -0
  52. package/dist/resources/cs-cz/common.d.ts.map +1 -1
  53. package/dist/resources/cs-cz/common.js +101 -5
  54. package/dist/resources/cs-cz/common.js.map +1 -1
  55. package/dist/resources/cs-cz/ui.d.ts +582 -582
  56. package/dist/resources/cs-cz/ui.js +582 -582
  57. package/dist/resources/cs-cz/ui.js.map +1 -1
  58. package/dist/resources/da-dk/common.d.ts +97 -1
  59. package/dist/resources/da-dk/common.d.ts.map +1 -1
  60. package/dist/resources/da-dk/common.js +104 -8
  61. package/dist/resources/da-dk/common.js.map +1 -1
  62. package/dist/resources/da-dk/ui.d.ts +568 -568
  63. package/dist/resources/da-dk/ui.js +568 -568
  64. package/dist/resources/da-dk/ui.js.map +1 -1
  65. package/dist/resources/de-de/common.d.ts +100 -4
  66. package/dist/resources/de-de/common.d.ts.map +1 -1
  67. package/dist/resources/de-de/common.js +107 -11
  68. package/dist/resources/de-de/common.js.map +1 -1
  69. package/dist/resources/de-de/ui.d.ts +569 -569
  70. package/dist/resources/de-de/ui.js +569 -569
  71. package/dist/resources/de-de/ui.js.map +1 -1
  72. package/dist/resources/en-us/common.d.ts +96 -0
  73. package/dist/resources/en-us/common.d.ts.map +1 -1
  74. package/dist/resources/en-us/common.js +103 -7
  75. package/dist/resources/en-us/common.js.map +1 -1
  76. package/dist/resources/fr-fr/common.d.ts +98 -2
  77. package/dist/resources/fr-fr/common.d.ts.map +1 -1
  78. package/dist/resources/fr-fr/common.js +105 -9
  79. package/dist/resources/fr-fr/common.js.map +1 -1
  80. package/dist/resources/fr-fr/ui.d.ts +559 -559
  81. package/dist/resources/fr-fr/ui.js +559 -559
  82. package/dist/resources/fr-fr/ui.js.map +1 -1
  83. package/dist/resources/zh-cn/common.d.ts +97 -1
  84. package/dist/resources/zh-cn/common.d.ts.map +1 -1
  85. package/dist/resources/zh-cn/common.js +104 -8
  86. package/dist/resources/zh-cn/common.js.map +1 -1
  87. package/dist/resources/zh-cn/ui.d.ts +575 -575
  88. package/dist/resources/zh-cn/ui.js +575 -575
  89. package/dist/resources/zh-cn/ui.js.map +1 -1
  90. package/dist/resources/zh-tw/common.d.ts +96 -0
  91. package/dist/resources/zh-tw/common.d.ts.map +1 -1
  92. package/dist/resources/zh-tw/common.js +103 -7
  93. package/dist/resources/zh-tw/common.js.map +1 -1
  94. package/dist/resources/zh-tw/ui.d.ts +575 -575
  95. package/dist/resources/zh-tw/ui.js +575 -575
  96. package/dist/resources/zh-tw/ui.js.map +1 -1
  97. package/dist/review-status.d.ts +55 -0
  98. package/dist/review-status.d.ts.map +1 -0
  99. package/dist/review-status.js +59 -0
  100. package/dist/review-status.js.map +1 -0
  101. package/dist/revision.d.ts +35 -0
  102. package/dist/revision.d.ts.map +1 -0
  103. package/dist/revision.js +55 -0
  104. package/dist/revision.js.map +1 -0
  105. package/dist/server.d.ts +36 -0
  106. package/dist/server.d.ts.map +1 -0
  107. package/dist/server.js +35 -0
  108. package/dist/server.js.map +1 -0
  109. package/dist/validate-message.d.ts +57 -0
  110. package/dist/validate-message.d.ts.map +1 -0
  111. package/dist/validate-message.js +178 -0
  112. package/dist/validate-message.js.map +1 -0
  113. package/locales/ar-EG/.meta.json +11134 -0
  114. package/locales/ar-EG/common.json +103 -7
  115. package/locales/ar-EG/ui.json +586 -586
  116. package/locales/cs-CZ/.meta.json +11134 -0
  117. package/locales/cs-CZ/common.json +101 -5
  118. package/locales/cs-CZ/ui.json +582 -582
  119. package/locales/da-DK/.meta.json +11134 -0
  120. package/locales/da-DK/common.json +104 -8
  121. package/locales/da-DK/ui.json +568 -568
  122. package/locales/de-DE/.meta.json +11134 -0
  123. package/locales/de-DE/common.json +107 -11
  124. package/locales/de-DE/ui.json +569 -569
  125. package/locales/en-US/common.json +103 -7
  126. package/locales/fr-FR/.meta.json +11134 -0
  127. package/locales/fr-FR/common.json +105 -9
  128. package/locales/fr-FR/ui.json +559 -559
  129. package/locales/zh-CN/.meta.json +11134 -0
  130. package/locales/zh-CN/common.json +104 -8
  131. package/locales/zh-CN/ui.json +575 -575
  132. package/locales/zh-TW/.meta.json +11134 -0
  133. package/locales/zh-TW/common.json +103 -7
  134. package/locales/zh-TW/ui.json +575 -575
  135. package/package.json +10 -2
package/dist/locales.d.ts CHANGED
@@ -1,10 +1,28 @@
1
1
  /**
2
- * Canonical locale registry (10-i18n-theming.md §2.1) the single
3
- * source of truth for the 8 BRIEF locales. Canonical ids use the underscore
4
- * form (`en_US`); BCP-47 tags are derived for everything `Intl`- and
5
- * i18next-facing. `dir` is derived from the locale, never independently
6
- * settable (02-design-system.md §4.2) adding a future RTL locale (he_IL,
7
- * fa_IR) is a registry data change, not a code change.
2
+ * Canonical locale registry (10-i18n-theming.md §2.1) plus the runtime
3
+ * overlay that lets an admin add locales the build never saw
4
+ * (23-runtime-translations.md §5).
5
+ *
6
+ * Two id types, and the split is load-bearing:
7
+ *
8
+ * - {@link BuiltinLocaleId} is the closed union of the eight COMPILED
9
+ * locales. Everything that must stay exhaustive keys off it —
10
+ * `REVIEW_STATUS`, the parity gate, `gen-resources.mjs`, `lazy.ts` — so a
11
+ * ninth *compiled* locale is still a compile error, and no runtime locale
12
+ * can ever reach a build-time gate.
13
+ * - {@link LocaleId} additionally admits runtime ids. `(string & {})` keeps
14
+ * literal autocomplete for the eight while accepting anything a custom
15
+ * locale row can hold.
16
+ *
17
+ * Every lookup helper here is TOTAL: none of them throws on an unknown id.
18
+ * That is deliberate. Once locales are data, the orphan case — a deleted
19
+ * locale still referenced by a user pref, a restored backup, an imported
20
+ * config bundle — is the NORMAL aftermath of the feature, not an edge case,
21
+ * and these helpers run inside a theme subscriber whose emitter has no
22
+ * per-listener isolation. A throw there is a white screen, not a warning.
23
+ *
24
+ * `dir` is still derived from the locale, never independently settable
25
+ * (02-design-system.md §4.2).
8
26
  */
9
27
  export interface LocaleEntry {
10
28
  /** Canonical underscore id stored in `adminium_settings`/`adminium_user_prefs`. */
@@ -24,6 +42,23 @@ export interface LocaleEntry {
24
42
  */
25
43
  fontHint: 'latin' | 'arabic' | 'cjk';
26
44
  }
45
+ /** A runtime locale entry, plus the fields only the DB registry knows. */
46
+ export interface RuntimeLocaleEntry extends LocaleEntry {
47
+ /** False for admin-created locales. */
48
+ builtin: boolean;
49
+ /** Offered in pickers? (23 §3.1 `enabled`.) */
50
+ enabled: boolean;
51
+ /** Picker ordering. */
52
+ sortOrder: number;
53
+ /**
54
+ * The REAL BCP-47 tag whose `Intl` behaviour this locale borrows (23 §5.6).
55
+ * For a built-in this is just `tag`; for a custom locale it is the admin's
56
+ * choice, because `tag` itself may be something `Intl` has never heard of.
57
+ */
58
+ intlTag: string;
59
+ /** Plural categories frozen at create time from `intlTag`. */
60
+ pluralCategories: readonly string[];
61
+ }
27
62
  export declare const LOCALES: readonly [{
28
63
  readonly id: "en_US";
29
64
  readonly tag: "en-US";
@@ -81,19 +116,73 @@ export declare const LOCALES: readonly [{
81
116
  readonly dir: "rtl";
82
117
  readonly fontHint: "arabic";
83
118
  }];
84
- export type LocaleId = (typeof LOCALES)[number]['id'];
85
- export declare const LOCALE_IDS: readonly LocaleId[];
119
+ /** The eight COMPILED locales — the exhaustive axis (23 §5.1). */
120
+ export type BuiltinLocaleId = (typeof LOCALES)[number]['id'];
121
+ /**
122
+ * Any locale id the running instance may hold — the eight compiled ones plus
123
+ * whatever an admin created. `(string & {})` preserves literal autocomplete
124
+ * for the eight instead of collapsing to a bare `string`.
125
+ */
126
+ export type LocaleId = BuiltinLocaleId | (string & {});
127
+ export declare const BUILTIN_LOCALE_IDS: readonly BuiltinLocaleId[];
128
+ /** @deprecated Prefer {@link BUILTIN_LOCALE_IDS} (or the runtime list) by name. */
129
+ export declare const LOCALE_IDS: readonly ("en_US" | "de_DE" | "fr_FR" | "cs_CZ" | "da_DK" | "zh_CN" | "zh_TW" | "ar_EG")[];
130
+ /**
131
+ * Canonical id shape: a language subtag plus up to two more (script, region),
132
+ * e.g. `en_US`, `sw_KE`, `zh_Hant_TW`. Three subtags is why the DB column is
133
+ * `varchar(35)` and why {@link tagFromLocaleId} replaces EVERY underscore.
134
+ */
135
+ export declare const LOCALE_ID_RE: RegExp;
136
+ /** Replace the runtime registry wholesale (boot, and every version bump). */
137
+ export declare function setRuntimeLocales(entries: readonly RuntimeLocaleEntry[]): void;
138
+ /** Drop the runtime registry — tests, and teardown. */
139
+ export declare function resetRuntimeLocales(): void;
140
+ /** Every locale the instance knows: compiled defaults merged under runtime rows. */
141
+ export declare function allLocales(): readonly RuntimeLocaleEntry[];
142
+ /** The locales a picker may offer, in picker order (23 §3.1 `enabled`). */
143
+ export declare function availableLocales(): readonly RuntimeLocaleEntry[];
144
+ /** True for one of the eight COMPILED locales. */
145
+ export declare function isBuiltinLocaleId(value: unknown): value is BuiltinLocaleId;
146
+ /**
147
+ * True for anything shaped like a locale id. This is a SHAPE check, not a
148
+ * membership check: a cached preference may name a locale this build does not
149
+ * compile in, and rejecting it would strand the user on en-US. Use
150
+ * {@link isBuiltinLocaleId} when compiled membership is what you mean.
151
+ */
86
152
  export declare function isLocaleId(value: unknown): value is LocaleId;
153
+ /**
154
+ * `en_US` → `en-US`, `zh_Hant_TW` → `zh-Hant-TW`.
155
+ *
156
+ * Replaces EVERY underscore. A single-replacement version leaves
157
+ * `zh-Hant_TW`, which is not a valid BCP-47 tag, and the formatter layer
158
+ * coalesces invalid tags to `en-US` — silently degrading exactly the way the
159
+ * admin-chosen `intlTag` exists to prevent.
160
+ */
161
+ export declare function tagFromLocaleId(id: string): string;
162
+ /**
163
+ * The presentation record for a locale: compiled entry, else runtime entry,
164
+ * else a synthesized one. Total by contract — see the file header for why
165
+ * throwing here is a white screen.
166
+ */
87
167
  export declare function localeEntry(id: LocaleId): LocaleEntry;
88
- /** `'rtl'` iff the registry says so (`ar_EG` today) — data-driven, not hardcoded. */
168
+ /** `'rtl'` iff the registry says so — data-driven, never hardcoded to `ar_EG`. */
89
169
  export declare function dirForLocale(id: LocaleId): 'ltr' | 'rtl';
90
170
  export declare function isRtlLocale(id: LocaleId): boolean;
91
- /** `ar_EG` → `ar-EG` for `Intl.*`, i18next, and the `lang` attribute. */
171
+ /** `ar_EG` → `ar-EG` for i18next and the `lang` attribute. */
92
172
  export declare function tagForLocale(id: LocaleId): string;
93
173
  /**
94
- * Best-effort inverse of {@link tagForLocale}: exact tag match, then a
95
- * language-only match (`de` `de_DE`), else `en_US`. Used to map
96
- * `i18next.language` / `navigator.language` back to a canonical id.
174
+ * The tag to hand `Intl.*` and `IntlMessageFormat` (23 §4.5). For a compiled
175
+ * locale this is just the tag; for a custom locale it is the admin-chosen
176
+ * borrow tag, because the locale's own tag may be something no ICU
177
+ * implementation has data for.
178
+ */
179
+ export declare function intlTagForLocale(id: LocaleId): string;
180
+ /** Plural categories to validate a message against (frozen at create time). */
181
+ export declare function pluralCategoriesForLocale(id: LocaleId): readonly string[];
182
+ /**
183
+ * Best-effort inverse of {@link tagForLocale}: exact compiled tag, then a
184
+ * runtime tag, then a language-only compiled match (`de` → `de_DE`), else
185
+ * `en_US`. Maps `i18next.language` / `navigator.language` back to an id.
97
186
  */
98
187
  export declare function localeFromTag(tag: string): LocaleId;
99
188
  //# sourceMappingURL=locales.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../src/locales.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,WAAW;IAC1B,mFAAmF;IACnF,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC;IACnB;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;CACtC;AAED,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASuB,CAAC;AAE5C,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAEtD,eAAO,MAAM,UAAU,EAA0C,SAAS,QAAQ,EAAE,CAAC;AAKrF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG,WAAW,CAIrD;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,KAAK,GAAG,KAAK,CAExD;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAEjD;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAMnD"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../src/locales.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,WAAW,WAAW;IAC1B,mFAAmF;IACnF,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC;IACnB;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;CACtC;AAED,0EAA0E;AAC1E,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASuB,CAAC;AAE5C,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAE7D;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,eAAe,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAEvD,eAAO,MAAM,kBAAkB,EAA0C,SAAS,eAAe,EAAE,CAAC;AAEpG,mFAAmF;AACnF,eAAO,MAAM,UAAU,4FAAqB,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAyC,CAAC;AAiBnE,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,IAAI,CAE9E;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,oFAAoF;AACpF,wBAAgB,UAAU,IAAI,SAAS,kBAAkB,EAAE,CA2B1D;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,IAAI,SAAS,kBAAkB,EAAE,CAEhE;AAID,kDAAkD;AAClD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAElD;AAOD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG,WAAW,CAErD;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,KAAK,GAAG,KAAK,CAExD;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAEjD;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAEjD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAErD;AAED,+EAA+E;AAC/E,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,QAAQ,GAAG,SAAS,MAAM,EAAE,CAQzE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CASnD"}
package/dist/locales.js CHANGED
@@ -1,10 +1,28 @@
1
1
  /**
2
- * Canonical locale registry (10-i18n-theming.md §2.1) the single
3
- * source of truth for the 8 BRIEF locales. Canonical ids use the underscore
4
- * form (`en_US`); BCP-47 tags are derived for everything `Intl`- and
5
- * i18next-facing. `dir` is derived from the locale, never independently
6
- * settable (02-design-system.md §4.2) adding a future RTL locale (he_IL,
7
- * fa_IR) is a registry data change, not a code change.
2
+ * Canonical locale registry (10-i18n-theming.md §2.1) plus the runtime
3
+ * overlay that lets an admin add locales the build never saw
4
+ * (23-runtime-translations.md §5).
5
+ *
6
+ * Two id types, and the split is load-bearing:
7
+ *
8
+ * - {@link BuiltinLocaleId} is the closed union of the eight COMPILED
9
+ * locales. Everything that must stay exhaustive keys off it —
10
+ * `REVIEW_STATUS`, the parity gate, `gen-resources.mjs`, `lazy.ts` — so a
11
+ * ninth *compiled* locale is still a compile error, and no runtime locale
12
+ * can ever reach a build-time gate.
13
+ * - {@link LocaleId} additionally admits runtime ids. `(string & {})` keeps
14
+ * literal autocomplete for the eight while accepting anything a custom
15
+ * locale row can hold.
16
+ *
17
+ * Every lookup helper here is TOTAL: none of them throws on an unknown id.
18
+ * That is deliberate. Once locales are data, the orphan case — a deleted
19
+ * locale still referenced by a user pref, a restored backup, an imported
20
+ * config bundle — is the NORMAL aftermath of the feature, not an edge case,
21
+ * and these helpers run inside a theme subscriber whose emitter has no
22
+ * per-listener isolation. A throw there is a white screen, not a warning.
23
+ *
24
+ * `dir` is still derived from the locale, never independently settable
25
+ * (02-design-system.md §4.2).
8
26
  */
9
27
  export const LOCALES = [
10
28
  { id: 'en_US', tag: 'en-US', english: 'English (US)', native: 'English (US)', dir: 'ltr', fontHint: 'latin' },
@@ -16,38 +34,145 @@ export const LOCALES = [
16
34
  { id: 'zh_TW', tag: 'zh-TW', english: 'Chinese (Traditional)', native: '繁體中文', dir: 'ltr', fontHint: 'cjk' },
17
35
  { id: 'ar_EG', tag: 'ar-EG', english: 'Arabic (Egypt)', native: 'العربية (مصر)', dir: 'rtl', fontHint: 'arabic' },
18
36
  ];
19
- export const LOCALE_IDS = LOCALES.map((l) => l.id);
37
+ export const BUILTIN_LOCALE_IDS = LOCALES.map((l) => l.id);
38
+ /** @deprecated Prefer {@link BUILTIN_LOCALE_IDS} (or the runtime list) by name. */
39
+ export const LOCALE_IDS = BUILTIN_LOCALE_IDS;
40
+ /**
41
+ * Canonical id shape: a language subtag plus up to two more (script, region),
42
+ * e.g. `en_US`, `sw_KE`, `zh_Hant_TW`. Three subtags is why the DB column is
43
+ * `varchar(35)` and why {@link tagFromLocaleId} replaces EVERY underscore.
44
+ */
45
+ export const LOCALE_ID_RE = /^[a-z]{2,3}(_[A-Za-z0-9]{2,8}){0,2}$/;
20
46
  const BY_ID = new Map(LOCALES.map((l) => [l.id, l]));
21
47
  const BY_TAG = new Map(LOCALES.map((l) => [l.tag, l]));
22
- export function isLocaleId(value) {
48
+ // --- runtime overlay ---------------------------------------------------------
49
+ /**
50
+ * Locales the running instance knows about beyond the compiled eight. Set
51
+ * once from the server manifest at boot and on every i18n version bump; empty
52
+ * on a server/CLI that never loads it, which is exactly the pre-23 behaviour.
53
+ *
54
+ * A module-level singleton is correct here: one Adminium instance has one
55
+ * meta store and therefore one locale registry, same as the compiled one.
56
+ */
57
+ let runtimeById = new Map();
58
+ /** Replace the runtime registry wholesale (boot, and every version bump). */
59
+ export function setRuntimeLocales(entries) {
60
+ runtimeById = new Map(entries.map((e) => [e.id, e]));
61
+ }
62
+ /** Drop the runtime registry — tests, and teardown. */
63
+ export function resetRuntimeLocales() {
64
+ runtimeById = new Map();
65
+ }
66
+ /** Every locale the instance knows: compiled defaults merged under runtime rows. */
67
+ export function allLocales() {
68
+ const merged = new Map();
69
+ for (const entry of LOCALES) {
70
+ merged.set(entry.id, {
71
+ ...entry,
72
+ builtin: true,
73
+ enabled: true,
74
+ sortOrder: 0,
75
+ intlTag: entry.tag,
76
+ pluralCategories: [],
77
+ });
78
+ }
79
+ for (const [id, entry] of runtimeById) {
80
+ const base = merged.get(id);
81
+ // A built-in row may only carry `enabled`/`sortOrder` (23 §3.1 field
82
+ // lock) — presentation always comes from the compiled entry, so an admin
83
+ // cannot flip ar_EG to ltr and corrupt a shipped bundle's rendering.
84
+ merged.set(id, base === undefined
85
+ ? entry
86
+ : { ...base, enabled: entry.enabled, sortOrder: entry.sortOrder, builtin: true });
87
+ }
88
+ return [...merged.values()].sort((a, b) => a.sortOrder - b.sortOrder || a.id.localeCompare(b.id));
89
+ }
90
+ /** The locales a picker may offer, in picker order (23 §3.1 `enabled`). */
91
+ export function availableLocales() {
92
+ return allLocales().filter((l) => l.enabled);
93
+ }
94
+ // --- lookups (all total) -----------------------------------------------------
95
+ /** True for one of the eight COMPILED locales. */
96
+ export function isBuiltinLocaleId(value) {
23
97
  return typeof value === 'string' && BY_ID.has(value);
24
98
  }
99
+ /**
100
+ * True for anything shaped like a locale id. This is a SHAPE check, not a
101
+ * membership check: a cached preference may name a locale this build does not
102
+ * compile in, and rejecting it would strand the user on en-US. Use
103
+ * {@link isBuiltinLocaleId} when compiled membership is what you mean.
104
+ */
105
+ export function isLocaleId(value) {
106
+ return typeof value === 'string' && LOCALE_ID_RE.test(value);
107
+ }
108
+ /**
109
+ * `en_US` → `en-US`, `zh_Hant_TW` → `zh-Hant-TW`.
110
+ *
111
+ * Replaces EVERY underscore. A single-replacement version leaves
112
+ * `zh-Hant_TW`, which is not a valid BCP-47 tag, and the formatter layer
113
+ * coalesces invalid tags to `en-US` — silently degrading exactly the way the
114
+ * admin-chosen `intlTag` exists to prevent.
115
+ */
116
+ export function tagFromLocaleId(id) {
117
+ return id.replaceAll('_', '-');
118
+ }
119
+ /** Synthesized entry for an id no registry knows — never throws. */
120
+ function synthesize(id) {
121
+ return { id, tag: tagFromLocaleId(id), english: id, native: id, dir: 'ltr', fontHint: 'latin' };
122
+ }
123
+ /**
124
+ * The presentation record for a locale: compiled entry, else runtime entry,
125
+ * else a synthesized one. Total by contract — see the file header for why
126
+ * throwing here is a white screen.
127
+ */
25
128
  export function localeEntry(id) {
26
- const entry = BY_ID.get(id);
27
- if (entry === undefined)
28
- throw new Error(`unknown locale id: ${JSON.stringify(id)}`);
29
- return entry;
129
+ return BY_ID.get(id) ?? runtimeById.get(id) ?? synthesize(id);
30
130
  }
31
- /** `'rtl'` iff the registry says so (`ar_EG` today) — data-driven, not hardcoded. */
131
+ /** `'rtl'` iff the registry says so — data-driven, never hardcoded to `ar_EG`. */
32
132
  export function dirForLocale(id) {
33
133
  return localeEntry(id).dir;
34
134
  }
35
135
  export function isRtlLocale(id) {
36
136
  return dirForLocale(id) === 'rtl';
37
137
  }
38
- /** `ar_EG` → `ar-EG` for `Intl.*`, i18next, and the `lang` attribute. */
138
+ /** `ar_EG` → `ar-EG` for i18next and the `lang` attribute. */
39
139
  export function tagForLocale(id) {
40
140
  return localeEntry(id).tag;
41
141
  }
42
142
  /**
43
- * Best-effort inverse of {@link tagForLocale}: exact tag match, then a
44
- * language-only match (`de` `de_DE`), else `en_US`. Used to map
45
- * `i18next.language` / `navigator.language` back to a canonical id.
143
+ * The tag to hand `Intl.*` and `IntlMessageFormat` (23 §4.5). For a compiled
144
+ * locale this is just the tag; for a custom locale it is the admin-chosen
145
+ * borrow tag, because the locale's own tag may be something no ICU
146
+ * implementation has data for.
147
+ */
148
+ export function intlTagForLocale(id) {
149
+ return runtimeById.get(id)?.intlTag ?? tagForLocale(id);
150
+ }
151
+ /** Plural categories to validate a message against (frozen at create time). */
152
+ export function pluralCategoriesForLocale(id) {
153
+ const frozen = runtimeById.get(id)?.pluralCategories;
154
+ if (frozen !== undefined && frozen.length > 0)
155
+ return frozen;
156
+ try {
157
+ return new Intl.PluralRules(intlTagForLocale(id)).resolvedOptions().pluralCategories;
158
+ }
159
+ catch {
160
+ return ['other'];
161
+ }
162
+ }
163
+ /**
164
+ * Best-effort inverse of {@link tagForLocale}: exact compiled tag, then a
165
+ * runtime tag, then a language-only compiled match (`de` → `de_DE`), else
166
+ * `en_US`. Maps `i18next.language` / `navigator.language` back to an id.
46
167
  */
47
168
  export function localeFromTag(tag) {
48
169
  const exact = BY_TAG.get(tag);
49
170
  if (exact !== undefined)
50
171
  return exact.id;
172
+ for (const entry of runtimeById.values()) {
173
+ if (entry.tag === tag)
174
+ return entry.id;
175
+ }
51
176
  const lang = tag.toLowerCase().split('-')[0] ?? '';
52
177
  const byLang = LOCALES.find((l) => l.tag.toLowerCase().startsWith(`${lang}-`) || l.tag.toLowerCase() === lang);
53
178
  return byLang?.id ?? 'en_US';
@@ -1 +1 @@
1
- {"version":3,"file":"locales.js","sourceRoot":"","sources":["../src/locales.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"locales.js","sourceRoot":"","sources":["../src/locales.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Runtime override layer (23-runtime-translations.md §4.3).
3
+ *
4
+ * The compiled bundle and the DB override tree are held SEPARATELY and merged
5
+ * in userland; the i18next resource store is written once, at `init`, and
6
+ * never mutated afterwards. Two facts about i18next 25 force that shape, and
7
+ * both were verified against its source rather than assumed:
8
+ *
9
+ * 1. `addResourceBundle` cannot DELETE a key — its `deepExtend` only ever
10
+ * writes keys present in the incoming object. So the store cannot express
11
+ * "reset this key to the built-in", which is the single most common admin
12
+ * operation. And `removeResourceBundle` is not an escape: it calls
13
+ * `removeNamespaces`, which splices the namespace out of `options.ns` for
14
+ * the WHOLE instance, breaking en-US resolution and every future backend
15
+ * load for that namespace.
16
+ * 2. The backend loader fires at most once per (language, namespace) per
17
+ * instance — `queueLoad` short-circuits on `hasResourceBundle` — so
18
+ * merging inside `loadBundle` is a first-load mechanism, not a
19
+ * steady-state one.
20
+ *
21
+ * Therefore: merge at load, and REBUILD the instance on every version bump,
22
+ * swapping it in through the existing provider seam. That is the only
23
+ * operation this i18next version performs losslessly.
24
+ */
25
+ import { type CreateI18nOptions, type I18nInstance } from './create-i18n.js';
26
+ import { type LocaleId } from './locales.js';
27
+ import type { Namespace, ResourceBundle } from './resources/index.js';
28
+ /** `tag → namespace → flat dotted key → message`. Sparse: only overrides. */
29
+ export type OverrideMap = Readonly<Record<string, Readonly<Partial<Record<Namespace, Readonly<Record<string, string>>>>>>>;
30
+ /**
31
+ * Merge flat dotted overrides into a (cloned) nested bundle.
32
+ *
33
+ * An empty-string value is written through deliberately — it is the third
34
+ * state, "render nothing" (23 §3.3), and is why `createI18n` sets
35
+ * `returnEmptyString: true`.
36
+ */
37
+ export declare function mergeOverrides(base: ResourceBundle | null, flat: Readonly<Record<string, string>>): ResourceBundle;
38
+ export interface CreateI18nWithOverridesOptions extends CreateI18nOptions {
39
+ /** DB-sourced overrides, keyed by BCP-47 tag. */
40
+ overrides?: OverrideMap | undefined;
41
+ }
42
+ /**
43
+ * `createI18n` with the override layer folded in.
44
+ *
45
+ * en-US overrides are applied through the SAME merge as every other locale,
46
+ * never through `createI18n`'s `resources` option: that option does
47
+ * `target[ns] = bundle`, a whole-namespace REPLACEMENT, so routing a sparse
48
+ * override tree through it would delete the ~1,500 other `common` keys.
49
+ */
50
+ export declare function createI18nWithOverrides(opts: CreateI18nWithOverridesOptions): Promise<I18nInstance>;
51
+ /**
52
+ * Rebuild the instance with a new override set and hand it back.
53
+ *
54
+ * The caller swaps the result into whatever holds the active instance and
55
+ * calls `bumpI18nRevision()` — which this does for you — so subscribers
56
+ * re-render. Rebuilding rather than patching is the whole point: see the
57
+ * module header.
58
+ */
59
+ export declare function rebuildWithOverrides(opts: CreateI18nWithOverridesOptions): Promise<I18nInstance>;
60
+ /** The BCP-47 key an override map uses for a locale id. */
61
+ export declare function overrideTag(locale: LocaleId): string;
62
+ //# sourceMappingURL=overrides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../src/overrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAc,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtE,6EAA6E;AAC7E,MAAM,MAAM,WAAW,GAAG,QAAQ,CAChC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACvF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,EAC3B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACrC,cAAc,CAiBhB;AAED,MAAM,WAAW,8BAA+B,SAAQ,iBAAiB;IACvE,iDAAiD;IACjD,SAAS,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC,YAAY,CAAC,CAkCvB;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC,YAAY,CAAC,CAIvB;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Runtime override layer (23-runtime-translations.md §4.3).
3
+ *
4
+ * The compiled bundle and the DB override tree are held SEPARATELY and merged
5
+ * in userland; the i18next resource store is written once, at `init`, and
6
+ * never mutated afterwards. Two facts about i18next 25 force that shape, and
7
+ * both were verified against its source rather than assumed:
8
+ *
9
+ * 1. `addResourceBundle` cannot DELETE a key — its `deepExtend` only ever
10
+ * writes keys present in the incoming object. So the store cannot express
11
+ * "reset this key to the built-in", which is the single most common admin
12
+ * operation. And `removeResourceBundle` is not an escape: it calls
13
+ * `removeNamespaces`, which splices the namespace out of `options.ns` for
14
+ * the WHOLE instance, breaking en-US resolution and every future backend
15
+ * load for that namespace.
16
+ * 2. The backend loader fires at most once per (language, namespace) per
17
+ * instance — `queueLoad` short-circuits on `hasResourceBundle` — so
18
+ * merging inside `loadBundle` is a first-load mechanism, not a
19
+ * steady-state one.
20
+ *
21
+ * Therefore: merge at load, and REBUILD the instance on every version bump,
22
+ * swapping it in through the existing provider seam. That is the only
23
+ * operation this i18next version performs losslessly.
24
+ */
25
+ import { createI18n } from './create-i18n.js';
26
+ import { tagForLocale } from './locales.js';
27
+ import { bumpI18nRevision } from './revision.js';
28
+ /**
29
+ * Merge flat dotted overrides into a (cloned) nested bundle.
30
+ *
31
+ * An empty-string value is written through deliberately — it is the third
32
+ * state, "render nothing" (23 §3.3), and is why `createI18n` sets
33
+ * `returnEmptyString: true`.
34
+ */
35
+ export function mergeOverrides(base, flat) {
36
+ const root = base === null ? {} : structuredClone(base);
37
+ for (const [path, value] of Object.entries(flat)) {
38
+ const parts = path.split('.');
39
+ let node = root;
40
+ for (let i = 0; i < parts.length - 1; i += 1) {
41
+ const part = parts[i];
42
+ const next = node[part];
43
+ // A key may override a leaf that used to be a subtree (or vice versa);
44
+ // replace rather than crash.
45
+ if (typeof next !== 'object' || next === null)
46
+ node[part] = {};
47
+ node = node[part];
48
+ }
49
+ node[parts[parts.length - 1]] = value;
50
+ }
51
+ return root;
52
+ }
53
+ /**
54
+ * `createI18n` with the override layer folded in.
55
+ *
56
+ * en-US overrides are applied through the SAME merge as every other locale,
57
+ * never through `createI18n`'s `resources` option: that option does
58
+ * `target[ns] = bundle`, a whole-namespace REPLACEMENT, so routing a sparse
59
+ * override tree through it would delete the ~1,500 other `common` keys.
60
+ */
61
+ export async function createI18nWithOverrides(opts) {
62
+ const { overrides, loadBundle, resources, ...rest } = opts;
63
+ if (overrides === undefined || Object.keys(overrides).length === 0) {
64
+ return createI18n(opts);
65
+ }
66
+ const enUsOverrides = overrides['en-US'];
67
+ const mergedEnUs = {};
68
+ if (enUsOverrides !== undefined) {
69
+ const { EN_US_RESOURCES } = await import('./resources/index.js');
70
+ for (const [ns, flat] of Object.entries(enUsOverrides)) {
71
+ if (flat === undefined)
72
+ continue;
73
+ mergedEnUs[ns] = mergeOverrides(EN_US_RESOURCES[ns], flat);
74
+ }
75
+ }
76
+ return createI18n({
77
+ ...rest,
78
+ resources: {
79
+ ...resources,
80
+ // Full merged namespaces, so the whole-namespace replacement is safe.
81
+ ...(Object.keys(mergedEnUs).length > 0 ? { 'en-US': mergedEnUs } : {}),
82
+ },
83
+ loadBundle: async (tag, ns) => {
84
+ const base = loadBundle === undefined ? null : await loadBundle(tag, ns);
85
+ const flat = overrides[tag]?.[ns];
86
+ const baseBundle = base === null ? null : 'default' in base ? base.default : base;
87
+ if (flat === undefined)
88
+ return baseBundle;
89
+ // Merged HERE rather than after the fact, so a slow compiled chunk
90
+ // landing later can never clobber an override that was already applied.
91
+ return { default: mergeOverrides(baseBundle, flat) };
92
+ },
93
+ });
94
+ }
95
+ /**
96
+ * Rebuild the instance with a new override set and hand it back.
97
+ *
98
+ * The caller swaps the result into whatever holds the active instance and
99
+ * calls `bumpI18nRevision()` — which this does for you — so subscribers
100
+ * re-render. Rebuilding rather than patching is the whole point: see the
101
+ * module header.
102
+ */
103
+ export async function rebuildWithOverrides(opts) {
104
+ const instance = await createI18nWithOverrides(opts);
105
+ bumpI18nRevision();
106
+ return instance;
107
+ }
108
+ /** The BCP-47 key an override map uses for a locale id. */
109
+ export function overrideTag(locale) {
110
+ return tagForLocale(locale);
111
+ }
112
+ //# sourceMappingURL=overrides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overrides.js","sourceRoot":"","sources":["../src/overrides.ts"],"names":[],"mappings":"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"}
package/dist/react.d.ts CHANGED
@@ -5,6 +5,12 @@ import { type LocaleId } from './locales.js';
5
5
  export interface I18nContextValue {
6
6
  i18n: I18nInstance;
7
7
  locale: LocaleId;
8
+ /**
9
+ * Bumps whenever resolved text changes without the language changing —
10
+ * i.e. when runtime overrides are applied (23 §4.4). Hooks depend on it so
11
+ * memoized consumers re-render on an admin's edit, not only on a switch.
12
+ */
13
+ revision: number;
8
14
  }
9
15
  export interface I18nProviderProps {
10
16
  i18n: I18nInstance;
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAA4C,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEvF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC;CAClB;AAID,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,iBAAiB,GAAG,SAAS,CAgB7E;AAQD,kEAAkE;AAClE,wBAAgB,OAAO,IAAI,YAAY,CAEtC;AAED,gEAAgE;AAChE,wBAAgB,SAAS,IAAI,QAAQ,CAEpC;AAED,gEAAgE;AAChE,wBAAgB,MAAM,IAAI,OAAO,CAEhC;AAED,4EAA4E;AAC5E,wBAAgB,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAShG;AAED,gFAAgF;AAChF,wBAAgB,YAAY,IAAI,gBAAgB,GAAG,IAAI,CAEtD;AAmBD;;;;;;GAMG;AACH,wBAAgB,SAAS,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAUrG;AAED,yEAAyE;AACzE,wBAAgB,MAAM,IAAI,UAAU,CAGnC;AAED,0CAA0C;AAC1C,eAAO,MAAM,aAAa,eAAS,CAAC"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAA4C,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGvF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC;IACjB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,iBAAiB,GAAG,SAAS,CAqB7E;AAQD,kEAAkE;AAClE,wBAAgB,OAAO,IAAI,YAAY,CAEtC;AAED,gEAAgE;AAChE,wBAAgB,SAAS,IAAI,QAAQ,CAEpC;AAED,gEAAgE;AAChE,wBAAgB,MAAM,IAAI,OAAO,CAEhC;AAED,4EAA4E;AAC5E,wBAAgB,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAUhG;AAED,gFAAgF;AAChF,wBAAgB,YAAY,IAAI,gBAAgB,GAAG,IAAI,CAEtD;AAKD;;;;;;GAMG;AACH,wBAAgB,SAAS,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAarG;AAED,yEAAyE;AACzE,wBAAgB,MAAM,IAAI,UAAU,CAGnC;AAED,0CAA0C;AAC1C,eAAO,MAAM,aAAa,eAAS,CAAC"}