@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editing.js","sourceRoot":"","sources":["../src/editing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,aAAa,EACb,QAAQ,EACR,UAAU,EACV,WAAW,EACX,cAAc,EACd,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ICU formatting for the NO-INSTANCE path.
3
+ *
4
+ * Every `t(key, fallback, args)` in this repo has to work before
5
+ * `initDashboardI18n()` has run and inside unit tests that never boot i18next
6
+ * — components render, and the English fallback is what they show. Returning
7
+ * the fallback *raw* in that state is wrong the moment the fallback has
8
+ * placeholders: the user (or the test) sees the literal `{count} changes`.
9
+ *
10
+ * That defect used to be invisible because 48 call sites hand-substituted
11
+ * their tokens with `.replace('{count}', …)` AFTER `t()` returned, which
12
+ * silently papered over it. Once those became real ICU arguments (23-T06)
13
+ * the gap showed up, so both fallback paths — `useMaybeT` in the React
14
+ * bindings and the dashboard's module-level `t()` — now share this one
15
+ * implementation.
16
+ */
17
+ /**
18
+ * Format `fallback` (always en-US source text) with `args`. Returns the
19
+ * fallback unchanged when there is nothing to interpolate or when the message
20
+ * does not parse — this path must never throw, since it is what renders when
21
+ * i18n is not up.
22
+ */
23
+ export declare function formatFallback(fallback: string, args?: Record<string, unknown>): string;
24
+ //# sourceMappingURL=fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAYvF"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ICU formatting for the NO-INSTANCE path.
3
+ *
4
+ * Every `t(key, fallback, args)` in this repo has to work before
5
+ * `initDashboardI18n()` has run and inside unit tests that never boot i18next
6
+ * — components render, and the English fallback is what they show. Returning
7
+ * the fallback *raw* in that state is wrong the moment the fallback has
8
+ * placeholders: the user (or the test) sees the literal `{count} changes`.
9
+ *
10
+ * That defect used to be invisible because 48 call sites hand-substituted
11
+ * their tokens with `.replace('{count}', …)` AFTER `t()` returned, which
12
+ * silently papered over it. Once those became real ICU arguments (23-T06)
13
+ * the gap showed up, so both fallback paths — `useMaybeT` in the React
14
+ * bindings and the dashboard's module-level `t()` — now share this one
15
+ * implementation.
16
+ */
17
+ import { IntlMessageFormat } from 'intl-messageformat';
18
+ const cache = new Map();
19
+ /**
20
+ * Format `fallback` (always en-US source text) with `args`. Returns the
21
+ * fallback unchanged when there is nothing to interpolate or when the message
22
+ * does not parse — this path must never throw, since it is what renders when
23
+ * i18n is not up.
24
+ */
25
+ export function formatFallback(fallback, args) {
26
+ if (args === undefined || !fallback.includes('{'))
27
+ return fallback;
28
+ try {
29
+ let message = cache.get(fallback);
30
+ if (message === undefined) {
31
+ message = new IntlMessageFormat(fallback, 'en-US', undefined, { ignoreTag: true });
32
+ cache.set(fallback, message);
33
+ }
34
+ return String(message.format(args));
35
+ }
36
+ catch {
37
+ return fallback;
38
+ }
39
+ }
40
+ //# sourceMappingURL=fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,IAA8B;IAC7E,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACnE,IAAI,CAAC;QACH,IAAI,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,GAAG,IAAI,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnF,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAuC,CAAC,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Bounded ring of ICU format failures (23-runtime-translations.md §4.5, T12).
3
+ *
4
+ * When a message throws at format time, `IcuFormat` returns the RAW string —
5
+ * so the user sees `{count, plural, one {…}}` sitting in the UI — and logs one
6
+ * `console.warn`. For compiled bundles that is acceptable: the parity gate
7
+ * makes it nearly impossible, and a developer sees the console.
8
+ *
9
+ * For admin-authored overrides it is not. The person who caused the failure is
10
+ * an admin in the Translations editor, not a developer with devtools open, and
11
+ * the write validator cannot catch everything (it checks the message against
12
+ * its source, but the ARGUMENTS a call site actually passes are not knowable
13
+ * statically). So failures land here, and the editor can show them.
14
+ *
15
+ * Deliberately bounded and in-memory: this is a diagnostic aid, not an audit
16
+ * trail. A runaway render loop over one bad message must not grow without
17
+ * limit.
18
+ */
19
+ export interface FormatFailure {
20
+ /** `namespace:key` where resolvable, else the bare key i18next gave us. */
21
+ key: string;
22
+ /** Language the failure happened under. */
23
+ lng: string;
24
+ message: string;
25
+ /** Epoch ms; the caller supplies it so this module stays clock-free. */
26
+ at: number;
27
+ /** How many times this exact (key, lng, message) has failed. */
28
+ count: number;
29
+ }
30
+ export declare function recordFormatFailure(input: Omit<FormatFailure, 'count'>): void;
31
+ /**
32
+ * Most recent first.
33
+ *
34
+ * Entries are COPIED: the records are mutated in place on every repeat, so
35
+ * handing out live references would let a caller's snapshot change under it
36
+ * (and let a caller write into the ring). `Readonly` states that guarantee;
37
+ * the copy is what actually provides it.
38
+ */
39
+ export declare function formatFailures(): readonly Readonly<FormatFailure>[];
40
+ export declare function clearFormatFailures(): void;
41
+ //# sourceMappingURL=format-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-errors.d.ts","sourceRoot":"","sources":["../src/format-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;CACf;AAaD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,GAAG,IAAI,CAmB7E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,SAAS,QAAQ,CAAC,aAAa,CAAC,EAAE,CAEnE;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Bounded ring of ICU format failures (23-runtime-translations.md §4.5, T12).
3
+ *
4
+ * When a message throws at format time, `IcuFormat` returns the RAW string —
5
+ * so the user sees `{count, plural, one {…}}` sitting in the UI — and logs one
6
+ * `console.warn`. For compiled bundles that is acceptable: the parity gate
7
+ * makes it nearly impossible, and a developer sees the console.
8
+ *
9
+ * For admin-authored overrides it is not. The person who caused the failure is
10
+ * an admin in the Translations editor, not a developer with devtools open, and
11
+ * the write validator cannot catch everything (it checks the message against
12
+ * its source, but the ARGUMENTS a call site actually passes are not knowable
13
+ * statically). So failures land here, and the editor can show them.
14
+ *
15
+ * Deliberately bounded and in-memory: this is a diagnostic aid, not an audit
16
+ * trail. A runaway render loop over one bad message must not grow without
17
+ * limit.
18
+ */
19
+ const MAX_ENTRIES = 50;
20
+ /**
21
+ * Keyed by `key\0lng\0message` so a repeat increments rather than evicts.
22
+ *
23
+ * Insertion order is maintained as RECENCY order (see the re-insert below), so
24
+ * the first key is always the least recently seen — which is exactly what
25
+ * eviction takes.
26
+ */
27
+ const failures = new Map();
28
+ export function recordFormatFailure(input) {
29
+ const id = `${input.key}\x00${input.lng}\x00${input.message}`;
30
+ const existing = failures.get(id);
31
+ if (existing !== undefined) {
32
+ existing.count += 1;
33
+ existing.at = input.at;
34
+ // Re-insert to move this key to the end. Updating in place would leave it
35
+ // wherever it first landed, and eviction below takes the FIRST key — so a
36
+ // message that is still failing would be evicted by unrelated one-offs.
37
+ // That runaway loop is the case this ring exists for; it must survive.
38
+ failures.delete(id);
39
+ failures.set(id, existing);
40
+ return;
41
+ }
42
+ if (failures.size >= MAX_ENTRIES) {
43
+ const oldest = failures.keys().next().value;
44
+ if (oldest !== undefined)
45
+ failures.delete(oldest);
46
+ }
47
+ failures.set(id, { ...input, count: 1 });
48
+ }
49
+ /**
50
+ * Most recent first.
51
+ *
52
+ * Entries are COPIED: the records are mutated in place on every repeat, so
53
+ * handing out live references would let a caller's snapshot change under it
54
+ * (and let a caller write into the ring). `Readonly` states that guarantee;
55
+ * the copy is what actually provides it.
56
+ */
57
+ export function formatFailures() {
58
+ return [...failures.values()].map((f) => ({ ...f })).sort((a, b) => b.at - a.at);
59
+ }
60
+ export function clearFormatFailures() {
61
+ failures.clear();
62
+ }
63
+ //# sourceMappingURL=format-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-errors.js","sourceRoot":"","sources":["../src/format-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAcH,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB;;;;;;GAMG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;AAElD,MAAM,UAAU,mBAAmB,CAAC,KAAmC;IACrE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC;IAC9D,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;QACpB,QAAQ,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;QACvB,0EAA0E;QAC1E,0EAA0E;QAC1E,wEAAwE;QACxE,uEAAuE;QACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QAC5C,IAAI,MAAM,KAAK,SAAS;YAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;IACD,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC"}
@@ -5,6 +5,8 @@ export declare class IcuFormat {
5
5
  readonly type: "i18nFormat";
6
6
  constructor(opts?: {
7
7
  onError?: (key: string, error: unknown) => void;
8
+ /** Override the i18next tag → Intl tag mapping (tests, embedding). */
9
+ resolveIntlTag?: (lng: string) => string;
8
10
  });
9
11
  /** i18next module hook (called by i18next during init; extra args ignored). */
10
12
  init(): void;
@@ -12,6 +14,8 @@ export declare class IcuFormat {
12
14
  parse(res: string, options: FormatValues, lng: string, _ns: string, key: string): string;
13
15
  /** i18next asks the format plugin whether to add ordinal/plural key suffixes — ICU handles plurals itself. */
14
16
  getSuffixes(): string[];
17
+ /** Drop compiled messages — call after a bundle swap so edits take effect. */
18
+ clearCache(): void;
15
19
  }
16
20
  export {};
17
21
  //# sourceMappingURL=icu-format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"icu-format.d.ts","sourceRoot":"","sources":["../src/icu-format.ts"],"names":[],"mappings":"AAYA,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;AAExD,qBAAa,SAAS;;IACpB,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB;IACpC,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;gBAK1B,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;KAAO;IAS1E,+EAA+E;IAC/E,IAAI,IAAI,IAAI;IAIZ,qGAAqG;IACrG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAgBxF,8GAA8G;IAC9G,WAAW,IAAI,MAAM,EAAE;CAGxB"}
1
+ {"version":3,"file":"icu-format.d.ts","sourceRoot":"","sources":["../src/icu-format.ts"],"names":[],"mappings":"AAeA,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;AAExD,qBAAa,SAAS;;IACpB,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB;IACpC,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;gBASpC,IAAI,GAAE;QACJ,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;QAChD,sEAAsE;QACtE,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;KACrC;IAoBR,+EAA+E;IAC/E,IAAI,IAAI,IAAI;IAIZ,qGAAqG;IACrG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAyBxF,8GAA8G;IAC9G,WAAW,IAAI,MAAM,EAAE;IAIvB,8EAA8E;IAC9E,UAAU,IAAI,IAAI;CAGnB"}
@@ -9,18 +9,33 @@
9
9
  * removes the interop landmine and gives us explicit error surfacing.
10
10
  */
11
11
  import { IntlMessageFormat } from 'intl-messageformat';
12
+ import { recordFormatFailure } from './format-errors.js';
13
+ import { intlTagForLocale, localeFromTag } from './locales.js';
12
14
  export class IcuFormat {
13
15
  static type = 'i18nFormat';
14
16
  type = 'i18nFormat';
15
17
  #cache = new Map();
16
18
  #onError;
19
+ #resolveIntlTag;
20
+ /** Language of the most recent parse — the error hook has no other source. */
21
+ #lastLng = 'en-US';
17
22
  constructor(opts = {}) {
18
23
  this.#onError =
19
24
  opts.onError ??
20
25
  ((key, error) => {
21
- // One structured warning per failure never throw at render time.
26
+ // Never throw at render time. The console warning is for developers;
27
+ // the ring is what the Translations editor reads, because the person
28
+ // who broke an admin-authored message is not looking at devtools
29
+ // (23 §4.5).
22
30
  console.warn(`[adminium/i18n] ICU parse failed for "${key}":`, error);
31
+ recordFormatFailure({
32
+ key,
33
+ lng: this.#lastLng,
34
+ message: error instanceof Error ? error.message : String(error),
35
+ at: Date.now(),
36
+ });
23
37
  });
38
+ this.#resolveIntlTag = opts.resolveIntlTag ?? defaultIntlTag;
24
39
  }
25
40
  /** i18next module hook (called by i18next during init; extra args ignored). */
26
41
  init() {
@@ -31,10 +46,19 @@ export class IcuFormat {
31
46
  if (typeof res !== 'string' || !res.includes('{'))
32
47
  return res;
33
48
  try {
34
- const cacheKey = `${lng}\x00${key}\x00${res}`;
49
+ // The tag handed to ICU is the locale's INTL tag, not i18next's active
50
+ // language (23 §4.5). For the compiled eight they are the same string;
51
+ // for an admin-created locale they are not, and using `lng` there means
52
+ // ICU has no plural data for the tag and silently resolves every
53
+ // message to `other` — with nothing thrown and nothing logged. The
54
+ // borrow tag is also part of the cache key, so re-pointing a locale's
55
+ // `intlTag` invalidates its compiled messages.
56
+ this.#lastLng = lng;
57
+ const intlTag = this.#resolveIntlTag(lng);
58
+ const cacheKey = `${intlTag}\x00${key}\x00${res}`;
35
59
  let message = this.#cache.get(cacheKey);
36
60
  if (message === undefined) {
37
- message = new IntlMessageFormat(res, lng, undefined, { ignoreTag: true });
61
+ message = new IntlMessageFormat(res, intlTag, undefined, { ignoreTag: true });
38
62
  this.#cache.set(cacheKey, message);
39
63
  }
40
64
  return String(message.format(options));
@@ -48,5 +72,21 @@ export class IcuFormat {
48
72
  getSuffixes() {
49
73
  return [];
50
74
  }
75
+ /** Drop compiled messages — call after a bundle swap so edits take effect. */
76
+ clearCache() {
77
+ this.#cache.clear();
78
+ }
79
+ }
80
+ /**
81
+ * i18next language tag → the tag ICU should format under. Runs through the
82
+ * locale registry so a custom locale gets its admin-chosen borrow tag; falls
83
+ * back to the tag itself for anything the registry does not know.
84
+ */
85
+ function defaultIntlTag(lng) {
86
+ const id = localeFromTag(lng);
87
+ const intlTag = intlTagForLocale(id);
88
+ // `localeFromTag` coalesces unknown tags to en_US; don't let that silently
89
+ // re-map a tag ICU would have handled perfectly well on its own.
90
+ return id === 'en_US' && lng !== 'en-US' ? lng : intlTag;
51
91
  }
52
92
  //# sourceMappingURL=icu-format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"icu-format.js","sourceRoot":"","sources":["../src/icu-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAIvD,MAAM,OAAO,SAAS;IACpB,MAAM,CAAU,IAAI,GAAG,YAAY,CAAC;IAC3B,IAAI,GAAG,YAAqB,CAAC;IAEtC,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAC;IAC9C,QAAQ,CAAwC;IAEhD,YAAY,OAA4D,EAAE;QACxE,IAAI,CAAC,QAAQ;YACX,IAAI,CAAC,OAAO;gBACZ,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;oBACd,mEAAmE;oBACnE,OAAO,CAAC,IAAI,CAAC,yCAAyC,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;gBACxE,CAAC,CAAC,CAAC;IACP,CAAC;IAED,+EAA+E;IAC/E,IAAI;QACF,8BAA8B;IAChC,CAAC;IAED,qGAAqG;IACrG,KAAK,CAAC,GAAW,EAAE,OAAqB,EAAE,GAAW,EAAE,GAAW,EAAE,GAAW;QAC7E,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,GAAG,GAAG,OAAO,GAAG,OAAO,GAAG,EAAE,CAAC;YAC9C,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,OAAO,GAAG,IAAI,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC1E,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACrC,CAAC;YACD,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAsD,CAAC,CAAC,CAAC;QACxF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC1B,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;IAED,8GAA8G;IAC9G,WAAW;QACT,OAAO,EAAE,CAAC;IACZ,CAAC"}
1
+ {"version":3,"file":"icu-format.js","sourceRoot":"","sources":["../src/icu-format.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,13 +4,21 @@
4
4
  * React bindings live in the `@adminium/i18n/react` entry.
5
5
  */
6
6
  export declare const PACKAGE_NAME = "@adminium/i18n";
7
- export { LOCALES, LOCALE_IDS, dirForLocale, isLocaleId, isRtlLocale, localeEntry, localeFromTag, tagForLocale, } from './locales.js';
8
- export type { LocaleEntry, LocaleId } from './locales.js';
7
+ export { BUILTIN_LOCALE_IDS, LOCALES, LOCALE_ID_RE, LOCALE_IDS, allLocales, availableLocales, dirForLocale, intlTagForLocale, isBuiltinLocaleId, isLocaleId, isRtlLocale, localeEntry, localeFromTag, pluralCategoriesForLocale, resetRuntimeLocales, setRuntimeLocales, tagForLocale, tagFromLocaleId, } from './locales.js';
8
+ export type { BuiltinLocaleId, LocaleEntry, LocaleId, RuntimeLocaleEntry } from './locales.js';
9
9
  export { createI18n, switchLocale } from './create-i18n.js';
10
+ export { createI18nWithOverrides, mergeOverrides, overrideTag, rebuildWithOverrides, } from './overrides.js';
11
+ export type { CreateI18nWithOverridesOptions, OverrideMap } from './overrides.js';
12
+ export { clearFormatFailures, formatFailures, recordFormatFailure, } from './format-errors.js';
13
+ export type { FormatFailure } from './format-errors.js';
10
14
  export type { BundleLoader, CreateI18nOptions, I18nInstance } from './create-i18n.js';
11
15
  export { EN_US_RESOURCES, NAMESPACES } from './resources/index.js';
12
16
  export type { Namespace, ResourceBundle } from './resources/index.js';
13
17
  export { loadLocaleBundle } from './resources/lazy.js';
18
+ export { formatFallback } from './fallback.js';
19
+ export { bumpI18nRevision, getI18nRevision, resetI18nRevision, subscribeI18nRevision, } from './revision.js';
20
+ export { REVIEW_STATUS, isReviewed, reviewedFraction } from './review-status.js';
21
+ export type { LocaleReview, ReviewCoverage } from './review-status.js';
14
22
  export { getFormatters, latnDataTag, weekInfo } from './format/index.js';
15
23
  export type { FmtContext, Formatters, WeekInfo } from './format/index.js';
16
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAE7C,OAAO,EACL,OAAO,EACP,UAAU,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,WAAW,EACX,aAAa,EACb,YAAY,GACb,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEtF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACnE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAE7C,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,YAAY,EACZ,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,WAAW,EACX,aAAa,EACb,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/F,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,WAAW,EACX,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,8BAA8B,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClF,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEtF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACnE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAWvD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjF,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEvE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -4,9 +4,22 @@
4
4
  * React bindings live in the `@adminium/i18n/react` entry.
5
5
  */
6
6
  export const PACKAGE_NAME = '@adminium/i18n';
7
- export { LOCALES, LOCALE_IDS, dirForLocale, isLocaleId, isRtlLocale, localeEntry, localeFromTag, tagForLocale, } from './locales.js';
7
+ export { BUILTIN_LOCALE_IDS, LOCALES, LOCALE_ID_RE, LOCALE_IDS, allLocales, availableLocales, dirForLocale, intlTagForLocale, isBuiltinLocaleId, isLocaleId, isRtlLocale, localeEntry, localeFromTag, pluralCategoriesForLocale, resetRuntimeLocales, setRuntimeLocales, tagForLocale, tagFromLocaleId, } from './locales.js';
8
8
  export { createI18n, switchLocale } from './create-i18n.js';
9
+ export { createI18nWithOverrides, mergeOverrides, overrideTag, rebuildWithOverrides, } from './overrides.js';
10
+ export { clearFormatFailures, formatFailures, recordFormatFailure, } from './format-errors.js';
9
11
  export { EN_US_RESOURCES, NAMESPACES } from './resources/index.js';
10
12
  export { loadLocaleBundle } from './resources/lazy.js';
13
+ // NOTE: the key index (`./keys.js`), the write validator
14
+ // (`./validate-message.js`) and the generated a11y-critical key list
15
+ // (`./a11y-keys.js`) are deliberately NOT re-exported here. They are
16
+ // EDIT-TIME concerns — only the server's translation routes and the editor
17
+ // need them — and the 458-entry `Set` literal in particular is not something
18
+ // Rollup will drop from the dashboard's entry chunk just because nothing
19
+ // references it. Import them from `@adminium/i18n/editing` instead; the
20
+ // dashboard's entry budget (`check-entry-budget.mjs`) is why.
21
+ export { formatFallback } from './fallback.js';
22
+ export { bumpI18nRevision, getI18nRevision, resetI18nRevision, subscribeI18nRevision, } from './revision.js';
23
+ export { REVIEW_STATUS, isReviewed, reviewedFraction } from './review-status.js';
11
24
  export { getFormatters, latnDataTag, weekInfo } from './format/index.js';
12
25
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAE7C,OAAO,EACL,OAAO,EACP,UAAU,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,WAAW,EACX,aAAa,EACb,YAAY,GACb,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAE7C,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,YAAY,EACZ,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,WAAW,EACX,aAAa,EACb,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,WAAW,EACX,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,yDAAyD;AACzD,qEAAqE;AACrE,qEAAqE;AACrE,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,wEAAwE;AACxE,8DAA8D;AAE9D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGjF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/keys.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The flattened en-US key surface (23-runtime-translations.md §6.1).
3
+ *
4
+ * The Translations editor has to let an admin find one message among ~2,800,
5
+ * and the server has to answer "is this a real key?" on every write. Both run
6
+ * against THIS index, in process, so the DB is only ever asked for an
7
+ * `(namespace, key) IN (…)` slice — never a portable `LIKE '%q%'` scan over a
8
+ * table that grows with every override.
9
+ *
10
+ * Built lazily and cached: a server that never opens the editor never pays
11
+ * for it.
12
+ */
13
+ import { type Namespace, type ResourceBundle } from './resources/index.js';
14
+ /** Nested bundle → flat `dot.path` → message. */
15
+ export declare function flattenBundle(bundle: ResourceBundle, prefix?: string, out?: Map<string, string>): Map<string, string>;
16
+ /** Flat map → nested bundle, the shape i18next's resource store wants. */
17
+ export declare function nestBundle(flat: ReadonlyMap<string, string>): ResourceBundle;
18
+ /** `namespace → key → en-US source text`, built once per process. */
19
+ export declare function sourceIndex(): ReadonlyMap<Namespace, ReadonlyMap<string, string>>;
20
+ /** The en-US source for a key, or `null` when the key does not exist. */
21
+ export declare function sourceMessage(namespace: string, key: string): string | null;
22
+ /** Total number of authored keys across every namespace. */
23
+ export declare function sourceKeyCount(): number;
24
+ /**
25
+ * The first segment of a key (`widgets.charts.foo` → `widgets`).
26
+ *
27
+ * The editor groups by this rather than by namespace, because the namespace
28
+ * axis is badly unbalanced in practice — `common` and `ui` hold effectively
29
+ * every key while `studio` and `generated` hold a handful each — so namespace
30
+ * is a poor primary navigation and a fine secondary filter.
31
+ */
32
+ export declare function keyGroup(key: string): string;
33
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAA+B,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAExG,iDAAiD;AACjD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,EACtB,MAAM,SAAK,EACX,GAAG,sBAA4B,GAC9B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAOrB;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,cAAc,CAc5E;AAID,qEAAqE;AACrE,wBAAgB,WAAW,IAAI,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAOjF;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3E;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,IAAI,MAAM,CAIvC;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5C"}
package/dist/keys.js ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The flattened en-US key surface (23-runtime-translations.md §6.1).
3
+ *
4
+ * The Translations editor has to let an admin find one message among ~2,800,
5
+ * and the server has to answer "is this a real key?" on every write. Both run
6
+ * against THIS index, in process, so the DB is only ever asked for an
7
+ * `(namespace, key) IN (…)` slice — never a portable `LIKE '%q%'` scan over a
8
+ * table that grows with every override.
9
+ *
10
+ * Built lazily and cached: a server that never opens the editor never pays
11
+ * for it.
12
+ */
13
+ import { EN_US_RESOURCES, NAMESPACES } from './resources/index.js';
14
+ /** Nested bundle → flat `dot.path` → message. */
15
+ export function flattenBundle(bundle, prefix = '', out = new Map()) {
16
+ for (const [key, value] of Object.entries(bundle)) {
17
+ const path = prefix === '' ? key : `${prefix}.${key}`;
18
+ if (typeof value === 'string')
19
+ out.set(path, value);
20
+ else
21
+ flattenBundle(value, path, out);
22
+ }
23
+ return out;
24
+ }
25
+ /** Flat map → nested bundle, the shape i18next's resource store wants. */
26
+ export function nestBundle(flat) {
27
+ const root = {};
28
+ for (const [path, value] of flat) {
29
+ const parts = path.split('.');
30
+ let node = root;
31
+ for (let i = 0; i < parts.length - 1; i += 1) {
32
+ const part = parts[i];
33
+ const next = node[part];
34
+ if (typeof next !== 'object' || next === null)
35
+ node[part] = {};
36
+ node = node[part];
37
+ }
38
+ node[parts[parts.length - 1]] = value;
39
+ }
40
+ return root;
41
+ }
42
+ let index = null;
43
+ /** `namespace → key → en-US source text`, built once per process. */
44
+ export function sourceIndex() {
45
+ if (index === null) {
46
+ const built = new Map();
47
+ for (const ns of NAMESPACES)
48
+ built.set(ns, flattenBundle(EN_US_RESOURCES[ns]));
49
+ index = built;
50
+ }
51
+ return index;
52
+ }
53
+ /** The en-US source for a key, or `null` when the key does not exist. */
54
+ export function sourceMessage(namespace, key) {
55
+ const ns = sourceIndex().get(namespace);
56
+ return ns?.get(key) ?? null;
57
+ }
58
+ /** Total number of authored keys across every namespace. */
59
+ export function sourceKeyCount() {
60
+ let total = 0;
61
+ for (const ns of sourceIndex().values())
62
+ total += ns.size;
63
+ return total;
64
+ }
65
+ /**
66
+ * The first segment of a key (`widgets.charts.foo` → `widgets`).
67
+ *
68
+ * The editor groups by this rather than by namespace, because the namespace
69
+ * axis is badly unbalanced in practice — `common` and `ui` hold effectively
70
+ * every key while `studio` and `generated` hold a handful each — so namespace
71
+ * is a poor primary navigation and a fine secondary filter.
72
+ */
73
+ export function keyGroup(key) {
74
+ const dot = key.indexOf('.');
75
+ return dot === -1 ? key : key.slice(0, dot);
76
+ }
77
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"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"}