@vielzeug/lingua 1.1.4 → 2.0.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/README.md +15 -54
- package/dist/_catalog.cjs +1 -1
- package/dist/_catalog.cjs.map +1 -1
- package/dist/_catalog.d.ts +13 -16
- package/dist/_catalog.d.ts.map +1 -1
- package/dist/_catalog.js +43 -36
- package/dist/_catalog.js.map +1 -1
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +3 -11
- package/dist/_dev.js.map +1 -1
- package/dist/_locale.cjs +2 -0
- package/dist/_locale.cjs.map +1 -0
- package/dist/_locale.d.ts +5 -0
- package/dist/_locale.d.ts.map +1 -0
- package/dist/_locale.js +26 -0
- package/dist/_locale.js.map +1 -0
- package/dist/_resources.cjs +2 -0
- package/dist/_resources.cjs.map +1 -0
- package/dist/_resources.d.ts +10 -0
- package/dist/_resources.d.ts.map +1 -0
- package/dist/_resources.js +51 -0
- package/dist/_resources.js.map +1 -0
- package/dist/_template.cjs +2 -0
- package/dist/_template.cjs.map +1 -0
- package/dist/_template.d.ts +8 -0
- package/dist/_template.d.ts.map +1 -0
- package/dist/_template.js +32 -0
- package/dist/_template.js.map +1 -0
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +6 -18
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -9
- package/dist/errors.js.map +1 -1
- package/dist/format.cjs +1 -1
- package/dist/format.cjs.map +1 -1
- package/dist/format.d.ts +0 -9
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +10 -12
- package/dist/format.js.map +1 -1
- package/dist/i18n.cjs +1 -1
- package/dist/i18n.cjs.map +1 -1
- package/dist/i18n.d.ts +25 -11
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +66 -183
- package/dist/i18n.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +4 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/lingua.cjs +1 -1
- package/dist/lingua.cjs.map +1 -1
- package/dist/lingua.iife.js +1 -1
- package/dist/lingua.iife.js.map +1 -1
- package/dist/lingua.js +1 -1
- package/dist/lingua.js.map +1 -1
- package/dist/translator.cjs +2 -0
- package/dist/translator.cjs.map +1 -0
- package/dist/translator.d.ts +20 -0
- package/dist/translator.d.ts.map +1 -0
- package/dist/translator.js +61 -0
- package/dist/translator.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validate.cjs +1 -1
- package/dist/validate.cjs.map +1 -1
- package/dist/validate.d.ts +5 -37
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +21 -52
- package/dist/validate.js.map +1 -1
- package/package.json +4 -4
- package/dist/_catalog-store.cjs +0 -2
- package/dist/_catalog-store.cjs.map +0 -1
- package/dist/_catalog-store.d.ts +0 -34
- package/dist/_catalog-store.d.ts.map +0 -1
- package/dist/_catalog-store.js +0 -81
- package/dist/_catalog-store.js.map +0 -1
- package/dist/_chain.cjs +0 -2
- package/dist/_chain.cjs.map +0 -1
- package/dist/_chain.d.ts +0 -21
- package/dist/_chain.d.ts.map +0 -1
- package/dist/_chain.js +0 -45
- package/dist/_chain.js.map +0 -1
- package/dist/_constants.cjs +0 -2
- package/dist/_constants.cjs.map +0 -1
- package/dist/_constants.d.ts +0 -3
- package/dist/_constants.d.ts.map +0 -1
- package/dist/_constants.js +0 -17
- package/dist/_constants.js.map +0 -1
- package/dist/_namespace-store.cjs +0 -2
- package/dist/_namespace-store.cjs.map +0 -1
- package/dist/_namespace-store.d.ts +0 -31
- package/dist/_namespace-store.d.ts.map +0 -1
- package/dist/_namespace-store.js +0 -54
- package/dist/_namespace-store.js.map +0 -1
- package/dist/_translate.cjs +0 -2
- package/dist/_translate.cjs.map +0 -1
- package/dist/_translate.d.ts +0 -21
- package/dist/_translate.d.ts.map +0 -1
- package/dist/_translate.js +0 -54
- package/dist/_translate.js.map +0 -1
- package/dist/i18n-types.d.ts +0 -261
- package/dist/i18n-types.d.ts.map +0 -1
- package/dist/template-BZRFdmaQ.cjs +0 -2
- package/dist/template-BZRFdmaQ.cjs.map +0 -1
- package/dist/template-BihTfBYb.js +0 -2
- package/dist/template-BihTfBYb.js.map +0 -1
- package/dist/template.cjs +0 -2
- package/dist/template.cjs.map +0 -1
- package/dist/template.d.ts +0 -15
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -49
- package/dist/template.js.map +0 -1
- package/dist/validate-D1U8TTeT.js +0 -2
- package/dist/validate-D1U8TTeT.js.map +0 -1
- package/dist/validate-x7lFFyV2.cjs +0 -2
- package/dist/validate-x7lFFyV2.cjs.map +0 -1
package/dist/errors.d.ts
CHANGED
|
@@ -1,30 +1,18 @@
|
|
|
1
|
-
/** Base class for all lingua errors. Use `instanceof LinguaError` or `LinguaError.is()` to catch any lingua-originated error. */
|
|
2
1
|
export declare class LinguaError extends Error {
|
|
3
2
|
constructor(message: string, opts?: ErrorOptions);
|
|
4
|
-
static is(
|
|
3
|
+
static is(error: unknown): error is LinguaError;
|
|
5
4
|
}
|
|
6
|
-
/** Thrown when a method is called on a disposed i18n instance. */
|
|
7
5
|
export declare class LinguaDisposedError extends LinguaError {
|
|
8
6
|
constructor();
|
|
9
7
|
}
|
|
10
|
-
|
|
11
|
-
export declare class LinguaInvalidCountError extends LinguaError {
|
|
8
|
+
export declare class LinguaInvalidCatalogError extends LinguaError {
|
|
12
9
|
}
|
|
13
|
-
/** Thrown when `vars.count` is set alongside the `count` parameter in `tp()`. */
|
|
14
|
-
export declare class LinguaCountInVarsError extends LinguaError {
|
|
15
|
-
}
|
|
16
|
-
/** Thrown when a requested locale has not been registered. */
|
|
17
|
-
export declare class LinguaMissingLocaleError extends LinguaError {
|
|
18
|
-
}
|
|
19
|
-
/** Thrown when a locale string is not a valid BCP 47 tag. */
|
|
20
10
|
export declare class LinguaInvalidLocaleError extends LinguaError {
|
|
21
11
|
}
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
export declare class LinguaInvalidPluralCountError extends LinguaError {
|
|
13
|
+
}
|
|
14
|
+
export declare class LinguaInvalidStateError extends LinguaError {
|
|
24
15
|
}
|
|
25
|
-
|
|
26
|
-
export declare class LinguaRestoreError extends LinguaError {
|
|
16
|
+
export declare class LinguaMissingCatalogError extends LinguaError {
|
|
27
17
|
}
|
|
28
|
-
export declare function checkDisposed(disposed: boolean): void;
|
|
29
|
-
export declare function checkDisposedAsync(disposed: boolean): Promise<never> | null;
|
|
30
18
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;IAMhD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW;CAGhD;AAED,qBAAa,mBAAoB,SAAQ,WAAW;;CAInD;AAED,qBAAa,yBAA0B,SAAQ,WAAW;CAAG;AAC7D,qBAAa,wBAAyB,SAAQ,WAAW;CAAG;AAC5D,qBAAa,6BAA8B,SAAQ,WAAW;CAAG;AACjE,qBAAa,uBAAwB,SAAQ,WAAW;CAAG;AAC3D,qBAAa,yBAA0B,SAAQ,WAAW;CAAG"}
|
package/dist/errors.js
CHANGED
|
@@ -8,16 +8,10 @@ var e = class e extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
}, t = class extends e {
|
|
10
10
|
constructor() {
|
|
11
|
-
super("Operation called on a disposed
|
|
11
|
+
super("Operation called on a disposed translation store.");
|
|
12
12
|
}
|
|
13
|
-
}, n = class extends e {}, r = class extends e {}, i = class extends e {}, a = class extends e {}, o = class extends e {}
|
|
14
|
-
function c(e) {
|
|
15
|
-
if (e) throw new t();
|
|
16
|
-
}
|
|
17
|
-
function l(e) {
|
|
18
|
-
return e ? Promise.reject(new t()) : null;
|
|
19
|
-
}
|
|
13
|
+
}, n = class extends e {}, r = class extends e {}, i = class extends e {}, a = class extends e {}, o = class extends e {};
|
|
20
14
|
//#endregion
|
|
21
|
-
export {
|
|
15
|
+
export { t as LinguaDisposedError, e as LinguaError, n as LinguaInvalidCatalogError, r as LinguaInvalidLocaleError, i as LinguaInvalidPluralCountError, a as LinguaInvalidStateError, o as LinguaMissingCatalogError };
|
|
22
16
|
|
|
23
17
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export class LinguaError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(error: unknown): error is LinguaError {\n return error instanceof LinguaError;\n }\n}\n\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed translation store.');\n }\n}\n\nexport class LinguaInvalidCatalogError extends LinguaError {}\nexport class LinguaInvalidLocaleError extends LinguaError {}\nexport class LinguaInvalidPluralCountError extends LinguaError {}\nexport class LinguaInvalidStateError extends LinguaError {}\nexport class LinguaMissingCatalogError extends LinguaError {}\n"],"mappings":";AAAA,IAAa,IAAb,MAAa,UAAoB,MAAM;CACrC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;CAEA,OAAO,GAAG,GAAsC;EAC9C,OAAO,aAAiB;CAC1B;AACF,GAEa,IAAb,cAAyC,EAAY;CACnD,cAAc;EACZ,MAAM,mDAAmD;CAC3D;AACF,GAEa,IAAb,cAA+C,EAAY,CAAC,GAC/C,IAAb,cAA8C,EAAY,CAAC,GAC9C,IAAb,cAAmD,EAAY,CAAC,GACnD,IAAb,cAA6C,EAAY,CAAC,GAC7C,IAAb,cAA+C,EAAY,CAAC"}
|
package/dist/format.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_bounded-cache.cjs");var t=128,n=[`years`,`months`,`weeks`,`days`,`hours`,`minutes`,`seconds`,`milliseconds`,`microseconds`,`nanoseconds`],r={days:`d`,hours:`h`,microseconds:`us`,milliseconds:`ms`,minutes:`min`,months:`mo`,nanoseconds:`ns`,seconds:`s`,weeks:`w`,years:`y`};function i(e){return n.map(t=>{let n=e[t];return typeof n==`number`?`${n}${r[t]}`:void 0}).filter(e=>e!==void 0).join(` `)}function a(r){let a=new Map,o=new Map,s=new Map,c=new Map,l=new Map,u=new Map,d=typeof r==`string`?()=>r:r;function
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_bounded-cache.cjs");require("./_dev.cjs");var t=128,n=[`years`,`months`,`weeks`,`days`,`hours`,`minutes`,`seconds`,`milliseconds`,`microseconds`,`nanoseconds`],r={days:`d`,hours:`h`,microseconds:`us`,milliseconds:`ms`,minutes:`min`,months:`mo`,nanoseconds:`ns`,seconds:`s`,weeks:`w`,years:`y`};function i(e){return n.map(t=>{let n=e[t];return typeof n==`number`?`${n}${r[t]}`:void 0}).filter(e=>e!==void 0).join(` `)}function a(r){let a=new Map,o=new Map,s=new Map,c=new Map,l=new Map,u=new Map,d=typeof r==`string`?()=>r:r,f=!1;function p(e,t){if(!t)return e;let n={};for(let e of Object.keys(t).sort())n[e]=t[e];try{return`${e}:${JSON.stringify(n)}`}catch{return f||=!0,e}}return{currency(n,r,i){let a=d(),s={...i,currency:r,style:`currency`};return e.getOrCreate(o,p(a,s),t,()=>new Intl.NumberFormat(a,s)).format(n)},date(n,r){let i=d();return e.getOrCreate(s,p(i,r),t,()=>new Intl.DateTimeFormat(i,r)).format(n)},duration(r,a){let o=d(),s=Intl;return s.DurationFormat?n.every(e=>r[e]===void 0)?``:e.getOrCreate(u,p(o,a),t,()=>new s.DurationFormat(o,a)).format(r):i(r)},list(n,r){let i=d(),a=n.map(e=>String(e)),o=r?.style??`long`,s=r?.type===`or`?`disjunction`:`conjunction`;return e.getOrCreate(l,p(i,{style:o,type:s}),t,()=>new Intl.ListFormat(i,{style:o,type:s})).format(a)},number(n,r){let i=d();return e.getOrCreate(a,p(i,r),t,()=>new Intl.NumberFormat(i,r)).format(n)},relative(n,r,i){let a=d();return e.getOrCreate(c,p(a,i),t,()=>new Intl.RelativeTimeFormat(a,i)).format(n,r)}}}exports.createFormatter=a;
|
|
2
2
|
//# sourceMappingURL=format.cjs.map
|
package/dist/format.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.cjs","names":[],"sources":["../src/format.ts"],"sourcesContent":["/**\n * /lingua/format\n *\n * Standalone Intl formatter factory. Import from `/lingua/format`.\n * Creates formatters for numbers, dates, currencies, lists, durations, and relative time.\n *\n * Pass a static locale string or a reactive locale getter.\n *\n * @example\n * ```ts\n * import { createFormatter } from '@vielzeug/lingua/format';\n *\n * // Static locale\n * const fmt = createFormatter('en-US');\n *\n * // Reactive — always reads the current locale from the i18n instance\n * const fmt = createFormatter(() => i18n.locale);\n *\n * fmt.number(1_234.56);\n * fmt.currency(9.99, 'USD');\n * fmt.date(new Date());\n * fmt.relative(-1, 'day');\n * fmt.list(['A', 'B', 'C']);\n * fmt.duration({ hours: 1, minutes: 30 });\n * ```\n */\n\nimport { getOrCreate } from './_bounded-cache';\n\nconst FORMAT_CACHE_MAX = 128;\n\nexport type DurationValue = Partial<\n Record<\n | 'days'\n | 'hours'\n | 'microseconds'\n | 'milliseconds'\n | 'minutes'\n | 'months'\n | 'nanoseconds'\n | 'seconds'\n | 'weeks'\n | 'years',\n number\n >\n>;\n\nexport type DurationFormatOptions = {\n hours?: '2-digit' | 'numeric';\n microseconds?: 'numeric';\n milliseconds?: 'numeric';\n minutes?: '2-digit' | 'numeric';\n nanoseconds?: 'numeric';\n seconds?: '2-digit' | 'numeric';\n style?: 'digital' | 'long' | 'narrow' | 'short';\n};\n\nexport type ListFormatOptions = {\n style?: 'long' | 'narrow' | 'short';\n type?: 'and' | 'or';\n};\n\nexport type Formatter = {\n /**\n * Clears all cached `Intl` formatter instances. Useful after a locale change on a long-running\n * SSR server, or to reclaim memory.\n *\n * @note This clears the cache for **all callers sharing this formatter instance**. When called on\n * `i18n.fmt`, the cache is cleared for every part of the application that reads `i18n.fmt`.\n * `setLocale()` already calls `clear()` automatically — manual calls are rarely needed.\n */\n clear(): void;\n currency(value: number, currency: string, options?: Omit<Intl.NumberFormatOptions, 'currency' | 'style'>): string;\n date(value: Date | number, options?: Intl.DateTimeFormatOptions): string;\n /**\n * Formats a duration using `Intl.DurationFormat` when available, or a compact labeled fallback\n * (e.g. `'1h 30min'`). Returns `''` when every field of `value` is `undefined`.\n *\n * @note The fallback labels (`h`, `min`, `s`, `ms`, etc.) are English-only and are not\n * locale-aware. If `Intl.DurationFormat` is unavailable in your target environment and\n * you need localised duration output, provide your own formatter using the individual\n * duration fields from `DurationValue`.\n */\n duration(value: DurationValue, options?: DurationFormatOptions): string;\n list(value: Array<string | number>, options?: ListFormatOptions): string;\n /** Formats a number with the current locale. */\n number(value: number, options?: Intl.NumberFormatOptions): string;\n relative(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;\n};\n\n// ─── Internal ─────────────────────────────────────────────────────────────────\n\nconst DURATION_UNITS: Array<keyof DurationValue> = [\n 'years',\n 'months',\n 'weeks',\n 'days',\n 'hours',\n 'minutes',\n 'seconds',\n 'milliseconds',\n 'microseconds',\n 'nanoseconds',\n];\n\nconst DURATION_LABELS: Record<keyof DurationValue, string> = {\n days: 'd',\n hours: 'h',\n microseconds: 'us',\n milliseconds: 'ms',\n minutes: 'min',\n months: 'mo',\n nanoseconds: 'ns',\n seconds: 's',\n weeks: 'w',\n years: 'y',\n};\n\ntype DurationFormatterCtor = new (\n locale: string,\n options?: DurationFormatOptions,\n) => { format(value: DurationValue): string };\n\nfunction durationLabeled(value: DurationValue): string {\n return DURATION_UNITS.map((unit) => {\n const amount = value[unit];\n\n return typeof amount === 'number' ? `${amount}${DURATION_LABELS[unit]}` : undefined;\n })\n .filter((part): part is string => part !== undefined)\n .join(' ');\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\n/**\n * Creates a formatter bound to a locale. Pass a static locale string or a\n * reactive getter function `() => string` (e.g. `() => i18n.locale`).\n */\nexport function createFormatter(source: string | (() => string)): Formatter {\n const numberCache = new Map<string, Intl.NumberFormat>();\n const currencyCache = new Map<string, Intl.NumberFormat>();\n const dateCache = new Map<string, Intl.DateTimeFormat>();\n const relativeCache = new Map<string, Intl.RelativeTimeFormat>();\n const listCache = new Map<string, Intl.ListFormat>();\n const durationCache = new Map<string, { format(value: DurationValue): string }>();\n\n const getLocale = typeof source === 'string' ? () => source : source;\n\n function cachedKey(locale: string, options?: object): string {\n if (!options) return locale;\n\n const sorted: Record<string, unknown> = {};\n\n for (const key of Object.keys(options as Record<string, unknown>).sort()) {\n sorted[key] = (options as Record<string, unknown>)[key];\n }\n\n try {\n return `${locale}:${JSON.stringify(sorted)}`;\n } catch {\n // Circular references or BigInt values — fall back to locale-only key.\n // Multiple callers with different unserializable options will share the same formatter instance.\n return locale;\n }\n }\n\n return {\n clear() {\n numberCache.clear();\n currencyCache.clear();\n dateCache.clear();\n relativeCache.clear();\n listCache.clear();\n durationCache.clear();\n },\n\n currency(value, currency, options) {\n const locale = getLocale();\n const opts: Intl.NumberFormatOptions = { ...options, currency, style: 'currency' };\n\n return getOrCreate(\n currencyCache,\n cachedKey(locale, opts),\n FORMAT_CACHE_MAX,\n () => new Intl.NumberFormat(locale, opts),\n ).format(value);\n },\n\n date(value, options) {\n const locale = getLocale();\n\n return getOrCreate(\n dateCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.DateTimeFormat(locale, options),\n ).format(value);\n },\n\n duration(value, options) {\n const locale = getLocale();\n const IntlExt = Intl as typeof Intl & { DurationFormat?: DurationFormatterCtor };\n\n if (!IntlExt.DurationFormat) return durationLabeled(value);\n\n if (DURATION_UNITS.every((u) => value[u] === undefined)) return '';\n\n return getOrCreate(\n durationCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new IntlExt.DurationFormat!(locale, options),\n ).format(value);\n },\n\n list(value, options) {\n const locale = getLocale();\n const items = value.map((v) => String(v));\n // list() has exactly two string options with small finite value domains.\n // We normalize both to their resolved defaults and build the key directly\n // rather than using cachedKey(). This also prevents cache misses when\n // callers pass { style: 'long', type: 'and' } vs {} (same ListFormat).\n const style = options?.style ?? 'long';\n const type = options?.type === 'or' ? 'disjunction' : 'conjunction';\n\n return getOrCreate(\n listCache,\n cachedKey(locale, { style, type }),\n FORMAT_CACHE_MAX,\n () => new Intl.ListFormat(locale, { style, type }),\n ).format(items);\n },\n\n number(value, options) {\n const locale = getLocale();\n\n return getOrCreate(\n numberCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.NumberFormat(locale, options),\n ).format(value);\n },\n\n relative(value, unit, options) {\n const locale = getLocale();\n\n return getOrCreate(\n relativeCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.RelativeTimeFormat(locale, options),\n ).format(value, unit);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"format.cjs","names":[],"sources":["../src/format.ts"],"sourcesContent":["/**\n * /lingua/format\n *\n * Standalone Intl formatter factory. Import from `/lingua/format`.\n * Creates formatters for numbers, dates, currencies, lists, durations, and relative time.\n *\n * Pass a static locale string or a reactive locale getter.\n *\n * @example\n * ```ts\n * import { createFormatter } from '@vielzeug/lingua/format';\n *\n * // Static locale\n * const fmt = createFormatter('en-US');\n *\n * // Reactive — always reads the current locale from the i18n instance\n * const fmt = createFormatter(() => i18n.locale);\n *\n * fmt.number(1_234.56);\n * fmt.currency(9.99, 'USD');\n * fmt.date(new Date());\n * fmt.relative(-1, 'day');\n * fmt.list(['A', 'B', 'C']);\n * fmt.duration({ hours: 1, minutes: 30 });\n * ```\n */\n\nimport { getOrCreate } from './_bounded-cache';\nimport { warn } from './_dev';\n\nconst FORMAT_CACHE_MAX = 128;\n\nexport type DurationValue = Partial<\n Record<\n | 'days'\n | 'hours'\n | 'microseconds'\n | 'milliseconds'\n | 'minutes'\n | 'months'\n | 'nanoseconds'\n | 'seconds'\n | 'weeks'\n | 'years',\n number\n >\n>;\n\nexport type DurationFormatOptions = {\n hours?: '2-digit' | 'numeric';\n microseconds?: 'numeric';\n milliseconds?: 'numeric';\n minutes?: '2-digit' | 'numeric';\n nanoseconds?: 'numeric';\n seconds?: '2-digit' | 'numeric';\n style?: 'digital' | 'long' | 'narrow' | 'short';\n};\n\nexport type ListFormatOptions = {\n style?: 'long' | 'narrow' | 'short';\n type?: 'and' | 'or';\n};\n\nexport type Formatter = {\n currency(value: number, currency: string, options?: Omit<Intl.NumberFormatOptions, 'currency' | 'style'>): string;\n date(value: Date | number, options?: Intl.DateTimeFormatOptions): string;\n /**\n * Formats a duration using `Intl.DurationFormat` when available, or a compact labeled fallback\n * (e.g. `'1h 30min'`). Returns `''` when every field of `value` is `undefined`.\n *\n * @note The fallback labels (`h`, `min`, `s`, `ms`, etc.) are English-only and are not\n * locale-aware. If `Intl.DurationFormat` is unavailable in your target environment and\n * you need localised duration output, provide your own formatter using the individual\n * duration fields from `DurationValue`.\n */\n duration(value: DurationValue, options?: DurationFormatOptions): string;\n list(value: Array<string | number>, options?: ListFormatOptions): string;\n /** Formats a number with the current locale. */\n number(value: number, options?: Intl.NumberFormatOptions): string;\n relative(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;\n};\n\n// ─── Internal ─────────────────────────────────────────────────────────────────\n\nconst DURATION_UNITS: Array<keyof DurationValue> = [\n 'years',\n 'months',\n 'weeks',\n 'days',\n 'hours',\n 'minutes',\n 'seconds',\n 'milliseconds',\n 'microseconds',\n 'nanoseconds',\n];\n\nconst DURATION_LABELS: Record<keyof DurationValue, string> = {\n days: 'd',\n hours: 'h',\n microseconds: 'us',\n milliseconds: 'ms',\n minutes: 'min',\n months: 'mo',\n nanoseconds: 'ns',\n seconds: 's',\n weeks: 'w',\n years: 'y',\n};\n\ntype DurationFormatterCtor = new (\n locale: string,\n options?: DurationFormatOptions,\n) => { format(value: DurationValue): string };\n\nfunction durationLabeled(value: DurationValue): string {\n return DURATION_UNITS.map((unit) => {\n const amount = value[unit];\n\n return typeof amount === 'number' ? `${amount}${DURATION_LABELS[unit]}` : undefined;\n })\n .filter((part): part is string => part !== undefined)\n .join(' ');\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\n/**\n * Creates a formatter bound to a locale. Pass a static locale string or a\n * reactive getter function `() => string` (e.g. `() => i18n.locale`).\n */\nexport function createFormatter(source: string | (() => string)): Formatter {\n const numberCache = new Map<string, Intl.NumberFormat>();\n const currencyCache = new Map<string, Intl.NumberFormat>();\n const dateCache = new Map<string, Intl.DateTimeFormat>();\n const relativeCache = new Map<string, Intl.RelativeTimeFormat>();\n const listCache = new Map<string, Intl.ListFormat>();\n const durationCache = new Map<string, { format(value: DurationValue): string }>();\n\n const getLocale = typeof source === 'string' ? () => source : source;\n\n // Warn at most once per formatter instance on the unserializable-options fallback —\n // a hot render loop with such options must not spam the console on every call.\n let warnedUnserializable = false;\n\n function cachedKey(locale: string, options?: object): string {\n if (!options) return locale;\n\n const sorted: Record<string, unknown> = {};\n\n for (const key of Object.keys(options as Record<string, unknown>).sort()) {\n sorted[key] = (options as Record<string, unknown>)[key];\n }\n\n try {\n return `${locale}:${JSON.stringify(sorted)}`;\n } catch {\n // Circular references or BigInt values — fall back to locale-only key.\n // Multiple callers with different unserializable options will share the same\n // formatter instance; never silently.\n if (!warnedUnserializable) {\n warnedUnserializable = true;\n warn('formatter options could not be serialized — falling back to a shared per-locale formatter instance.');\n }\n\n return locale;\n }\n }\n\n return {\n currency(value, currency, options) {\n const locale = getLocale();\n const opts: Intl.NumberFormatOptions = { ...options, currency, style: 'currency' };\n\n return getOrCreate(\n currencyCache,\n cachedKey(locale, opts),\n FORMAT_CACHE_MAX,\n () => new Intl.NumberFormat(locale, opts),\n ).format(value);\n },\n\n date(value, options) {\n const locale = getLocale();\n\n return getOrCreate(\n dateCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.DateTimeFormat(locale, options),\n ).format(value);\n },\n\n duration(value, options) {\n const locale = getLocale();\n const IntlExt = Intl as typeof Intl & { DurationFormat?: DurationFormatterCtor };\n\n if (!IntlExt.DurationFormat) return durationLabeled(value);\n\n if (DURATION_UNITS.every((u) => value[u] === undefined)) return '';\n\n return getOrCreate(\n durationCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new IntlExt.DurationFormat!(locale, options),\n ).format(value);\n },\n\n list(value, options) {\n const locale = getLocale();\n const items = value.map((v) => String(v));\n // list() has exactly two string options with small finite value domains.\n // We normalize both to their resolved defaults and build the key directly\n // rather than using cachedKey(). This also prevents cache misses when\n // callers pass { style: 'long', type: 'and' } vs {} (same ListFormat).\n const style = options?.style ?? 'long';\n const type = options?.type === 'or' ? 'disjunction' : 'conjunction';\n\n return getOrCreate(\n listCache,\n cachedKey(locale, { style, type }),\n FORMAT_CACHE_MAX,\n () => new Intl.ListFormat(locale, { style, type }),\n ).format(items);\n },\n\n number(value, options) {\n const locale = getLocale();\n\n return getOrCreate(\n numberCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.NumberFormat(locale, options),\n ).format(value);\n },\n\n relative(value, unit, options) {\n const locale = getLocale();\n\n return getOrCreate(\n relativeCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.RelativeTimeFormat(locale, options),\n ).format(value, unit);\n },\n };\n}\n"],"mappings":"iIA8BA,IAAM,EAAmB,IAsDnB,EAA6C,CACjD,QACA,SACA,QACA,OACA,QACA,UACA,UACA,eACA,eACA,aACF,EAEM,EAAuD,CAC3D,KAAM,IACN,MAAO,IACP,aAAc,KACd,aAAc,KACd,QAAS,MACT,OAAQ,KACR,YAAa,KACb,QAAS,IACT,MAAO,IACP,MAAO,GACT,EAOA,SAAS,EAAgB,EAA8B,CACrD,OAAO,EAAe,IAAK,GAAS,CAClC,IAAM,EAAS,EAAM,GAErB,OAAO,OAAO,GAAW,SAAW,GAAG,IAAS,EAAgB,KAAU,IAAA,EAC5E,CAAC,CAAC,CACC,OAAQ,GAAyB,IAAS,IAAA,EAAS,CAAC,CACpD,KAAK,GAAG,CACb,CAQA,SAAgB,EAAgB,EAA4C,CAC1E,IAAM,EAAc,IAAI,IAClB,EAAgB,IAAI,IACpB,EAAY,IAAI,IAChB,EAAgB,IAAI,IACpB,EAAY,IAAI,IAChB,EAAgB,IAAI,IAEpB,EAAY,OAAO,GAAW,aAAiB,EAAS,EAI1D,EAAuB,GAE3B,SAAS,EAAU,EAAgB,EAA0B,CAC3D,GAAI,CAAC,EAAS,OAAO,EAErB,IAAM,EAAkC,CAAC,EAEzC,IAAK,IAAM,KAAO,OAAO,KAAK,CAAkC,CAAC,CAAC,KAAK,EACrE,EAAO,GAAQ,EAAoC,GAGrD,GAAI,CACF,MAAO,GAAG,EAAO,GAAG,KAAK,UAAU,CAAM,GAC3C,MAAQ,CASN,MALA,CACE,IAAuB,GAIlB,CACT,CACF,CAEA,MAAO,CACL,SAAS,EAAO,EAAU,EAAS,CACjC,IAAM,EAAS,EAAU,EACnB,EAAiC,CAAE,GAAG,EAAS,WAAU,MAAO,UAAW,EAEjF,OAAO,EAAA,YACL,EACA,EAAU,EAAQ,CAAI,EACtB,MACM,IAAI,KAAK,aAAa,EAAQ,CAAI,CAC1C,CAAC,CAAC,OAAO,CAAK,CAChB,EAEA,KAAK,EAAO,EAAS,CACnB,IAAM,EAAS,EAAU,EAEzB,OAAO,EAAA,YACL,EACA,EAAU,EAAQ,CAAO,EACzB,MACM,IAAI,KAAK,eAAe,EAAQ,CAAO,CAC/C,CAAC,CAAC,OAAO,CAAK,CAChB,EAEA,SAAS,EAAO,EAAS,CACvB,IAAM,EAAS,EAAU,EACnB,EAAU,KAMhB,OAJK,EAAQ,eAET,EAAe,MAAO,GAAM,EAAM,KAAO,IAAA,EAAS,EAAU,GAEzD,EAAA,YACL,EACA,EAAU,EAAQ,CAAO,EACzB,MACM,IAAI,EAAQ,eAAgB,EAAQ,CAAO,CACnD,CAAC,CAAC,OAAO,CAAK,EATsB,EAAgB,CAAK,CAU3D,EAEA,KAAK,EAAO,EAAS,CACnB,IAAM,EAAS,EAAU,EACnB,EAAQ,EAAM,IAAK,GAAM,OAAO,CAAC,CAAC,EAKlC,EAAQ,GAAS,OAAS,OAC1B,EAAO,GAAS,OAAS,KAAO,cAAgB,cAEtD,OAAO,EAAA,YACL,EACA,EAAU,EAAQ,CAAE,QAAO,MAAK,CAAC,EACjC,MACM,IAAI,KAAK,WAAW,EAAQ,CAAE,QAAO,MAAK,CAAC,CACnD,CAAC,CAAC,OAAO,CAAK,CAChB,EAEA,OAAO,EAAO,EAAS,CACrB,IAAM,EAAS,EAAU,EAEzB,OAAO,EAAA,YACL,EACA,EAAU,EAAQ,CAAO,EACzB,MACM,IAAI,KAAK,aAAa,EAAQ,CAAO,CAC7C,CAAC,CAAC,OAAO,CAAK,CAChB,EAEA,SAAS,EAAO,EAAM,EAAS,CAC7B,IAAM,EAAS,EAAU,EAEzB,OAAO,EAAA,YACL,EACA,EAAU,EAAQ,CAAO,EACzB,MACM,IAAI,KAAK,mBAAmB,EAAQ,CAAO,CACnD,CAAC,CAAC,OAAO,EAAO,CAAI,CACtB,CACF,CACF"}
|
package/dist/format.d.ts
CHANGED
|
@@ -39,15 +39,6 @@ export type ListFormatOptions = {
|
|
|
39
39
|
type?: 'and' | 'or';
|
|
40
40
|
};
|
|
41
41
|
export type Formatter = {
|
|
42
|
-
/**
|
|
43
|
-
* Clears all cached `Intl` formatter instances. Useful after a locale change on a long-running
|
|
44
|
-
* SSR server, or to reclaim memory.
|
|
45
|
-
*
|
|
46
|
-
* @note This clears the cache for **all callers sharing this formatter instance**. When called on
|
|
47
|
-
* `i18n.fmt`, the cache is cleared for every part of the application that reads `i18n.fmt`.
|
|
48
|
-
* `setLocale()` already calls `clear()` automatically — manual calls are rarely needed.
|
|
49
|
-
*/
|
|
50
|
-
clear(): void;
|
|
51
42
|
currency(value: number, currency: string, options?: Omit<Intl.NumberFormatOptions, 'currency' | 'style'>): string;
|
|
52
43
|
date(value: Date | number, options?: Intl.DateTimeFormatOptions): string;
|
|
53
44
|
/**
|
package/dist/format.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAOH,MAAM,MAAM,aAAa,GAAG,OAAO,CACjC,MAAM,CACF,MAAM,GACN,OAAO,GACP,cAAc,GACd,cAAc,GACd,SAAS,GACT,QAAQ,GACR,aAAa,GACb,SAAS,GACT,OAAO,GACP,OAAO,EACT,MAAM,CACP,CACF,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,MAAM,CAAC;IAClH,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,GAAG,MAAM,CAAC;IACzE;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAAC;IACxE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IACzE,gDAAgD;IAChD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,GAAG,MAAM,CAAC;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,yBAAyB,GAAG,MAAM,CAAC;CAC9G,CAAC;AA+CF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAsH1E"}
|
package/dist/format.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getOrCreate as e } from "./_bounded-cache.js";
|
|
2
|
+
import "./_dev.js";
|
|
2
3
|
//#region src/format.ts
|
|
3
4
|
var t = 128, n = [
|
|
4
5
|
"years",
|
|
@@ -30,40 +31,37 @@ function i(e) {
|
|
|
30
31
|
}).filter((e) => e !== void 0).join(" ");
|
|
31
32
|
}
|
|
32
33
|
function a(r) {
|
|
33
|
-
let a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map(), d = typeof r == "string" ? () => r : r;
|
|
34
|
-
function
|
|
34
|
+
let a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map(), d = typeof r == "string" ? () => r : r, f = !1;
|
|
35
|
+
function p(e, t) {
|
|
35
36
|
if (!t) return e;
|
|
36
37
|
let n = {};
|
|
37
38
|
for (let e of Object.keys(t).sort()) n[e] = t[e];
|
|
38
39
|
try {
|
|
39
40
|
return `${e}:${JSON.stringify(n)}`;
|
|
40
41
|
} catch {
|
|
41
|
-
return e;
|
|
42
|
+
return f ||= !0, e;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
return {
|
|
45
|
-
clear() {
|
|
46
|
-
a.clear(), o.clear(), s.clear(), c.clear(), l.clear(), u.clear();
|
|
47
|
-
},
|
|
48
46
|
currency(n, r, i) {
|
|
49
47
|
let a = d(), s = {
|
|
50
48
|
...i,
|
|
51
49
|
currency: r,
|
|
52
50
|
style: "currency"
|
|
53
51
|
};
|
|
54
|
-
return e(o,
|
|
52
|
+
return e(o, p(a, s), t, () => new Intl.NumberFormat(a, s)).format(n);
|
|
55
53
|
},
|
|
56
54
|
date(n, r) {
|
|
57
55
|
let i = d();
|
|
58
|
-
return e(s,
|
|
56
|
+
return e(s, p(i, r), t, () => new Intl.DateTimeFormat(i, r)).format(n);
|
|
59
57
|
},
|
|
60
58
|
duration(r, a) {
|
|
61
59
|
let o = d(), s = Intl;
|
|
62
|
-
return s.DurationFormat ? n.every((e) => r[e] === void 0) ? "" : e(u,
|
|
60
|
+
return s.DurationFormat ? n.every((e) => r[e] === void 0) ? "" : e(u, p(o, a), t, () => new s.DurationFormat(o, a)).format(r) : i(r);
|
|
63
61
|
},
|
|
64
62
|
list(n, r) {
|
|
65
63
|
let i = d(), a = n.map((e) => String(e)), o = r?.style ?? "long", s = r?.type === "or" ? "disjunction" : "conjunction";
|
|
66
|
-
return e(l,
|
|
64
|
+
return e(l, p(i, {
|
|
67
65
|
style: o,
|
|
68
66
|
type: s
|
|
69
67
|
}), t, () => new Intl.ListFormat(i, {
|
|
@@ -73,11 +71,11 @@ function a(r) {
|
|
|
73
71
|
},
|
|
74
72
|
number(n, r) {
|
|
75
73
|
let i = d();
|
|
76
|
-
return e(a,
|
|
74
|
+
return e(a, p(i, r), t, () => new Intl.NumberFormat(i, r)).format(n);
|
|
77
75
|
},
|
|
78
76
|
relative(n, r, i) {
|
|
79
77
|
let a = d();
|
|
80
|
-
return e(c,
|
|
78
|
+
return e(c, p(a, i), t, () => new Intl.RelativeTimeFormat(a, i)).format(n, r);
|
|
81
79
|
}
|
|
82
80
|
};
|
|
83
81
|
}
|
package/dist/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","names":[],"sources":["../src/format.ts"],"sourcesContent":["/**\n * /lingua/format\n *\n * Standalone Intl formatter factory. Import from `/lingua/format`.\n * Creates formatters for numbers, dates, currencies, lists, durations, and relative time.\n *\n * Pass a static locale string or a reactive locale getter.\n *\n * @example\n * ```ts\n * import { createFormatter } from '@vielzeug/lingua/format';\n *\n * // Static locale\n * const fmt = createFormatter('en-US');\n *\n * // Reactive — always reads the current locale from the i18n instance\n * const fmt = createFormatter(() => i18n.locale);\n *\n * fmt.number(1_234.56);\n * fmt.currency(9.99, 'USD');\n * fmt.date(new Date());\n * fmt.relative(-1, 'day');\n * fmt.list(['A', 'B', 'C']);\n * fmt.duration({ hours: 1, minutes: 30 });\n * ```\n */\n\nimport { getOrCreate } from './_bounded-cache';\n\nconst FORMAT_CACHE_MAX = 128;\n\nexport type DurationValue = Partial<\n Record<\n | 'days'\n | 'hours'\n | 'microseconds'\n | 'milliseconds'\n | 'minutes'\n | 'months'\n | 'nanoseconds'\n | 'seconds'\n | 'weeks'\n | 'years',\n number\n >\n>;\n\nexport type DurationFormatOptions = {\n hours?: '2-digit' | 'numeric';\n microseconds?: 'numeric';\n milliseconds?: 'numeric';\n minutes?: '2-digit' | 'numeric';\n nanoseconds?: 'numeric';\n seconds?: '2-digit' | 'numeric';\n style?: 'digital' | 'long' | 'narrow' | 'short';\n};\n\nexport type ListFormatOptions = {\n style?: 'long' | 'narrow' | 'short';\n type?: 'and' | 'or';\n};\n\nexport type Formatter = {\n /**\n * Clears all cached `Intl` formatter instances. Useful after a locale change on a long-running\n * SSR server, or to reclaim memory.\n *\n * @note This clears the cache for **all callers sharing this formatter instance**. When called on\n * `i18n.fmt`, the cache is cleared for every part of the application that reads `i18n.fmt`.\n * `setLocale()` already calls `clear()` automatically — manual calls are rarely needed.\n */\n clear(): void;\n currency(value: number, currency: string, options?: Omit<Intl.NumberFormatOptions, 'currency' | 'style'>): string;\n date(value: Date | number, options?: Intl.DateTimeFormatOptions): string;\n /**\n * Formats a duration using `Intl.DurationFormat` when available, or a compact labeled fallback\n * (e.g. `'1h 30min'`). Returns `''` when every field of `value` is `undefined`.\n *\n * @note The fallback labels (`h`, `min`, `s`, `ms`, etc.) are English-only and are not\n * locale-aware. If `Intl.DurationFormat` is unavailable in your target environment and\n * you need localised duration output, provide your own formatter using the individual\n * duration fields from `DurationValue`.\n */\n duration(value: DurationValue, options?: DurationFormatOptions): string;\n list(value: Array<string | number>, options?: ListFormatOptions): string;\n /** Formats a number with the current locale. */\n number(value: number, options?: Intl.NumberFormatOptions): string;\n relative(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;\n};\n\n// ─── Internal ─────────────────────────────────────────────────────────────────\n\nconst DURATION_UNITS: Array<keyof DurationValue> = [\n 'years',\n 'months',\n 'weeks',\n 'days',\n 'hours',\n 'minutes',\n 'seconds',\n 'milliseconds',\n 'microseconds',\n 'nanoseconds',\n];\n\nconst DURATION_LABELS: Record<keyof DurationValue, string> = {\n days: 'd',\n hours: 'h',\n microseconds: 'us',\n milliseconds: 'ms',\n minutes: 'min',\n months: 'mo',\n nanoseconds: 'ns',\n seconds: 's',\n weeks: 'w',\n years: 'y',\n};\n\ntype DurationFormatterCtor = new (\n locale: string,\n options?: DurationFormatOptions,\n) => { format(value: DurationValue): string };\n\nfunction durationLabeled(value: DurationValue): string {\n return DURATION_UNITS.map((unit) => {\n const amount = value[unit];\n\n return typeof amount === 'number' ? `${amount}${DURATION_LABELS[unit]}` : undefined;\n })\n .filter((part): part is string => part !== undefined)\n .join(' ');\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\n/**\n * Creates a formatter bound to a locale. Pass a static locale string or a\n * reactive getter function `() => string` (e.g. `() => i18n.locale`).\n */\nexport function createFormatter(source: string | (() => string)): Formatter {\n const numberCache = new Map<string, Intl.NumberFormat>();\n const currencyCache = new Map<string, Intl.NumberFormat>();\n const dateCache = new Map<string, Intl.DateTimeFormat>();\n const relativeCache = new Map<string, Intl.RelativeTimeFormat>();\n const listCache = new Map<string, Intl.ListFormat>();\n const durationCache = new Map<string, { format(value: DurationValue): string }>();\n\n const getLocale = typeof source === 'string' ? () => source : source;\n\n function cachedKey(locale: string, options?: object): string {\n if (!options) return locale;\n\n const sorted: Record<string, unknown> = {};\n\n for (const key of Object.keys(options as Record<string, unknown>).sort()) {\n sorted[key] = (options as Record<string, unknown>)[key];\n }\n\n try {\n return `${locale}:${JSON.stringify(sorted)}`;\n } catch {\n // Circular references or BigInt values — fall back to locale-only key.\n // Multiple callers with different unserializable options will share the same formatter instance.\n return locale;\n }\n }\n\n return {\n clear() {\n numberCache.clear();\n currencyCache.clear();\n dateCache.clear();\n relativeCache.clear();\n listCache.clear();\n durationCache.clear();\n },\n\n currency(value, currency, options) {\n const locale = getLocale();\n const opts: Intl.NumberFormatOptions = { ...options, currency, style: 'currency' };\n\n return getOrCreate(\n currencyCache,\n cachedKey(locale, opts),\n FORMAT_CACHE_MAX,\n () => new Intl.NumberFormat(locale, opts),\n ).format(value);\n },\n\n date(value, options) {\n const locale = getLocale();\n\n return getOrCreate(\n dateCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.DateTimeFormat(locale, options),\n ).format(value);\n },\n\n duration(value, options) {\n const locale = getLocale();\n const IntlExt = Intl as typeof Intl & { DurationFormat?: DurationFormatterCtor };\n\n if (!IntlExt.DurationFormat) return durationLabeled(value);\n\n if (DURATION_UNITS.every((u) => value[u] === undefined)) return '';\n\n return getOrCreate(\n durationCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new IntlExt.DurationFormat!(locale, options),\n ).format(value);\n },\n\n list(value, options) {\n const locale = getLocale();\n const items = value.map((v) => String(v));\n // list() has exactly two string options with small finite value domains.\n // We normalize both to their resolved defaults and build the key directly\n // rather than using cachedKey(). This also prevents cache misses when\n // callers pass { style: 'long', type: 'and' } vs {} (same ListFormat).\n const style = options?.style ?? 'long';\n const type = options?.type === 'or' ? 'disjunction' : 'conjunction';\n\n return getOrCreate(\n listCache,\n cachedKey(locale, { style, type }),\n FORMAT_CACHE_MAX,\n () => new Intl.ListFormat(locale, { style, type }),\n ).format(items);\n },\n\n number(value, options) {\n const locale = getLocale();\n\n return getOrCreate(\n numberCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.NumberFormat(locale, options),\n ).format(value);\n },\n\n relative(value, unit, options) {\n const locale = getLocale();\n\n return getOrCreate(\n relativeCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.RelativeTimeFormat(locale, options),\n ).format(value, unit);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"format.js","names":[],"sources":["../src/format.ts"],"sourcesContent":["/**\n * /lingua/format\n *\n * Standalone Intl formatter factory. Import from `/lingua/format`.\n * Creates formatters for numbers, dates, currencies, lists, durations, and relative time.\n *\n * Pass a static locale string or a reactive locale getter.\n *\n * @example\n * ```ts\n * import { createFormatter } from '@vielzeug/lingua/format';\n *\n * // Static locale\n * const fmt = createFormatter('en-US');\n *\n * // Reactive — always reads the current locale from the i18n instance\n * const fmt = createFormatter(() => i18n.locale);\n *\n * fmt.number(1_234.56);\n * fmt.currency(9.99, 'USD');\n * fmt.date(new Date());\n * fmt.relative(-1, 'day');\n * fmt.list(['A', 'B', 'C']);\n * fmt.duration({ hours: 1, minutes: 30 });\n * ```\n */\n\nimport { getOrCreate } from './_bounded-cache';\nimport { warn } from './_dev';\n\nconst FORMAT_CACHE_MAX = 128;\n\nexport type DurationValue = Partial<\n Record<\n | 'days'\n | 'hours'\n | 'microseconds'\n | 'milliseconds'\n | 'minutes'\n | 'months'\n | 'nanoseconds'\n | 'seconds'\n | 'weeks'\n | 'years',\n number\n >\n>;\n\nexport type DurationFormatOptions = {\n hours?: '2-digit' | 'numeric';\n microseconds?: 'numeric';\n milliseconds?: 'numeric';\n minutes?: '2-digit' | 'numeric';\n nanoseconds?: 'numeric';\n seconds?: '2-digit' | 'numeric';\n style?: 'digital' | 'long' | 'narrow' | 'short';\n};\n\nexport type ListFormatOptions = {\n style?: 'long' | 'narrow' | 'short';\n type?: 'and' | 'or';\n};\n\nexport type Formatter = {\n currency(value: number, currency: string, options?: Omit<Intl.NumberFormatOptions, 'currency' | 'style'>): string;\n date(value: Date | number, options?: Intl.DateTimeFormatOptions): string;\n /**\n * Formats a duration using `Intl.DurationFormat` when available, or a compact labeled fallback\n * (e.g. `'1h 30min'`). Returns `''` when every field of `value` is `undefined`.\n *\n * @note The fallback labels (`h`, `min`, `s`, `ms`, etc.) are English-only and are not\n * locale-aware. If `Intl.DurationFormat` is unavailable in your target environment and\n * you need localised duration output, provide your own formatter using the individual\n * duration fields from `DurationValue`.\n */\n duration(value: DurationValue, options?: DurationFormatOptions): string;\n list(value: Array<string | number>, options?: ListFormatOptions): string;\n /** Formats a number with the current locale. */\n number(value: number, options?: Intl.NumberFormatOptions): string;\n relative(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;\n};\n\n// ─── Internal ─────────────────────────────────────────────────────────────────\n\nconst DURATION_UNITS: Array<keyof DurationValue> = [\n 'years',\n 'months',\n 'weeks',\n 'days',\n 'hours',\n 'minutes',\n 'seconds',\n 'milliseconds',\n 'microseconds',\n 'nanoseconds',\n];\n\nconst DURATION_LABELS: Record<keyof DurationValue, string> = {\n days: 'd',\n hours: 'h',\n microseconds: 'us',\n milliseconds: 'ms',\n minutes: 'min',\n months: 'mo',\n nanoseconds: 'ns',\n seconds: 's',\n weeks: 'w',\n years: 'y',\n};\n\ntype DurationFormatterCtor = new (\n locale: string,\n options?: DurationFormatOptions,\n) => { format(value: DurationValue): string };\n\nfunction durationLabeled(value: DurationValue): string {\n return DURATION_UNITS.map((unit) => {\n const amount = value[unit];\n\n return typeof amount === 'number' ? `${amount}${DURATION_LABELS[unit]}` : undefined;\n })\n .filter((part): part is string => part !== undefined)\n .join(' ');\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\n/**\n * Creates a formatter bound to a locale. Pass a static locale string or a\n * reactive getter function `() => string` (e.g. `() => i18n.locale`).\n */\nexport function createFormatter(source: string | (() => string)): Formatter {\n const numberCache = new Map<string, Intl.NumberFormat>();\n const currencyCache = new Map<string, Intl.NumberFormat>();\n const dateCache = new Map<string, Intl.DateTimeFormat>();\n const relativeCache = new Map<string, Intl.RelativeTimeFormat>();\n const listCache = new Map<string, Intl.ListFormat>();\n const durationCache = new Map<string, { format(value: DurationValue): string }>();\n\n const getLocale = typeof source === 'string' ? () => source : source;\n\n // Warn at most once per formatter instance on the unserializable-options fallback —\n // a hot render loop with such options must not spam the console on every call.\n let warnedUnserializable = false;\n\n function cachedKey(locale: string, options?: object): string {\n if (!options) return locale;\n\n const sorted: Record<string, unknown> = {};\n\n for (const key of Object.keys(options as Record<string, unknown>).sort()) {\n sorted[key] = (options as Record<string, unknown>)[key];\n }\n\n try {\n return `${locale}:${JSON.stringify(sorted)}`;\n } catch {\n // Circular references or BigInt values — fall back to locale-only key.\n // Multiple callers with different unserializable options will share the same\n // formatter instance; never silently.\n if (!warnedUnserializable) {\n warnedUnserializable = true;\n warn('formatter options could not be serialized — falling back to a shared per-locale formatter instance.');\n }\n\n return locale;\n }\n }\n\n return {\n currency(value, currency, options) {\n const locale = getLocale();\n const opts: Intl.NumberFormatOptions = { ...options, currency, style: 'currency' };\n\n return getOrCreate(\n currencyCache,\n cachedKey(locale, opts),\n FORMAT_CACHE_MAX,\n () => new Intl.NumberFormat(locale, opts),\n ).format(value);\n },\n\n date(value, options) {\n const locale = getLocale();\n\n return getOrCreate(\n dateCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.DateTimeFormat(locale, options),\n ).format(value);\n },\n\n duration(value, options) {\n const locale = getLocale();\n const IntlExt = Intl as typeof Intl & { DurationFormat?: DurationFormatterCtor };\n\n if (!IntlExt.DurationFormat) return durationLabeled(value);\n\n if (DURATION_UNITS.every((u) => value[u] === undefined)) return '';\n\n return getOrCreate(\n durationCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new IntlExt.DurationFormat!(locale, options),\n ).format(value);\n },\n\n list(value, options) {\n const locale = getLocale();\n const items = value.map((v) => String(v));\n // list() has exactly two string options with small finite value domains.\n // We normalize both to their resolved defaults and build the key directly\n // rather than using cachedKey(). This also prevents cache misses when\n // callers pass { style: 'long', type: 'and' } vs {} (same ListFormat).\n const style = options?.style ?? 'long';\n const type = options?.type === 'or' ? 'disjunction' : 'conjunction';\n\n return getOrCreate(\n listCache,\n cachedKey(locale, { style, type }),\n FORMAT_CACHE_MAX,\n () => new Intl.ListFormat(locale, { style, type }),\n ).format(items);\n },\n\n number(value, options) {\n const locale = getLocale();\n\n return getOrCreate(\n numberCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.NumberFormat(locale, options),\n ).format(value);\n },\n\n relative(value, unit, options) {\n const locale = getLocale();\n\n return getOrCreate(\n relativeCache,\n cachedKey(locale, options),\n FORMAT_CACHE_MAX,\n () => new Intl.RelativeTimeFormat(locale, options),\n ).format(value, unit);\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/i18n.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
require("./_dev.cjs");const e=require("./errors.cjs"),t=require("./_locale.cjs"),n=require("./_resources.cjs"),r=require("./translator.cjs");function i(i){let a=n.createCatalogStore(i.catalogs),o=i.fallback,s=(Array.isArray(o)?o:o?[o]:[]).map(t.canonicalLocale),c=new AbortController,l=new Set,u=!1,d=t.canonicalLocale(i.locale??`en`),f=0,p=()=>({locale:d,revision:f,translator:r.createTranslatorFromCompiled(a.catalogMap(),{...i,fallback:o,locale:d})}),m=p(),h=()=>{if(u)throw new e.LinguaDisposedError},g=()=>{u||(u=!0,l.clear(),c.abort())},_=e=>{try{e(m)}catch{}},v=()=>{f++,m=p();for(let e of[...l])_(e)},y=e=>t.localeChain(d,s).includes(e);return{get disposalSignal(){return c.signal},dispose:g,get disposed(){return u},getSnapshot(){return m},isLoaded(e){return!u&&a.isLoaded(e?.locale??d)},async load(e){h();let n=t.canonicalLocale(e?.locale??d),r=await a.load(n);!u&&r&&y(n)&&v()},get locale(){return d},segments(e,t){return m.translator.segmentsDynamic(e,t)},segmentsDynamic(e,t){return m.translator.segmentsDynamic(e,t)},serialize(){return h(),a.state(d)},async setLocale(e){h();let n=t.canonicalLocale(e);n!==d&&(d=n,v())},subscribe(e,t){if(h(),t?.signal?.aborted)return()=>{};let n=()=>{l.delete(e),t?.signal?.removeEventListener(`abort`,n)};return t?.signal?.addEventListener(`abort`,n,{once:!0}),l.add(e),t?.immediate&&_(e),n},[Symbol.dispose]:g,translate(e,t={}){return m.translator.translateDynamic(e,t)},translateDynamic(e,t){return m.translator.translateDynamic(e,t)}}}function a(t,n){if(t.version!==3)throw new e.LinguaInvalidStateError(`Unsupported lingua state version: ${String(t.version)}.`);return i({...n,catalogs:t.catalogs,locale:t.locale})}exports.createTranslationStore=i,exports.hydrateTranslationStore=a;
|
|
2
2
|
//# sourceMappingURL=i18n.cjs.map
|
package/dist/i18n.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.cjs","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import type {\n I18n,\n I18nOptions,\n I18nSnapshot,\n I18nState,\n Locale,\n MessageBranchKeys,\n MessageLeafKeys,\n ScopedI18n,\n SubscribeOptions,\n TpOptions,\n TranslateVars,\n Unsubscribe,\n} from './i18n-types';\n\nimport { getOrCreate } from './_bounded-cache';\nimport { CatalogEntry, type Messages, flattenStrings } from './_catalog';\nimport {\n type CatalogStore,\n type Loader,\n type LocaleSource,\n createCatalogStore,\n validateCatalogInDev,\n} from './_catalog-store';\nimport { type LocaleCaches, buildLocaleChain, canon, createLocaleCaches } from './_chain';\nimport { error as logError } from './_dev';\nimport { type NamespaceFactory, type NamespaceStore, createNamespaceStore } from './_namespace-store';\nimport {\n type TranslateContext,\n hasKey as hasKeyIn,\n translate as translateIn,\n translatePlural as translatePluralIn,\n} from './_translate';\nimport { LinguaDisposedError, LinguaRestoreError, checkDisposed } from './errors';\nimport { type Formatter, createFormatter } from './format';\n\nexport {\n LinguaCountInVarsError,\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCountError,\n LinguaInvalidLocaleError,\n LinguaMissingLocaleError,\n LinguaNamespaceMissingError,\n LinguaRestoreError,\n} from './errors';\n\nexport type {\n I18n,\n I18nOptions,\n I18nSnapshot,\n I18nState,\n Locale,\n MessageBranchKeys,\n MessageLeafKeys,\n ScopedI18n,\n SubscribeOptions,\n TpOptions,\n TranslateVars,\n Unsubscribe,\n} from './i18n-types';\nexport type { Loader, LocaleSource } from './_catalog-store';\nexport type { Messages } from './_catalog';\nexport type { NamespaceFactory } from './_namespace-store';\n\n// Bound for the per-instance `scope()` cache — see its declaration below for why it's bounded.\nconst SCOPE_CACHE_MAX = 128;\n\n// ─── Locale state ─────────────────────────────────────────────────────────────\n// Replaced atomically on every locale change.\n\ntype LocaleState = {\n readonly chain: readonly Locale[];\n readonly chainSet: ReadonlySet<Locale>;\n readonly locale: Locale;\n};\n\nfunction buildState(locale: Locale, fallback: Locale[], caches: LocaleCaches): LocaleState {\n const { chain, set } = buildLocaleChain(locale, fallback, caches);\n\n return { chain, chainSet: set, locale };\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\n/** Overload: explicit type parameter (strict typing) */\nexport function createI18n<M extends Messages>(config: I18nOptions<M>): I18n<M>;\n/** Overload: no type parameter (loose typing, allows heterogeneous catalogs) */\nexport function createI18n(config?: I18nOptions<Messages>): I18n<Messages>;\nexport function createI18n<M extends Messages = Messages>(config?: I18nOptions<M>): I18n<M> {\n return _createI18nImpl<M>(config);\n}\n\n// ─── Internal seed shape ──────────────────────────────────────────────────────\n\ntype I18nSeed<M extends Messages> = {\n catalogStore?: CatalogStore<M>;\n nsStore?: NamespaceStore;\n};\n\nfunction _createI18nImpl<M extends Messages = Messages>(config?: I18nOptions<M>, _seed?: I18nSeed<M>): I18n<M> {\n const cfg: I18nOptions<M> = config ?? {};\n\n // ─── Per-instance caches (no shared module-level state) ───────────────────\n const caches: LocaleCaches = createLocaleCaches();\n\n const canonL = (loc: string) => canon(loc, caches);\n\n const fallback = Array.isArray(cfg.fallback) ? cfg.fallback.map(canonL) : cfg.fallback ? [canonL(cfg.fallback)] : [];\n\n // ─── Disposal ─────────────────────────────────────────────────────────────\n let disposed = false;\n const disposeController = new AbortController();\n\n // ─── Bounded stores ───────────────────────────────────────────────────────\n const catalogStore: CatalogStore<M> = createCatalogStore(() => disposed);\n const nsStore: NamespaceStore = createNamespaceStore(() => disposed);\n\n // ─── Locale state ─────────────────────────────────────────────────────────\n let state: LocaleState = buildState(canonL(cfg.locale ?? 'en'), fallback, caches);\n\n // ─── Subscribers ──────────────────────────────────────────────────────────\n const subscribers = new Set<(snapshot: I18nSnapshot) => void>();\n const subscriptionUnsubscribers = new Set<Unsubscribe>();\n\n const onMissingKey = cfg.onMissingKey ?? ((key: string) => key);\n const onMissingVar = cfg.onMissingVar ?? ((varName: string) => `{${varName}}`);\n const onSubscriberError = cfg.onSubscriberError ?? ((error: unknown) => logError('subscriber error', error));\n\n // ─── Lazy formatter — avoids Intl overhead for SSR forks that only need t(). ──\n let _fmt: Formatter | undefined;\n\n // ─── Scope cache — stable object references per prefix for reactive framework renders. ──\n // Bounded the same way format.ts bounds its Intl-formatter caches: `scope()` is meant for a\n // static, finite set of prefixes (nav sections, form names, ...), but nothing stopped a caller\n // from passing a dynamic per-item prefix (e.g. `scope(`item.${id}`)` in a list render) and\n // growing this unboundedly for the instance's lifetime. Oldest entry evicts past the cap.\n const scopeCache = new Map<string, ScopedI18n>();\n\n const getFormatter = (): Formatter => {\n if (!_fmt) _fmt = createFormatter(() => state.locale);\n\n return _fmt;\n };\n\n // ─── Translate helpers ────────────────────────────────────────────────────\n // The actual algorithm (fallback-chain lookup, plural-form selection, interpolation) lives in\n // `_translate.ts` — this instance just supplies fresh context (current locale/chain) per call.\n\n const translateContext = (): TranslateContext => ({\n caches,\n catalogStore,\n chain: state.chain,\n locale: state.locale,\n onMissingKey,\n onMissingVar,\n });\n\n // Shared by has() and scope().has().\n const hasKey = (base: string): boolean => hasKeyIn(translateContext(), base);\n\n const translate = (key: MessageLeafKeys<M> | (string & {}), vars?: TranslateVars): string =>\n translateIn(translateContext(), String(key), vars);\n\n const translatePlural = (key: MessageBranchKeys<M> | (string & {}), count: number, options?: TpOptions): string =>\n translatePluralIn(translateContext(), String(key), count, options);\n\n // ─── bump() ───────────────────────────────────────────────────────────────\n // Rebuilds the snapshot and notifies all current subscribers.\n\n let snapshot: I18nSnapshot = {\n locale: state.locale,\n t: translate,\n tp: translatePlural,\n };\n\n const bump = (): void => {\n snapshot = { locale: state.locale, t: translate, tp: translatePlural };\n\n const listeners = [...subscribers];\n\n for (const listener of listeners) {\n try {\n listener(snapshot);\n } catch (error) {\n onSubscriberError(error);\n }\n }\n };\n\n // ─── Wire catalog store onChange → bump ───────────────────────────────────\n catalogStore.onChange = (loc: Locale) => {\n if (state.chainSet.has(loc)) bump();\n };\n\n // ─── Seed from parent fork ─────────────────────────────────────────────────\n if (_seed?.catalogStore) {\n catalogStore.seedFrom(_seed.catalogStore.snapshotCatalogs(), _seed.catalogStore.snapshotLoaders());\n }\n\n if (_seed?.nsStore) {\n nsStore.seedFrom(_seed.nsStore);\n }\n\n // ─── Initial catalogs from config ─────────────────────────────────────────\n if (cfg.catalogs) {\n const staticEntries = new Map<Locale, CatalogEntry>();\n const loaderEntries = new Map<Locale, Loader<M>>();\n\n for (const [loc, source] of Object.entries(cfg.catalogs)) {\n const normalized = canonL(loc);\n\n if (typeof source === 'function') {\n loaderEntries.set(normalized, source as Loader<M>);\n } else {\n const entry = new CatalogEntry();\n\n entry.setAll(flattenStrings(source as M));\n staticEntries.set(normalized, entry);\n validateCatalogInDev(normalized, source as M);\n }\n }\n\n // Use seedFrom for zero-overhead init (no bump, no notifications during setup)\n catalogStore.seedFrom(staticEntries, loaderEntries);\n }\n\n // ─── Preload helper ───────────────────────────────────────────────────────\n\n const preload = (loc: Locale): Promise<void> => catalogStore.preload(canonL(loc));\n\n // ─── Monotonic generation counter — last writer wins for concurrent setLocale() ──\n let switchGen = 0;\n\n // ─── Subscribe helper ─────────────────────────────────────────────────────\n\n const subscribeInternal = (callback: (snapshot: I18nSnapshot) => void, options?: SubscribeOptions): Unsubscribe => {\n const signal = options?.signal;\n\n const unsubscribe = (): void => {\n subscribers.delete(callback);\n signal?.removeEventListener('abort', unsubscribe);\n subscriptionUnsubscribers.delete(unsubscribe);\n };\n\n if (disposed) throw new LinguaDisposedError();\n\n if (signal?.aborted) return unsubscribe;\n\n if (options?.immediate === true) {\n try {\n callback(snapshot);\n } catch (error) {\n onSubscriberError(error);\n\n return unsubscribe;\n }\n }\n\n subscribers.add(callback);\n signal?.addEventListener('abort', unsubscribe, { once: true });\n subscriptionUnsubscribers.add(unsubscribe);\n\n return unsubscribe;\n };\n\n // ─── Public object ─────────────────────────────────────────────────────────\n // Named `const` (not a bare `return { ... }`) so `[Symbol.dispose]` below can close over\n // `source` directly instead of `this` — `this` inside an object-literal method is only bound\n // when called as `obj.method()`; detaching it first (`const d = i18n[Symbol.dispose]; d()`,\n // the exact shape most framework cleanup-hook APIs expect) would otherwise throw.\n\n const source: I18n<M> = {\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n for (const unsubscribe of [...subscriptionUnsubscribers]) unsubscribe();\n subscriptionUnsubscribers.clear();\n subscribers.clear();\n catalogStore.dispose();\n nsStore.dispose();\n scopeCache.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n extend(ns: string, factory: NamespaceFactory, loc?: Locale): Promise<void> {\n checkDisposed(disposed);\n\n nsStore.registerNamespace(ns, factory);\n\n const normalized = loc ? canonL(loc) : state.locale;\n\n return nsStore.loadNamespace(ns, normalized, (l, messages) => catalogStore.patch(l, messages));\n },\n\n get fmt(): Formatter {\n return getFormatter();\n },\n\n fork(overrides?: Omit<I18nOptions<M>, 'catalogs'>): I18n<M> {\n return _createI18nImpl(\n {\n fallback: overrides?.fallback ?? (fallback.length > 0 ? fallback : undefined),\n locale: overrides?.locale ?? state.locale,\n onMissingKey: overrides?.onMissingKey ?? cfg.onMissingKey,\n onMissingVar: overrides?.onMissingVar ?? cfg.onMissingVar,\n onSubscriberError: overrides?.onSubscriberError ?? cfg.onSubscriberError,\n },\n {\n catalogStore,\n nsStore,\n },\n );\n },\n\n getSnapshot() {\n return snapshot;\n },\n\n getState(): I18nState {\n const catalogsOut: Record<Locale, Record<string, string>> = {};\n\n for (const [loc, entry] of catalogStore.snapshotCatalogs()) {\n catalogsOut[loc] = Object.fromEntries([...entry.entries.entries()].map(([k, { message }]) => [k, message]));\n }\n\n return { catalogs: catalogsOut, locale: state.locale };\n },\n\n getSupportedLocales(sorted?: boolean): Locale[] {\n const locales = [...catalogStore.knownLocales()];\n\n return sorted === true ? locales.sort() : locales;\n },\n\n has(key: MessageLeafKeys<M> | MessageBranchKeys<M> | (string & {})): boolean {\n return hasKey(String(key));\n },\n\n isLoaded(loc: Locale): boolean {\n try {\n return catalogStore.isLoaded(canonL(loc));\n } catch {\n return false;\n }\n },\n\n isNamespaceLoaded(ns: string, loc?: Locale): boolean {\n if (!loc) return nsStore.isLoaded(ns, state.locale);\n\n try {\n return nsStore.isLoaded(ns, canonL(loc));\n } catch {\n return false;\n }\n },\n\n isNamespaceRegistered(ns: string): boolean {\n return nsStore.isRegistered(ns);\n },\n\n isRegistered(loc: Locale): boolean {\n try {\n return catalogStore.isRegistered(canonL(loc));\n } catch {\n return false;\n }\n },\n\n loadNamespace(ns: string, loc?: Locale): Promise<void> {\n const normalized = loc ? canonL(loc) : state.locale;\n\n return nsStore.loadNamespace(ns, normalized, (l, messages) => catalogStore.patch(l, messages));\n },\n\n get locale(): Locale {\n return state.locale;\n },\n\n preload,\n\n register(loc: Locale, source: LocaleSource<M>): Promise<void> {\n const normalized = canonL(loc);\n\n return catalogStore.register(normalized, source, nsStore);\n },\n\n registerNamespace(ns: string, factory: NamespaceFactory): void {\n nsStore.registerNamespace(ns, factory);\n },\n\n restoreState(st: I18nState): void {\n checkDisposed(disposed);\n\n if (!Object.hasOwn(st.catalogs, st.locale)) {\n throw new LinguaRestoreError(`restoreState: locale \"${st.locale}\" has no catalog in the provided state.`);\n }\n\n const freshEntries = new Map<Locale, CatalogEntry>();\n const knownLocales = catalogStore.knownLocales();\n\n for (const knownLocale of knownLocales) {\n nsStore.clearLocale(knownLocale);\n }\n\n // No validateCatalogInDev() call here, unlike register()/construction/preload(): `state`\n // is already flattened dot-notation (I18nState.catalogs), not the nested Messages shape\n // validateCatalog() expects, and restoreState() exists specifically for cheap SSR\n // hydration of a payload that was already registered — and therefore already\n // validated — once on the system that produced it. Re-validating here would mean\n // un-flattening every restored catalog back into nested form on every hydration, for a\n // check that's already run.\n for (const [loc, flatCatalog] of Object.entries(st.catalogs)) {\n const normalized = canonL(loc);\n const entry = new CatalogEntry();\n\n entry.setAll(Object.entries(flatCatalog));\n freshEntries.set(normalized, entry);\n }\n\n // Dispose and re-seed the catalog store with the restored entries.\n // onChange is re-wired immediately after dispose.\n catalogStore.dispose();\n catalogStore.onChange = (loc: Locale) => {\n if (state.chainSet.has(loc)) bump();\n };\n catalogStore.seedFrom(freshEntries, new Map());\n\n const normalized = canonL(st.locale);\n\n state = buildState(normalized, fallback, caches);\n _fmt?.clear();\n bump();\n },\n\n scope(prefix: MessageBranchKeys<M> | (string & {})): ScopedI18n {\n const pre = String(prefix);\n\n return getOrCreate(scopeCache, pre, SCOPE_CACHE_MAX, () => ({\n get fmt() {\n return getFormatter();\n },\n has: (key) => hasKey(`${pre}.${key}`),\n t: (key, vars?) => translate(`${pre}.${key}`, vars),\n tp: (key, count, options?) => translatePlural(`${pre}.${key}`, count, options),\n }));\n },\n\n async setLocale(next: Locale): Promise<void> {\n checkDisposed(disposed);\n\n const normalized = canonL(next);\n\n if (state.locale === normalized) return;\n\n // Monotonic generation counter — last writer wins.\n const gen = ++switchGen;\n\n await preload(normalized);\n\n if (disposed || switchGen !== gen) return;\n\n state = buildState(normalized, fallback, caches);\n _fmt?.clear();\n bump();\n },\n\n subscribe: subscribeInternal,\n\n [Symbol.dispose](): void {\n source.dispose();\n },\n\n t: translate,\n\n tp: translatePlural,\n };\n\n return source;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"i18n.cjs","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import type { Catalog, TranslationStoreOptions, TranslationState, Locale, SubscribeOptions } from './types';\n\nimport { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaInvalidStateError } from './errors';\nimport { type Translator, createTranslatorFromCompiled } from './translator';\n\nexport type TranslationSnapshot<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n readonly revision: number;\n readonly translator: Translator<C>;\n};\n\nexport type TranslationStore<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n getSnapshot(): TranslationSnapshot<C>;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): TranslationState<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (snapshot: TranslationSnapshot<C>) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createTranslationStore<C extends Catalog>(options: TranslationStoreOptions<C>): TranslationStore<C> {\n const catalogs = createCatalogStore(options.catalogs);\n const fallback = options.fallback;\n const fallbackLocales = (Array.isArray(fallback) ? fallback : fallback ? [fallback] : []).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(snapshot: TranslationSnapshot<C>) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let revision = 0;\n\n const buildSnapshot = (): TranslationSnapshot<C> => ({\n locale,\n revision,\n translator: createTranslatorFromCompiled<C>(catalogs.catalogMap(), { ...options, fallback, locale }),\n });\n let snapshot = buildSnapshot();\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const dispatch = (listener: (next: TranslationSnapshot<C>) => void): void => {\n try {\n listener(snapshot);\n } catch (error) {\n logError('subscriber error', error);\n }\n };\n\n const notify = (): void => {\n revision++;\n snapshot = buildSnapshot();\n\n for (const listener of [...subscribers]) dispatch(listener);\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return snapshot;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await catalogs.load(targetLocale);\n\n if (!disposed && changed && relevant(targetLocale)) notify();\n },\n get locale() {\n return locale;\n },\n segments(key: string, translateOptions) {\n return snapshot.translator.segmentsDynamic(key, translateOptions);\n },\n segmentsDynamic(key, translateOptions) {\n return snapshot.translator.segmentsDynamic(key, translateOptions);\n },\n serialize() {\n assertLive();\n\n return catalogs.state(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n\n if (next === locale) return;\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) dispatch(listener);\n\n return unsubscribe;\n },\n [Symbol.dispose]: dispose,\n translate(key: string, translateOptions = {}) {\n return snapshot.translator.translateDynamic(key, translateOptions);\n },\n translateDynamic(key, translateOptions) {\n return snapshot.translator.translateDynamic(key, translateOptions);\n },\n } as TranslationStore<C>;\n}\n\nexport function hydrateTranslationStore<C extends Catalog>(\n state: TranslationState<C>,\n options?: Omit<TranslationStoreOptions<C>, 'locale' | 'catalogs'>,\n): TranslationStore<C> {\n if (state.version !== 3) {\n throw new LinguaInvalidStateError(`Unsupported lingua state version: ${String(state.version)}.`);\n }\n\n return createTranslationStore({ ...options, catalogs: state.catalogs, locale: state.locale });\n}\n"],"mappings":"6IA2BA,SAAgB,EAA0C,EAA0D,CAClH,IAAM,EAAW,EAAA,mBAAmB,EAAQ,QAAQ,EAC9C,EAAW,EAAQ,SACnB,GAAmB,MAAM,QAAQ,CAAQ,EAAI,EAAW,EAAW,CAAC,CAAQ,EAAI,CAAC,EAAA,CAAG,IAAI,EAAA,eAAe,EACvG,EAAa,IAAI,gBACjB,EAAc,IAAI,IACpB,EAAW,GACX,EAAS,EAAA,gBAAgB,EAAQ,QAAU,IAAI,EAC/C,EAAW,EAET,OAA+C,CACnD,SACA,WACA,WAAY,EAAA,6BAAgC,EAAS,WAAW,EAAG,CAAE,GAAG,EAAS,WAAU,QAAO,CAAC,CACrG,GACI,EAAW,EAAc,EAEvB,MAAyB,CAC7B,GAAI,EAAU,MAAM,IAAI,EAAA,mBAC1B,EAEM,MAAsB,CACtB,IAEJ,EAAW,GACX,EAAY,MAAM,EAClB,EAAW,MAAM,EACnB,EAEM,EAAY,GAA2D,CAC3E,GAAI,CACF,EAAS,CAAQ,CACnB,MAAgB,CAEhB,CACF,EAEM,MAAqB,CACzB,IACA,EAAW,EAAc,EAEzB,IAAK,IAAM,IAAY,CAAC,GAAG,CAAW,EAAG,EAAS,CAAQ,CAC5D,EAEM,EAAY,GAA+B,EAAA,YAAY,EAAQ,CAAe,CAAC,CAAC,SAAS,CAAS,EAExG,MAAO,CACL,IAAI,gBAAiB,CACnB,OAAO,EAAW,MACpB,EACA,UACA,IAAI,UAAW,CACb,OAAO,CACT,EACA,aAAc,CACZ,OAAO,CACT,EACA,SAAS,EAAa,CACpB,MAAO,CAAC,GAAY,EAAS,SAAS,GAAa,QAAU,CAAM,CACrE,EACA,MAAM,KAAK,EAAa,CACtB,EAAW,EAEX,IAAM,EAAe,EAAA,gBAAgB,GAAa,QAAU,CAAM,EAC5D,EAAU,MAAM,EAAS,KAAK,CAAY,EAE5C,CAAC,GAAY,GAAW,EAAS,CAAY,GAAG,EAAO,CAC7D,EACA,IAAI,QAAS,CACX,OAAO,CACT,EACA,SAAS,EAAa,EAAkB,CACtC,OAAO,EAAS,WAAW,gBAAgB,EAAK,CAAgB,CAClE,EACA,gBAAgB,EAAK,EAAkB,CACrC,OAAO,EAAS,WAAW,gBAAgB,EAAK,CAAgB,CAClE,EACA,WAAY,CAGV,OAFA,EAAW,EAEJ,EAAS,MAAM,CAAM,CAC9B,EACA,MAAM,UAAU,EAAY,CAC1B,EAAW,EAEX,IAAM,EAAO,EAAA,gBAAgB,CAAU,EAEnC,IAAS,IAEb,EAAS,EACT,EAAO,EACT,EACA,UAAU,EAAU,EAAkB,CAGpC,GAFA,EAAW,EAEP,GAAkB,QAAQ,QAAS,UAAa,CAAC,EAErD,IAAM,MAA0B,CAC9B,EAAY,OAAO,CAAQ,EAC3B,GAAkB,QAAQ,oBAAoB,QAAS,CAAW,CACpE,EAOA,OALA,GAAkB,QAAQ,iBAAiB,QAAS,EAAa,CAAE,KAAM,EAAK,CAAC,EAC/E,EAAY,IAAI,CAAQ,EAEpB,GAAkB,WAAW,EAAS,CAAQ,EAE3C,CACT,GACC,OAAO,SAAU,EAClB,UAAU,EAAa,EAAmB,CAAC,EAAG,CAC5C,OAAO,EAAS,WAAW,iBAAiB,EAAK,CAAgB,CACnE,EACA,iBAAiB,EAAK,EAAkB,CACtC,OAAO,EAAS,WAAW,iBAAiB,EAAK,CAAgB,CACnE,CACF,CACF,CAEA,SAAgB,EACd,EACA,EACqB,CACrB,GAAI,EAAM,UAAY,EACpB,MAAM,IAAI,EAAA,wBAAwB,qCAAqC,OAAO,EAAM,OAAO,EAAE,EAAE,EAGjG,OAAO,EAAuB,CAAE,GAAG,EAAS,SAAU,EAAM,SAAU,OAAQ,EAAM,MAAO,CAAC,CAC9F"}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import type { Catalog, TranslationStoreOptions, TranslationState, Locale, SubscribeOptions } from './types';
|
|
2
|
+
import { type Translator } from './translator';
|
|
3
|
+
export type TranslationSnapshot<C extends Catalog = Catalog> = {
|
|
4
|
+
readonly locale: Locale;
|
|
5
|
+
readonly revision: number;
|
|
6
|
+
readonly translator: Translator<C>;
|
|
7
|
+
};
|
|
8
|
+
export type TranslationStore<C extends Catalog = Catalog> = Translator<C> & {
|
|
9
|
+
[Symbol.dispose](): void;
|
|
10
|
+
readonly disposalSignal: AbortSignal;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
readonly disposed: boolean;
|
|
13
|
+
getSnapshot(): TranslationSnapshot<C>;
|
|
14
|
+
isLoaded(options?: {
|
|
15
|
+
locale?: Locale;
|
|
16
|
+
}): boolean;
|
|
17
|
+
load(options?: {
|
|
18
|
+
locale?: Locale;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
serialize(): TranslationState<C>;
|
|
21
|
+
setLocale(locale: Locale): Promise<void>;
|
|
22
|
+
subscribe(listener: (snapshot: TranslationSnapshot<C>) => void, options?: SubscribeOptions): () => void;
|
|
23
|
+
};
|
|
24
|
+
export declare function createTranslationStore<C extends Catalog>(options: TranslationStoreOptions<C>): TranslationStore<C>;
|
|
25
|
+
export declare function hydrateTranslationStore<C extends Catalog>(state: TranslationState<C>, options?: Omit<TranslationStoreOptions<C>, 'locale' | 'catalogs'>): TranslationStore<C>;
|
|
12
26
|
//# sourceMappingURL=i18n.d.ts.map
|
package/dist/i18n.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAM5G,OAAO,EAAE,KAAK,UAAU,EAAgC,MAAM,cAAc,CAAC;AAE7E,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG;IAC1E,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,OAAO,IAAI,IAAI,CAAC;IAChB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,WAAW,IAAI,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;IACjD,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,SAAS,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC;IACjC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,SAAS,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAAC;CACzG,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAqHlH;AAED,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,OAAO,EACvD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC1B,OAAO,CAAC,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC,GAChE,gBAAgB,CAAC,CAAC,CAAC,CAMrB"}
|