@magicvr/schema-ui-lib 0.1.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.
- package/i18n/catalog.d.ts +51 -0
- package/i18n/format.d.ts +17 -0
- package/i18n/locale.d.ts +51 -0
- package/i18n/money.d.ts +69 -0
- package/i18n/runtime.d.ts +90 -0
- package/i18n/timezone.d.ts +57 -0
- package/index.js +5765 -0
- package/lib/datetime.d.ts +14 -0
- package/lib/fetch-timeout.d.ts +13 -0
- package/lib/index.d.ts +12 -0
- package/lib/utils.d.ts +2 -0
- package/package.json +30 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translation catalog (S1 · C2/C3).
|
|
3
|
+
*
|
|
4
|
+
* Catalogs are pure data files under `messages/`; `en-US` is the canonical
|
|
5
|
+
* baseline. Resolution order for a key in locale L:
|
|
6
|
+
*
|
|
7
|
+
* catalog[L] → catalog[en-US] → observable missing-key event → key itself
|
|
8
|
+
*
|
|
9
|
+
* A key is "missing" only when neither the current catalog nor the en-US
|
|
10
|
+
* catalog has it; the en-US fallback is silent (designed behavior). Missing
|
|
11
|
+
* keys never render empty, never throw, and never block the flow; they are
|
|
12
|
+
* observable via the `schema-ui:missing-translation` window event (deduped
|
|
13
|
+
* per locale+key, so the first occurrence always reports).
|
|
14
|
+
*/
|
|
15
|
+
import { type Locale } from "./locale";
|
|
16
|
+
export type MessageParams = Record<string, string | number>;
|
|
17
|
+
export interface MissingTranslationDetail {
|
|
18
|
+
locale: Locale;
|
|
19
|
+
key: string;
|
|
20
|
+
/** Optional rendering context (e.g. "nav.sidebar", "page.users.form"). */
|
|
21
|
+
path?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const MISSING_TRANSLATION_EVENT = "schema-ui:missing-translation";
|
|
24
|
+
/** True when the key exists in the given locale catalog. */
|
|
25
|
+
export declare function hasTranslation(key: string, locale: Locale): boolean;
|
|
26
|
+
/** Raw catalog text for a key, or null when the locale catalog lacks it. */
|
|
27
|
+
export declare function lookupTranslation(key: string, locale: Locale): string | null;
|
|
28
|
+
/** Replaces `{name}` placeholders with params; unknown placeholders stay. */
|
|
29
|
+
export declare function interpolate(template: string, params?: MessageParams): string;
|
|
30
|
+
/** Publishes a deduped missing-key report to the window event bus. */
|
|
31
|
+
export declare function reportMissingTranslation(detail: MissingTranslationDetail): void;
|
|
32
|
+
/** Resets the missing-key dedupe set (test seam). */
|
|
33
|
+
export declare function resetMissingTranslationReports(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Resolves a message key for a locale with the frozen fallback chain.
|
|
36
|
+
* Never throws, never returns an empty string for a missing key.
|
|
37
|
+
*
|
|
38
|
+
* Fallback order: catalog[locale] → catalog[en-US] → `literalFallback`
|
|
39
|
+
* (protocol literal text, when supplied) → key itself.
|
|
40
|
+
*/
|
|
41
|
+
export declare function translate(key: string, params?: MessageParams, locale?: Locale, path?: string, literalFallback?: string): string;
|
|
42
|
+
/** Binds a locale (+ optional context path) to a translate function. */
|
|
43
|
+
export declare function createTranslator(locale: Locale, options?: {
|
|
44
|
+
path?: string;
|
|
45
|
+
}): (key: string, params?: MessageParams, literalFallback?: string) => string;
|
|
46
|
+
/**
|
|
47
|
+
* Resolves a schema/manifest text prop pair — the `*Key` field wins over the
|
|
48
|
+
* literal protocol text, and the literal text is the last fallback before the
|
|
49
|
+
* key itself (frozen chain: 当前语种 → en-US → 字面文本 → key).
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveTextProp(props: Record<string, unknown> | undefined, keyProp: string, literalProp: string, t: (key: string, params?: MessageParams, literalFallback?: string) => string, fallback?: string): string;
|
package/i18n/format.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale-aware date/number formatting (S1 · C5).
|
|
3
|
+
*
|
|
4
|
+
* Formatting follows the effective locale through Intl.* — no custom format
|
|
5
|
+
* templates (VP-007: "首版不暴露任意日期/数字格式模板,随有效 locale").
|
|
6
|
+
* Formatting is fail-safe: invalid inputs render empty, invalid timezones
|
|
7
|
+
* degrade to the locale's default zone instead of throwing.
|
|
8
|
+
*/
|
|
9
|
+
import { type Locale } from "./locale";
|
|
10
|
+
export interface FormatOptions {
|
|
11
|
+
/** IANA timezone name; omitted = the environment's default zone. */
|
|
12
|
+
timeZone?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Formats a date value in the given locale. Returns "" for invalid input. */
|
|
15
|
+
export declare function formatDate(value: Date | string | number, locale?: Locale, options?: FormatOptions): string;
|
|
16
|
+
/** Formats a finite number in the given locale. Returns "" for invalid input. */
|
|
17
|
+
export declare function formatNumber(value: number, locale?: Locale, options?: Intl.NumberFormatOptions): string;
|
package/i18n/locale.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale pure-logic unit (S1 · C1).
|
|
3
|
+
*
|
|
4
|
+
* `resolveLocale` is a side-effect-free function that computes the effective
|
|
5
|
+
* locale from the user's explicit choice, the system default, and the browser
|
|
6
|
+
* language preferences. Keeping the decision logic in a plain function lets
|
|
7
|
+
* vitest exercise every branch without a browser.
|
|
8
|
+
*
|
|
9
|
+
* Frozen priority (VP-007 / D-002 §I-L10N-002, user-confirmed 2026-08-09):
|
|
10
|
+
*
|
|
11
|
+
* user explicit choice → system default (non-auto) → browser preference
|
|
12
|
+
* (auto) → en-US safe fallback
|
|
13
|
+
*/
|
|
14
|
+
export declare const SUPPORTED_LOCALES: readonly ["zh-CN", "en-US"];
|
|
15
|
+
export type Locale = (typeof SUPPORTED_LOCALES)[number];
|
|
16
|
+
/** The user-facing choice; "auto" defers to system/browser preference. */
|
|
17
|
+
export type LocalePreference = Locale | "auto";
|
|
18
|
+
export declare const DEFAULT_LOCALE: Locale;
|
|
19
|
+
export interface LocaleResolutionInput {
|
|
20
|
+
/**
|
|
21
|
+
* localStorage["schema-ui:locale"] — the user's explicit choice.
|
|
22
|
+
* null / undefined / invalid → no explicit choice.
|
|
23
|
+
*/
|
|
24
|
+
stored: string | null;
|
|
25
|
+
/**
|
|
26
|
+
* System default from the public bootstrap (/api/branding defaultLocale).
|
|
27
|
+
* "auto" or null → no system default.
|
|
28
|
+
*/
|
|
29
|
+
systemDefault: string | null;
|
|
30
|
+
/** Browser language preferences in order (navigator.languages). */
|
|
31
|
+
browserLanguages: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
export declare function isSupportedLocale(raw: string | null | undefined): raw is Locale;
|
|
34
|
+
/**
|
|
35
|
+
* Normalizes a BCP 47-ish candidate to a supported locale.
|
|
36
|
+
* Accepts exact tags, case variants, underscore separators, and language-only
|
|
37
|
+
* prefixes ("zh", "zh-cn", "zh_CN", "en-US", "en", "en-us", "en-GB" → en-US).
|
|
38
|
+
* Returns null for anything else (including "auto").
|
|
39
|
+
*/
|
|
40
|
+
export declare function normalizeLocaleCandidate(raw: string | null | undefined): Locale | null;
|
|
41
|
+
/**
|
|
42
|
+
* Resolves the effective locale using the frozen priority. Pure — no I/O.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveLocale(input: LocaleResolutionInput): Locale;
|
|
45
|
+
/**
|
|
46
|
+
* Normalizes a raw stored value into a LocalePreference.
|
|
47
|
+
* Any value that is not a supported locale resolves to "auto".
|
|
48
|
+
*/
|
|
49
|
+
export declare function normalizePreference(raw: string | null | undefined): LocalePreference;
|
|
50
|
+
/** Real browser inputs for the boot path (provider default). */
|
|
51
|
+
export declare function defaultBrowserLanguages(): readonly string[];
|
package/i18n/money.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money / number pure-logic unit (workspace-020 · R3 · C1/C2/C3/C5).
|
|
3
|
+
*
|
|
4
|
+
* Contract GOAL-002 D-001 §3 / §4.3 (user-confirmed I-002, 2026-08-26):
|
|
5
|
+
* - Display + input semantics live on the frontend; API stays a machine
|
|
6
|
+
* contract (amounts = int64 JSON in the smallest currency unit).
|
|
7
|
+
* - No custom format templates: symbols / positions / fraction digits are
|
|
8
|
+
* derived from `Intl.NumberFormat` (locale + currency).
|
|
9
|
+
* - Embedded default currency map (§4.3): zh-CN → CNY, en-US → USD,
|
|
10
|
+
* unknown locale → USD. Missing configuration never throws.
|
|
11
|
+
* - Input parsing normalizes localized strings back to machine values
|
|
12
|
+
* (amount in minor units); unparsable input → null (callers show a
|
|
13
|
+
* localized input error and must NOT submit the raw string).
|
|
14
|
+
*/
|
|
15
|
+
import { type Locale } from "./locale";
|
|
16
|
+
/** Site/machine default currency (ISO 4217). */
|
|
17
|
+
export declare const DEFAULT_CURRENCY = "USD";
|
|
18
|
+
/** Default minor-unit exponent per currency group (ISO 4217 minor units). */
|
|
19
|
+
export declare const DEFAULT_MINOR_UNITS = 2;
|
|
20
|
+
/** Uppercase three-letter ISO 4217 code; null for anything else. */
|
|
21
|
+
export declare function normalizeCurrencyCode(raw: string | null | undefined): string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Embedded default currency for a locale (§4.3). Unknown locales fall back to
|
|
24
|
+
* USD; never throws.
|
|
25
|
+
*/
|
|
26
|
+
export declare function defaultCurrencyFor(locale: Locale | string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Effective default currency: the explicit site default (branding
|
|
29
|
+
* `defaultCurrency`, ISO 4217) wins when set; otherwise the embedded
|
|
30
|
+
* per-locale map (§4.3) applies. Never throws.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveEffectiveCurrency(locale: Locale, siteDefault: string | null | undefined): string;
|
|
33
|
+
/** Locale group/decimal separators derived from Intl (no hardcoded tables). */
|
|
34
|
+
export interface LocaleSeparators {
|
|
35
|
+
group: string;
|
|
36
|
+
decimal: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function localeSeparators(locale: Locale): LocaleSeparators;
|
|
39
|
+
export interface MoneyFormatOptions {
|
|
40
|
+
/** ISO 4217 currency code (uppercase 3 letters). Explicit override. */
|
|
41
|
+
currency?: string;
|
|
42
|
+
/** Site-wide default currency from /api/branding ("" = unset). */
|
|
43
|
+
siteDefaultCurrency?: string;
|
|
44
|
+
/** Minor-unit exponent. Defaults to 2 (CNY/USD). */
|
|
45
|
+
minorUnits?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Formats an amount given as machine value (minor units) into a
|
|
49
|
+
* locale+currency display string. Invalid input renders "" (fail-safe).
|
|
50
|
+
* R4 F-007: values beyond Number.MAX_SAFE_INTEGER render "" — the machine
|
|
51
|
+
* contract declares int64 minor units, which JS number cannot carry.
|
|
52
|
+
*/
|
|
53
|
+
export declare function formatMoney(minorValue: number, locale: Locale, options?: MoneyFormatOptions): string;
|
|
54
|
+
export interface MoneyParseOptions {
|
|
55
|
+
/** Expected ISO 4217 currency (affects symbol stripping only). */
|
|
56
|
+
currency?: string;
|
|
57
|
+
/** Site-wide default currency from /api/branding ("" = unset). */
|
|
58
|
+
siteDefaultCurrency?: string;
|
|
59
|
+
/** Minor-unit exponent for the returned integer. Defaults to 2. */
|
|
60
|
+
minorUnits?: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parses a localized money string into the machine value (minor-unit
|
|
64
|
+
* integer per contract §3.3). Returns null when the input is not a
|
|
65
|
+
* parseable amount — callers must NOT submit the raw string.
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseLocalizedMoney(raw: string, locale: Locale, options?: MoneyParseOptions): number | null;
|
|
68
|
+
/** Parses a localized plain number into a machine number; null on failure. */
|
|
69
|
+
export declare function parseLocalizedNumber(raw: string, locale: Locale): number | null;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* I18n React runtime (S1 · C4/C5).
|
|
3
|
+
*
|
|
4
|
+
* - Resolves the effective locale via `resolveLocale` (frozen priority).
|
|
5
|
+
* - Persists the user's explicit choice in localStorage["schema-ui:locale"]
|
|
6
|
+
* (single channel, same pattern as the theme mechanism; login/logout never
|
|
7
|
+
* clears it — D-002 §I-L10N-002).
|
|
8
|
+
* - Applies `document.documentElement.lang` on locale change.
|
|
9
|
+
* - Exposes `t` / `formatDate` / `formatNumber` to components.
|
|
10
|
+
*/
|
|
11
|
+
import { type ReactNode } from "react";
|
|
12
|
+
import { type MessageParams } from "./catalog";
|
|
13
|
+
import { type Locale, type LocalePreference } from "./locale";
|
|
14
|
+
import { type TimezonePreference } from "./timezone";
|
|
15
|
+
export declare const LOCALE_STORAGE_KEY = "schema-ui:locale";
|
|
16
|
+
export declare function readStoredLocale(): string | null;
|
|
17
|
+
export declare function writeStoredLocale(preference: LocalePreference): void;
|
|
18
|
+
/** Applies the effective locale to <html lang>. No-op outside a browser. */
|
|
19
|
+
export declare function applyLocaleToDocument(locale: Locale): void;
|
|
20
|
+
/** Returns the provider's currently effective locale (defaults en-US). */
|
|
21
|
+
export declare function getActiveLocale(): Locale;
|
|
22
|
+
/** Internal: keeps the registry in sync with the provider's effective locale. */
|
|
23
|
+
export declare function setActiveLocale(locale: Locale): void;
|
|
24
|
+
export interface I18nState {
|
|
25
|
+
/** Effective (resolved) locale — always a supported locale. */
|
|
26
|
+
locale: Locale;
|
|
27
|
+
/** User preference; "auto" defers to system/browser defaults. */
|
|
28
|
+
preference: LocalePreference;
|
|
29
|
+
/** Sets the user preference and persists it (localStorage single channel). */
|
|
30
|
+
setPreference: (preference: LocalePreference) => void;
|
|
31
|
+
/** Effective timezone (IANA name or "auto" per contract §2 / L1–L4). */
|
|
32
|
+
timezone: string;
|
|
33
|
+
/** User timezone preference; "auto" defers to session/site defaults. */
|
|
34
|
+
timezonePreference: TimezonePreference;
|
|
35
|
+
/** Sets the user timezone preference and persists it (single channel). */
|
|
36
|
+
setTimezonePreference: (preference: TimezonePreference) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Site-wide default currency (ISO 4217 from /api/branding; "" = unset).
|
|
39
|
+
* Money consumers use this as the explicit currency before falling back
|
|
40
|
+
* to the embedded per-locale map (contract §4.1 / §4.3).
|
|
41
|
+
*/
|
|
42
|
+
defaultCurrency: string;
|
|
43
|
+
/** Translate a catalog key with the effective locale. */
|
|
44
|
+
t: (key: string, params?: MessageParams) => string;
|
|
45
|
+
/** Locale-aware date formatting (defaults to the effective timezone). */
|
|
46
|
+
formatDate: (value: Date | string | number, options?: {
|
|
47
|
+
timeZone?: string;
|
|
48
|
+
}) => string;
|
|
49
|
+
/** Locale-aware number formatting. */
|
|
50
|
+
formatNumber: (value: number, options?: Intl.NumberFormatOptions) => string;
|
|
51
|
+
}
|
|
52
|
+
export interface I18nProviderProps {
|
|
53
|
+
children: ReactNode;
|
|
54
|
+
/** System default locale from the public bootstrap; null/"auto" = none. */
|
|
55
|
+
systemDefault?: string | null;
|
|
56
|
+
/** Test seam: explicit stored preference (defaults to localStorage). */
|
|
57
|
+
stored?: string | null;
|
|
58
|
+
/** Test seam: browser language list (defaults to navigator.languages). */
|
|
59
|
+
browserLanguages?: readonly string[];
|
|
60
|
+
/**
|
|
61
|
+
* Site default timezone from /api/branding (contract §2 · L3). The provider
|
|
62
|
+
* also captures it from `systemDefaultUrl` when present; this prop wins.
|
|
63
|
+
*/
|
|
64
|
+
siteTimezone?: string | null;
|
|
65
|
+
/** Test seam: explicit stored timezone preference (defaults to localStorage). */
|
|
66
|
+
storedTimezone?: string | null;
|
|
67
|
+
/** Test seam: session timezone probe (defaults to detectBrowserTimezone). */
|
|
68
|
+
detectTimezone?: () => string;
|
|
69
|
+
/** Site default currency (ISO 4217); test seam for /api/branding. */
|
|
70
|
+
siteDefaultCurrency?: string | null;
|
|
71
|
+
/**
|
|
72
|
+
* When set, the provider fetches this public startup endpoint once and
|
|
73
|
+
* re-resolves the system default locale from `defaultLocale` (VP-007 S3:
|
|
74
|
+
* the shell/login apply the site-wide default when the user has no
|
|
75
|
+
* explicit choice) and the site default timezone from `siteTimezone`
|
|
76
|
+
* (workspace-020 · contract §2 · L3). Applies after the initial resolve.
|
|
77
|
+
*/
|
|
78
|
+
systemDefaultUrl?: string;
|
|
79
|
+
}
|
|
80
|
+
export declare function I18nProvider({ children, systemDefault, stored, browserLanguages, siteTimezone, storedTimezone, detectTimezone, siteDefaultCurrency, systemDefaultUrl, }: I18nProviderProps): import("react").JSX.Element;
|
|
81
|
+
export declare function useI18n(): I18nState;
|
|
82
|
+
/**
|
|
83
|
+
* Tolerant translator hook for deep renderer internals.
|
|
84
|
+
*
|
|
85
|
+
* Returns the provider's translator, or a safe default (en-US resolution +
|
|
86
|
+
* missing-key observable fallback) when no provider is mounted. Production
|
|
87
|
+
* always mounts I18nProvider; bare component tests and pre-provider surfaces
|
|
88
|
+
* degrade to the documented safe fallback instead of throwing.
|
|
89
|
+
*/
|
|
90
|
+
export declare function useTranslate(): (key: string, params?: MessageParams) => string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timezone pure-logic unit (workspace-020 · R2 · C1).
|
|
3
|
+
*
|
|
4
|
+
* `resolveEffectiveTimezone` computes the effective timezone from the user's
|
|
5
|
+
* explicit override, the session probe, and the site default, per contract
|
|
6
|
+
* GOAL-002 D-001 §2 (user-confirmed I-001, 2026-08-26):
|
|
7
|
+
*
|
|
8
|
+
* L1 user override (localStorage "schema-ui:timezone") → L2 session probe
|
|
9
|
+
* (Intl) → L3 site default (siteTimezone) → L4 "auto" fallback
|
|
10
|
+
*
|
|
11
|
+
* Keeping the decision logic in plain functions lets vitest exercise every
|
|
12
|
+
* branch; the probe is injectable so tests do not depend on the host zone.
|
|
13
|
+
*/
|
|
14
|
+
export declare const TIMEZONE_STORAGE_KEY = "schema-ui:timezone";
|
|
15
|
+
export declare const AUTO_TIMEZONE = "auto";
|
|
16
|
+
/** The user-facing choice; a timezone IANA name or "auto". */
|
|
17
|
+
export type TimezonePreference = string | "auto";
|
|
18
|
+
export interface TimezoneResolutionInput {
|
|
19
|
+
/**
|
|
20
|
+
* localStorage["schema-ui:timezone"] — the user's explicit override.
|
|
21
|
+
* null / undefined / invalid / "auto" → no override (skip to L2).
|
|
22
|
+
*/
|
|
23
|
+
stored: string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Site default from the public bootstrap (/api/branding siteTimezone).
|
|
26
|
+
* "auto" / "" / null → unset (skip to L4 when L2 is empty).
|
|
27
|
+
*/
|
|
28
|
+
siteDefault: string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Session probe of the host zone (injectable; real default is
|
|
31
|
+
* `detectBrowserTimezone`). Invalid or empty results are skipped.
|
|
32
|
+
*/
|
|
33
|
+
detect: () => string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validates an IANA timezone name via Intl (RangeError on invalid names).
|
|
37
|
+
* Side-effect-free apart from the noexcept Intl probe; returns false for
|
|
38
|
+
* empty / non-string / unknown zones instead of throwing.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isValidIanaTimeZone(raw: string | null | undefined): raw is string;
|
|
41
|
+
/**
|
|
42
|
+
* Normalizes a raw stored/site value into a TimezonePreference.
|
|
43
|
+
* Any value that is not a valid IANA name resolves to "auto".
|
|
44
|
+
*/
|
|
45
|
+
export declare function normalizeTimezonePreference(raw: string | null | undefined): TimezonePreference;
|
|
46
|
+
/** Reads the stored user override; best-effort (privacy mode → null). */
|
|
47
|
+
export declare function readStoredTimezone(): string | null;
|
|
48
|
+
/** Persists the user override; "auto" removes the key (single channel). */
|
|
49
|
+
export declare function writeStoredTimezone(preference: TimezonePreference): void;
|
|
50
|
+
/** Real session probe: the host zone from Intl.resolvedOptions(). */
|
|
51
|
+
export declare function detectBrowserTimezone(): string;
|
|
52
|
+
/**
|
|
53
|
+
* Resolves the effective timezone per contract §2 (L1 → L2 → L3 → L4).
|
|
54
|
+
* Returns an IANA name, or "auto" when nothing is configured/detectable —
|
|
55
|
+
* consumers then fall back to the locale's default zone.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveEffectiveTimezone(input: TimezoneResolutionInput): string;
|