@object-ui/i18n 17.3.0 → 17.5.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 (41) hide show
  1. package/CHANGELOG.md +1295 -1
  2. package/README.md +0 -13
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.js +11 -1
  5. package/dist/locales/ar.d.ts +466 -107
  6. package/dist/locales/ar.js +559 -153
  7. package/dist/locales/de.d.ts +466 -107
  8. package/dist/locales/de.js +574 -175
  9. package/dist/locales/en.d.ts +502 -143
  10. package/dist/locales/en.js +678 -145
  11. package/dist/locales/es.d.ts +466 -107
  12. package/dist/locales/es.js +553 -155
  13. package/dist/locales/fr.d.ts +466 -107
  14. package/dist/locales/fr.js +553 -152
  15. package/dist/locales/index.d.ts +4734 -1144
  16. package/dist/locales/ja.d.ts +466 -107
  17. package/dist/locales/ja.js +545 -146
  18. package/dist/locales/ko.d.ts +466 -107
  19. package/dist/locales/ko.js +551 -153
  20. package/dist/locales/pt.d.ts +466 -107
  21. package/dist/locales/pt.js +552 -154
  22. package/dist/locales/ru.d.ts +466 -107
  23. package/dist/locales/ru.js +563 -153
  24. package/dist/locales/zh.d.ts +501 -142
  25. package/dist/locales/zh.js +542 -142
  26. package/dist/pickLocalized.d.ts +21 -0
  27. package/dist/pickLocalized.js +40 -7
  28. package/dist/provider.d.ts +83 -1
  29. package/dist/provider.js +303 -29
  30. package/dist/useDisplayLocale.d.ts +49 -0
  31. package/dist/useDisplayLocale.js +55 -0
  32. package/dist/useObjectLabel.d.ts +8 -2
  33. package/dist/useObjectLabel.js +28 -8
  34. package/dist/useSafeTranslation.js +50 -2
  35. package/dist/utils/index.d.ts +1 -0
  36. package/dist/utils/index.js +1 -0
  37. package/dist/utils/number-display.d.ts +32 -0
  38. package/dist/utils/number-display.js +32 -0
  39. package/dist/utils/spec-formatters.d.ts +39 -13
  40. package/dist/utils/spec-formatters.js +3 -3
  41. package/package.json +4 -3
@@ -0,0 +1,55 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ import { useLocalization } from './LocalizationContext';
9
+ import { useObjectTranslation } from './provider';
10
+ /**
11
+ * `useDisplayLocale` — the BCP-47 tag that field / cell / metric renderers
12
+ * format numbers and other `Intl` values with.
13
+ *
14
+ * This composes the two locale channels this repo already has; it does not add
15
+ * a third. The precedence, highest first:
16
+ *
17
+ * 1. **`useLocalization().locale`** — the tenant's resolved regional default
18
+ * (ADR-0053, served by `GET /api/v1/auth/me/localization`). An org that has
19
+ * explicitly configured a display locale means it, so it outranks the UI
20
+ * language. Frequently `undefined`: the endpoint is
21
+ * cosmetic and non-blocking, so an unconfigured or still-loading tenant
22
+ * simply has no answer here (the state objectui#4033 was measured in — a
23
+ * fresh database, console running `zh-CN`).
24
+ *
25
+ * 2. **`useObjectTranslation().language`** — the ACTIVE UI language, i.e. what
26
+ * the user picked in the language switcher. This is the channel that makes
27
+ * grouping and decimal marks follow the user's language when the tenant has
28
+ * stated no regional preference.
29
+ *
30
+ * 3. **`'en'`** — a concrete last resort rather than `undefined`. `undefined`
31
+ * would hand `Intl` the *machine's* locale, which is invisible in review and
32
+ * non-deterministic in CI; `'en'` at least fails the same way everywhere.
33
+ * Note this is `'en'`, not `'en-US'`: the point of objectui#4033 is that
34
+ * `en-US` is not the world's default. In practice step 2 already answers,
35
+ * since `useObjectTranslation` falls back to the react-i18next global
36
+ * instance and ultimately reports `'en'` itself.
37
+ *
38
+ * Provider-safe at every step: `useLocalization` returns `{}` outside its
39
+ * provider and `useObjectTranslation` reads an optional context, so a renderer
40
+ * mounted standalone (tests, docs demos) degrades instead of throwing.
41
+ *
42
+ * **This composition is also the whole `'zh'` ↔ BCP-47 story** (objectui#4468).
43
+ * There is no mapping table anywhere and none is needed: the UI language codes
44
+ * this renderer ships (`'zh'`, `'ja'`, `'de'`, …) are already well-formed
45
+ * BCP-47 language subtags, so `Intl` accepts them verbatim. The one thing a
46
+ * caller must not do is reach past this hook for the raw tenant locale and hand
47
+ * `Intl` the `undefined` it gets on an unconfigured workspace — `undefined`
48
+ * means "the MACHINE's locale", which is neither channel. Every date, number
49
+ * and currency renderer goes through here for exactly that reason.
50
+ */
51
+ export function useDisplayLocale() {
52
+ const { locale } = useLocalization();
53
+ const { language } = useObjectTranslation();
54
+ return locale || language || 'en';
55
+ }
@@ -151,7 +151,10 @@ export declare function useObjectLabel(): {
151
151
  }) => string;
152
152
  /**
153
153
  * Resolve translated list-view label.
154
- * Convention (per @objectstack/spec): `{ns}.objects.{objectName}._views.{viewName}.label`.
154
+ * Convention (per @objectstack/spec): `{ns}.objects.{objectName}._views.{viewName}.label`,
155
+ * where `{viewName}` is the **bare** view name — pass either the bare name or
156
+ * the qualified runtime id, the object prefix is stripped either way. A key
157
+ * that spells the prefix out does not resolve (see `viewSuffixes`).
155
158
  */
156
159
  viewLabel: (objectName: string, viewName: string, fallback: string) => string;
157
160
  /**
@@ -371,7 +374,10 @@ export declare function useSafeFieldLabel(): {
371
374
  }) => string;
372
375
  /**
373
376
  * Resolve translated list-view label.
374
- * Convention (per @objectstack/spec): `{ns}.objects.{objectName}._views.{viewName}.label`.
377
+ * Convention (per @objectstack/spec): `{ns}.objects.{objectName}._views.{viewName}.label`,
378
+ * where `{viewName}` is the **bare** view name — pass either the bare name or
379
+ * the qualified runtime id, the object prefix is stripped either way. A key
380
+ * that spells the prefix out does not resolve (see `viewSuffixes`).
375
381
  */
376
382
  viewLabel: (objectName: string, viewName: string, fallback: string) => string;
377
383
  /**
@@ -188,19 +188,36 @@ export function useObjectLabel() {
188
188
  /**
189
189
  * Build suffix candidates for a list-view scoped key.
190
190
  *
191
- * The runtime uses qualified view ids (`<objectName>.<viewName>`) in URLs
192
- * and metadata records, while translation bundles are keyed by the authored
193
- * view name under `_views`. Resolve the unqualified name first so every
194
- * surface can pass its canonical id without leaking the metadata fallback.
191
+ * The runtime uses qualified view ids (`<objectName>.<viewName>`) in URLs and
192
+ * metadata records, while translation bundles are keyed by the **bare** view
193
+ * name under `_views` — the runtime view identity's own name, stripped of the
194
+ * object prefix. That single spelling is canonical per the objectstack#5164
195
+ * ruling A (2026-08-06): the i18n extractor asks the view composer for the key
196
+ * (objectstack#6124) and `packages/lint` enforces exactly that spelling
197
+ * (objectstack#6038), so this resolver accepts exactly what those produce.
198
+ *
199
+ * Deliberately NOT a second candidate: the prefixed full name
200
+ * (`_views.<objectName>.<viewName>`). Accepting it made this client more
201
+ * lenient than the server-side resolver, which only ever reads the one key
202
+ * (objectstack#5165) — a bundle authored against the prefixed spelling showed
203
+ * translated labels in the Console while every consumer that does not run a
204
+ * second resolution pass (REST boundary, mobile, plain HTTP, SDUI) still got
205
+ * English. A prefixed key now simply misses, and the label falls back to the
206
+ * metadata default on every surface alike, so the authoring mistake is visible
207
+ * instead of half-hidden. It is caught at authoring time by `os lint`'s
208
+ * `translation-target-unknown`, not papered over here.
209
+ *
210
+ * The object-name candidates (`objects.<ns__obj>` then `objects.<obj>`) are a
211
+ * separate axis and stay: they let bundles written against short object names
212
+ * resolve when the runtime presents fully-qualified ones.
195
213
  */
196
214
  const viewSuffixes = (objectName, viewName, tail) => {
197
215
  const objectNames = [objectName, stripNamespace(objectName)];
198
216
  const matchedPrefix = objectNames
199
217
  .map((name) => `${name}.`)
200
218
  .find((prefix) => viewName.startsWith(prefix));
201
- const shortViewName = matchedPrefix ? viewName.slice(matchedPrefix.length) : viewName;
202
- const viewNames = shortViewName === viewName ? [viewName] : [shortViewName, viewName];
203
- return viewNames.flatMap((name) => objectSuffixes(objectName, `_views.${name}.${tail}`));
219
+ const bareViewName = matchedPrefix ? viewName.slice(matchedPrefix.length) : viewName;
220
+ return objectSuffixes(objectName, `_views.${bareViewName}.${tail}`);
204
221
  };
205
222
  return {
206
223
  /**
@@ -309,7 +326,10 @@ export function useObjectLabel() {
309
326
  reportLabel: (reportDef) => resolve(reportSuffixes(reportDef.name, 'label'), reportDef.label ?? reportDef.name),
310
327
  /**
311
328
  * Resolve translated list-view label.
312
- * Convention (per @objectstack/spec): `{ns}.objects.{objectName}._views.{viewName}.label`.
329
+ * Convention (per @objectstack/spec): `{ns}.objects.{objectName}._views.{viewName}.label`,
330
+ * where `{viewName}` is the **bare** view name — pass either the bare name or
331
+ * the qualified runtime id, the object prefix is stripped either way. A key
332
+ * that spells the prefix out does not resolve (see `viewSuffixes`).
313
333
  */
314
334
  viewLabel: (objectName, viewName, fallback) => resolve(viewSuffixes(objectName, viewName, 'label'), fallback),
315
335
  /**
@@ -3,20 +3,68 @@
3
3
  *
4
4
  * When no I18nProvider is available (e.g., in tests or standalone usage),
5
5
  * this hook falls back to the provided default translations instead of
6
- * returning raw i18n keys.
6
+ * returning raw i18n keys — and, for a key the `defaults` map does not carry,
7
+ * to the call site's own inline `t(key, { defaultValue })` (objectui#3865).
8
+ * The lookup order is i18next's, so a provider-less host and a provider-mounted
9
+ * one render the same string: `defaults[key]` (the pack value's stand-in here)
10
+ * -> `defaultValue` -> the key.
7
11
  *
8
12
  * @param defaults - Fallback English translations keyed by i18n key
9
13
  * @param testKey - A key to test if i18n is properly configured (must be in defaults)
10
14
  */
11
15
  import { useObjectTranslation } from './provider';
16
+ /**
17
+ * The i18next option that names the string to use when the lookup misses.
18
+ *
19
+ * It is a LOOKUP CONTROL, not interpolation data (objectui#3865): it chooses
20
+ * which string is rendered, so it must never also be spliced into a
21
+ * `{{defaultValue}}` hole in the string it chose — or in any other.
22
+ */
23
+ const DEFAULT_VALUE_OPTION = 'defaultValue';
12
24
  export function createSafeTranslation(defaults, testKey) {
13
25
  // Factory-level fallback: one stable reference per defaults map, so
14
26
  // downstream useMemo/useCallback deps don't invalidate every render in
15
27
  // the no-translations case.
16
28
  const fallbackT = (key, options) => {
17
- let value = defaults[key] || key;
29
+ // Lookup order: defaults table -> inline `defaultValue` -> the key itself
30
+ // (objectui#3865). The table is the pack value's stand-in on this path, so
31
+ // it takes the pack's position in i18next's own order — verified against a
32
+ // real i18next 26.3.6 instance configured the way `createI18n` configures
33
+ // it: `t('anchor', { defaultValue: 'INLINE' })` is the pack's `'Anchor
34
+ // value'`, `t('missing', { defaultValue: 'INLINE' })` is `'INLINE'`, and
35
+ // `t('missing')` is `'missing'`.
36
+ //
37
+ // Before this, the inline default was DEAD on both paths for every
38
+ // component behind this factory: the provider path never reaches it
39
+ // (the pack value always wins), and this path did not read it at all —
40
+ // it rendered the raw key and then treated `defaultValue` as one more
41
+ // interpolation variable. A census over all 26 `createSafeTranslation`
42
+ // hooks measured 27 keys whose table lacks them, 21 of those carrying an
43
+ // inline default that used to be dropped (objectui#3865; the fact is
44
+ // recorded on objectui#3810, whose option C rests on it).
45
+ //
46
+ // Non-strings are ignored rather than coerced — this function returns a
47
+ // `string`, and i18next itself declines a `null` default. `||` (not `??`)
48
+ // keeps the whole chain consistent with the pre-existing `defaults[key] ||`
49
+ // step: an empty string at any position falls through to the next.
50
+ const inlineDefault = options?.[DEFAULT_VALUE_OPTION];
51
+ let value = defaults[key] || (typeof inlineDefault === 'string' ? inlineDefault : '') || key;
18
52
  if (options) {
19
53
  for (const [k, v] of Object.entries(options)) {
54
+ // Reserved: see DEFAULT_VALUE_OPTION. Skipped whatever its type, so an
55
+ // ignored non-string default cannot re-enter through this loop.
56
+ //
57
+ // This is the one deliberate divergence from i18next on this path, and
58
+ // it is unreachable with today's strings: i18next passes the whole
59
+ // options object to its interpolator, so it WOULD render
60
+ // `'Fallback: {{defaultValue}}'` as `'Fallback: INLINE'` (measured on
61
+ // 26.3.6) — but no value in this repo spells that hole (zero hits for
62
+ // `{{defaultValue` across packages/apps/examples), and splicing a
63
+ // fallback string into a hole named after itself has no sensible
64
+ // reading. The alternative — letting a call site's fallback text leak
65
+ // into an unrelated table value — is the worse of the two.
66
+ if (k === DEFAULT_VALUE_OPTION)
67
+ continue;
20
68
  // `split(needle).join(value)` — deliberately not `replace`, and not
21
69
  // `replaceAll` either (objectui#3418). This path must agree with
22
70
  // i18next, which serves the *provider* path; any divergence is a
@@ -1,3 +1,4 @@
1
1
  export { formatDate, formatDateTime, formatRelativeTime, formatCurrency, formatNumber, type DateFormatOptions, type CurrencyFormatOptions, type NumberFormatOptions, } from './formatting';
2
2
  export { resolvePlural, formatDateSpec, formatNumberSpec, applyLocaleConfig, type SpecPluralRule, type SpecDateFormat, type SpecNumberFormat, type SpecLocaleConfig, } from './spec-formatters';
3
3
  export { isSpecTranslationData, transformSpecTranslations, type SpecTranslationData, } from './spec-translations';
4
+ export { formatDisplayNumber, shouldGroupDisplayNumber, type DisplayNumberFormatOptions, } from './number-display';
@@ -1,3 +1,4 @@
1
1
  export { formatDate, formatDateTime, formatRelativeTime, formatCurrency, formatNumber, } from './formatting';
2
2
  export { resolvePlural, formatDateSpec, formatNumberSpec, applyLocaleConfig, } from './spec-formatters';
3
3
  export { isSpecTranslationData, transformSpecTranslations, } from './spec-translations';
4
+ export { formatDisplayNumber, shouldGroupDisplayNumber, } from './number-display';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * RE-EXPORT ONLY — the implementation moved to `@object-ui/core`
10
+ * (`utils/number-display.ts`) in objectui#4576. Read it there; nothing is
11
+ * redeclared here, so there is no second copy to drift.
12
+ *
13
+ * Why it moved: `@object-ui/core`'s `dataset-format.ts` needs this exact
14
+ * policy, and it could not import it while it lived here — `core` is the
15
+ * React-free engine and this package depends on `i18next`/`react-i18next` and
16
+ * peer-depends on React. So `formatMeasure` kept a parallel `Intl`
17
+ * implementation, and the two drifted apart in the one place `Intl` and a
18
+ * hand-built string disagree: a German session read `1.234,5 %` from a list
19
+ * cell beside `1.234,5%` from a dashboard measure (objectui#4576). The function
20
+ * is pure, so the boundary was never a property of the code — only of where the
21
+ * code sat. Moving it DOWN removed the obstacle; this package gained a
22
+ * dependency on `@object-ui/core` (no cycle: `core` imports nothing from here).
23
+ *
24
+ * This file survives as a re-export rather than being deleted so that BOTH
25
+ * historical import paths keep working byte-compatibly — the package entry
26
+ * (`@object-ui/i18n`, used by `fields`, `components` and `plugin-dashboard`)
27
+ * and the relative `../utils/number-display` this package's own suite uses.
28
+ * `number-display.reexport-identity.test.ts` pins that the two paths resolve to
29
+ * the SAME function object and the same type, so a future edit cannot quietly
30
+ * reintroduce a copy.
31
+ */
32
+ export { formatDisplayNumber, shouldGroupDisplayNumber, type DisplayNumberFormatOptions, } from '@object-ui/core';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * RE-EXPORT ONLY — the implementation moved to `@object-ui/core`
10
+ * (`utils/number-display.ts`) in objectui#4576. Read it there; nothing is
11
+ * redeclared here, so there is no second copy to drift.
12
+ *
13
+ * Why it moved: `@object-ui/core`'s `dataset-format.ts` needs this exact
14
+ * policy, and it could not import it while it lived here — `core` is the
15
+ * React-free engine and this package depends on `i18next`/`react-i18next` and
16
+ * peer-depends on React. So `formatMeasure` kept a parallel `Intl`
17
+ * implementation, and the two drifted apart in the one place `Intl` and a
18
+ * hand-built string disagree: a German session read `1.234,5 %` from a list
19
+ * cell beside `1.234,5%` from a dashboard measure (objectui#4576). The function
20
+ * is pure, so the boundary was never a property of the code — only of where the
21
+ * code sat. Moving it DOWN removed the obstacle; this package gained a
22
+ * dependency on `@object-ui/core` (no cycle: `core` imports nothing from here).
23
+ *
24
+ * This file survives as a re-export rather than being deleted so that BOTH
25
+ * historical import paths keep working byte-compatibly — the package entry
26
+ * (`@object-ui/i18n`, used by `fields`, `components` and `plugin-dashboard`)
27
+ * and the relative `../utils/number-display` this package's own suite uses.
28
+ * `number-display.reexport-identity.test.ts` pins that the two paths resolve to
29
+ * the SAME function object and the same type, so a future edit cannot quietly
30
+ * reintroduce a copy.
31
+ */
32
+ export { formatDisplayNumber, shouldGroupDisplayNumber, } from '@object-ui/core';
@@ -6,18 +6,33 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  /**
9
- * @object-ui/i18n - Spec-aligned i18n utilities
9
+ * @object-ui/i18n - locale formatting helpers
10
10
  *
11
- * Runtime consumers for @objectstack/spec v2.0.7 i18n types:
12
- * - PluralRuleSchema → resolvePlural()
13
- * - DateFormatSchema → formatDateSpec()
14
- * - NumberFormatSchema → formatNumberSpec()
15
- * - LocaleConfigSchema → applyLocaleConfig()
11
+ * ## Provenance of the four shapes below (objectui#4597)
12
+ *
13
+ * They were authored against `@objectstack/spec/ui`, which exported
14
+ * `PluralRuleSchema`, `DateFormatSchema`, `NumberFormatSchema` and
15
+ * `LocaleConfigSchema` up to and including 17.0.0-rc.5. The pinned
16
+ * 17.0.0-rc.6 removed all four under ADR-0049 enforce-or-remove
17
+ * (objectstack#5055, maintainer ruling 2026-08-06): they carried no key on any
18
+ * authorable shape and nothing ever parsed them, so the vocabulary was retired
19
+ * rather than given a door. The key sets here were faithful to the retired
20
+ * schemas — this is a retirement, not a drift.
21
+ *
22
+ * Nothing in the pinned protocol models locale formatting today, so the
23
+ * interfaces below are declarations this package owns outright, not a view onto
24
+ * a protocol type. The retirement note records that localisation returns as
25
+ * authorable metadata through a new ADR — "the formatter that reads a
26
+ * LocaleConfig first, the vocabulary second" — so re-derive these at that point
27
+ * instead of assuming they still track anything upstream.
16
28
  *
17
29
  * @module spec-formatters
18
30
  */
19
31
  /**
20
- * Spec-aligned PluralRule (mirrors @objectstack/spec PluralRuleSchema).
32
+ * Plural forms for a single translation key, in CLDR categories.
33
+ *
34
+ * Local shape — authored against the protocol's `PluralRuleSchema`, retired in
35
+ * 17.0.0-rc.6 (see the module doc), so there is nothing upstream to derive from.
21
36
  */
22
37
  export interface SpecPluralRule {
23
38
  /** Translation key */
@@ -54,7 +69,10 @@ export interface SpecPluralRule {
54
69
  */
55
70
  export declare function resolvePlural(rule: SpecPluralRule, count: number, locale?: string): string;
56
71
  /**
57
- * Spec-aligned DateFormat (mirrors @objectstack/spec DateFormatSchema).
72
+ * Date/time formatting options passed through to `Intl.DateTimeFormat`.
73
+ *
74
+ * Local shape — authored against the protocol's `DateFormatSchema`, retired in
75
+ * 17.0.0-rc.6 (see the module doc), so there is nothing upstream to derive from.
58
76
  */
59
77
  export interface SpecDateFormat {
60
78
  dateStyle?: 'full' | 'long' | 'medium' | 'short';
@@ -63,7 +81,7 @@ export interface SpecDateFormat {
63
81
  hour12?: boolean;
64
82
  }
65
83
  /**
66
- * Format a date using @objectstack/spec DateFormatSchema configuration.
84
+ * Format a date using a {@link SpecDateFormat} configuration.
67
85
  *
68
86
  * @example
69
87
  * ```ts
@@ -76,7 +94,11 @@ export interface SpecDateFormat {
76
94
  */
77
95
  export declare function formatDateSpec(date: Date | string | number, format: SpecDateFormat, locale?: string): string;
78
96
  /**
79
- * Spec-aligned NumberFormat (mirrors @objectstack/spec NumberFormatSchema).
97
+ * Number formatting options passed through to `Intl.NumberFormat`.
98
+ *
99
+ * Local shape — authored against the protocol's `NumberFormatSchema`, retired
100
+ * in 17.0.0-rc.6 (see the module doc), so there is nothing upstream to derive
101
+ * from.
80
102
  */
81
103
  export interface SpecNumberFormat {
82
104
  style?: 'currency' | 'percent' | 'decimal' | 'unit';
@@ -87,7 +109,7 @@ export interface SpecNumberFormat {
87
109
  useGrouping?: boolean;
88
110
  }
89
111
  /**
90
- * Format a number using @objectstack/spec NumberFormatSchema configuration.
112
+ * Format a number using a {@link SpecNumberFormat} configuration.
91
113
  *
92
114
  * @example
93
115
  * ```ts
@@ -100,7 +122,11 @@ export interface SpecNumberFormat {
100
122
  */
101
123
  export declare function formatNumberSpec(value: number, format: SpecNumberFormat, locale?: string): string;
102
124
  /**
103
- * Spec-aligned LocaleConfig (mirrors @objectstack/spec LocaleConfigSchema).
125
+ * One locale's formatting defaults, resolved by `applyLocaleConfig()`.
126
+ *
127
+ * Local shape — authored against the protocol's `LocaleConfigSchema`, retired
128
+ * in 17.0.0-rc.6 (see the module doc), so there is nothing upstream to derive
129
+ * from.
104
130
  */
105
131
  export interface SpecLocaleConfig {
106
132
  /** BCP 47 language code (e.g., 'en-US', 'zh-CN') */
@@ -115,7 +141,7 @@ export interface SpecLocaleConfig {
115
141
  dateFormat?: SpecDateFormat;
116
142
  }
117
143
  /**
118
- * Apply a LocaleConfigSchema to configure i18n formatting defaults.
144
+ * Apply a {@link SpecLocaleConfig} to configure i18n formatting defaults.
119
145
  * Returns resolved formatting functions bound to the locale config.
120
146
  *
121
147
  * @example
@@ -36,7 +36,7 @@ export function resolvePlural(rule, count, locale = 'en') {
36
36
  return template.replace(/\{count\}/g, String(count));
37
37
  }
38
38
  /**
39
- * Format a date using @objectstack/spec DateFormatSchema configuration.
39
+ * Format a date using a {@link SpecDateFormat} configuration.
40
40
  *
41
41
  * @example
42
42
  * ```ts
@@ -63,7 +63,7 @@ export function formatDateSpec(date, format, locale = 'en') {
63
63
  return new Intl.DateTimeFormat(locale, options).format(d);
64
64
  }
65
65
  /**
66
- * Format a number using @objectstack/spec NumberFormatSchema configuration.
66
+ * Format a number using a {@link SpecNumberFormat} configuration.
67
67
  *
68
68
  * @example
69
69
  * ```ts
@@ -91,7 +91,7 @@ export function formatNumberSpec(value, format, locale = 'en') {
91
91
  return new Intl.NumberFormat(locale, options).format(value);
92
92
  }
93
93
  /**
94
- * Apply a LocaleConfigSchema to configure i18n formatting defaults.
94
+ * Apply a {@link SpecLocaleConfig} to configure i18n formatting defaults.
95
95
  * Returns resolved formatting functions bound to the locale config.
96
96
  *
97
97
  * @example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/i18n",
3
- "version": "17.3.0",
3
+ "version": "17.5.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -31,7 +31,8 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "i18next": "^26.3.6",
34
- "react-i18next": "^17.0.11"
34
+ "react-i18next": "^17.0.11",
35
+ "@object-ui/core": "17.5.0"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "react": "^18.0.0 || ^19.0.0"
@@ -68,7 +69,7 @@
68
69
  "scripts": {
69
70
  "build": "tsc",
70
71
  "test": "vitest run",
71
- "type-check": "tsc --noEmit",
72
+ "type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
72
73
  "lint": "eslint ."
73
74
  }
74
75
  }