@adminiumjs/i18n 0.1.2 → 0.2.2-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) 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 +479 -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 +38 -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 +17 -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 +41 -0
  15. package/dist/fallback.js.map +1 -0
  16. package/dist/format/index.d.ts.map +1 -1
  17. package/dist/format/index.js +1 -0
  18. package/dist/format/index.js.map +1 -1
  19. package/dist/format-errors.d.ts +41 -0
  20. package/dist/format-errors.d.ts.map +1 -0
  21. package/dist/format-errors.js +64 -0
  22. package/dist/format-errors.js.map +1 -0
  23. package/dist/icu-format.d.ts +4 -0
  24. package/dist/icu-format.d.ts.map +1 -1
  25. package/dist/icu-format.js +44 -3
  26. package/dist/icu-format.js.map +1 -1
  27. package/dist/index.d.ts +10 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +15 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/keys.d.ts +33 -0
  32. package/dist/keys.d.ts.map +1 -0
  33. package/dist/keys.js +78 -0
  34. package/dist/keys.js.map +1 -0
  35. package/dist/locales.d.ts +106 -13
  36. package/dist/locales.d.ts.map +1 -1
  37. package/dist/locales.js +147 -17
  38. package/dist/locales.js.map +1 -1
  39. package/dist/overrides.d.ts +62 -0
  40. package/dist/overrides.d.ts.map +1 -0
  41. package/dist/overrides.js +113 -0
  42. package/dist/overrides.js.map +1 -0
  43. package/dist/react.d.ts +6 -0
  44. package/dist/react.d.ts.map +1 -1
  45. package/dist/react.js +18 -26
  46. package/dist/react.js.map +1 -1
  47. package/dist/resources/ar-eg/common.d.ts +786 -3
  48. package/dist/resources/ar-eg/common.d.ts.map +1 -1
  49. package/dist/resources/ar-eg/common.js +813 -29
  50. package/dist/resources/ar-eg/common.js.map +1 -1
  51. package/dist/resources/ar-eg/errors.d.ts +1 -0
  52. package/dist/resources/ar-eg/errors.d.ts.map +1 -1
  53. package/dist/resources/ar-eg/errors.js +2 -0
  54. package/dist/resources/ar-eg/errors.js.map +1 -1
  55. package/dist/resources/ar-eg/generated.d.ts.map +1 -1
  56. package/dist/resources/ar-eg/generated.js +1 -0
  57. package/dist/resources/ar-eg/generated.js.map +1 -1
  58. package/dist/resources/ar-eg/studio.d.ts.map +1 -1
  59. package/dist/resources/ar-eg/studio.js +1 -0
  60. package/dist/resources/ar-eg/studio.js.map +1 -1
  61. package/dist/resources/ar-eg/ui.d.ts +586 -586
  62. package/dist/resources/ar-eg/ui.d.ts.map +1 -1
  63. package/dist/resources/ar-eg/ui.js +587 -586
  64. package/dist/resources/ar-eg/ui.js.map +1 -1
  65. package/dist/resources/cs-cz/common.d.ts +786 -3
  66. package/dist/resources/cs-cz/common.d.ts.map +1 -1
  67. package/dist/resources/cs-cz/common.js +811 -27
  68. package/dist/resources/cs-cz/common.js.map +1 -1
  69. package/dist/resources/cs-cz/errors.d.ts +1 -0
  70. package/dist/resources/cs-cz/errors.d.ts.map +1 -1
  71. package/dist/resources/cs-cz/errors.js +2 -0
  72. package/dist/resources/cs-cz/errors.js.map +1 -1
  73. package/dist/resources/cs-cz/generated.d.ts.map +1 -1
  74. package/dist/resources/cs-cz/generated.js +1 -0
  75. package/dist/resources/cs-cz/generated.js.map +1 -1
  76. package/dist/resources/cs-cz/studio.d.ts.map +1 -1
  77. package/dist/resources/cs-cz/studio.js +1 -0
  78. package/dist/resources/cs-cz/studio.js.map +1 -1
  79. package/dist/resources/cs-cz/ui.d.ts +582 -582
  80. package/dist/resources/cs-cz/ui.d.ts.map +1 -1
  81. package/dist/resources/cs-cz/ui.js +583 -582
  82. package/dist/resources/cs-cz/ui.js.map +1 -1
  83. package/dist/resources/da-dk/common.d.ts +787 -4
  84. package/dist/resources/da-dk/common.d.ts.map +1 -1
  85. package/dist/resources/da-dk/common.js +814 -30
  86. package/dist/resources/da-dk/common.js.map +1 -1
  87. package/dist/resources/da-dk/errors.d.ts +1 -0
  88. package/dist/resources/da-dk/errors.d.ts.map +1 -1
  89. package/dist/resources/da-dk/errors.js +2 -0
  90. package/dist/resources/da-dk/errors.js.map +1 -1
  91. package/dist/resources/da-dk/generated.d.ts.map +1 -1
  92. package/dist/resources/da-dk/generated.js +1 -0
  93. package/dist/resources/da-dk/generated.js.map +1 -1
  94. package/dist/resources/da-dk/studio.d.ts.map +1 -1
  95. package/dist/resources/da-dk/studio.js +1 -0
  96. package/dist/resources/da-dk/studio.js.map +1 -1
  97. package/dist/resources/da-dk/ui.d.ts +568 -568
  98. package/dist/resources/da-dk/ui.d.ts.map +1 -1
  99. package/dist/resources/da-dk/ui.js +569 -568
  100. package/dist/resources/da-dk/ui.js.map +1 -1
  101. package/dist/resources/de-de/common.d.ts +790 -7
  102. package/dist/resources/de-de/common.d.ts.map +1 -1
  103. package/dist/resources/de-de/common.js +817 -33
  104. package/dist/resources/de-de/common.js.map +1 -1
  105. package/dist/resources/de-de/errors.d.ts +1 -0
  106. package/dist/resources/de-de/errors.d.ts.map +1 -1
  107. package/dist/resources/de-de/errors.js +2 -0
  108. package/dist/resources/de-de/errors.js.map +1 -1
  109. package/dist/resources/de-de/generated.d.ts.map +1 -1
  110. package/dist/resources/de-de/generated.js +1 -0
  111. package/dist/resources/de-de/generated.js.map +1 -1
  112. package/dist/resources/de-de/studio.d.ts.map +1 -1
  113. package/dist/resources/de-de/studio.js +1 -0
  114. package/dist/resources/de-de/studio.js.map +1 -1
  115. package/dist/resources/de-de/ui.d.ts +569 -569
  116. package/dist/resources/de-de/ui.d.ts.map +1 -1
  117. package/dist/resources/de-de/ui.js +570 -569
  118. package/dist/resources/de-de/ui.js.map +1 -1
  119. package/dist/resources/en-us/common.d.ts +786 -3
  120. package/dist/resources/en-us/common.d.ts.map +1 -1
  121. package/dist/resources/en-us/common.js +813 -29
  122. package/dist/resources/en-us/common.js.map +1 -1
  123. package/dist/resources/en-us/errors.d.ts +1 -0
  124. package/dist/resources/en-us/errors.d.ts.map +1 -1
  125. package/dist/resources/en-us/errors.js +2 -0
  126. package/dist/resources/en-us/errors.js.map +1 -1
  127. package/dist/resources/en-us/generated.d.ts.map +1 -1
  128. package/dist/resources/en-us/generated.js +1 -0
  129. package/dist/resources/en-us/generated.js.map +1 -1
  130. package/dist/resources/en-us/studio.d.ts.map +1 -1
  131. package/dist/resources/en-us/studio.js +1 -0
  132. package/dist/resources/en-us/studio.js.map +1 -1
  133. package/dist/resources/en-us/ui.d.ts.map +1 -1
  134. package/dist/resources/en-us/ui.js +1 -0
  135. package/dist/resources/en-us/ui.js.map +1 -1
  136. package/dist/resources/fr-fr/common.d.ts +788 -5
  137. package/dist/resources/fr-fr/common.d.ts.map +1 -1
  138. package/dist/resources/fr-fr/common.js +815 -31
  139. package/dist/resources/fr-fr/common.js.map +1 -1
  140. package/dist/resources/fr-fr/errors.d.ts +1 -0
  141. package/dist/resources/fr-fr/errors.d.ts.map +1 -1
  142. package/dist/resources/fr-fr/errors.js +2 -0
  143. package/dist/resources/fr-fr/errors.js.map +1 -1
  144. package/dist/resources/fr-fr/generated.d.ts.map +1 -1
  145. package/dist/resources/fr-fr/generated.js +1 -0
  146. package/dist/resources/fr-fr/generated.js.map +1 -1
  147. package/dist/resources/fr-fr/studio.d.ts.map +1 -1
  148. package/dist/resources/fr-fr/studio.js +1 -0
  149. package/dist/resources/fr-fr/studio.js.map +1 -1
  150. package/dist/resources/fr-fr/ui.d.ts +559 -559
  151. package/dist/resources/fr-fr/ui.d.ts.map +1 -1
  152. package/dist/resources/fr-fr/ui.js +560 -559
  153. package/dist/resources/fr-fr/ui.js.map +1 -1
  154. package/dist/resources/index.d.ts.map +1 -1
  155. package/dist/resources/index.js +1 -0
  156. package/dist/resources/index.js.map +1 -1
  157. package/dist/resources/lazy.d.ts.map +1 -1
  158. package/dist/resources/lazy.js.map +1 -1
  159. package/dist/resources/zh-cn/common.d.ts +787 -4
  160. package/dist/resources/zh-cn/common.d.ts.map +1 -1
  161. package/dist/resources/zh-cn/common.js +814 -30
  162. package/dist/resources/zh-cn/common.js.map +1 -1
  163. package/dist/resources/zh-cn/errors.d.ts +1 -0
  164. package/dist/resources/zh-cn/errors.d.ts.map +1 -1
  165. package/dist/resources/zh-cn/errors.js +2 -0
  166. package/dist/resources/zh-cn/errors.js.map +1 -1
  167. package/dist/resources/zh-cn/generated.d.ts.map +1 -1
  168. package/dist/resources/zh-cn/generated.js +1 -0
  169. package/dist/resources/zh-cn/generated.js.map +1 -1
  170. package/dist/resources/zh-cn/studio.d.ts.map +1 -1
  171. package/dist/resources/zh-cn/studio.js +1 -0
  172. package/dist/resources/zh-cn/studio.js.map +1 -1
  173. package/dist/resources/zh-cn/ui.d.ts +575 -575
  174. package/dist/resources/zh-cn/ui.d.ts.map +1 -1
  175. package/dist/resources/zh-cn/ui.js +576 -575
  176. package/dist/resources/zh-cn/ui.js.map +1 -1
  177. package/dist/resources/zh-tw/common.d.ts +786 -3
  178. package/dist/resources/zh-tw/common.d.ts.map +1 -1
  179. package/dist/resources/zh-tw/common.js +813 -29
  180. package/dist/resources/zh-tw/common.js.map +1 -1
  181. package/dist/resources/zh-tw/errors.d.ts +1 -0
  182. package/dist/resources/zh-tw/errors.d.ts.map +1 -1
  183. package/dist/resources/zh-tw/errors.js +2 -0
  184. package/dist/resources/zh-tw/errors.js.map +1 -1
  185. package/dist/resources/zh-tw/generated.d.ts.map +1 -1
  186. package/dist/resources/zh-tw/generated.js +1 -0
  187. package/dist/resources/zh-tw/generated.js.map +1 -1
  188. package/dist/resources/zh-tw/studio.d.ts.map +1 -1
  189. package/dist/resources/zh-tw/studio.js +1 -0
  190. package/dist/resources/zh-tw/studio.js.map +1 -1
  191. package/dist/resources/zh-tw/ui.d.ts +575 -575
  192. package/dist/resources/zh-tw/ui.d.ts.map +1 -1
  193. package/dist/resources/zh-tw/ui.js +576 -575
  194. package/dist/resources/zh-tw/ui.js.map +1 -1
  195. package/dist/review-status.d.ts +55 -0
  196. package/dist/review-status.d.ts.map +1 -0
  197. package/dist/review-status.js +60 -0
  198. package/dist/review-status.js.map +1 -0
  199. package/dist/revision.d.ts +35 -0
  200. package/dist/revision.d.ts.map +1 -0
  201. package/dist/revision.js +56 -0
  202. package/dist/revision.js.map +1 -0
  203. package/dist/server.d.ts +36 -0
  204. package/dist/server.d.ts.map +1 -0
  205. package/dist/server.js +36 -0
  206. package/dist/server.js.map +1 -0
  207. package/dist/validate-message.d.ts +57 -0
  208. package/dist/validate-message.d.ts.map +1 -0
  209. package/dist/validate-message.js +179 -0
  210. package/dist/validate-message.js.map +1 -0
  211. package/locales/ar-EG/.meta.json +13246 -0
  212. package/locales/ar-EG/common.json +812 -29
  213. package/locales/ar-EG/errors.json +1 -0
  214. package/locales/ar-EG/ui.json +586 -586
  215. package/locales/cs-CZ/.meta.json +13246 -0
  216. package/locales/cs-CZ/common.json +810 -27
  217. package/locales/cs-CZ/errors.json +1 -0
  218. package/locales/cs-CZ/ui.json +582 -582
  219. package/locales/da-DK/.meta.json +13246 -0
  220. package/locales/da-DK/common.json +813 -30
  221. package/locales/da-DK/errors.json +1 -0
  222. package/locales/da-DK/ui.json +568 -568
  223. package/locales/de-DE/.meta.json +13246 -0
  224. package/locales/de-DE/common.json +816 -33
  225. package/locales/de-DE/errors.json +1 -0
  226. package/locales/de-DE/ui.json +569 -569
  227. package/locales/en-US/common.json +812 -29
  228. package/locales/en-US/errors.json +1 -0
  229. package/locales/fr-FR/.meta.json +13246 -0
  230. package/locales/fr-FR/common.json +814 -31
  231. package/locales/fr-FR/errors.json +1 -0
  232. package/locales/fr-FR/ui.json +559 -559
  233. package/locales/zh-CN/.meta.json +13246 -0
  234. package/locales/zh-CN/common.json +813 -30
  235. package/locales/zh-CN/errors.json +1 -0
  236. package/locales/zh-CN/ui.json +575 -575
  237. package/locales/zh-TW/.meta.json +13246 -0
  238. package/locales/zh-TW/common.json +812 -29
  239. package/locales/zh-TW/errors.json +1 -0
  240. package/locales/zh-TW/ui.json +575 -575
  241. package/package.json +11 -3
package/dist/locales.d.ts CHANGED
@@ -1,10 +1,32 @@
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. `emitTheme` isolates each
22
+ * listener now (23 §4.4), so a throw here no longer errors the React commit —
23
+ * it is logged and swallowed, and this subscriber simply does not finish. That
24
+ * is a quieter failure, not a smaller one: ThemeProvider stamps `dir`/`lang`
25
+ * BEFORE it emits, so the page turns RTL while every string stays in the old
26
+ * language, and nothing on screen says why.
27
+ *
28
+ * `dir` is still derived from the locale, never independently settable
29
+ * (02-design-system.md §4.2).
8
30
  */
9
31
  export interface LocaleEntry {
10
32
  /** Canonical underscore id stored in `adminium_settings`/`adminium_user_prefs`. */
@@ -24,6 +46,23 @@ export interface LocaleEntry {
24
46
  */
25
47
  fontHint: 'latin' | 'arabic' | 'cjk';
26
48
  }
49
+ /** A runtime locale entry, plus the fields only the DB registry knows. */
50
+ export interface RuntimeLocaleEntry extends LocaleEntry {
51
+ /** False for admin-created locales. */
52
+ builtin: boolean;
53
+ /** Offered in pickers? (23 §3.1 `enabled`.) */
54
+ enabled: boolean;
55
+ /** Picker ordering. */
56
+ sortOrder: number;
57
+ /**
58
+ * The REAL BCP-47 tag whose `Intl` behaviour this locale borrows (23 §5.6).
59
+ * For a built-in this is just `tag`; for a custom locale it is the admin's
60
+ * choice, because `tag` itself may be something `Intl` has never heard of.
61
+ */
62
+ intlTag: string;
63
+ /** Plural categories frozen at create time from `intlTag`. */
64
+ pluralCategories: readonly string[];
65
+ }
27
66
  export declare const LOCALES: readonly [{
28
67
  readonly id: "en_US";
29
68
  readonly tag: "en-US";
@@ -81,19 +120,73 @@ export declare const LOCALES: readonly [{
81
120
  readonly dir: "rtl";
82
121
  readonly fontHint: "arabic";
83
122
  }];
84
- export type LocaleId = (typeof LOCALES)[number]['id'];
85
- export declare const LOCALE_IDS: readonly LocaleId[];
123
+ /** The eight COMPILED locales — the exhaustive axis (23 §5.1). */
124
+ export type BuiltinLocaleId = (typeof LOCALES)[number]['id'];
125
+ /**
126
+ * Any locale id the running instance may hold — the eight compiled ones plus
127
+ * whatever an admin created. `(string & {})` preserves literal autocomplete
128
+ * for the eight instead of collapsing to a bare `string`.
129
+ */
130
+ export type LocaleId = BuiltinLocaleId | (string & {});
131
+ export declare const BUILTIN_LOCALE_IDS: readonly BuiltinLocaleId[];
132
+ /** @deprecated Prefer {@link BUILTIN_LOCALE_IDS} (or the runtime list) by name. */
133
+ export declare const LOCALE_IDS: readonly ("en_US" | "de_DE" | "fr_FR" | "cs_CZ" | "da_DK" | "zh_CN" | "zh_TW" | "ar_EG")[];
134
+ /**
135
+ * Canonical id shape: a language subtag plus up to two more (script, region),
136
+ * e.g. `en_US`, `sw_KE`, `zh_Hant_TW`. Three subtags is why the DB column is
137
+ * `varchar(35)` and why {@link tagFromLocaleId} replaces EVERY underscore.
138
+ */
139
+ export declare const LOCALE_ID_RE: RegExp;
140
+ /** Replace the runtime registry wholesale (boot, and every version bump). */
141
+ export declare function setRuntimeLocales(entries: readonly RuntimeLocaleEntry[]): void;
142
+ /** Drop the runtime registry — tests, and teardown. */
143
+ export declare function resetRuntimeLocales(): void;
144
+ /** Every locale the instance knows: compiled defaults merged under runtime rows. */
145
+ export declare function allLocales(): readonly RuntimeLocaleEntry[];
146
+ /** The locales a picker may offer, in picker order (23 §3.1 `enabled`). */
147
+ export declare function availableLocales(): readonly RuntimeLocaleEntry[];
148
+ /** True for one of the eight COMPILED locales. */
149
+ export declare function isBuiltinLocaleId(value: unknown): value is BuiltinLocaleId;
150
+ /**
151
+ * True for anything shaped like a locale id. This is a SHAPE check, not a
152
+ * membership check: a cached preference may name a locale this build does not
153
+ * compile in, and rejecting it would strand the user on en-US. Use
154
+ * {@link isBuiltinLocaleId} when compiled membership is what you mean.
155
+ */
86
156
  export declare function isLocaleId(value: unknown): value is LocaleId;
157
+ /**
158
+ * `en_US` → `en-US`, `zh_Hant_TW` → `zh-Hant-TW`.
159
+ *
160
+ * Replaces EVERY underscore. A single-replacement version leaves
161
+ * `zh-Hant_TW`, which is not a valid BCP-47 tag, and the formatter layer
162
+ * coalesces invalid tags to `en-US` — silently degrading exactly the way the
163
+ * admin-chosen `intlTag` exists to prevent.
164
+ */
165
+ export declare function tagFromLocaleId(id: string): string;
166
+ /**
167
+ * The presentation record for a locale: compiled entry, else runtime entry,
168
+ * else a synthesized one. Total by contract — see the file header for why
169
+ * throwing here is a white screen.
170
+ */
87
171
  export declare function localeEntry(id: LocaleId): LocaleEntry;
88
- /** `'rtl'` iff the registry says so (`ar_EG` today) — data-driven, not hardcoded. */
172
+ /** `'rtl'` iff the registry says so — data-driven, never hardcoded to `ar_EG`. */
89
173
  export declare function dirForLocale(id: LocaleId): 'ltr' | 'rtl';
90
174
  export declare function isRtlLocale(id: LocaleId): boolean;
91
- /** `ar_EG` → `ar-EG` for `Intl.*`, i18next, and the `lang` attribute. */
175
+ /** `ar_EG` → `ar-EG` for i18next and the `lang` attribute. */
92
176
  export declare function tagForLocale(id: LocaleId): string;
93
177
  /**
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.
178
+ * The tag to hand `Intl.*` and `IntlMessageFormat` (23 §4.5). For a compiled
179
+ * locale this is just the tag; for a custom locale it is the admin-chosen
180
+ * borrow tag, because the locale's own tag may be something no ICU
181
+ * implementation has data for.
182
+ */
183
+ export declare function intlTagForLocale(id: LocaleId): string;
184
+ /** Plural categories to validate a message against (frozen at create time). */
185
+ export declare function pluralCategoriesForLocale(id: LocaleId): readonly string[];
186
+ /**
187
+ * Best-effort inverse of {@link tagForLocale}: exact compiled tag, then a
188
+ * runtime tag, then a language-only compiled match (`de` → `de_DE`), else
189
+ * `en_US`. Maps `i18next.language` / `navigator.language` back to an id.
97
190
  */
98
191
  export declare function localeFromTag(tag: string): LocaleId;
99
192
  //# 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":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;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,33 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
1
2
  /**
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.
3
+ * Canonical locale registry (10-i18n-theming.md §2.1) plus the runtime
4
+ * overlay that lets an admin add locales the build never saw
5
+ * (23-runtime-translations.md §5).
6
+ *
7
+ * Two id types, and the split is load-bearing:
8
+ *
9
+ * - {@link BuiltinLocaleId} is the closed union of the eight COMPILED
10
+ * locales. Everything that must stay exhaustive keys off it —
11
+ * `REVIEW_STATUS`, the parity gate, `gen-resources.mjs`, `lazy.ts` — so a
12
+ * ninth *compiled* locale is still a compile error, and no runtime locale
13
+ * can ever reach a build-time gate.
14
+ * - {@link LocaleId} additionally admits runtime ids. `(string & {})` keeps
15
+ * literal autocomplete for the eight while accepting anything a custom
16
+ * locale row can hold.
17
+ *
18
+ * Every lookup helper here is TOTAL: none of them throws on an unknown id.
19
+ * That is deliberate. Once locales are data, the orphan case — a deleted
20
+ * locale still referenced by a user pref, a restored backup, an imported
21
+ * config bundle — is the NORMAL aftermath of the feature, not an edge case,
22
+ * and these helpers run inside a theme subscriber. `emitTheme` isolates each
23
+ * listener now (23 §4.4), so a throw here no longer errors the React commit —
24
+ * it is logged and swallowed, and this subscriber simply does not finish. That
25
+ * is a quieter failure, not a smaller one: ThemeProvider stamps `dir`/`lang`
26
+ * BEFORE it emits, so the page turns RTL while every string stays in the old
27
+ * language, and nothing on screen says why.
28
+ *
29
+ * `dir` is still derived from the locale, never independently settable
30
+ * (02-design-system.md §4.2).
8
31
  */
9
32
  export const LOCALES = [
10
33
  { id: 'en_US', tag: 'en-US', english: 'English (US)', native: 'English (US)', dir: 'ltr', fontHint: 'latin' },
@@ -16,38 +39,145 @@ export const LOCALES = [
16
39
  { id: 'zh_TW', tag: 'zh-TW', english: 'Chinese (Traditional)', native: '繁體中文', dir: 'ltr', fontHint: 'cjk' },
17
40
  { id: 'ar_EG', tag: 'ar-EG', english: 'Arabic (Egypt)', native: 'العربية (مصر)', dir: 'rtl', fontHint: 'arabic' },
18
41
  ];
19
- export const LOCALE_IDS = LOCALES.map((l) => l.id);
42
+ export const BUILTIN_LOCALE_IDS = LOCALES.map((l) => l.id);
43
+ /** @deprecated Prefer {@link BUILTIN_LOCALE_IDS} (or the runtime list) by name. */
44
+ export const LOCALE_IDS = BUILTIN_LOCALE_IDS;
45
+ /**
46
+ * Canonical id shape: a language subtag plus up to two more (script, region),
47
+ * e.g. `en_US`, `sw_KE`, `zh_Hant_TW`. Three subtags is why the DB column is
48
+ * `varchar(35)` and why {@link tagFromLocaleId} replaces EVERY underscore.
49
+ */
50
+ export const LOCALE_ID_RE = /^[a-z]{2,3}(_[A-Za-z0-9]{2,8}){0,2}$/;
20
51
  const BY_ID = new Map(LOCALES.map((l) => [l.id, l]));
21
52
  const BY_TAG = new Map(LOCALES.map((l) => [l.tag, l]));
22
- export function isLocaleId(value) {
53
+ // --- runtime overlay ---------------------------------------------------------
54
+ /**
55
+ * Locales the running instance knows about beyond the compiled eight. Set
56
+ * once from the server manifest at boot and on every i18n version bump; empty
57
+ * on a server/CLI that never loads it, which is exactly the pre-23 behaviour.
58
+ *
59
+ * A module-level singleton is correct here: one Adminium instance has one
60
+ * meta store and therefore one locale registry, same as the compiled one.
61
+ */
62
+ let runtimeById = new Map();
63
+ /** Replace the runtime registry wholesale (boot, and every version bump). */
64
+ export function setRuntimeLocales(entries) {
65
+ runtimeById = new Map(entries.map((e) => [e.id, e]));
66
+ }
67
+ /** Drop the runtime registry — tests, and teardown. */
68
+ export function resetRuntimeLocales() {
69
+ runtimeById = new Map();
70
+ }
71
+ /** Every locale the instance knows: compiled defaults merged under runtime rows. */
72
+ export function allLocales() {
73
+ const merged = new Map();
74
+ for (const entry of LOCALES) {
75
+ merged.set(entry.id, {
76
+ ...entry,
77
+ builtin: true,
78
+ enabled: true,
79
+ sortOrder: 0,
80
+ intlTag: entry.tag,
81
+ pluralCategories: [],
82
+ });
83
+ }
84
+ for (const [id, entry] of runtimeById) {
85
+ const base = merged.get(id);
86
+ // A built-in row may only carry `enabled`/`sortOrder` (23 §3.1 field
87
+ // lock) — presentation always comes from the compiled entry, so an admin
88
+ // cannot flip ar_EG to ltr and corrupt a shipped bundle's rendering.
89
+ merged.set(id, base === undefined
90
+ ? entry
91
+ : { ...base, enabled: entry.enabled, sortOrder: entry.sortOrder, builtin: true });
92
+ }
93
+ return [...merged.values()].sort((a, b) => a.sortOrder - b.sortOrder || a.id.localeCompare(b.id));
94
+ }
95
+ /** The locales a picker may offer, in picker order (23 §3.1 `enabled`). */
96
+ export function availableLocales() {
97
+ return allLocales().filter((l) => l.enabled);
98
+ }
99
+ // --- lookups (all total) -----------------------------------------------------
100
+ /** True for one of the eight COMPILED locales. */
101
+ export function isBuiltinLocaleId(value) {
23
102
  return typeof value === 'string' && BY_ID.has(value);
24
103
  }
104
+ /**
105
+ * True for anything shaped like a locale id. This is a SHAPE check, not a
106
+ * membership check: a cached preference may name a locale this build does not
107
+ * compile in, and rejecting it would strand the user on en-US. Use
108
+ * {@link isBuiltinLocaleId} when compiled membership is what you mean.
109
+ */
110
+ export function isLocaleId(value) {
111
+ return typeof value === 'string' && LOCALE_ID_RE.test(value);
112
+ }
113
+ /**
114
+ * `en_US` → `en-US`, `zh_Hant_TW` → `zh-Hant-TW`.
115
+ *
116
+ * Replaces EVERY underscore. A single-replacement version leaves
117
+ * `zh-Hant_TW`, which is not a valid BCP-47 tag, and the formatter layer
118
+ * coalesces invalid tags to `en-US` — silently degrading exactly the way the
119
+ * admin-chosen `intlTag` exists to prevent.
120
+ */
121
+ export function tagFromLocaleId(id) {
122
+ return id.replaceAll('_', '-');
123
+ }
124
+ /** Synthesized entry for an id no registry knows — never throws. */
125
+ function synthesize(id) {
126
+ return { id, tag: tagFromLocaleId(id), english: id, native: id, dir: 'ltr', fontHint: 'latin' };
127
+ }
128
+ /**
129
+ * The presentation record for a locale: compiled entry, else runtime entry,
130
+ * else a synthesized one. Total by contract — see the file header for why
131
+ * throwing here is a white screen.
132
+ */
25
133
  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;
134
+ return BY_ID.get(id) ?? runtimeById.get(id) ?? synthesize(id);
30
135
  }
31
- /** `'rtl'` iff the registry says so (`ar_EG` today) — data-driven, not hardcoded. */
136
+ /** `'rtl'` iff the registry says so — data-driven, never hardcoded to `ar_EG`. */
32
137
  export function dirForLocale(id) {
33
138
  return localeEntry(id).dir;
34
139
  }
35
140
  export function isRtlLocale(id) {
36
141
  return dirForLocale(id) === 'rtl';
37
142
  }
38
- /** `ar_EG` → `ar-EG` for `Intl.*`, i18next, and the `lang` attribute. */
143
+ /** `ar_EG` → `ar-EG` for i18next and the `lang` attribute. */
39
144
  export function tagForLocale(id) {
40
145
  return localeEntry(id).tag;
41
146
  }
42
147
  /**
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.
148
+ * The tag to hand `Intl.*` and `IntlMessageFormat` (23 §4.5). For a compiled
149
+ * locale this is just the tag; for a custom locale it is the admin-chosen
150
+ * borrow tag, because the locale's own tag may be something no ICU
151
+ * implementation has data for.
152
+ */
153
+ export function intlTagForLocale(id) {
154
+ return runtimeById.get(id)?.intlTag ?? tagForLocale(id);
155
+ }
156
+ /** Plural categories to validate a message against (frozen at create time). */
157
+ export function pluralCategoriesForLocale(id) {
158
+ const frozen = runtimeById.get(id)?.pluralCategories;
159
+ if (frozen !== undefined && frozen.length > 0)
160
+ return frozen;
161
+ try {
162
+ return new Intl.PluralRules(intlTagForLocale(id)).resolvedOptions().pluralCategories;
163
+ }
164
+ catch {
165
+ return ['other'];
166
+ }
167
+ }
168
+ /**
169
+ * Best-effort inverse of {@link tagForLocale}: exact compiled tag, then a
170
+ * runtime tag, then a language-only compiled match (`de` → `de_DE`), else
171
+ * `en_US`. Maps `i18next.language` / `navigator.language` back to an id.
46
172
  */
47
173
  export function localeFromTag(tag) {
48
174
  const exact = BY_TAG.get(tag);
49
175
  if (exact !== undefined)
50
176
  return exact.id;
177
+ for (const entry of runtimeById.values()) {
178
+ if (entry.tag === tag)
179
+ return entry.id;
180
+ }
51
181
  const lang = tag.toLowerCase().split('-')[0] ?? '';
52
182
  const byLang = LOCALES.find((l) => l.tag.toLowerCase().startsWith(`${lang}-`) || l.tag.toLowerCase() === lang);
53
183
  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":"AAAA,yCAAyC;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAuCH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE;IAC7G,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE;IAClG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE;IACnG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE;IACjG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE;IAChG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE;IAC3G,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE;IAC5G,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE;CACxE,CAAC;AAY5C,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAA0C,CAAC;AAEpG,mFAAmF;AACnF,MAAM,CAAC,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,sCAAsC,CAAC;AAEnE,MAAM,KAAK,GAAkD,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACpG,MAAM,MAAM,GAAkD,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtG,gFAAgF;AAEhF;;;;;;;GAOG;AACH,IAAI,WAAW,GAA4C,IAAI,GAAG,EAAE,CAAC;AAErE,6EAA6E;AAC7E,MAAM,UAAU,iBAAiB,CAAC,OAAsC;IACtE,WAAW,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,mBAAmB;IACjC,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;AAC1B,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;IACrD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE;YACnB,GAAG,KAAK;YACR,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,KAAK,CAAC,GAAG;YAClB,gBAAgB,EAAE,EAAE;SACrB,CAAC,CAAC;IACL,CAAC;IACD,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5B,qEAAqE;QACrE,yEAAyE;QACzE,qEAAqE;QACrE,MAAM,CAAC,GAAG,CACR,EAAE,EACF,IAAI,KAAK,SAAS;YAChB,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CACnF,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,gBAAgB;IAC9B,OAAO,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED,gFAAgF;AAEhF,kDAAkD;AAClD,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,OAAO,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,oEAAoE;AACpE,SAAS,UAAU,CAAC,EAAU;IAC5B,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,eAAe,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAClG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,EAAY;IACtC,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,EAAY;IACvC,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAY;IACtC,OAAO,YAAY,CAAC,EAAE,CAAC,KAAK,KAAK,CAAC;AACpC,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,EAAY;IACvC,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAY;IAC3C,OAAO,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,OAAO,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,yBAAyB,CAAC,EAAY;IACpD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;IACrD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC7D,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,gBAAgB,CAAC;IACvF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,EAAE,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC,EAAE,CAAC;IACzC,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,CAAC;IAC/G,OAAO,MAAM,EAAE,EAAE,IAAI,OAAO,CAAC;AAC/B,CAAC"}
@@ -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":"AACA;;;;;;;;;;;;;;;;;;;;;;;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,113 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ /**
3
+ * Runtime override layer (23-runtime-translations.md §4.3).
4
+ *
5
+ * The compiled bundle and the DB override tree are held SEPARATELY and merged
6
+ * in userland; the i18next resource store is written once, at `init`, and
7
+ * never mutated afterwards. Two facts about i18next 25 force that shape, and
8
+ * both were verified against its source rather than assumed:
9
+ *
10
+ * 1. `addResourceBundle` cannot DELETE a key — its `deepExtend` only ever
11
+ * writes keys present in the incoming object. So the store cannot express
12
+ * "reset this key to the built-in", which is the single most common admin
13
+ * operation. And `removeResourceBundle` is not an escape: it calls
14
+ * `removeNamespaces`, which splices the namespace out of `options.ns` for
15
+ * the WHOLE instance, breaking en-US resolution and every future backend
16
+ * load for that namespace.
17
+ * 2. The backend loader fires at most once per (language, namespace) per
18
+ * instance — `queueLoad` short-circuits on `hasResourceBundle` — so
19
+ * merging inside `loadBundle` is a first-load mechanism, not a
20
+ * steady-state one.
21
+ *
22
+ * Therefore: merge at load, and REBUILD the instance on every version bump,
23
+ * swapping it in through the existing provider seam. That is the only
24
+ * operation this i18next version performs losslessly.
25
+ */
26
+ import { createI18n } from './create-i18n.js';
27
+ import { tagForLocale } from './locales.js';
28
+ import { bumpI18nRevision } from './revision.js';
29
+ /**
30
+ * Merge flat dotted overrides into a (cloned) nested bundle.
31
+ *
32
+ * An empty-string value is written through deliberately — it is the third
33
+ * state, "render nothing" (23 §3.3), and is why `createI18n` sets
34
+ * `returnEmptyString: true`.
35
+ */
36
+ export function mergeOverrides(base, flat) {
37
+ const root = base === null ? {} : structuredClone(base);
38
+ for (const [path, value] of Object.entries(flat)) {
39
+ const parts = path.split('.');
40
+ let node = root;
41
+ for (let i = 0; i < parts.length - 1; i += 1) {
42
+ const part = parts[i];
43
+ const next = node[part];
44
+ // A key may override a leaf that used to be a subtree (or vice versa);
45
+ // replace rather than crash.
46
+ if (typeof next !== 'object' || next === null)
47
+ node[part] = {};
48
+ node = node[part];
49
+ }
50
+ node[parts[parts.length - 1]] = value;
51
+ }
52
+ return root;
53
+ }
54
+ /**
55
+ * `createI18n` with the override layer folded in.
56
+ *
57
+ * en-US overrides are applied through the SAME merge as every other locale,
58
+ * never through `createI18n`'s `resources` option: that option does
59
+ * `target[ns] = bundle`, a whole-namespace REPLACEMENT, so routing a sparse
60
+ * override tree through it would delete the ~1,500 other `common` keys.
61
+ */
62
+ export async function createI18nWithOverrides(opts) {
63
+ const { overrides, loadBundle, resources, ...rest } = opts;
64
+ if (overrides === undefined || Object.keys(overrides).length === 0) {
65
+ return createI18n(opts);
66
+ }
67
+ const enUsOverrides = overrides['en-US'];
68
+ const mergedEnUs = {};
69
+ if (enUsOverrides !== undefined) {
70
+ const { EN_US_RESOURCES } = await import('./resources/index.js');
71
+ for (const [ns, flat] of Object.entries(enUsOverrides)) {
72
+ if (flat === undefined)
73
+ continue;
74
+ mergedEnUs[ns] = mergeOverrides(EN_US_RESOURCES[ns], flat);
75
+ }
76
+ }
77
+ return createI18n({
78
+ ...rest,
79
+ resources: {
80
+ ...resources,
81
+ // Full merged namespaces, so the whole-namespace replacement is safe.
82
+ ...(Object.keys(mergedEnUs).length > 0 ? { 'en-US': mergedEnUs } : {}),
83
+ },
84
+ loadBundle: async (tag, ns) => {
85
+ const base = loadBundle === undefined ? null : await loadBundle(tag, ns);
86
+ const flat = overrides[tag]?.[ns];
87
+ const baseBundle = base === null ? null : 'default' in base ? base.default : base;
88
+ if (flat === undefined)
89
+ return baseBundle;
90
+ // Merged HERE rather than after the fact, so a slow compiled chunk
91
+ // landing later can never clobber an override that was already applied.
92
+ return { default: mergeOverrides(baseBundle, flat) };
93
+ },
94
+ });
95
+ }
96
+ /**
97
+ * Rebuild the instance with a new override set and hand it back.
98
+ *
99
+ * The caller swaps the result into whatever holds the active instance and
100
+ * calls `bumpI18nRevision()` — which this does for you — so subscribers
101
+ * re-render. Rebuilding rather than patching is the whole point: see the
102
+ * module header.
103
+ */
104
+ export async function rebuildWithOverrides(opts) {
105
+ const instance = await createI18nWithOverrides(opts);
106
+ bumpI18nRevision();
107
+ return instance;
108
+ }
109
+ /** The BCP-47 key an override map uses for a locale id. */
110
+ export function overrideTag(locale) {
111
+ return tagForLocale(locale);
112
+ }
113
+ //# 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":"AAiBA,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"}