@i18n-micro/core
Advanced tools
| /** | ||
| * Shared reactive state for framework i18n adapters (subscribe / getSnapshot / notify). | ||
| */ | ||
| export interface ReactiveI18nStoreOptions { | ||
| locale: string; | ||
| fallbackLocale?: string; | ||
| route?: string; | ||
| } | ||
| export interface ReactiveI18nStore { | ||
| subscribe(listener: () => void): () => void; | ||
| getSnapshot(): string; | ||
| getLocale(): string; | ||
| setLocale(locale: string): void; | ||
| getFallbackLocale(): string; | ||
| setFallbackLocale(locale: string): void; | ||
| getRoute(): string; | ||
| setRoute(routeName: string): void; | ||
| notify(): void; | ||
| } | ||
| export declare function createReactiveI18nStore(options: ReactiveI18nStoreOptions): ReactiveI18nStore; |
+62
-4
| import { CleanTranslation, MissingHandler, Params, PluralFunc, TranslationKey, Translations } from '@i18n-micro/types'; | ||
| import { FormatService } from './format-service'; | ||
| import { FormatService, DateTimeFormatsConfig, NumberFormatsConfig } from './format-service'; | ||
| import { TranslationStorage, useTranslationHelper } from './translation'; | ||
@@ -10,2 +10,6 @@ export interface BaseI18nOptions { | ||
| getCustomMissingHandler?: () => MissingHandler | null; | ||
| /** Named number formats per locale (Vue I18n-compatible). */ | ||
| numberFormats?: NumberFormatsConfig; | ||
| /** Named datetime formats per locale (Vue I18n-compatible `datetimeFormats`). */ | ||
| datetimeFormats?: DateTimeFormatsConfig; | ||
| } | ||
@@ -25,2 +29,7 @@ /** | ||
| getCustomMissingHandler?: () => MissingHandler | null; | ||
| /** | ||
| * Set on the server when the SSR payload should carry only the keys the render | ||
| * actually used. `null` on the client and in `chunk` mode, so the lookup path | ||
| * stays a plain property read. | ||
| */ | ||
| constructor(options?: BaseI18nOptions); | ||
@@ -56,2 +65,22 @@ /** | ||
| /** | ||
| * Two live layers as one tree, via {@link mergeTranslationLayers}. | ||
| * | ||
| * Used when the hot path keeps two objects instead of merging them — the fallback locale, | ||
| * or the Nuxt page-transition layer — so the dump answers what `t()` answers. | ||
| */ | ||
| protected resolveTranslationTree(lower: Record<string, unknown>, upper: Record<string, unknown>): Translations; | ||
| /** | ||
| * Dump of what `t()` can resolve right now for the active locale and route. | ||
| * | ||
| * Live tree, not a clone — read-only; mutate via {@link setTranslation} / merge helpers. | ||
| * When a second layer is live (fallback locale, Nuxt transition hot-reload), walks both | ||
| * through {@link resolveTranslationTree} so the dump matches `t()`. | ||
| */ | ||
| resolveTranslations(routeContext?: unknown): Translations; | ||
| /** | ||
| * Called after {@link setTranslation} changes the dictionary. Adapters override it to | ||
| * bump whatever their reactivity is built on. | ||
| */ | ||
| protected onTranslationsChanged(): void; | ||
| /** | ||
| * Context passed to missing-key handlers. | ||
@@ -64,2 +93,7 @@ */ | ||
| /** | ||
| * Dev-only client `console.warn`, gated by `missingWarn`. | ||
| * Shared by missing translations and missing named formats. | ||
| */ | ||
| protected warnDev(message: string): void; | ||
| /** | ||
| * Warn or invoke handler when translation is missing. | ||
@@ -69,2 +103,7 @@ */ | ||
| /** | ||
| * Warn when a named number/datetime format key is missing. | ||
| * Falls back to default Intl options (visible in dev via {@link warnDev}). | ||
| */ | ||
| protected warnMissingFormat(kind: 'number' | 'datetime', key: string, locale: string): void; | ||
| /** | ||
| * Get translation for a key | ||
@@ -82,9 +121,15 @@ */ | ||
| /** | ||
| * Format number | ||
| * Format number. | ||
| * Supports Vue I18n-style named formats: `tn(1000, 'currency')`. | ||
| */ | ||
| tn(value: number, options?: Intl.NumberFormatOptions): string; | ||
| tn(value: number, key: string, overrides?: Intl.NumberFormatOptions): string; | ||
| tn(value: number, key: string, locale: string, overrides?: Intl.NumberFormatOptions): string; | ||
| /** | ||
| * Format date | ||
| * Format date. | ||
| * Supports Vue I18n-style named formats: `td(date, 'short')`. | ||
| */ | ||
| td(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string; | ||
| td(value: Date | number | string, key: string, overrides?: Intl.DateTimeFormatOptions): string; | ||
| td(value: Date | number | string, key: string, locale: string, overrides?: Intl.DateTimeFormatOptions): string; | ||
| /** | ||
@@ -99,5 +144,18 @@ * Format relative time | ||
| /** | ||
| * Clear cache | ||
| * Replace the value at `key` — a subtree, a string, a number, anything. | ||
| * | ||
| * `set` and not `merge`: `setTranslation('aaa', { fff: 'ggg' })` leaves `aaa` holding only | ||
| * `fff`, and `setTranslation('aaa', 'fff')` leaves a string where the subtree was. Use | ||
| * `mergeTranslations` when the existing siblings should survive. | ||
| * | ||
| * Writes to the active locale and route, so the change is visible to `t()` immediately and | ||
| * lives exactly as long as the chunk it belongs to. | ||
| */ | ||
| setTranslation(key: TranslationKey, value: unknown): void; | ||
| /** | ||
| * Clear translation + formatter caches | ||
| */ | ||
| clearCache(): void; | ||
| private resolveNumberFormatArgs; | ||
| private resolveDateTimeFormatArgs; | ||
| /** | ||
@@ -104,0 +162,0 @@ * Core translation loading logic (without reactivity) |
@@ -0,2 +1,32 @@ | ||
| export type NumberFormatsConfig = Record<string, Record<string, Intl.NumberFormatOptions>>; | ||
| export type DateTimeFormatsConfig = Record<string, Record<string, Intl.DateTimeFormatOptions>>; | ||
| export interface FormatServiceOptions { | ||
| numberFormats?: NumberFormatsConfig; | ||
| /** Vue I18n-compatible name (`datetimeFormats`). */ | ||
| datetimeFormats?: DateTimeFormatsConfig; | ||
| } | ||
| /** | ||
| * Shared Intl formatters with: | ||
| * - Map cache keyed by locale + options (avoids `new Intl.*Format` on every call) | ||
| * - Named formats (`numberFormats` / `datetimeFormats`) for Vue I18n-compatible `$tn(n, 'currency')` | ||
| */ | ||
| export declare class FormatService { | ||
| private numberFormats; | ||
| private datetimeFormats; | ||
| private numberCache; | ||
| private dateCache; | ||
| private relativeCache; | ||
| constructor(options?: FormatServiceOptions); | ||
| setNumberFormats(formats: NumberFormatsConfig): void; | ||
| setDateTimeFormats(formats: DateTimeFormatsConfig): void; | ||
| getNumberFormats(): NumberFormatsConfig; | ||
| getDateTimeFormats(): DateTimeFormatsConfig; | ||
| clearCache(): void; | ||
| /** Resolve a named number format for a locale (exact match, then language subtag). */ | ||
| resolveNumberFormat(locale: string, key: string): Intl.NumberFormatOptions | undefined; | ||
| /** Resolve a named datetime format for a locale (exact match, then language subtag). */ | ||
| resolveDateTimeFormat(locale: string, key: string): Intl.DateTimeFormatOptions | undefined; | ||
| getNumberFormatter(locale: string, options?: Intl.NumberFormatOptions): Intl.NumberFormat; | ||
| getDateTimeFormatter(locale: string, options?: Intl.DateTimeFormatOptions): Intl.DateTimeFormat; | ||
| getRelativeTimeFormatter(locale: string, options?: Intl.RelativeTimeFormatOptions): Intl.RelativeTimeFormat; | ||
| formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string; | ||
@@ -3,0 +33,0 @@ formatDate(value: Date | number | string, locale: string, options?: Intl.DateTimeFormatOptions): string; |
@@ -1,1 +0,1 @@ | ||
| export { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, type MergeTranslationChunkOptions, } from './helpers'; | ||
| export { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, collectTranslationPaths, mergeTranslationChunk, mergeTranslationLayers, resolveTranslation, setTranslationAtKey, translationCacheKey, withPrefixStrategy, type MergeTranslationChunkOptions, } from './helpers'; |
+1
-1
@@ -1,1 +0,1 @@ | ||
| "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=/\{(\w+)\}/g,c="index";function d(t,r){return`${t}:${r||c}`}function f(t,r){if(t==null)return null;const n=a(t,r);return n===void 0?null:n}function g(t,r){return f(t,r)!==null}function y(t,r,n){return Object.keys(t).length===0?r:n?.preserveExisting?Object.assign({},r,t):Object.assign({},t,r)}function x(t,r){return!r||t.indexOf("{")===-1?t:t.replace(s,(n,e)=>{const i=r[e];return i!==void 0?String(i):`{${e}}`})}function a(t,r){if(t==null||typeof r!="string"||r.length===0)return;if(Object.prototype.hasOwnProperty.call(t,r))return t[r];if(!r.includes("."))return;const n=r.split(".");let e=t;for(const i of n){if(e==null||typeof e!="object")return;const u=e;if(!Object.prototype.hasOwnProperty.call(u,i))return;e=u[i]}return e}function p(t){return t==="prefix"||t==="prefix_and_default"}function P(t){return t==="no_prefix"}function v(t){return t==="prefix"}function S(t){return t==="prefix_except_default"}function h(t){return t==="prefix_and_default"}const _=(t,r,n,e,i)=>{const u=i(t,n);if(!u)return null;const l=u.toString().split("|");if(l.length===0)return null;const o=r<l.length?l[r]:l[l.length-1];return o?o.trim().replace("{count}",r.toString()):null};exports.defaultPlural=_;exports.getByPath=a;exports.hasTranslationValue=g;exports.interpolate=x;exports.isNoPrefixStrategy=P;exports.isPrefixAndDefaultStrategy=h;exports.isPrefixExceptDefaultStrategy=S;exports.isPrefixStrategy=v;exports.mergeTranslationChunk=y;exports.resolveTranslation=f;exports.translationCacheKey=d;exports.withPrefixStrategy=p; | ||
| "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=/\{(\w+)\}/g,v="index";function x(t,n){return`${t}:${n||v}`}function u(t,n){if(t==null)return null;const o=_(t,n);return o===void 0?null:o}function P(t,n){return u(t,n)!==null}function T(t,n,o){return Object.keys(t).length===0?n:o?.preserveExisting?a(n,t):a(t,n)}const c=t=>t!==null&&typeof t=="object"&&!Array.isArray(t);function a(t,n){const o={...t};for(const e of Object.keys(n)){if(e==="__proto__")continue;const r=n[e],i=o[e];o[e]=c(r)&&c(i)?a(i,r):r}return o}function O(t,n){return!n||t.indexOf("{")===-1?t:t.replace(h,(o,e)=>{const r=n[e];return r!==void 0?String(r):`{${e}}`})}function _(t,n){if(t==null||typeof n!="string"||n.length===0)return;if(Object.prototype.hasOwnProperty.call(t,n))return t[n];if(!n.includes("."))return;const o=n.split(".");let e=t;for(const r of o){if(e==null||typeof e!="object")return;const i=e;if(!Object.prototype.hasOwnProperty.call(i,r))return;e=i[r]}return e}function S(t,n,o){if(typeof n!="string"||n.length===0)return t;if(Object.prototype.hasOwnProperty.call(t,n)||!n.includes("."))return n==="__proto__"?t:{...t,[n]:o};const e=n.split(".");if(e.some(s=>s==="__proto__"))return t;const r={...t};let i=r;for(const s of e.slice(0,-1)){const l=i[s],f=c(l)?{...l}:{};i[s]=f,i=f}return i[e[e.length-1]]=o,r}function b(t,n){const o=y(t,n),e={};return d(t,n,"",e),Object.keys(e).length===0?o:{...o,...e}}function y(t,n){const o={...t};for(const e of Object.keys(n)){if(e==="__proto__")continue;const r=n[e];if(r==null)continue;const i=o[e];o[e]=c(r)&&c(i)?y(i,r):r}return o}function d(t,n,o,e){for(const r of Object.keys(t)){if(r==="__proto__")continue;const i=t[r];if(!c(i))continue;const s=o?`${o}.${r}`:r,l=n[r];c(l)?d(i,l,s,e):l!=null&&g(i,s,e)}}function g(t,n,o){for(const e of Object.keys(t)){if(e==="__proto__")continue;const r=t[e],i=`${n}.${e}`;c(r)?g(r,i,o):o[i]=r}}function p(t,n,o=""){for(const e of Object.keys(t)){if(e==="__proto__")continue;const r=o?`${o}.${e}`:e;n.add(r);const i=t[e];c(i)&&p(i,n,r)}}function $(t){return t==="prefix"||t==="prefix_and_default"}function m(t){return t==="no_prefix"}function A(t){return t==="prefix"}function E(t){return t==="prefix_except_default"}function j(t){return t==="prefix_and_default"}const k=(t,n,o,e,r)=>{const i=r(t,o);if(!i)return null;const s=i.toString().split("|");if(s.length===0)return null;const l=n<s.length?s[n]:s[s.length-1];return l?l.trim().replace("{count}",n.toString()):null};exports.collectTranslationPaths=p;exports.defaultPlural=k;exports.getByPath=_;exports.hasTranslationValue=P;exports.interpolate=O;exports.isNoPrefixStrategy=m;exports.isPrefixAndDefaultStrategy=j;exports.isPrefixExceptDefaultStrategy=E;exports.isPrefixStrategy=A;exports.mergeTranslationChunk=T;exports.mergeTranslationLayers=b;exports.resolveTranslation=u;exports.setTranslationAtKey=S;exports.translationCacheKey=x;exports.withPrefixStrategy=$; |
+42
-0
@@ -9,5 +9,47 @@ import { Params, PluralFunc, Strategies } from '@i18n-micro/types'; | ||
| } | ||
| /** | ||
| * Merge two translation chunks, descending into nested objects. | ||
| * | ||
| * `Object.assign` is wrong here, and quietly so: chunks are trees, so a shallow merge of | ||
| * `{ nav: { about, home } }` with `{ nav: { extra } }` replaces the whole `nav` subtree | ||
| * and loses `about` and `home`. Nothing throws — the keys simply resolve to themselves | ||
| * later, which is the raw-key render the loader exists to prevent. | ||
| * | ||
| * Written here rather than reusing `@i18n-micro/utils/deep-merge`: that package carries | ||
| * build-time dependencies, and `core` is installed by every consumer at runtime. | ||
| */ | ||
| export declare function mergeTranslationChunk(existing: Record<string, unknown>, incoming: Record<string, unknown>, options?: MergeTranslationChunkOptions): Record<string, unknown>; | ||
| export declare function interpolate(template: string, params: Params): string; | ||
| export declare function getByPath(obj: Record<string, unknown> | null | undefined, path: string): unknown; | ||
| /** | ||
| * A translation tree with `key` set to `value`, whatever either of them is. | ||
| * | ||
| * Replaces rather than merges: `set('aaa', { x: 1 })` on `{ aaa: { bbb: 'ccc' } }` leaves | ||
| * `aaa` holding only `x`, and `set('aaa', 'text')` leaves a string where a subtree was. | ||
| * Merging is what `mergeTranslationChunk` is for. | ||
| * | ||
| * The tree is not mutated — only the nodes along the path are copied, so the call costs the | ||
| * depth of the key and not the size of the dictionary, and callers holding the old tree | ||
| * (a frozen SSR chunk, a rendered snapshot) keep seeing what they had. | ||
| * | ||
| * Key resolution mirrors {@link getByPath}: an existing flat key wins over the dotted path, | ||
| * so a dictionary written as `{ 'a.b': 'x' }` is updated in place rather than gaining a | ||
| * nested `a.b` that `t('a.b')` would never read. | ||
| */ | ||
| export declare function setTranslationAtKey(tree: Record<string, unknown>, key: string, value: unknown): Record<string, unknown>; | ||
| /** | ||
| * Two live lookup layers as one tree, shaped the way a single layer already is. | ||
| * | ||
| * `upper` wins, exactly as a per-key fallthrough does, so the result answers every key the | ||
| * two layers together can answer — with one exception that a tree cannot express: where | ||
| * `upper` holds a scalar and `lower` holds an object at the same path, `t('a')` reads the | ||
| * scalar while `t('a.b')` still reaches into `lower`. Those descendants come back as flat | ||
| * dotted keys, which is what `getByPath` looks at first anyway. | ||
| * | ||
| * The alternative — resolving every collected path one at a time — returned a different | ||
| * shape depending on whether a second layer happened to be live, and repeated each nested | ||
| * leaf as a flat key beside the object holding it. | ||
| */ | ||
| export declare function mergeTranslationLayers(lower: Record<string, unknown>, upper: Record<string, unknown>): Record<string, unknown>; | ||
| export declare function collectTranslationPaths(obj: Record<string, unknown>, paths: Set<string>, prefix?: string): void; | ||
| export declare function withPrefixStrategy(strategy: Strategies): strategy is "prefix" | "prefix_and_default"; | ||
@@ -14,0 +56,0 @@ export declare function isNoPrefixStrategy(strategy: Strategies): strategy is "no_prefix"; |
+42
-0
@@ -9,5 +9,47 @@ import { Params, PluralFunc, Strategies } from '@i18n-micro/types'; | ||
| } | ||
| /** | ||
| * Merge two translation chunks, descending into nested objects. | ||
| * | ||
| * `Object.assign` is wrong here, and quietly so: chunks are trees, so a shallow merge of | ||
| * `{ nav: { about, home } }` with `{ nav: { extra } }` replaces the whole `nav` subtree | ||
| * and loses `about` and `home`. Nothing throws — the keys simply resolve to themselves | ||
| * later, which is the raw-key render the loader exists to prevent. | ||
| * | ||
| * Written here rather than reusing `@i18n-micro/utils/deep-merge`: that package carries | ||
| * build-time dependencies, and `core` is installed by every consumer at runtime. | ||
| */ | ||
| export declare function mergeTranslationChunk(existing: Record<string, unknown>, incoming: Record<string, unknown>, options?: MergeTranslationChunkOptions): Record<string, unknown>; | ||
| export declare function interpolate(template: string, params: Params): string; | ||
| export declare function getByPath(obj: Record<string, unknown> | null | undefined, path: string): unknown; | ||
| /** | ||
| * A translation tree with `key` set to `value`, whatever either of them is. | ||
| * | ||
| * Replaces rather than merges: `set('aaa', { x: 1 })` on `{ aaa: { bbb: 'ccc' } }` leaves | ||
| * `aaa` holding only `x`, and `set('aaa', 'text')` leaves a string where a subtree was. | ||
| * Merging is what `mergeTranslationChunk` is for. | ||
| * | ||
| * The tree is not mutated — only the nodes along the path are copied, so the call costs the | ||
| * depth of the key and not the size of the dictionary, and callers holding the old tree | ||
| * (a frozen SSR chunk, a rendered snapshot) keep seeing what they had. | ||
| * | ||
| * Key resolution mirrors {@link getByPath}: an existing flat key wins over the dotted path, | ||
| * so a dictionary written as `{ 'a.b': 'x' }` is updated in place rather than gaining a | ||
| * nested `a.b` that `t('a.b')` would never read. | ||
| */ | ||
| export declare function setTranslationAtKey(tree: Record<string, unknown>, key: string, value: unknown): Record<string, unknown>; | ||
| /** | ||
| * Two live lookup layers as one tree, shaped the way a single layer already is. | ||
| * | ||
| * `upper` wins, exactly as a per-key fallthrough does, so the result answers every key the | ||
| * two layers together can answer — with one exception that a tree cannot express: where | ||
| * `upper` holds a scalar and `lower` holds an object at the same path, `t('a')` reads the | ||
| * scalar while `t('a.b')` still reaches into `lower`. Those descendants come back as flat | ||
| * dotted keys, which is what `getByPath` looks at first anyway. | ||
| * | ||
| * The alternative — resolving every collected path one at a time — returned a different | ||
| * shape depending on whether a second layer happened to be live, and repeated each nested | ||
| * leaf as a flat key beside the object holding it. | ||
| */ | ||
| export declare function mergeTranslationLayers(lower: Record<string, unknown>, upper: Record<string, unknown>): Record<string, unknown>; | ||
| export declare function collectTranslationPaths(obj: Record<string, unknown>, paths: Set<string>, prefix?: string): void; | ||
| export declare function withPrefixStrategy(strategy: Strategies): strategy is "prefix" | "prefix_and_default"; | ||
@@ -14,0 +56,0 @@ export declare function isNoPrefixStrategy(strategy: Strategies): strategy is "no_prefix"; |
+124
-57
@@ -1,74 +0,141 @@ | ||
| const f = /\{(\w+)\}/g, c = "index"; | ||
| function d(r, t) { | ||
| return `${r}:${t || c}`; | ||
| const p = /\{(\w+)\}/g, y = "index"; | ||
| function O(t, n) { | ||
| return `${t}:${n || y}`; | ||
| } | ||
| function s(r, t) { | ||
| if (r == null) return null; | ||
| const e = a(r, t); | ||
| return e === void 0 ? null : e; | ||
| function g(t, n) { | ||
| if (t == null) return null; | ||
| const o = v(t, n); | ||
| return o === void 0 ? null : o; | ||
| } | ||
| function p(r, t) { | ||
| return s(r, t) !== null; | ||
| function x(t, n) { | ||
| return g(t, n) !== null; | ||
| } | ||
| function g(r, t, e) { | ||
| return Object.keys(r).length === 0 ? t : e?.preserveExisting ? Object.assign({}, t, r) : Object.assign({}, r, t); | ||
| function T(t, n, o) { | ||
| return Object.keys(t).length === 0 ? n : o?.preserveExisting ? l(n, t) : l(t, n); | ||
| } | ||
| function x(r, t) { | ||
| return !t || r.indexOf("{") === -1 ? r : r.replace(f, (e, n) => { | ||
| const i = t[n]; | ||
| return i !== void 0 ? String(i) : `{${n}}`; | ||
| const f = (t) => t !== null && typeof t == "object" && !Array.isArray(t); | ||
| function l(t, n) { | ||
| const o = { ...t }; | ||
| for (const e of Object.keys(n)) { | ||
| if (e === "__proto__") continue; | ||
| const r = n[e], i = o[e]; | ||
| o[e] = f(r) && f(i) ? l(i, r) : r; | ||
| } | ||
| return o; | ||
| } | ||
| function P(t, n) { | ||
| return !n || t.indexOf("{") === -1 ? t : t.replace(p, (o, e) => { | ||
| const r = n[e]; | ||
| return r !== void 0 ? String(r) : `{${e}}`; | ||
| }); | ||
| } | ||
| function a(r, t) { | ||
| if (r == null || typeof t != "string" || t.length === 0) return; | ||
| if (Object.prototype.hasOwnProperty.call(r, t)) | ||
| return r[t]; | ||
| if (!t.includes(".")) return; | ||
| const e = t.split("."); | ||
| let n = r; | ||
| for (const i of e) { | ||
| if (n == null || typeof n != "object") return; | ||
| const u = n; | ||
| if (!Object.prototype.hasOwnProperty.call(u, i)) return; | ||
| n = u[i]; | ||
| function v(t, n) { | ||
| if (t == null || typeof n != "string" || n.length === 0) return; | ||
| if (Object.prototype.hasOwnProperty.call(t, n)) | ||
| return t[n]; | ||
| if (!n.includes(".")) return; | ||
| const o = n.split("."); | ||
| let e = t; | ||
| for (const r of o) { | ||
| if (e == null || typeof e != "object") return; | ||
| const i = e; | ||
| if (!Object.prototype.hasOwnProperty.call(i, r)) return; | ||
| e = i[r]; | ||
| } | ||
| return n; | ||
| return e; | ||
| } | ||
| function v(r) { | ||
| return r === "prefix" || r === "prefix_and_default"; | ||
| function b(t, n, o) { | ||
| if (typeof n != "string" || n.length === 0) return t; | ||
| if (Object.prototype.hasOwnProperty.call(t, n) || !n.includes(".")) | ||
| return n === "__proto__" ? t : { ...t, [n]: o }; | ||
| const e = n.split("."); | ||
| if (e.some((c) => c === "__proto__")) return t; | ||
| const r = { ...t }; | ||
| let i = r; | ||
| for (const c of e.slice(0, -1)) { | ||
| const s = i[c], u = f(s) ? { ...s } : {}; | ||
| i[c] = u, i = u; | ||
| } | ||
| return i[e[e.length - 1]] = o, r; | ||
| } | ||
| function y(r) { | ||
| return r === "no_prefix"; | ||
| function S(t, n) { | ||
| const o = a(t, n), e = {}; | ||
| return _(t, n, "", e), Object.keys(e).length === 0 ? o : { ...o, ...e }; | ||
| } | ||
| function _(r) { | ||
| return r === "prefix"; | ||
| function a(t, n) { | ||
| const o = { ...t }; | ||
| for (const e of Object.keys(n)) { | ||
| if (e === "__proto__") continue; | ||
| const r = n[e]; | ||
| if (r == null) continue; | ||
| const i = o[e]; | ||
| o[e] = f(r) && f(i) ? a(i, r) : r; | ||
| } | ||
| return o; | ||
| } | ||
| function O(r) { | ||
| return r === "prefix_except_default"; | ||
| function _(t, n, o, e) { | ||
| for (const r of Object.keys(t)) { | ||
| if (r === "__proto__") continue; | ||
| const i = t[r]; | ||
| if (!f(i)) continue; | ||
| const c = o ? `${o}.${r}` : r, s = n[r]; | ||
| f(s) ? _(i, s, c, e) : s != null && d(i, c, e); | ||
| } | ||
| } | ||
| function P(r) { | ||
| return r === "prefix_and_default"; | ||
| function d(t, n, o) { | ||
| for (const e of Object.keys(t)) { | ||
| if (e === "__proto__") continue; | ||
| const r = t[e], i = `${n}.${e}`; | ||
| f(r) ? d(r, i, o) : o[i] = r; | ||
| } | ||
| } | ||
| const S = (r, t, e, n, i) => { | ||
| const u = i(r, e); | ||
| if (!u) | ||
| function h(t, n, o = "") { | ||
| for (const e of Object.keys(t)) { | ||
| if (e === "__proto__") continue; | ||
| const r = o ? `${o}.${e}` : e; | ||
| n.add(r); | ||
| const i = t[e]; | ||
| f(i) && h(i, n, r); | ||
| } | ||
| } | ||
| function $(t) { | ||
| return t === "prefix" || t === "prefix_and_default"; | ||
| } | ||
| function E(t) { | ||
| return t === "no_prefix"; | ||
| } | ||
| function j(t) { | ||
| return t === "prefix"; | ||
| } | ||
| function k(t) { | ||
| return t === "prefix_except_default"; | ||
| } | ||
| function w(t) { | ||
| return t === "prefix_and_default"; | ||
| } | ||
| const A = (t, n, o, e, r) => { | ||
| const i = r(t, o); | ||
| if (!i) | ||
| return null; | ||
| const l = u.toString().split("|"); | ||
| if (l.length === 0) return null; | ||
| const o = t < l.length ? l[t] : l[l.length - 1]; | ||
| return o ? o.trim().replace("{count}", t.toString()) : null; | ||
| const c = i.toString().split("|"); | ||
| if (c.length === 0) return null; | ||
| const s = n < c.length ? c[n] : c[c.length - 1]; | ||
| return s ? s.trim().replace("{count}", n.toString()) : null; | ||
| }; | ||
| export { | ||
| S as defaultPlural, | ||
| a as getByPath, | ||
| p as hasTranslationValue, | ||
| x as interpolate, | ||
| y as isNoPrefixStrategy, | ||
| P as isPrefixAndDefaultStrategy, | ||
| O as isPrefixExceptDefaultStrategy, | ||
| _ as isPrefixStrategy, | ||
| g as mergeTranslationChunk, | ||
| s as resolveTranslation, | ||
| d as translationCacheKey, | ||
| v as withPrefixStrategy | ||
| h as collectTranslationPaths, | ||
| A as defaultPlural, | ||
| v as getByPath, | ||
| x as hasTranslationValue, | ||
| P as interpolate, | ||
| E as isNoPrefixStrategy, | ||
| w as isPrefixAndDefaultStrategy, | ||
| k as isPrefixExceptDefaultStrategy, | ||
| j as isPrefixStrategy, | ||
| T as mergeTranslationChunk, | ||
| S as mergeTranslationLayers, | ||
| g as resolveTranslation, | ||
| b as setTranslationAtKey, | ||
| O as translationCacheKey, | ||
| $ as withPrefixStrategy | ||
| }; |
+1
-1
@@ -1,1 +0,1 @@ | ||
| "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./helpers.cjs");class g{formatNumber(t,e,r){return new Intl.NumberFormat(e,r).format(t)}formatDate(t,e,r){const s=new Date(t);return Number.isNaN(s.getTime())?"Invalid Date":new Intl.DateTimeFormat(e,r).format(s)}formatRelativeTime(t,e,r){const s=new Date(t),n=new Intl.RelativeTimeFormat(e,r);if(Number.isNaN(s.getTime()))return n.format(0,"second");const i=Math.floor((Date.now()-s.getTime())/1e3),o=[{unit:"year",seconds:31536e3},{unit:"month",seconds:2592e3},{unit:"day",seconds:86400},{unit:"hour",seconds:3600},{unit:"minute",seconds:60},{unit:"second",seconds:1}];for(const{unit:u,seconds:h}of o){const l=Math.floor(i/h);if(l>=1)return n.format(-l,u)}return n.format(0,"second")}}function f(c){const t=c?.translations??new Map;return{hasCache(e,r){return t.has(a.translationCacheKey(e,r))},getCache(e,r){return t.get(a.translationCacheKey(e,r))},setCache(e,r,s){},hasTranslation(e,r){for(const[s,n]of t)if(s.startsWith(`${e}:`)&&a.getByPath(n,r)!==void 0)return!0;return!1},hasPageTranslation(e,r){return t.has(a.translationCacheKey(e,r))},getTranslation(e,r,s){const n=t.get(a.translationCacheKey(e,r));return n?a.resolveTranslation(n,s):null},loadTranslations(e,r,s="index"){const n=a.translationCacheKey(e,s),i=t.get(n);i?Object.assign(i,r):t.set(n,{...r})},setTranslations(e,r,s="index"){t.set(a.translationCacheKey(e,s),r)},loadPageTranslations(e,r,s){const n=a.translationCacheKey(e,r),i=t.get(n);!i||Object.keys(i).length===0?t.set(n,{...s}):Object.assign(i,s)},mergeTranslation(e,r,s,n=!1){const i=a.translationCacheKey(e,r),o=t.get(i);o?Object.assign(o,s):t.set(i,{...s})},clearCache(){t.clear()}}}class m{constructor(t={}){this.formatter=new g,this.helper=f(t.storage),this.formatter=new g,this.pluralFunc=t.plural||a.defaultPlural,this.missingWarn=t.missingWarn??!0,this.missingHandler=t.missingHandler,this.getCustomMissingHandler=t.getCustomMissingHandler}touch(){}resolveRouteName(t){return typeof t=="string"?t:this.getRoute()}resolveLookup(t,e){const r=this.getLocale(),s=this.resolveRouteName(e);let n=this.helper.getTranslation(r,s,String(t));if(n===null){const i=this.getFallbackLocale();r!==i&&(n=this.helper.getTranslation(i,s,String(t)))}return n}resolveHas(t,e){const r=this.getLocale(),s=this.resolveRouteName(e);return this.helper.getTranslation(r,s,String(t))!==null}getMissingContext(t){return{locale:this.getLocale(),routeName:this.resolveRouteName(t)}}warnMissing(t,e){const{locale:r,routeName:s}=this.getMissingContext(e),n=this.getCustomMissingHandler?.();if(n){n(r,String(t),s);return}if(this.missingHandler){this.missingHandler(r,String(t),s);return}this.missingWarn&&process.env.NODE_ENV!=="production"&&typeof window<"u"&&console.warn(`Not found '${t}' key in '${r}' locale messages for route '${s}'.`)}t(t,e,r,s){if(!t)return"";this.touch();const n=this.resolveLookup(t,s);return n==null?(this.warnMissing(t,s),r===void 0?t:r||t):typeof n!="string"||!e?n:a.interpolate(n,e)}ts(t,e,r,s){return this.t(t,e,r,s)?.toString()??r??t}tc(t,e,r){this.touch();const{count:s,...n}=typeof e=="number"?{count:e}:e;if(s===void 0)return r??t;const i=(u,h,l)=>this.t(u,h,l);return this.pluralFunc(t,Number.parseInt(s.toString(),10),n,this.getLocale(),i)??r??t}tn(t,e){return this.touch(),this.formatter.formatNumber(t,this.getLocale(),e)}td(t,e){return this.touch(),this.formatter.formatDate(t,this.getLocale(),e)}tdr(t,e){return this.touch(),this.formatter.formatRelativeTime(t,this.getLocale(),e)}has(t,e){return this.touch(),this.resolveHas(t,e)}clearCache(){this.helper.clearCache()}loadTranslationsCore(t,e,r,s="index"){r?this.helper.mergeTranslation(t,s,e,!0):this.helper.setTranslations(t,e,s)}loadRouteTranslationsCore(t,e,r,s){s?this.helper.mergeTranslation(t,e,r,!0):this.helper.loadPageTranslations(t,e,r)}}exports.defaultPlural=a.defaultPlural;exports.getByPath=a.getByPath;exports.hasTranslationValue=a.hasTranslationValue;exports.interpolate=a.interpolate;exports.isNoPrefixStrategy=a.isNoPrefixStrategy;exports.isPrefixAndDefaultStrategy=a.isPrefixAndDefaultStrategy;exports.isPrefixExceptDefaultStrategy=a.isPrefixExceptDefaultStrategy;exports.isPrefixStrategy=a.isPrefixStrategy;exports.mergeTranslationChunk=a.mergeTranslationChunk;exports.resolveTranslation=a.resolveTranslation;exports.translationCacheKey=a.translationCacheKey;exports.withPrefixStrategy=a.withPrefixStrategy;exports.BaseI18n=m;exports.FormatService=g;exports.useTranslationHelper=f; | ||
| "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./helpers.cjs");function d(l){if(!l)return"";const t=Object.keys(l).sort();if(t.length===0)return"";const e={};for(const a of t)e[a]=l[a];return JSON.stringify(e)}function m(l,t){const e=d(t);return e?`${l}\0${e}`:l}class g{constructor(t={}){this.numberCache=new Map,this.dateCache=new Map,this.relativeCache=new Map,this.numberFormats=t.numberFormats??{},this.datetimeFormats=t.datetimeFormats??{}}setNumberFormats(t){this.numberFormats=t,this.numberCache.clear()}setDateTimeFormats(t){this.datetimeFormats=t,this.dateCache.clear()}getNumberFormats(){return this.numberFormats}getDateTimeFormats(){return this.datetimeFormats}clearCache(){this.numberCache.clear(),this.dateCache.clear(),this.relativeCache.clear()}resolveNumberFormat(t,e){return this.numberFormats[t]?.[e]??this.numberFormats[t.split("-")[0]]?.[e]}resolveDateTimeFormat(t,e){return this.datetimeFormats[t]?.[e]??this.datetimeFormats[t.split("-")[0]]?.[e]}getNumberFormatter(t,e){const a=m(t,e);let r=this.numberCache.get(a);return r||(r=new Intl.NumberFormat(t,e),this.numberCache.set(a,r)),r}getDateTimeFormatter(t,e){const a=m(t,e);let r=this.dateCache.get(a);return r||(r=new Intl.DateTimeFormat(t,e),this.dateCache.set(a,r)),r}getRelativeTimeFormatter(t,e){const a=m(t,e);let r=this.relativeCache.get(a);return r||(r=new Intl.RelativeTimeFormat(t,e),this.relativeCache.set(a,r)),r}formatNumber(t,e,a){return this.getNumberFormatter(e,a).format(t)}formatDate(t,e,a){const r=new Date(t);return Number.isNaN(r.getTime())?"Invalid Date":this.getDateTimeFormatter(e,a).format(r)}formatRelativeTime(t,e,a){const r=new Date(t),s=this.getRelativeTimeFormatter(e,a);if(Number.isNaN(r.getTime()))return s.format(0,"second");const n=Math.floor((Date.now()-r.getTime())/1e3),o=[{unit:"year",seconds:31536e3},{unit:"month",seconds:2592e3},{unit:"day",seconds:86400},{unit:"hour",seconds:3600},{unit:"minute",seconds:60},{unit:"second",seconds:1}];for(const{unit:h,seconds:u}of o){const c=Math.floor(n/u);if(c>=1)return s.format(-c,h)}return s.format(0,"second")}}function f(l){const t=l?.translations??new Map;return{hasCache(e,a){return t.has(i.translationCacheKey(e,a))},getCache(e,a){return t.get(i.translationCacheKey(e,a))},setCache(e,a,r){},hasTranslation(e,a){for(const[r,s]of t)if(r.startsWith(`${e}:`)&&i.getByPath(s,a)!==void 0)return!0;return!1},hasPageTranslation(e,a){return t.has(i.translationCacheKey(e,a))},getTranslation(e,a,r){const s=t.get(i.translationCacheKey(e,a));return s?i.resolveTranslation(s,r):null},loadTranslations(e,a,r="index"){const s=i.translationCacheKey(e,r),n=t.get(s);n?Object.assign(n,a):t.set(s,{...a})},setTranslations(e,a,r="index"){t.set(i.translationCacheKey(e,r),a)},loadPageTranslations(e,a,r){const s=i.translationCacheKey(e,a),n=t.get(s);!n||Object.keys(n).length===0?t.set(s,{...r}):Object.assign(n,r)},mergeTranslation(e,a,r,s=!1){const n=i.translationCacheKey(e,a),o=t.get(n);o?Object.assign(o,r):t.set(n,{...r})},clearCache(){t.clear()}}}class T{constructor(t={}){this.formatter=new g,this.helper=f(t.storage);const e={numberFormats:t.numberFormats,datetimeFormats:t.datetimeFormats};this.formatter=new g(e),this.pluralFunc=t.plural||i.defaultPlural,this.missingWarn=t.missingWarn??!0,this.missingHandler=t.missingHandler,this.getCustomMissingHandler=t.getCustomMissingHandler}touch(){}resolveRouteName(t){return typeof t=="string"?t:this.getRoute()}resolveLookup(t,e){const a=this.getLocale(),r=this.resolveRouteName(e),s=this.helper.getTranslation(a,r,String(t));if(s!==null)return s;const n=this.getFallbackLocale();return a!==n?this.helper.getTranslation(n,r,String(t)):null}resolveHas(t,e){const a=this.getLocale(),r=this.resolveRouteName(e);return this.helper.getTranslation(a,r,String(t))!==null}resolveTranslationTree(t,e){return i.mergeTranslationLayers(t,e)}resolveTranslations(t){this.touch();const e=this.getLocale(),a=this.resolveRouteName(t),r=this.helper.getCache(e,a)??{},s=this.getFallbackLocale();if(s===e)return r;const n=this.helper.getCache(s,a);return n?this.resolveTranslationTree(n,r):r}onTranslationsChanged(){}getMissingContext(t){return{locale:this.getLocale(),routeName:this.resolveRouteName(t)}}warnDev(t){this.missingWarn&&process.env.NODE_ENV!=="production"&&(typeof window>"u"||console.warn(t))}warnMissing(t,e){const{locale:a,routeName:r}=this.getMissingContext(e),s=this.getCustomMissingHandler?.();if(s){s(a,String(t),r);return}if(this.missingHandler){this.missingHandler(a,String(t),r);return}this.warnDev(`Not found '${t}' key in '${a}' locale messages for route '${r}'.`)}warnMissingFormat(t,e,a){this.warnDev(`Not found '${e}' ${t} format in '${a}' locale. Falling back to default Intl options.`)}t(t,e,a,r){if(!t)return"";this.touch();const s=this.resolveLookup(t,r);return s==null?(this.warnMissing(t,r),a===void 0?t:a||t):typeof s!="string"||!e?s:i.interpolate(s,e)}ts(t,e,a,r){return this.t(t,e,a,r)?.toString()??a??t}tc(t,e,a){this.touch();const{count:r,...s}=typeof e=="number"?{count:e}:e;if(r===void 0)return a??t;const n=(h,u,c)=>this.t(h,u,c);return this.pluralFunc(t,Number.parseInt(r.toString(),10),s,this.getLocale(),n)??a??t}tn(t,e,a,r){this.touch();const s=this.resolveNumberFormatArgs(e,a,r);return this.formatter.formatNumber(t,s.locale,s.options)}td(t,e,a,r){this.touch();const s=this.resolveDateTimeFormatArgs(e,a,r);return this.formatter.formatDate(t,s.locale,s.options)}tdr(t,e){return this.touch(),this.formatter.formatRelativeTime(t,this.getLocale(),e)}has(t,e){return this.touch(),this.resolveHas(t,e)}setTranslation(t,e){const a=this.getLocale(),r=this.getRoute(),s=this.helper.getCache(a,r)??{};this.helper.setTranslations(a,i.setTranslationAtKey(s,String(t),e),r),this.onTranslationsChanged()}clearCache(){this.helper.clearCache(),this.formatter.clearCache()}resolveNumberFormatArgs(t,e,a){if(typeof t!="string")return{locale:this.getLocale(),options:t};let r=this.getLocale(),s;typeof e=="string"?(r=e,s=a):s=e;const n=this.formatter.resolveNumberFormat(r,t);return n||this.warnMissingFormat("number",t,r),!n&&!s?{locale:r,options:void 0}:{locale:r,options:n?{...n,...s}:s}}resolveDateTimeFormatArgs(t,e,a){if(typeof t!="string")return{locale:this.getLocale(),options:t};let r=this.getLocale(),s;typeof e=="string"?(r=e,s=a):s=e;const n=this.formatter.resolveDateTimeFormat(r,t);return n||this.warnMissingFormat("datetime",t,r),!n&&!s?{locale:r,options:void 0}:{locale:r,options:n?{...n,...s}:s}}loadTranslationsCore(t,e,a,r="index"){a?this.helper.mergeTranslation(t,r,e,!0):this.helper.setTranslations(t,e,r)}loadRouteTranslationsCore(t,e,a,r){r?this.helper.mergeTranslation(t,e,a,!0):this.helper.loadPageTranslations(t,e,a)}}function b(l){let t=l.locale,e=l.fallbackLocale??l.locale,a=l.route??"index",r=0;const s=new Set,n=()=>{r++,s.forEach(o=>o())};return{subscribe(o){return s.add(o),()=>s.delete(o)},getSnapshot(){return`${t}:${a}:${r}`},getLocale(){return t},setLocale(o){t!==o&&(t=o,n())},getFallbackLocale(){return e},setFallbackLocale(o){e!==o&&(e=o,n())},getRoute(){return a},setRoute(o){a!==o&&(a=o,n())},notify:n}}exports.collectTranslationPaths=i.collectTranslationPaths;exports.defaultPlural=i.defaultPlural;exports.getByPath=i.getByPath;exports.hasTranslationValue=i.hasTranslationValue;exports.interpolate=i.interpolate;exports.isNoPrefixStrategy=i.isNoPrefixStrategy;exports.isPrefixAndDefaultStrategy=i.isPrefixAndDefaultStrategy;exports.isPrefixExceptDefaultStrategy=i.isPrefixExceptDefaultStrategy;exports.isPrefixStrategy=i.isPrefixStrategy;exports.mergeTranslationChunk=i.mergeTranslationChunk;exports.mergeTranslationLayers=i.mergeTranslationLayers;exports.resolveTranslation=i.resolveTranslation;exports.setTranslationAtKey=i.setTranslationAtKey;exports.translationCacheKey=i.translationCacheKey;exports.withPrefixStrategy=i.withPrefixStrategy;exports.BaseI18n=T;exports.FormatService=g;exports.createReactiveI18nStore=b;exports.useTranslationHelper=f; |
+4
-3
| import { BaseI18n, BaseI18nOptions } from './base'; | ||
| import { FormatService } from './format-service'; | ||
| import { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, MergeTranslationChunkOptions } from './helpers'; | ||
| import { FormatService, DateTimeFormatsConfig, FormatServiceOptions, NumberFormatsConfig } from './format-service'; | ||
| import { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, collectTranslationPaths, mergeTranslationChunk, mergeTranslationLayers, resolveTranslation, setTranslationAtKey, translationCacheKey, withPrefixStrategy, MergeTranslationChunkOptions } from './helpers'; | ||
| import { TranslationStorage, useTranslationHelper } from './translation'; | ||
| export { useTranslationHelper, interpolate, getByPath, hasTranslationValue, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural, FormatService, BaseI18n, type MergeTranslationChunkOptions, type TranslationStorage, type BaseI18nOptions, }; | ||
| import { createReactiveI18nStore, ReactiveI18nStore } from './reactive-store'; | ||
| export { useTranslationHelper, interpolate, getByPath, hasTranslationValue, collectTranslationPaths, mergeTranslationChunk, mergeTranslationLayers, resolveTranslation, setTranslationAtKey, translationCacheKey, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural, FormatService, BaseI18n, createReactiveI18nStore, type MergeTranslationChunkOptions, type TranslationStorage, type BaseI18nOptions, type ReactiveI18nStore, type NumberFormatsConfig, type DateTimeFormatsConfig, type FormatServiceOptions, }; |
+4
-3
| import { BaseI18n, BaseI18nOptions } from './base'; | ||
| import { FormatService } from './format-service'; | ||
| import { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, MergeTranslationChunkOptions } from './helpers'; | ||
| import { FormatService, DateTimeFormatsConfig, FormatServiceOptions, NumberFormatsConfig } from './format-service'; | ||
| import { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, collectTranslationPaths, mergeTranslationChunk, mergeTranslationLayers, resolveTranslation, setTranslationAtKey, translationCacheKey, withPrefixStrategy, MergeTranslationChunkOptions } from './helpers'; | ||
| import { TranslationStorage, useTranslationHelper } from './translation'; | ||
| export { useTranslationHelper, interpolate, getByPath, hasTranslationValue, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural, FormatService, BaseI18n, type MergeTranslationChunkOptions, type TranslationStorage, type BaseI18nOptions, }; | ||
| import { createReactiveI18nStore, ReactiveI18nStore } from './reactive-store'; | ||
| export { useTranslationHelper, interpolate, getByPath, hasTranslationValue, collectTranslationPaths, mergeTranslationChunk, mergeTranslationLayers, resolveTranslation, setTranslationAtKey, translationCacheKey, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural, FormatService, BaseI18n, createReactiveI18nStore, type MergeTranslationChunkOptions, type TranslationStorage, type BaseI18nOptions, type ReactiveI18nStore, type NumberFormatsConfig, type DateTimeFormatsConfig, type FormatServiceOptions, }; |
+243
-74
@@ -1,16 +0,70 @@ | ||
| import { translationCacheKey as a, resolveTranslation as f, getByPath as m, defaultPlural as d, interpolate as v } from "./helpers.mjs"; | ||
| import { hasTranslationValue as w, isNoPrefixStrategy as x, isPrefixAndDefaultStrategy as S, isPrefixExceptDefaultStrategy as D, isPrefixStrategy as H, mergeTranslationChunk as L, withPrefixStrategy as M } from "./helpers.mjs"; | ||
| class g { | ||
| import { translationCacheKey as l, resolveTranslation as g, getByPath as d, defaultPlural as b, mergeTranslationLayers as F, interpolate as T, setTranslationAtKey as v } from "./helpers.mjs"; | ||
| import { collectTranslationPaths as x, hasTranslationValue as y, isNoPrefixStrategy as R, isPrefixAndDefaultStrategy as M, isPrefixExceptDefaultStrategy as $, isPrefixStrategy as H, mergeTranslationChunk as P, withPrefixStrategy as k } from "./helpers.mjs"; | ||
| function C(o) { | ||
| if (!o) return ""; | ||
| const t = Object.keys(o).sort(); | ||
| if (t.length === 0) return ""; | ||
| const e = {}; | ||
| for (const s of t) | ||
| e[s] = o[s]; | ||
| return JSON.stringify(e); | ||
| } | ||
| function m(o, t) { | ||
| const e = C(t); | ||
| return e ? `${o}\0${e}` : o; | ||
| } | ||
| class f { | ||
| constructor(t = {}) { | ||
| this.numberCache = /* @__PURE__ */ new Map(), this.dateCache = /* @__PURE__ */ new Map(), this.relativeCache = /* @__PURE__ */ new Map(), this.numberFormats = t.numberFormats ?? {}, this.datetimeFormats = t.datetimeFormats ?? {}; | ||
| } | ||
| setNumberFormats(t) { | ||
| this.numberFormats = t, this.numberCache.clear(); | ||
| } | ||
| setDateTimeFormats(t) { | ||
| this.datetimeFormats = t, this.dateCache.clear(); | ||
| } | ||
| getNumberFormats() { | ||
| return this.numberFormats; | ||
| } | ||
| getDateTimeFormats() { | ||
| return this.datetimeFormats; | ||
| } | ||
| clearCache() { | ||
| this.numberCache.clear(), this.dateCache.clear(), this.relativeCache.clear(); | ||
| } | ||
| /** Resolve a named number format for a locale (exact match, then language subtag). */ | ||
| resolveNumberFormat(t, e) { | ||
| return this.numberFormats[t]?.[e] ?? this.numberFormats[t.split("-")[0]]?.[e]; | ||
| } | ||
| /** Resolve a named datetime format for a locale (exact match, then language subtag). */ | ||
| resolveDateTimeFormat(t, e) { | ||
| return this.datetimeFormats[t]?.[e] ?? this.datetimeFormats[t.split("-")[0]]?.[e]; | ||
| } | ||
| getNumberFormatter(t, e) { | ||
| const s = m(t, e); | ||
| let r = this.numberCache.get(s); | ||
| return r || (r = new Intl.NumberFormat(t, e), this.numberCache.set(s, r)), r; | ||
| } | ||
| getDateTimeFormatter(t, e) { | ||
| const s = m(t, e); | ||
| let r = this.dateCache.get(s); | ||
| return r || (r = new Intl.DateTimeFormat(t, e), this.dateCache.set(s, r)), r; | ||
| } | ||
| getRelativeTimeFormatter(t, e) { | ||
| const s = m(t, e); | ||
| let r = this.relativeCache.get(s); | ||
| return r || (r = new Intl.RelativeTimeFormat(t, e), this.relativeCache.set(s, r)), r; | ||
| } | ||
| formatNumber(t, e, s) { | ||
| return new Intl.NumberFormat(e, s).format(t); | ||
| return this.getNumberFormatter(e, s).format(t); | ||
| } | ||
| formatDate(t, e, s) { | ||
| const r = new Date(t); | ||
| return Number.isNaN(r.getTime()) ? "Invalid Date" : new Intl.DateTimeFormat(e, s).format(r); | ||
| return Number.isNaN(r.getTime()) ? "Invalid Date" : this.getDateTimeFormatter(e, s).format(r); | ||
| } | ||
| formatRelativeTime(t, e, s) { | ||
| const r = new Date(t), n = new Intl.RelativeTimeFormat(e, s); | ||
| const r = new Date(t), a = this.getRelativeTimeFormatter(e, s); | ||
| if (Number.isNaN(r.getTime())) | ||
| return n.format(0, "second"); | ||
| const i = Math.floor((Date.now() - r.getTime()) / 1e3), o = [ | ||
| return a.format(0, "second"); | ||
| const n = Math.floor((Date.now() - r.getTime()) / 1e3), i = [ | ||
| { unit: "year", seconds: 31536e3 }, | ||
@@ -23,18 +77,18 @@ { unit: "month", seconds: 2592e3 }, | ||
| ]; | ||
| for (const { unit: u, seconds: h } of o) { | ||
| const l = Math.floor(i / h); | ||
| if (l >= 1) | ||
| return n.format(-l, u); | ||
| for (const { unit: h, seconds: u } of i) { | ||
| const c = Math.floor(n / u); | ||
| if (c >= 1) | ||
| return a.format(-c, h); | ||
| } | ||
| return n.format(0, "second"); | ||
| return a.format(0, "second"); | ||
| } | ||
| } | ||
| function T(c) { | ||
| const t = c?.translations ?? /* @__PURE__ */ new Map(); | ||
| function p(o) { | ||
| const t = o?.translations ?? /* @__PURE__ */ new Map(); | ||
| return { | ||
| hasCache(e, s) { | ||
| return t.has(a(e, s)); | ||
| return t.has(l(e, s)); | ||
| }, | ||
| getCache(e, s) { | ||
| return t.get(a(e, s)); | ||
| return t.get(l(e, s)); | ||
| }, | ||
@@ -44,4 +98,4 @@ setCache(e, s, r) { | ||
| hasTranslation(e, s) { | ||
| for (const [r, n] of t) | ||
| if (r.startsWith(`${e}:`) && m(n, s) !== void 0) | ||
| for (const [r, a] of t) | ||
| if (r.startsWith(`${e}:`) && d(a, s) !== void 0) | ||
| return !0; | ||
@@ -51,22 +105,22 @@ return !1; | ||
| hasPageTranslation(e, s) { | ||
| return t.has(a(e, s)); | ||
| return t.has(l(e, s)); | ||
| }, | ||
| getTranslation(e, s, r) { | ||
| const n = t.get(a(e, s)); | ||
| return n ? f(n, r) : null; | ||
| const a = t.get(l(e, s)); | ||
| return a ? g(a, r) : null; | ||
| }, | ||
| loadTranslations(e, s, r = "index") { | ||
| const n = a(e, r), i = t.get(n); | ||
| i ? Object.assign(i, s) : t.set(n, { ...s }); | ||
| const a = l(e, r), n = t.get(a); | ||
| n ? Object.assign(n, s) : t.set(a, { ...s }); | ||
| }, | ||
| setTranslations(e, s, r = "index") { | ||
| t.set(a(e, r), s); | ||
| t.set(l(e, r), s); | ||
| }, | ||
| loadPageTranslations(e, s, r) { | ||
| const n = a(e, s), i = t.get(n); | ||
| !i || Object.keys(i).length === 0 ? t.set(n, { ...r }) : Object.assign(i, r); | ||
| const a = l(e, s), n = t.get(a); | ||
| !n || Object.keys(n).length === 0 ? t.set(a, { ...r }) : Object.assign(n, r); | ||
| }, | ||
| mergeTranslation(e, s, r, n = !1) { | ||
| const i = a(e, s), o = t.get(i); | ||
| o ? Object.assign(o, r) : t.set(i, { ...r }); | ||
| mergeTranslation(e, s, r, a = !1) { | ||
| const n = l(e, s), i = t.get(n); | ||
| i ? Object.assign(i, r) : t.set(n, { ...r }); | ||
| }, | ||
@@ -78,5 +132,15 @@ clearCache() { | ||
| } | ||
| class N { | ||
| class w { | ||
| /** | ||
| * Set on the server when the SSR payload should carry only the keys the render | ||
| * actually used. `null` on the client and in `chunk` mode, so the lookup path | ||
| * stays a plain property read. | ||
| */ | ||
| constructor(t = {}) { | ||
| this.formatter = new g(), this.helper = T(t.storage), this.formatter = new g(), this.pluralFunc = t.plural || d, this.missingWarn = t.missingWarn ?? !0, this.missingHandler = t.missingHandler, this.getCustomMissingHandler = t.getCustomMissingHandler; | ||
| this.formatter = new f(), this.helper = p(t.storage); | ||
| const e = { | ||
| numberFormats: t.numberFormats, | ||
| datetimeFormats: t.datetimeFormats | ||
| }; | ||
| this.formatter = new f(e), this.pluralFunc = t.plural || b, this.missingWarn = t.missingWarn ?? !0, this.missingHandler = t.missingHandler, this.getCustomMissingHandler = t.getCustomMissingHandler; | ||
| } | ||
@@ -99,9 +163,6 @@ // --- Protected hooks (subclasses may override) --- | ||
| resolveLookup(t, e) { | ||
| const s = this.getLocale(), r = this.resolveRouteName(e); | ||
| let n = this.helper.getTranslation(s, r, String(t)); | ||
| if (n === null) { | ||
| const i = this.getFallbackLocale(); | ||
| s !== i && (n = this.helper.getTranslation(i, r, String(t))); | ||
| } | ||
| return n; | ||
| const s = this.getLocale(), r = this.resolveRouteName(e), a = this.helper.getTranslation(s, r, String(t)); | ||
| if (a !== null) return a; | ||
| const n = this.getFallbackLocale(); | ||
| return s !== n ? this.helper.getTranslation(n, r, String(t)) : null; | ||
| } | ||
@@ -116,2 +177,31 @@ /** | ||
| /** | ||
| * Two live layers as one tree, via {@link mergeTranslationLayers}. | ||
| * | ||
| * Used when the hot path keeps two objects instead of merging them — the fallback locale, | ||
| * or the Nuxt page-transition layer — so the dump answers what `t()` answers. | ||
| */ | ||
| resolveTranslationTree(t, e) { | ||
| return F(t, e); | ||
| } | ||
| /** | ||
| * Dump of what `t()` can resolve right now for the active locale and route. | ||
| * | ||
| * Live tree, not a clone — read-only; mutate via {@link setTranslation} / merge helpers. | ||
| * When a second layer is live (fallback locale, Nuxt transition hot-reload), walks both | ||
| * through {@link resolveTranslationTree} so the dump matches `t()`. | ||
| */ | ||
| resolveTranslations(t) { | ||
| this.touch(); | ||
| const e = this.getLocale(), s = this.resolveRouteName(t), r = this.helper.getCache(e, s) ?? {}, a = this.getFallbackLocale(); | ||
| if (a === e) return r; | ||
| const n = this.helper.getCache(a, s); | ||
| return n ? this.resolveTranslationTree(n, r) : r; | ||
| } | ||
| /** | ||
| * Called after {@link setTranslation} changes the dictionary. Adapters override it to | ||
| * bump whatever their reactivity is built on. | ||
| */ | ||
| onTranslationsChanged() { | ||
| } | ||
| /** | ||
| * Context passed to missing-key handlers. | ||
@@ -123,8 +213,15 @@ */ | ||
| /** | ||
| * Dev-only client `console.warn`, gated by `missingWarn`. | ||
| * Shared by missing translations and missing named formats. | ||
| */ | ||
| warnDev(t) { | ||
| this.missingWarn && process.env.NODE_ENV !== "production" && (typeof window > "u" || console.warn(t)); | ||
| } | ||
| /** | ||
| * Warn or invoke handler when translation is missing. | ||
| */ | ||
| warnMissing(t, e) { | ||
| const { locale: s, routeName: r } = this.getMissingContext(e), n = this.getCustomMissingHandler?.(); | ||
| if (n) { | ||
| n(s, String(t), r); | ||
| const { locale: s, routeName: r } = this.getMissingContext(e), a = this.getCustomMissingHandler?.(); | ||
| if (a) { | ||
| a(s, String(t), r); | ||
| return; | ||
@@ -136,4 +233,11 @@ } | ||
| } | ||
| this.missingWarn && process.env.NODE_ENV !== "production" && typeof window < "u" && console.warn(`Not found '${t}' key in '${s}' locale messages for route '${r}'.`); | ||
| this.warnDev(`Not found '${t}' key in '${s}' locale messages for route '${r}'.`); | ||
| } | ||
| /** | ||
| * Warn when a named number/datetime format key is missing. | ||
| * Falls back to default Intl options (visible in dev via {@link warnDev}). | ||
| */ | ||
| warnMissingFormat(t, e, s) { | ||
| this.warnDev(`Not found '${e}' ${t} format in '${s}' locale. Falling back to default Intl options.`); | ||
| } | ||
| // --- Public methods (implemented in base class) --- | ||
@@ -146,4 +250,4 @@ /** | ||
| this.touch(); | ||
| const n = this.resolveLookup(t, r); | ||
| return n == null ? (this.warnMissing(t, r), s === void 0 ? t : s || t) : typeof n != "string" || !e ? n : v(n, e); | ||
| const a = this.resolveLookup(t, r); | ||
| return a == null ? (this.warnMissing(t, r), s === void 0 ? t : s || t) : typeof a != "string" || !e ? a : T(a, e); | ||
| } | ||
@@ -161,19 +265,17 @@ /** | ||
| this.touch(); | ||
| const { count: r, ...n } = typeof e == "number" ? { count: e } : e; | ||
| const { count: r, ...a } = typeof e == "number" ? { count: e } : e; | ||
| if (r === void 0) | ||
| return s ?? t; | ||
| const i = (u, h, l) => this.t(u, h, l); | ||
| return this.pluralFunc(t, Number.parseInt(r.toString(), 10), n, this.getLocale(), i) ?? s ?? t; | ||
| const n = (h, u, c) => this.t(h, u, c); | ||
| return this.pluralFunc(t, Number.parseInt(r.toString(), 10), a, this.getLocale(), n) ?? s ?? t; | ||
| } | ||
| /** | ||
| * Format number | ||
| */ | ||
| tn(t, e) { | ||
| return this.touch(), this.formatter.formatNumber(t, this.getLocale(), e); | ||
| tn(t, e, s, r) { | ||
| this.touch(); | ||
| const a = this.resolveNumberFormatArgs(e, s, r); | ||
| return this.formatter.formatNumber(t, a.locale, a.options); | ||
| } | ||
| /** | ||
| * Format date | ||
| */ | ||
| td(t, e) { | ||
| return this.touch(), this.formatter.formatDate(t, this.getLocale(), e); | ||
| td(t, e, s, r) { | ||
| this.touch(); | ||
| const a = this.resolveDateTimeFormatArgs(e, s, r); | ||
| return this.formatter.formatDate(t, a.locale, a.options); | ||
| } | ||
@@ -193,7 +295,37 @@ /** | ||
| /** | ||
| * Clear cache | ||
| * Replace the value at `key` — a subtree, a string, a number, anything. | ||
| * | ||
| * `set` and not `merge`: `setTranslation('aaa', { fff: 'ggg' })` leaves `aaa` holding only | ||
| * `fff`, and `setTranslation('aaa', 'fff')` leaves a string where the subtree was. Use | ||
| * `mergeTranslations` when the existing siblings should survive. | ||
| * | ||
| * Writes to the active locale and route, so the change is visible to `t()` immediately and | ||
| * lives exactly as long as the chunk it belongs to. | ||
| */ | ||
| setTranslation(t, e) { | ||
| const s = this.getLocale(), r = this.getRoute(), a = this.helper.getCache(s, r) ?? {}; | ||
| this.helper.setTranslations(s, v(a, String(t), e), r), this.onTranslationsChanged(); | ||
| } | ||
| /** | ||
| * Clear translation + formatter caches | ||
| */ | ||
| clearCache() { | ||
| this.helper.clearCache(); | ||
| this.helper.clearCache(), this.formatter.clearCache(); | ||
| } | ||
| resolveNumberFormatArgs(t, e, s) { | ||
| if (typeof t != "string") | ||
| return { locale: this.getLocale(), options: t }; | ||
| let r = this.getLocale(), a; | ||
| typeof e == "string" ? (r = e, a = s) : a = e; | ||
| const n = this.formatter.resolveNumberFormat(r, t); | ||
| return n || this.warnMissingFormat("number", t, r), !n && !a ? { locale: r, options: void 0 } : { locale: r, options: n ? { ...n, ...a } : a }; | ||
| } | ||
| resolveDateTimeFormatArgs(t, e, s) { | ||
| if (typeof t != "string") | ||
| return { locale: this.getLocale(), options: t }; | ||
| let r = this.getLocale(), a; | ||
| typeof e == "string" ? (r = e, a = s) : a = e; | ||
| const n = this.formatter.resolveDateTimeFormat(r, t); | ||
| return n || this.warnMissingFormat("datetime", t, r), !n && !a ? { locale: r, options: void 0 } : { locale: r, options: n ? { ...n, ...a } : a }; | ||
| } | ||
| // --- Public methods (for subclasses to use) --- | ||
@@ -215,18 +347,55 @@ /** | ||
| } | ||
| function L(o) { | ||
| let t = o.locale, e = o.fallbackLocale ?? o.locale, s = o.route ?? "index", r = 0; | ||
| const a = /* @__PURE__ */ new Set(), n = () => { | ||
| r++, a.forEach((i) => i()); | ||
| }; | ||
| return { | ||
| subscribe(i) { | ||
| return a.add(i), () => a.delete(i); | ||
| }, | ||
| getSnapshot() { | ||
| return `${t}:${s}:${r}`; | ||
| }, | ||
| getLocale() { | ||
| return t; | ||
| }, | ||
| setLocale(i) { | ||
| t !== i && (t = i, n()); | ||
| }, | ||
| getFallbackLocale() { | ||
| return e; | ||
| }, | ||
| setFallbackLocale(i) { | ||
| e !== i && (e = i, n()); | ||
| }, | ||
| getRoute() { | ||
| return s; | ||
| }, | ||
| setRoute(i) { | ||
| s !== i && (s = i, n()); | ||
| }, | ||
| notify: n | ||
| }; | ||
| } | ||
| export { | ||
| N as BaseI18n, | ||
| g as FormatService, | ||
| d as defaultPlural, | ||
| m as getByPath, | ||
| w as hasTranslationValue, | ||
| v as interpolate, | ||
| x as isNoPrefixStrategy, | ||
| S as isPrefixAndDefaultStrategy, | ||
| D as isPrefixExceptDefaultStrategy, | ||
| w as BaseI18n, | ||
| f as FormatService, | ||
| x as collectTranslationPaths, | ||
| L as createReactiveI18nStore, | ||
| b as defaultPlural, | ||
| d as getByPath, | ||
| y as hasTranslationValue, | ||
| T as interpolate, | ||
| R as isNoPrefixStrategy, | ||
| M as isPrefixAndDefaultStrategy, | ||
| $ as isPrefixExceptDefaultStrategy, | ||
| H as isPrefixStrategy, | ||
| L as mergeTranslationChunk, | ||
| f as resolveTranslation, | ||
| a as translationCacheKey, | ||
| T as useTranslationHelper, | ||
| M as withPrefixStrategy | ||
| P as mergeTranslationChunk, | ||
| F as mergeTranslationLayers, | ||
| g as resolveTranslation, | ||
| v as setTranslationAtKey, | ||
| l as translationCacheKey, | ||
| p as useTranslationHelper, | ||
| k as withPrefixStrategy | ||
| }; |
+6
-6
| { | ||
| "name": "@i18n-micro/core", | ||
| "version": "1.3.4", | ||
| "version": "1.3.8", | ||
| "description": "Core utilities for translations, formatting, and locale routing in Nuxt I18n Micro.", | ||
@@ -73,9 +73,9 @@ "keywords": [ | ||
| "dependencies": { | ||
| "@i18n-micro/types": "1.2.6" | ||
| "@i18n-micro/types": "1.2.7" | ||
| }, | ||
| "devDependencies": { | ||
| "publint": "^0.3.17", | ||
| "vite": "^7.3.1", | ||
| "vite": "^7.3.6", | ||
| "vite-plugin-dts": "^4.5.4", | ||
| "vitest": "^3.2.4" | ||
| "vitest": "^4.1.10" | ||
| }, | ||
@@ -88,6 +88,6 @@ "engines": { | ||
| "check:package": "publint", | ||
| "test": "jest", | ||
| "test:perf": "jest --config jest.perf.config.cjs", | ||
| "test": "vitest run", | ||
| "test:perf": "vitest run --config vitest.perf.config.ts", | ||
| "test:dist": "vitest run --config vitest.dist.config.ts" | ||
| } | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
61027
66.83%16
6.67%893
83.37%1
Infinity%+ Added
- Removed
Updated