@vielzeug/lingua 2.4.1 → 26.10.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 +9 -28
- package/dist/_bounded-cache.cjs +1 -1
- package/dist/_bounded-cache.cjs.map +1 -1
- package/dist/_bounded-cache.js +1 -9
- package/dist/_bounded-cache.js.map +1 -1
- package/dist/_catalog.cjs +1 -1
- package/dist/_catalog.cjs.map +1 -1
- package/dist/_catalog.js +1 -49
- package/dist/_catalog.js.map +1 -1
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.d.ts +1 -1
- package/dist/_dev.d.ts.map +1 -1
- package/dist/_dev.js +1 -6
- package/dist/_dev.js.map +1 -1
- package/dist/_locale.cjs +1 -1
- package/dist/_locale.cjs.map +1 -1
- package/dist/_locale.js +1 -25
- package/dist/_locale.js.map +1 -1
- package/dist/_resources.cjs +1 -1
- package/dist/_resources.cjs.map +1 -1
- package/dist/_resources.d.ts +9 -3
- package/dist/_resources.d.ts.map +1 -1
- package/dist/_resources.js +1 -50
- package/dist/_resources.js.map +1 -1
- package/dist/_template.cjs +1 -1
- package/dist/_template.cjs.map +1 -1
- package/dist/_template.d.ts +2 -1
- package/dist/_template.d.ts.map +1 -1
- package/dist/_template.js +1 -31
- package/dist/_template.js.map +1 -1
- package/dist/catalog.cjs +1 -1
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.ts +10 -10
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +1 -9
- package/dist/catalog.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -13
- 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.map +1 -1
- package/dist/format.js +1 -84
- 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 +11 -7
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +1 -90
- package/dist/i18n.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- 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/ripple.cjs +2 -0
- package/dist/ripple.cjs.map +1 -0
- package/dist/ripple.d.ts +24 -0
- package/dist/ripple.d.ts.map +1 -0
- package/dist/ripple.js +2 -0
- package/dist/ripple.js.map +1 -0
- package/dist/translator.cjs +1 -1
- package/dist/translator.cjs.map +1 -1
- package/dist/translator.d.ts +15 -10
- package/dist/translator.d.ts.map +1 -1
- package/dist/translator.js +1 -67
- package/dist/translator.js.map +1 -1
- package/dist/types.d.ts +38 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.cjs +1 -1
- package/dist/validate.cjs.map +1 -1
- package/dist/validate.js +1 -45
- package/dist/validate.js.map +1 -1
- package/package.json +25 -8
package/dist/catalog.d.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Catalog, TextKey } from './types';
|
|
1
|
+
import type { Catalog, MessageKey } from './types';
|
|
3
2
|
/** Enumerate every message key in a catalog as a dotted path.
|
|
4
3
|
*
|
|
5
4
|
* Traverses nested grouping objects and explicit `{ plural: ... }` messages,
|
|
6
|
-
* producing the same dotted paths that `
|
|
5
|
+
* producing the same dotted paths that `MessageKey<C>` represents at the type
|
|
7
6
|
* level. Use this to derive key arrays from the catalog itself instead of
|
|
8
7
|
* maintaining a parallel list that can go stale.
|
|
9
8
|
*
|
|
10
|
-
* Pass
|
|
11
|
-
*
|
|
9
|
+
* Pass any catalog subtree to enumerate group-scoped keys. For an `I18n`
|
|
10
|
+
* instance, pass its current locale catalog explicitly.
|
|
12
11
|
*
|
|
13
12
|
* @example
|
|
14
13
|
* ```ts
|
|
@@ -16,12 +15,13 @@ import type { Catalog, TextKey } from './types';
|
|
|
16
15
|
* nav: { home: '...', settings: '...' },
|
|
17
16
|
* };
|
|
18
17
|
* const keys = catalogKeys(messages); // ['nav.home', 'nav.settings']
|
|
18
|
+
* const navKeys = catalogKeys(messages.nav); // ['home', 'settings']
|
|
19
19
|
*
|
|
20
|
-
* // From
|
|
21
|
-
* const i18n =
|
|
22
|
-
* const
|
|
20
|
+
* // From an i18n instance — read the current locale's catalog first
|
|
21
|
+
* const i18n = createI18n({ catalogs: { en: messages }, locale: 'en' });
|
|
22
|
+
* const state = i18n.serialize();
|
|
23
|
+
* const allKeys = catalogKeys(state.catalogs[state.locale]);
|
|
23
24
|
* ```
|
|
24
25
|
*/
|
|
25
|
-
export declare function catalogKeys<C extends Catalog>(
|
|
26
|
-
export declare function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<TextKey<C>>;
|
|
26
|
+
export declare function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<MessageKey<C>>;
|
|
27
27
|
//# sourceMappingURL=catalog.d.ts.map
|
package/dist/catalog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAEvF"}
|
package/dist/catalog.js
CHANGED
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
//#region src/catalog.ts
|
|
3
|
-
function t(t) {
|
|
4
|
-
let n = typeof t == "object" && t && "serialize" in t && "getSnapshot" in t ? t.serialize().catalogs[t.locale] : t;
|
|
5
|
-
return [...e(n).keys()];
|
|
6
|
-
}
|
|
7
|
-
//#endregion
|
|
8
|
-
export { t as catalogKeys };
|
|
9
|
-
|
|
1
|
+
import{compileCatalog as e}from"./_catalog.js";function catalogKeys(t){return[...e(t).keys()]}export{catalogKeys};
|
|
10
2
|
//# sourceMappingURL=catalog.js.map
|
package/dist/catalog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","names":[],"sources":["../src/catalog.ts"],"sourcesContent":["import { compileCatalog } from './_catalog';\nimport type {
|
|
1
|
+
{"version":3,"file":"catalog.js","names":[],"sources":["../src/catalog.ts"],"sourcesContent":["import { compileCatalog } from './_catalog';\nimport type { Catalog, MessageKey } from './types';\n\n/** Enumerate every message key in a catalog as a dotted path.\n *\n * Traverses nested grouping objects and explicit `{ plural: ... }` messages,\n * producing the same dotted paths that `MessageKey<C>` represents at the type\n * level. Use this to derive key arrays from the catalog itself instead of\n * maintaining a parallel list that can go stale.\n *\n * Pass any catalog subtree to enumerate group-scoped keys. For an `I18n`\n * instance, pass its current locale catalog explicitly.\n *\n * @example\n * ```ts\n * const messages = {\n * nav: { home: '...', settings: '...' },\n * };\n * const keys = catalogKeys(messages); // ['nav.home', 'nav.settings']\n * const navKeys = catalogKeys(messages.nav); // ['home', 'settings']\n *\n * // From an i18n instance — read the current locale's catalog first\n * const i18n = createI18n({ catalogs: { en: messages }, locale: 'en' });\n * const state = i18n.serialize();\n * const allKeys = catalogKeys(state.catalogs[state.locale]);\n * ```\n */\nexport function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<MessageKey<C>> {\n return [...compileCatalog(catalog).keys()] as unknown as ReadonlyArray<MessageKey<C>>;\n}\n"],"mappings":"+CA2BA,SAAgB,YAA+B,EAA0C,CACvF,MAAO,CAAC,GAAG,EAAe,CAAO,CAAC,CAAC,KAAK,CAAC,CAC3C"}
|
package/dist/errors.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var LinguaError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},LinguaDisposedError=class extends LinguaError{constructor(){super(`Operation called on a disposed i18n instance.`)}},LinguaInvalidCatalogError=class extends LinguaError{},LinguaInvalidLocaleError=class extends LinguaError{},LinguaInvalidPluralCountError=class extends LinguaError{},LinguaInvalidStateError=class extends LinguaError{},LinguaMissingCatalogError=class extends LinguaError{},LinguaMissingKeyError=class extends LinguaError{},LinguaMissingValueError=class extends LinguaError{};exports.LinguaDisposedError=LinguaDisposedError,exports.LinguaError=LinguaError,exports.LinguaInvalidCatalogError=LinguaInvalidCatalogError,exports.LinguaInvalidLocaleError=LinguaInvalidLocaleError,exports.LinguaInvalidPluralCountError=LinguaInvalidPluralCountError,exports.LinguaInvalidStateError=LinguaInvalidStateError,exports.LinguaMissingCatalogError=LinguaMissingCatalogError,exports.LinguaMissingKeyError=LinguaMissingKeyError,exports.LinguaMissingValueError=LinguaMissingValueError;
|
|
2
2
|
//# sourceMappingURL=errors.cjs.map
|
package/dist/errors.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","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\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed
|
|
1
|
+
{"version":3,"file":"errors.cjs","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\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed i18n instance.');\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 {}\nexport class LinguaMissingKeyError extends LinguaError {}\nexport class LinguaMissingValueError extends LinguaError {}\n"],"mappings":"AAAA,IAAa,YAAb,cAAiC,KAAM,CACrC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,oBAAb,cAAyC,WAAY,CACnD,aAAc,CACZ,MAAM,+CAA+C,CACvD,CACF,EAEa,0BAAb,cAA+C,WAAY,CAAC,EAC/C,yBAAb,cAA8C,WAAY,CAAC,EAC9C,8BAAb,cAAmD,WAAY,CAAC,EACnD,wBAAb,cAA6C,WAAY,CAAC,EAC7C,0BAAb,cAA+C,WAAY,CAAC,EAC/C,sBAAb,cAA2C,WAAY,CAAC,EAC3C,wBAAb,cAA6C,WAAY,CAAC"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -14,4 +14,8 @@ export declare class LinguaInvalidStateError extends LinguaError {
|
|
|
14
14
|
}
|
|
15
15
|
export declare class LinguaMissingCatalogError extends LinguaError {
|
|
16
16
|
}
|
|
17
|
+
export declare class LinguaMissingKeyError extends LinguaError {
|
|
18
|
+
}
|
|
19
|
+
export declare class LinguaMissingValueError extends LinguaError {
|
|
20
|
+
}
|
|
17
21
|
//# 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,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;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"}
|
|
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;CAKjD;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;AAC7D,qBAAa,qBAAsB,SAAQ,WAAW;CAAG;AACzD,qBAAa,uBAAwB,SAAQ,WAAW;CAAG"}
|
package/dist/errors.js
CHANGED
|
@@ -1,14 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
var e = class extends Error {
|
|
3
|
-
constructor(e, t) {
|
|
4
|
-
super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
|
|
5
|
-
}
|
|
6
|
-
}, t = class extends e {
|
|
7
|
-
constructor() {
|
|
8
|
-
super("Operation called on a disposed translation store.");
|
|
9
|
-
}
|
|
10
|
-
}, n = class extends e {}, r = class extends e {}, i = class extends e {}, a = class extends e {}, o = class extends e {};
|
|
11
|
-
//#endregion
|
|
12
|
-
export { t as LinguaDisposedError, e as LinguaError, n as LinguaInvalidCatalogError, r as LinguaInvalidLocaleError, i as LinguaInvalidPluralCountError, a as LinguaInvalidStateError, o as LinguaMissingCatalogError };
|
|
13
|
-
|
|
1
|
+
var LinguaError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},LinguaDisposedError=class extends LinguaError{constructor(){super(`Operation called on a disposed i18n instance.`)}},LinguaInvalidCatalogError=class extends LinguaError{},LinguaInvalidLocaleError=class extends LinguaError{},LinguaInvalidPluralCountError=class extends LinguaError{},LinguaInvalidStateError=class extends LinguaError{},LinguaMissingCatalogError=class extends LinguaError{},LinguaMissingKeyError=class extends LinguaError{},LinguaMissingValueError=class extends LinguaError{};export{LinguaDisposedError,LinguaError,LinguaInvalidCatalogError,LinguaInvalidLocaleError,LinguaInvalidPluralCountError,LinguaInvalidStateError,LinguaMissingCatalogError,LinguaMissingKeyError,LinguaMissingValueError};
|
|
14
2
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed
|
|
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\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed i18n instance.');\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 {}\nexport class LinguaMissingKeyError extends LinguaError {}\nexport class LinguaMissingValueError extends LinguaError {}\n"],"mappings":"AAAA,IAAa,YAAb,cAAiC,KAAM,CACrC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,oBAAb,cAAyC,WAAY,CACnD,aAAc,CACZ,MAAM,+CAA+C,CACvD,CACF,EAEa,0BAAb,cAA+C,WAAY,CAAC,EAC/C,yBAAb,cAA8C,WAAY,CAAC,EAC9C,8BAAb,cAAmD,WAAY,CAAC,EACnD,wBAAb,cAA6C,WAAY,CAAC,EAC7C,0BAAb,cAA+C,WAAY,CAAC,EAC/C,sBAAb,cAA2C,WAAY,CAAC,EAC3C,wBAAb,cAA6C,WAAY,CAAC"}
|
package/dist/format.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_bounded-cache.cjs");
|
|
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 durationLabeled(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 createFormatter(r){let i=new Map,a=new Map,o=new Map,s=new Map,c=new Map,l=new Map,u=typeof r==`string`?()=>r:r;function cachedKey(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}}function cached(n,r,i){return r===void 0?i():e.getOrCreate(n,r,t,i)}return{currency(e,t,n){let r=u(),i={...n,currency:t,style:`currency`};return cached(a,cachedKey(r,i),()=>new Intl.NumberFormat(r,i)).format(e)},date(e,t){let n=u();return cached(o,cachedKey(n,t),()=>new Intl.DateTimeFormat(n,t)).format(e)},duration(e,t){let r=u(),i=Intl;return i.DurationFormat?n.every(t=>e[t]===void 0)?``:cached(l,cachedKey(r,t),()=>new i.DurationFormat(r,t)).format(e):durationLabeled(e)},list(e,t){let n=u(),r=e.map(e=>String(e)),i=t?.style??`long`,a=t?.type===`or`?`disjunction`:`conjunction`;return cached(c,cachedKey(n,{style:i,type:a}),()=>new Intl.ListFormat(n,{style:i,type:a})).format(r)},number(e,t){let n=u();return cached(i,cachedKey(n,t),()=>new Intl.NumberFormat(n,t)).format(e)},relative(e,t,n){let r=u();return cached(s,cachedKey(r,n),()=>new Intl.RelativeTimeFormat(r,n)).format(e,t)}}}exports.createFormatter=createFormatter;
|
|
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';\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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"}
|
|
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 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 // Options that cannot be serialized (circular references, BigInt) get no cache key at\n // all, so each call builds a fresh formatter — sharing one instance across different\n // options would silently format wrong.\n function cachedKey(locale: string, options?: object): string | undefined {\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 return undefined;\n }\n }\n\n function cached<T>(cache: Map<string, T>, key: string | undefined, build: () => T): T {\n return key === undefined ? build() : getOrCreate(cache, key, FORMAT_CACHE_MAX, build);\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 cached(currencyCache, cachedKey(locale, opts), () => new Intl.NumberFormat(locale, opts)).format(value);\n },\n\n date(value, options) {\n const locale = getLocale();\n\n return cached(dateCache, cachedKey(locale, options), () => new Intl.DateTimeFormat(locale, options)).format(\n value,\n );\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 cached(\n durationCache,\n cachedKey(locale, options),\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 cached(\n listCache,\n cachedKey(locale, { style, type }),\n () => new Intl.ListFormat(locale, { style, type }),\n ).format(items);\n },\n\n number(value, options) {\n const locale = getLocale();\n\n return cached(numberCache, cachedKey(locale, options), () => new Intl.NumberFormat(locale, options)).format(\n value,\n );\n },\n\n relative(value, unit, options) {\n const locale = getLocale();\n\n return cached(\n relativeCache,\n cachedKey(locale, options),\n () => new Intl.RelativeTimeFormat(locale, options),\n ).format(value, unit);\n },\n };\n}\n"],"mappings":"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"}
|
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;AAMH,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,CAmG1E"}
|
package/dist/format.js
CHANGED
|
@@ -1,85 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "./_dev.js";
|
|
3
|
-
//#region src/format.ts
|
|
4
|
-
var t = 128, n = [
|
|
5
|
-
"years",
|
|
6
|
-
"months",
|
|
7
|
-
"weeks",
|
|
8
|
-
"days",
|
|
9
|
-
"hours",
|
|
10
|
-
"minutes",
|
|
11
|
-
"seconds",
|
|
12
|
-
"milliseconds",
|
|
13
|
-
"microseconds",
|
|
14
|
-
"nanoseconds"
|
|
15
|
-
], r = {
|
|
16
|
-
days: "d",
|
|
17
|
-
hours: "h",
|
|
18
|
-
microseconds: "us",
|
|
19
|
-
milliseconds: "ms",
|
|
20
|
-
minutes: "min",
|
|
21
|
-
months: "mo",
|
|
22
|
-
nanoseconds: "ns",
|
|
23
|
-
seconds: "s",
|
|
24
|
-
weeks: "w",
|
|
25
|
-
years: "y"
|
|
26
|
-
};
|
|
27
|
-
function i(e) {
|
|
28
|
-
return n.map((t) => {
|
|
29
|
-
let n = e[t];
|
|
30
|
-
return typeof n == "number" ? `${n}${r[t]}` : void 0;
|
|
31
|
-
}).filter((e) => e !== void 0).join(" ");
|
|
32
|
-
}
|
|
33
|
-
function a(r) {
|
|
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) {
|
|
36
|
-
if (!t) return e;
|
|
37
|
-
let n = {};
|
|
38
|
-
for (let e of Object.keys(t).sort()) n[e] = t[e];
|
|
39
|
-
try {
|
|
40
|
-
return `${e}:${JSON.stringify(n)}`;
|
|
41
|
-
} catch {
|
|
42
|
-
return f ||= !0, e;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
currency(n, r, i) {
|
|
47
|
-
let a = d(), s = {
|
|
48
|
-
...i,
|
|
49
|
-
currency: r,
|
|
50
|
-
style: "currency"
|
|
51
|
-
};
|
|
52
|
-
return e(o, p(a, s), t, () => new Intl.NumberFormat(a, s)).format(n);
|
|
53
|
-
},
|
|
54
|
-
date(n, r) {
|
|
55
|
-
let i = d();
|
|
56
|
-
return e(s, p(i, r), t, () => new Intl.DateTimeFormat(i, r)).format(n);
|
|
57
|
-
},
|
|
58
|
-
duration(r, a) {
|
|
59
|
-
let o = d(), s = Intl;
|
|
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);
|
|
61
|
-
},
|
|
62
|
-
list(n, r) {
|
|
63
|
-
let i = d(), a = n.map((e) => String(e)), o = r?.style ?? "long", s = r?.type === "or" ? "disjunction" : "conjunction";
|
|
64
|
-
return e(l, p(i, {
|
|
65
|
-
style: o,
|
|
66
|
-
type: s
|
|
67
|
-
}), t, () => new Intl.ListFormat(i, {
|
|
68
|
-
style: o,
|
|
69
|
-
type: s
|
|
70
|
-
})).format(a);
|
|
71
|
-
},
|
|
72
|
-
number(n, r) {
|
|
73
|
-
let i = d();
|
|
74
|
-
return e(a, p(i, r), t, () => new Intl.NumberFormat(i, r)).format(n);
|
|
75
|
-
},
|
|
76
|
-
relative(n, r, i) {
|
|
77
|
-
let a = d();
|
|
78
|
-
return e(c, p(a, i), t, () => new Intl.RelativeTimeFormat(a, i)).format(n, r);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
//#endregion
|
|
83
|
-
export { a as createFormatter };
|
|
84
|
-
|
|
1
|
+
import{getOrCreate as e}from"./_bounded-cache.js";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 durationLabeled(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 createFormatter(r){let i=new Map,a=new Map,o=new Map,s=new Map,c=new Map,l=new Map,u=typeof r==`string`?()=>r:r;function cachedKey(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}}function cached(n,r,i){return r===void 0?i():e(n,r,t,i)}return{currency(e,t,n){let r=u(),i={...n,currency:t,style:`currency`};return cached(a,cachedKey(r,i),()=>new Intl.NumberFormat(r,i)).format(e)},date(e,t){let n=u();return cached(o,cachedKey(n,t),()=>new Intl.DateTimeFormat(n,t)).format(e)},duration(e,t){let r=u(),i=Intl;return i.DurationFormat?n.every(t=>e[t]===void 0)?``:cached(l,cachedKey(r,t),()=>new i.DurationFormat(r,t)).format(e):durationLabeled(e)},list(e,t){let n=u(),r=e.map(e=>String(e)),i=t?.style??`long`,a=t?.type===`or`?`disjunction`:`conjunction`;return cached(c,cachedKey(n,{style:i,type:a}),()=>new Intl.ListFormat(n,{style:i,type:a})).format(r)},number(e,t){let n=u();return cached(i,cachedKey(n,t),()=>new Intl.NumberFormat(n,t)).format(e)},relative(e,t,n){let r=u();return cached(s,cachedKey(r,n),()=>new Intl.RelativeTimeFormat(r,n)).format(e,t)}}}export{createFormatter};
|
|
85
2
|
//# sourceMappingURL=format.js.map
|
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';\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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"}
|
|
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 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 // Options that cannot be serialized (circular references, BigInt) get no cache key at\n // all, so each call builds a fresh formatter — sharing one instance across different\n // options would silently format wrong.\n function cachedKey(locale: string, options?: object): string | undefined {\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 return undefined;\n }\n }\n\n function cached<T>(cache: Map<string, T>, key: string | undefined, build: () => T): T {\n return key === undefined ? build() : getOrCreate(cache, key, FORMAT_CACHE_MAX, build);\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 cached(currencyCache, cachedKey(locale, opts), () => new Intl.NumberFormat(locale, opts)).format(value);\n },\n\n date(value, options) {\n const locale = getLocale();\n\n return cached(dateCache, cachedKey(locale, options), () => new Intl.DateTimeFormat(locale, options)).format(\n value,\n );\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 cached(\n durationCache,\n cachedKey(locale, options),\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 cached(\n listCache,\n cachedKey(locale, { style, type }),\n () => new Intl.ListFormat(locale, { style, type }),\n ).format(items);\n },\n\n number(value, options) {\n const locale = getLocale();\n\n return cached(numberCache, cachedKey(locale, options), () => new Intl.NumberFormat(locale, options)).format(\n value,\n );\n },\n\n relative(value, unit, options) {\n const locale = getLocale();\n\n return cached(\n relativeCache,\n cachedKey(locale, options),\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
|
-
require("./
|
|
1
|
+
const e=require("./errors.cjs");require("./_dev.cjs");const t=require("./_locale.cjs"),n=require("./_resources.cjs"),r=require("./translator.cjs");function createI18n(i){if(i.state&&i.state.version!==1)throw new e.LinguaInvalidStateError(`Unsupported lingua state version: ${String(i.state.version)}.`);let a=i.fallback,o=(Array.isArray(a)?a:a?[a]:[]).map(t.canonicalLocale),s=new AbortController,c=new Set,l=!1,u=t.canonicalLocale(i.locale??i.state?.locale??`en`),d=0,f=0,p=n.createCatalogStore({catalogs:i.catalogs,loadCatalog:i.loadCatalog});if(i.state)for(let[e,t]of Object.entries(i.state.catalogs))p.hydrate(e,t);let loadTargets=async e=>{let t=[];for(let n of new Set([e,...o]))await p.load(n)&&t.push(n);return t},buildTranslator=()=>{let n=t.localeChain(u,o).some(p.isLoaded)?i.missing:()=>{throw new e.LinguaMissingCatalogError(`No catalog loaded for locale "${u}". Call load() before translating.`)};return r.createTranslatorFromCompiled(p.catalogMap(),{fallback:a,locale:u,missing:n})},buildSnapshot=()=>({locale:u,revision:d,translator:buildTranslator()}),m=buildSnapshot(),assertLive=()=>{if(l)throw new e.LinguaDisposedError},dispose=()=>{l||(l=!0,c.clear(),s.abort())},dispatch=e=>{try{e(m)}catch{}},notify=()=>{d++,m=buildSnapshot();for(let e of[...c])dispatch(e)},relevant=e=>t.localeChain(u,o).includes(e);return{get disposalSignal(){return s.signal},dispose,get disposed(){return l},getSnapshot(){return m},isLoaded(e){return!l&&p.isLoaded(e?.locale??u)},async load(e){assertLive();let n=t.canonicalLocale(e?.locale??u),r=await loadTargets(n);assertLive(),r.some(relevant)&¬ify()},get locale(){return u},parts(e,t){return m.translator.partsDynamic(e,t)},partsDynamic(e,t){return m.translator.partsDynamic(e,t)},serialize(){return assertLive(),p.state(u)},async setLocale(e){assertLive();let n=t.canonicalLocale(e),r=++f,i=await loadTargets(n);if(assertLive(),r===f){if(n===u){i.some(relevant)&¬ify();return}u=n,notify()}},subscribe(e,t){if(assertLive(),t?.signal?.aborted)return()=>{};let unsubscribe=()=>{c.delete(e),t?.signal?.removeEventListener(`abort`,unsubscribe)};return t?.signal?.addEventListener(`abort`,unsubscribe,{once:!0}),c.add(e),t?.immediate&&dispatch(e),unsubscribe},[Symbol.dispose]:dispose,translate(e,t={}){return m.translator.translateDynamic(e,t)},translateDynamic(e,t){return m.translator.translateDynamic(e,t)}}}exports.createI18n=createI18n;
|
|
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 { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaInvalidStateError } from './errors';\nimport { createTranslatorFromCompiled, type Translator } from './translator';\nimport type { Catalog, Locale, SubscribeOptions, TranslationState
|
|
1
|
+
{"version":3,"file":"i18n.cjs","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaInvalidStateError, LinguaMissingCatalogError } from './errors';\nimport { createTranslatorFromCompiled, type Translator } from './translator';\nimport type { Catalog, I18nOptions, Locale, SubscribeOptions, TranslationState } from './types';\n\nexport type I18nSnapshot<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n readonly revision: number;\n /**\n * Translator for this snapshot's locale and loaded catalogs. Components can hold the\n * snapshot and translate through it without importing the whole i18n instance; always\n * re-read it after a notification instead of caching a translator across snapshots.\n */\n readonly translator: Translator<C>;\n};\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n getSnapshot(): I18nSnapshot<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: I18nSnapshot<C>) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n if (options.state && options.state.version !== 1) {\n throw new LinguaInvalidStateError(`Unsupported lingua state version: ${String(options.state.version)}.`);\n }\n\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: I18nSnapshot<C>) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? options.state?.locale ?? 'en');\n let revision = 0;\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n // Hydrate from serialized state (SSR).\n if (options.state) {\n for (const [stateLocale, catalog] of Object.entries(options.state.catalogs)) {\n catalogs.hydrate(stateLocale, catalog);\n }\n }\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildTranslator = (): Translator<C> => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const missing = ready\n ? options.missing\n : () => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslatorFromCompiled<C>(catalogs.catalogMap(), { fallback, locale, missing });\n };\n\n const buildSnapshot = (): I18nSnapshot<C> => ({ locale, revision, translator: buildTranslator() });\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: I18nSnapshot<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 loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n parts(key: string, translateOptions) {\n return snapshot.translator.partsDynamic(key, translateOptions);\n },\n partsDynamic(key, translateOptions) {\n return snapshot.translator.partsDynamic(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 const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\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 I18n<C>;\n}\n"],"mappings":"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"}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { type Translator } from './translator';
|
|
2
|
-
import type { Catalog, Locale, SubscribeOptions, TranslationState
|
|
3
|
-
export type
|
|
2
|
+
import type { Catalog, I18nOptions, Locale, SubscribeOptions, TranslationState } from './types';
|
|
3
|
+
export type I18nSnapshot<C extends Catalog = Catalog> = {
|
|
4
4
|
readonly locale: Locale;
|
|
5
5
|
readonly revision: number;
|
|
6
|
+
/**
|
|
7
|
+
* Translator for this snapshot's locale and loaded catalogs. Components can hold the
|
|
8
|
+
* snapshot and translate through it without importing the whole i18n instance; always
|
|
9
|
+
* re-read it after a notification instead of caching a translator across snapshots.
|
|
10
|
+
*/
|
|
6
11
|
readonly translator: Translator<C>;
|
|
7
12
|
};
|
|
8
|
-
export type
|
|
13
|
+
export type I18n<C extends Catalog = Catalog> = Translator<C> & {
|
|
9
14
|
[Symbol.dispose](): void;
|
|
10
15
|
readonly disposalSignal: AbortSignal;
|
|
11
16
|
dispose(): void;
|
|
12
17
|
readonly disposed: boolean;
|
|
13
|
-
getSnapshot():
|
|
18
|
+
getSnapshot(): I18nSnapshot<C>;
|
|
14
19
|
isLoaded(options?: {
|
|
15
20
|
locale?: Locale;
|
|
16
21
|
}): boolean;
|
|
@@ -19,8 +24,7 @@ export type TranslationStore<C extends Catalog = Catalog> = Translator<C> & {
|
|
|
19
24
|
}): Promise<void>;
|
|
20
25
|
serialize(): TranslationState<C>;
|
|
21
26
|
setLocale(locale: Locale): Promise<void>;
|
|
22
|
-
subscribe(listener: (snapshot:
|
|
27
|
+
subscribe(listener: (snapshot: I18nSnapshot<C>) => void, options?: SubscribeOptions): () => void;
|
|
23
28
|
};
|
|
24
|
-
export declare function
|
|
25
|
-
export declare function hydrateTranslationStore<C extends Catalog>(state: TranslationState<C>, options?: Omit<TranslationStoreOptions<C>, 'locale' | 'catalogs'>): TranslationStore<C>;
|
|
29
|
+
export declare function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C>;
|
|
26
30
|
//# 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":"AAIA,OAAO,EAAgC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhG,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,IAAI,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG;IAC9D,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,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/B,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,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAAC;CAClG,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAgK9E"}
|
package/dist/i18n.js
CHANGED
|
@@ -1,91 +1,2 @@
|
|
|
1
|
-
import "./_dev.js";
|
|
2
|
-
import { LinguaDisposedError as e, LinguaInvalidStateError as t } from "./errors.js";
|
|
3
|
-
import { canonicalLocale as n, localeChain as r } from "./_locale.js";
|
|
4
|
-
import { createCatalogStore as i } from "./_resources.js";
|
|
5
|
-
import { createTranslatorFromCompiled as a } from "./translator.js";
|
|
6
|
-
//#region src/i18n.ts
|
|
7
|
-
function o(t) {
|
|
8
|
-
let o = i(t.catalogs), s = t.fallback, c = (Array.isArray(s) ? s : s ? [s] : []).map(n), l = new AbortController(), u = /* @__PURE__ */ new Set(), d = !1, f = n(t.locale ?? "en"), p = 0, m = () => ({
|
|
9
|
-
locale: f,
|
|
10
|
-
revision: p,
|
|
11
|
-
translator: a(o.catalogMap(), {
|
|
12
|
-
...t,
|
|
13
|
-
fallback: s,
|
|
14
|
-
locale: f
|
|
15
|
-
})
|
|
16
|
-
}), h = m(), g = () => {
|
|
17
|
-
if (d) throw new e();
|
|
18
|
-
}, _ = () => {
|
|
19
|
-
d || (d = !0, u.clear(), l.abort());
|
|
20
|
-
}, v = (e) => {
|
|
21
|
-
try {
|
|
22
|
-
e(h);
|
|
23
|
-
} catch {}
|
|
24
|
-
}, y = () => {
|
|
25
|
-
p++, h = m();
|
|
26
|
-
for (let e of [...u]) v(e);
|
|
27
|
-
}, b = (e) => r(f, c).includes(e);
|
|
28
|
-
return {
|
|
29
|
-
get disposalSignal() {
|
|
30
|
-
return l.signal;
|
|
31
|
-
},
|
|
32
|
-
dispose: _,
|
|
33
|
-
get disposed() {
|
|
34
|
-
return d;
|
|
35
|
-
},
|
|
36
|
-
getSnapshot() {
|
|
37
|
-
return h;
|
|
38
|
-
},
|
|
39
|
-
isLoaded(e) {
|
|
40
|
-
return !d && o.isLoaded(e?.locale ?? f);
|
|
41
|
-
},
|
|
42
|
-
async load(e) {
|
|
43
|
-
g();
|
|
44
|
-
let t = n(e?.locale ?? f), r = await o.load(t);
|
|
45
|
-
!d && r && b(t) && y();
|
|
46
|
-
},
|
|
47
|
-
get locale() {
|
|
48
|
-
return f;
|
|
49
|
-
},
|
|
50
|
-
segments(e, t) {
|
|
51
|
-
return h.translator.segmentsDynamic(e, t);
|
|
52
|
-
},
|
|
53
|
-
segmentsDynamic(e, t) {
|
|
54
|
-
return h.translator.segmentsDynamic(e, t);
|
|
55
|
-
},
|
|
56
|
-
serialize() {
|
|
57
|
-
return g(), o.state(f);
|
|
58
|
-
},
|
|
59
|
-
async setLocale(e) {
|
|
60
|
-
g();
|
|
61
|
-
let t = n(e);
|
|
62
|
-
t !== f && (f = t, y());
|
|
63
|
-
},
|
|
64
|
-
subscribe(e, t) {
|
|
65
|
-
if (g(), t?.signal?.aborted) return () => {};
|
|
66
|
-
let n = () => {
|
|
67
|
-
u.delete(e), t?.signal?.removeEventListener("abort", n);
|
|
68
|
-
};
|
|
69
|
-
return t?.signal?.addEventListener("abort", n, { once: !0 }), u.add(e), t?.immediate && v(e), n;
|
|
70
|
-
},
|
|
71
|
-
[Symbol.dispose]: _,
|
|
72
|
-
translate(e, t = {}) {
|
|
73
|
-
return h.translator.translateDynamic(e, t);
|
|
74
|
-
},
|
|
75
|
-
translateDynamic(e, t) {
|
|
76
|
-
return h.translator.translateDynamic(e, t);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
function s(e, n) {
|
|
81
|
-
if (e.version !== 3) throw new t(`Unsupported lingua state version: ${String(e.version)}.`);
|
|
82
|
-
return o({
|
|
83
|
-
...n,
|
|
84
|
-
catalogs: e.catalogs,
|
|
85
|
-
locale: e.locale
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
//#endregion
|
|
89
|
-
export { o as createTranslationStore, s as hydrateTranslationStore };
|
|
90
|
-
|
|
1
|
+
import{LinguaDisposedError as e,LinguaInvalidStateError as t,LinguaMissingCatalogError as n}from"./errors.js";import"./_dev.js";import{canonicalLocale as r,localeChain as i}from"./_locale.js";import{createCatalogStore as a}from"./_resources.js";import{createTranslatorFromCompiled as o}from"./translator.js";function createI18n(s){if(s.state&&s.state.version!==1)throw new t(`Unsupported lingua state version: ${String(s.state.version)}.`);let c=s.fallback,l=(Array.isArray(c)?c:c?[c]:[]).map(r),u=new AbortController,d=new Set,f=!1,p=r(s.locale??s.state?.locale??`en`),m=0,h=0,g=a({catalogs:s.catalogs,loadCatalog:s.loadCatalog});if(s.state)for(let[e,t]of Object.entries(s.state.catalogs))g.hydrate(e,t);let loadTargets=async e=>{let t=[];for(let n of new Set([e,...l]))await g.load(n)&&t.push(n);return t},buildTranslator=()=>{let e=i(p,l).some(g.isLoaded)?s.missing:()=>{throw new n(`No catalog loaded for locale "${p}". Call load() before translating.`)};return o(g.catalogMap(),{fallback:c,locale:p,missing:e})},buildSnapshot=()=>({locale:p,revision:m,translator:buildTranslator()}),_=buildSnapshot(),assertLive=()=>{if(f)throw new e},dispose=()=>{f||(f=!0,d.clear(),u.abort())},dispatch=e=>{try{e(_)}catch{}},notify=()=>{m++,_=buildSnapshot();for(let e of[...d])dispatch(e)},relevant=e=>i(p,l).includes(e);return{get disposalSignal(){return u.signal},dispose,get disposed(){return f},getSnapshot(){return _},isLoaded(e){return!f&&g.isLoaded(e?.locale??p)},async load(e){assertLive();let t=r(e?.locale??p),n=await loadTargets(t);assertLive(),n.some(relevant)&¬ify()},get locale(){return p},parts(e,t){return _.translator.partsDynamic(e,t)},partsDynamic(e,t){return _.translator.partsDynamic(e,t)},serialize(){return assertLive(),g.state(p)},async setLocale(e){assertLive();let t=r(e),n=++h,i=await loadTargets(t);if(assertLive(),n===h){if(t===p){i.some(relevant)&¬ify();return}p=t,notify()}},subscribe(e,t){if(assertLive(),t?.signal?.aborted)return()=>{};let unsubscribe=()=>{d.delete(e),t?.signal?.removeEventListener(`abort`,unsubscribe)};return t?.signal?.addEventListener(`abort`,unsubscribe,{once:!0}),d.add(e),t?.immediate&&dispatch(e),unsubscribe},[Symbol.dispose]:dispose,translate(e,t={}){return _.translator.translateDynamic(e,t)},translateDynamic(e,t){return _.translator.translateDynamic(e,t)}}}export{createI18n};
|
|
91
2
|
//# sourceMappingURL=i18n.js.map
|