@i18n-micro/core 1.3.0 → 1.3.2

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 CHANGED
@@ -67,14 +67,7 @@ console.log(formattedDate) // '10/5/2023'
67
67
  console.log(formattedRelativeTime) // 'just now'
68
68
 
69
69
  // Handle locale-specific routing
70
- const routeService = new RouteService(
71
- i18nConfig,
72
- router,
73
- hashLocaleDefault,
74
- noPrefixDefault,
75
- navigateTo,
76
- setCookie
77
- )
70
+ const routeService = new RouteService(i18nConfig, router, hashLocaleDefault, noPrefixDefault, navigateTo, setCookie)
78
71
 
79
72
  const localizedRoute = routeService.getLocalizedRoute('/about', currentRoute, 'en')
80
73
  console.log(localizedRoute) // Localized route object
@@ -85,6 +78,7 @@ console.log(localizedRoute) // Localized route object
85
78
  ### `useTranslationHelper`
86
79
 
87
80
  #### Methods
81
+
88
82
  - **`hasCache(locale: string, page: string): boolean`**:
89
83
  Checks if translations for a specific route and locale are cached.
90
84
  - **`getCache(locale: string, routeName: string): Map<string, Translations | unknown> | undefined`**:
@@ -107,13 +101,16 @@ console.log(localizedRoute) // Localized route object
107
101
  ### `interpolate`
108
102
 
109
103
  #### Function
104
+
110
105
  ```typescript
111
106
  interpolate(template: string, params: Params): string
112
107
  ```
108
+
113
109
  - **`template`**: The translation string with placeholders (e.g., `'Hello, {name}!'`).
114
110
  - **`params`**: An object containing key-value pairs for interpolation (e.g., `{ name: 'John' }`).
115
111
 
116
112
  #### Example
113
+
117
114
  ```typescript
118
115
  const result = interpolate('Hello, {name}!', { name: 'John' })
119
116
  console.log(result) // 'Hello, John!'
@@ -122,6 +119,7 @@ console.log(result) // 'Hello, John!'
122
119
  ### `FormatService`
123
120
 
124
121
  #### Methods
122
+
125
123
  - **`formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string`**:
126
124
  Formats a number according to the specified locale and options.
127
125
  - **`formatDate(value: Date | number | string, locale: string, options?: Intl.DateTimeFormatOptions): string`**:
@@ -132,6 +130,7 @@ console.log(result) // 'Hello, John!'
132
130
  ### `RouteService`
133
131
 
134
132
  #### Methods
133
+
135
134
  - **`getCurrentLocale(route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string`**:
136
135
  Returns the current locale based on the route or configuration.
137
136
  - **`getCurrentName(route: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string | null`**:
package/dist/base.d.ts CHANGED
@@ -34,15 +34,41 @@ export declare abstract class BaseI18n {
34
34
  * Get current route name
35
35
  */
36
36
  abstract getRoute(): string;
37
+ /**
38
+ * Called before translation lookup — subclasses use for reactivity tracking.
39
+ */
40
+ protected touch(): void;
41
+ /**
42
+ * Resolve route name from optional route context (string route name or adapter-specific object).
43
+ */
44
+ protected resolveRouteName(routeContext?: unknown): string;
45
+ /**
46
+ * Lookup translation value. Returns null when missing.
47
+ */
48
+ protected resolveLookup(key: TranslationKey, routeContext?: unknown): unknown | null;
49
+ /**
50
+ * Check if translation exists in lookup source.
51
+ */
52
+ protected resolveHas(key: TranslationKey, routeContext?: unknown): boolean;
53
+ /**
54
+ * Context passed to missing-key handlers.
55
+ */
56
+ protected getMissingContext(routeContext?: unknown): {
57
+ locale: string;
58
+ routeName: string;
59
+ };
60
+ /**
61
+ * Warn or invoke handler when translation is missing.
62
+ */
63
+ protected warnMissing(key: TranslationKey, routeContext?: unknown): void;
37
64
  /**
38
65
  * Get translation for a key
39
- * Based on logic from src/runtime/plugins/01.plugin.ts
40
66
  */
41
- t(key: TranslationKey, params?: Params, defaultValue?: string | null, routeName?: string): CleanTranslation;
67
+ t(key: TranslationKey, params?: Params, defaultValue?: string | null, routeContext?: unknown): CleanTranslation;
42
68
  /**
43
69
  * Get translation as string
44
70
  */
45
- ts(key: TranslationKey, params?: Params, defaultValue?: string, routeName?: string): string;
71
+ ts(key: TranslationKey, params?: Params, defaultValue?: string, routeContext?: unknown): string;
46
72
  /**
47
73
  * Plural translation
48
74
  */
@@ -61,9 +87,8 @@ export declare abstract class BaseI18n {
61
87
  tdr(value: Date | number | string, options?: Intl.RelativeTimeFormatOptions): string;
62
88
  /**
63
89
  * Check if translation exists
64
- * Based on logic from src/runtime/plugins/01.plugin.ts
65
90
  */
66
- has(key: TranslationKey, routeName?: string): boolean;
91
+ has(key: TranslationKey, routeContext?: unknown): boolean;
67
92
  /**
68
93
  * Clear cache
69
94
  */
@@ -0,0 +1 @@
1
+ export { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, type MergeTranslationChunkOptions, } from './helpers';
@@ -0,0 +1 @@
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;
@@ -0,0 +1,27 @@
1
+ import { Params, PluralFunc, Strategies } from '@i18n-micro/types';
2
+ export declare function translationCacheKey(locale: string, routeName?: string): string;
3
+ export declare function resolveTranslation(obj: Record<string, unknown> | null | undefined, key: string): unknown | null;
4
+ export declare function hasTranslationValue(obj: Record<string, unknown> | null | undefined, key: string): boolean;
5
+ export interface MergeTranslationChunkOptions {
6
+ /** When true, existing keys win over incoming. Default: incoming wins. */
7
+ preserveExisting?: boolean;
8
+ }
9
+ export declare function mergeTranslationChunk(existing: Record<string, unknown>, incoming: Record<string, unknown>, options?: MergeTranslationChunkOptions): Record<string, unknown>;
10
+ export declare function interpolate(template: string, params: Params): string;
11
+ export declare function getByPath(obj: Record<string, unknown> | null | undefined, path: string): unknown;
12
+ export declare function withPrefixStrategy(strategy: Strategies): strategy is "prefix" | "prefix_and_default";
13
+ export declare function isNoPrefixStrategy(strategy: Strategies): strategy is "no_prefix";
14
+ export declare function isPrefixStrategy(strategy: Strategies): strategy is "prefix";
15
+ export declare function isPrefixExceptDefaultStrategy(strategy: Strategies): strategy is "prefix_except_default";
16
+ export declare function isPrefixAndDefaultStrategy(strategy: Strategies): strategy is "prefix_and_default";
17
+ /**
18
+ * Default pluralization function
19
+ * Splits translation by '|' and selects form based on count
20
+ * @param key - Translation key
21
+ * @param count - Count for pluralization
22
+ * @param params - Parameters for translation
23
+ * @param _locale - Current locale (unused in default implementation)
24
+ * @param getTranslation - Function to get translation value
25
+ * @returns Selected plural form or null if not found
26
+ */
27
+ export declare const defaultPlural: PluralFunc;
package/dist/helpers.d.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  import { Params, PluralFunc, Strategies } from '@i18n-micro/types';
2
+ export declare function translationCacheKey(locale: string, routeName?: string): string;
3
+ export declare function resolveTranslation(obj: Record<string, unknown> | null | undefined, key: string): unknown | null;
4
+ export declare function hasTranslationValue(obj: Record<string, unknown> | null | undefined, key: string): boolean;
5
+ export interface MergeTranslationChunkOptions {
6
+ /** When true, existing keys win over incoming. Default: incoming wins. */
7
+ preserveExisting?: boolean;
8
+ }
9
+ export declare function mergeTranslationChunk(existing: Record<string, unknown>, incoming: Record<string, unknown>, options?: MergeTranslationChunkOptions): Record<string, unknown>;
2
10
  export declare function interpolate(template: string, params: Params): string;
11
+ export declare function getByPath(obj: Record<string, unknown> | null | undefined, path: string): unknown;
3
12
  export declare function withPrefixStrategy(strategy: Strategies): strategy is "prefix" | "prefix_and_default";
4
13
  export declare function isNoPrefixStrategy(strategy: Strategies): strategy is "no_prefix";
5
14
  export declare function isPrefixStrategy(strategy: Strategies): strategy is "prefix";
@@ -0,0 +1,74 @@
1
+ const f = /\{(\w+)\}/g, c = "index";
2
+ function d(r, t) {
3
+ return `${r}:${t || c}`;
4
+ }
5
+ function s(r, t) {
6
+ if (r == null) return null;
7
+ const e = a(r, t);
8
+ return e === void 0 ? null : e;
9
+ }
10
+ function p(r, t) {
11
+ return s(r, t) !== null;
12
+ }
13
+ function g(r, t, e) {
14
+ return Object.keys(r).length === 0 ? t : e?.preserveExisting ? Object.assign({}, t, r) : Object.assign({}, r, t);
15
+ }
16
+ function x(r, t) {
17
+ return !t || r.indexOf("{") === -1 ? r : r.replace(f, (e, n) => {
18
+ const i = t[n];
19
+ return i !== void 0 ? String(i) : `{${n}}`;
20
+ });
21
+ }
22
+ function a(r, t) {
23
+ if (r == null || typeof t != "string" || t.length === 0) return;
24
+ if (Object.prototype.hasOwnProperty.call(r, t))
25
+ return r[t];
26
+ if (!t.includes(".")) return;
27
+ const e = t.split(".");
28
+ let n = r;
29
+ for (const i of e) {
30
+ if (n == null || typeof n != "object") return;
31
+ const u = n;
32
+ if (!Object.prototype.hasOwnProperty.call(u, i)) return;
33
+ n = u[i];
34
+ }
35
+ return n;
36
+ }
37
+ function v(r) {
38
+ return r === "prefix" || r === "prefix_and_default";
39
+ }
40
+ function y(r) {
41
+ return r === "no_prefix";
42
+ }
43
+ function _(r) {
44
+ return r === "prefix";
45
+ }
46
+ function O(r) {
47
+ return r === "prefix_except_default";
48
+ }
49
+ function P(r) {
50
+ return r === "prefix_and_default";
51
+ }
52
+ const S = (r, t, e, n, i) => {
53
+ const u = i(r, e);
54
+ if (!u)
55
+ return null;
56
+ const l = u.toString().split("|");
57
+ if (l.length === 0) return null;
58
+ const o = t < l.length ? l[t] : l[l.length - 1];
59
+ return o ? o.trim().replace("{count}", t.toString()) : null;
60
+ };
61
+ export {
62
+ S as defaultPlural,
63
+ a as getByPath,
64
+ p as hasTranslationValue,
65
+ x as interpolate,
66
+ y as isNoPrefixStrategy,
67
+ P as isPrefixAndDefaultStrategy,
68
+ O as isPrefixExceptDefaultStrategy,
69
+ _ as isPrefixStrategy,
70
+ g as mergeTranslationChunk,
71
+ s as resolveTranslation,
72
+ d as translationCacheKey,
73
+ v as withPrefixStrategy
74
+ };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class f{formatNumber(t,e,n){return new Intl.NumberFormat(e,n).format(t)}formatDate(t,e,n){const r=new Date(t);return Number.isNaN(r.getTime())?"Invalid Date":new Intl.DateTimeFormat(e,n).format(r)}formatRelativeTime(t,e,n){const r=new Date(t);if(Number.isNaN(r.getTime()))return new Intl.RelativeTimeFormat(e,n).format(0,"second");const i=Math.floor((new Date().getTime()-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:l,seconds:c}of o){const u=Math.floor(i/c);if(u>=1)return new Intl.RelativeTimeFormat(e,n).format(-u,l)}return new Intl.RelativeTimeFormat(e,n).format(0,"second")}}const p=/\{(\w+)\}/g;function h(s,t){return t?s.replace(p,(e,n)=>{const r=t[n];return r!==void 0?String(r):`{${n}}`}):s}function x(s){return s==="prefix"||s==="prefix_and_default"}function T(s){return s==="no_prefix"}function v(s){return s==="prefix"}function $(s){return s==="prefix_except_default"}function S(s){return s==="prefix_and_default"}const m=(s,t,e,n,r)=>{const a=r(s,e);if(!a)return null;const i=a.toString().split("|");if(i.length===0)return null;const o=t<i.length?i[t]:i[i.length-1];return o?o.trim().replace("{count}",t.toString()):null};function g(s,t){if(!s||typeof t!="string")return null;if(t in s){const r=s[t];return r}const e=t.split(".");let n=s;for(const r of e)if(n&&typeof n=="object"&&r in n)n=n[r];else return null;return n??null}function d(s){const t=s?.translations??new Map;return{hasCache(e,n){const r=n||"index";return t.has(`${e}:${r}`)},getCache(e,n){const r=n||"index";return t.get(`${e}:${r}`)},setCache(e,n,r){},hasTranslation(e,n){for(const[r,a]of t)if(r.startsWith(`${e}:`)&&g(a,n)!==null)return!0;return!1},hasPageTranslation(e,n){const r=n||"index";return t.has(`${e}:${r}`)},getTranslation(e,n,r){const a=n||"index";return g(t.get(`${e}:${a}`),r)},loadTranslations(e,n,r="index"){const i=`${e}:${r||"index"}`,o=t.get(i)??{};t.set(i,{...o,...n})},setTranslations(e,n,r="index"){const a=r||"index";t.set(`${e}:${a}`,n)},loadPageTranslations(e,n,r){const i=`${e}:${n||"index"}`,o=t.get(i);!o||Object.keys(o).length===0?t.set(i,r):t.set(i,{...o,...r})},mergeTranslation(e,n,r,a=!1){const o=`${e}:${n||"index"}`,l=t.get(o)??{};t.set(o,{...l,...r})},clearCache(){t.clear()}}}class w{constructor(t={}){this.formatter=new f,this.helper=d(t.storage),this.formatter=new f,this.pluralFunc=t.plural||m,this.missingWarn=t.missingWarn??!0,this.missingHandler=t.missingHandler,this.getCustomMissingHandler=t.getCustomMissingHandler}t(t,e,n,r){if(!t)return"";const a=this.getLocale(),i=r||this.getRoute();let o=this.helper.getTranslation(a,i,t);if(!o){const l=this.getFallbackLocale();a!==l&&(o=this.helper.getTranslation(l,i,t))}if(!o){const l=this.getCustomMissingHandler?.();l?l(a,t,i):this.missingHandler?this.missingHandler(a,t,i):this.missingWarn&&process.env.NODE_ENV!=="production"&&typeof window<"u"&&console.warn(`Not found '${t}' key in '${a}' locale messages for route '${i}'.`),o=n===void 0?t:n||t}return typeof o=="string"&&e?h(o,e):o}ts(t,e,n,r){return this.t(t,e,n,r)?.toString()??n??t}tc(t,e,n){const{count:r,...a}=typeof e=="number"?{count:e}:e;if(r===void 0)return n??t;const i=(l,c,u)=>this.t(l,c,u);return this.pluralFunc(t,Number.parseInt(r.toString(),10),a,this.getLocale(),i)??n??t}tn(t,e){return this.formatter.formatNumber(t,this.getLocale(),e)}td(t,e){return this.formatter.formatDate(t,this.getLocale(),e)}tdr(t,e){return this.formatter.formatRelativeTime(t,this.getLocale(),e)}has(t,e){const n=e||this.getRoute(),r=this.getLocale();return!!this.helper.getTranslation(r,n,t)}clearCache(){this.helper.clearCache()}loadTranslationsCore(t,e,n,r="index"){n?this.helper.mergeTranslation(t,r,e,!0):this.helper.setTranslations(t,e,r)}loadRouteTranslationsCore(t,e,n,r){r?this.helper.mergeTranslation(t,e,n,!0):this.helper.loadPageTranslations(t,e,n)}}exports.BaseI18n=w;exports.FormatService=f;exports.defaultPlural=m;exports.interpolate=h;exports.isNoPrefixStrategy=T;exports.isPrefixAndDefaultStrategy=S;exports.isPrefixExceptDefaultStrategy=$;exports.isPrefixStrategy=v;exports.useTranslationHelper=d;exports.withPrefixStrategy=x;
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;
@@ -0,0 +1,5 @@
1
+ import { BaseI18n, BaseI18nOptions } from './base';
2
+ import { FormatService } from './format-service';
3
+ import { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, MergeTranslationChunkOptions } from './helpers';
4
+ import { TranslationStorage, useTranslationHelper } from './translation';
5
+ export { useTranslationHelper, interpolate, getByPath, hasTranslationValue, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural, FormatService, BaseI18n, type MergeTranslationChunkOptions, type TranslationStorage, type BaseI18nOptions, };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BaseI18n, BaseI18nOptions } from './base';
2
2
  import { FormatService } from './format-service';
3
- import { defaultPlural, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, withPrefixStrategy } from './helpers';
3
+ import { defaultPlural, getByPath, hasTranslationValue, interpolate, isNoPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy, isPrefixStrategy, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, MergeTranslationChunkOptions } from './helpers';
4
4
  import { TranslationStorage, useTranslationHelper } from './translation';
5
- export { useTranslationHelper, interpolate, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural, FormatService, BaseI18n, type TranslationStorage, type BaseI18nOptions, };
5
+ export { useTranslationHelper, interpolate, getByPath, hasTranslationValue, mergeTranslationChunk, resolveTranslation, translationCacheKey, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural, FormatService, BaseI18n, type MergeTranslationChunkOptions, type TranslationStorage, type BaseI18nOptions, };
package/dist/index.mjs CHANGED
@@ -1,16 +1,18 @@
1
- class f {
2
- formatNumber(t, e, n) {
3
- return new Intl.NumberFormat(e, n).format(t);
1
+ import { translationCacheKey as a, resolveTranslation as f, getByPath as m, defaultPlural as d, interpolate as v } from "./helpers.mjs";
2
+ 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";
3
+ class g {
4
+ formatNumber(t, e, s) {
5
+ return new Intl.NumberFormat(e, s).format(t);
4
6
  }
5
- formatDate(t, e, n) {
7
+ formatDate(t, e, s) {
6
8
  const r = new Date(t);
7
- return Number.isNaN(r.getTime()) ? "Invalid Date" : new Intl.DateTimeFormat(e, n).format(r);
9
+ return Number.isNaN(r.getTime()) ? "Invalid Date" : new Intl.DateTimeFormat(e, s).format(r);
8
10
  }
9
- formatRelativeTime(t, e, n) {
10
- const r = new Date(t);
11
+ formatRelativeTime(t, e, s) {
12
+ const r = new Date(t), n = new Intl.RelativeTimeFormat(e, s);
11
13
  if (Number.isNaN(r.getTime()))
12
- return new Intl.RelativeTimeFormat(e, n).format(0, "second");
13
- const i = Math.floor(((/* @__PURE__ */ new Date()).getTime() - r.getTime()) / 1e3), o = [
14
+ return n.format(0, "second");
15
+ const i = Math.floor((Date.now() - r.getTime()) / 1e3), o = [
14
16
  { unit: "year", seconds: 31536e3 },
15
17
  { unit: "month", seconds: 2592e3 },
16
18
  { unit: "day", seconds: 86400 },
@@ -18,172 +20,164 @@ class f {
18
20
  { unit: "minute", seconds: 60 },
19
21
  { unit: "second", seconds: 1 }
20
22
  ];
21
- for (const { unit: l, seconds: u } of o) {
22
- const c = Math.floor(i / u);
23
- if (c >= 1)
24
- return new Intl.RelativeTimeFormat(e, n).format(-c, l);
23
+ for (const { unit: u, seconds: h } of o) {
24
+ const l = Math.floor(i / h);
25
+ if (l >= 1)
26
+ return n.format(-l, u);
25
27
  }
26
- return new Intl.RelativeTimeFormat(e, n).format(0, "second");
28
+ return n.format(0, "second");
27
29
  }
28
30
  }
29
- const g = /\{(\w+)\}/g;
30
- function m(s, t) {
31
- return t ? s.replace(g, (e, n) => {
32
- const r = t[n];
33
- return r !== void 0 ? String(r) : `{${n}}`;
34
- }) : s;
35
- }
36
- function x(s) {
37
- return s === "prefix" || s === "prefix_and_default";
38
- }
39
- function T(s) {
40
- return s === "no_prefix";
41
- }
42
- function $(s) {
43
- return s === "prefix";
44
- }
45
- function v(s) {
46
- return s === "prefix_except_default";
47
- }
48
- function w(s) {
49
- return s === "prefix_and_default";
50
- }
51
- const d = (s, t, e, n, r) => {
52
- const a = r(s, e);
53
- if (!a)
54
- return null;
55
- const i = a.toString().split("|");
56
- if (i.length === 0) return null;
57
- const o = t < i.length ? i[t] : i[i.length - 1];
58
- return o ? o.trim().replace("{count}", t.toString()) : null;
59
- };
60
- function h(s, t) {
61
- if (!s || typeof t != "string") return null;
62
- if (t in s) {
63
- const r = s[t];
64
- return r;
65
- }
66
- const e = t.split(".");
67
- let n = s;
68
- for (const r of e)
69
- if (n && typeof n == "object" && r in n)
70
- n = n[r];
71
- else
72
- return null;
73
- return n ?? null;
74
- }
75
- function p(s) {
76
- const t = s?.translations ?? /* @__PURE__ */ new Map();
31
+ function T(c) {
32
+ const t = c?.translations ?? /* @__PURE__ */ new Map();
77
33
  return {
78
- hasCache(e, n) {
79
- const r = n || "index";
80
- return t.has(`${e}:${r}`);
34
+ hasCache(e, s) {
35
+ return t.has(a(e, s));
81
36
  },
82
- getCache(e, n) {
83
- const r = n || "index";
84
- return t.get(`${e}:${r}`);
37
+ getCache(e, s) {
38
+ return t.get(a(e, s));
85
39
  },
86
- setCache(e, n, r) {
40
+ setCache(e, s, r) {
87
41
  },
88
- hasTranslation(e, n) {
89
- for (const [r, a] of t)
90
- if (r.startsWith(`${e}:`) && h(a, n) !== null)
42
+ hasTranslation(e, s) {
43
+ for (const [r, n] of t)
44
+ if (r.startsWith(`${e}:`) && m(n, s) !== void 0)
91
45
  return !0;
92
46
  return !1;
93
47
  },
94
- hasPageTranslation(e, n) {
95
- const r = n || "index";
96
- return t.has(`${e}:${r}`);
48
+ hasPageTranslation(e, s) {
49
+ return t.has(a(e, s));
97
50
  },
98
- getTranslation(e, n, r) {
99
- const a = n || "index";
100
- return h(t.get(`${e}:${a}`), r);
51
+ getTranslation(e, s, r) {
52
+ const n = t.get(a(e, s));
53
+ return n ? f(n, r) : null;
101
54
  },
102
- loadTranslations(e, n, r = "index") {
103
- const i = `${e}:${r || "index"}`, o = t.get(i) ?? {};
104
- t.set(i, { ...o, ...n });
55
+ loadTranslations(e, s, r = "index") {
56
+ const n = a(e, r), i = t.get(n);
57
+ i ? Object.assign(i, s) : t.set(n, { ...s });
105
58
  },
106
- setTranslations(e, n, r = "index") {
107
- const a = r || "index";
108
- t.set(`${e}:${a}`, n);
59
+ setTranslations(e, s, r = "index") {
60
+ t.set(a(e, r), s);
109
61
  },
110
- loadPageTranslations(e, n, r) {
111
- const i = `${e}:${n || "index"}`, o = t.get(i);
112
- !o || Object.keys(o).length === 0 ? t.set(i, r) : t.set(i, { ...o, ...r });
62
+ loadPageTranslations(e, s, r) {
63
+ const n = a(e, s), i = t.get(n);
64
+ !i || Object.keys(i).length === 0 ? t.set(n, { ...r }) : Object.assign(i, r);
113
65
  },
114
- mergeTranslation(e, n, r, a = !1) {
115
- const o = `${e}:${n || "index"}`, l = t.get(o) ?? {};
116
- t.set(o, { ...l, ...r });
66
+ mergeTranslation(e, s, r, n = !1) {
67
+ const i = a(e, s), o = t.get(i);
68
+ o ? Object.assign(o, r) : t.set(i, { ...r });
117
69
  },
118
70
  clearCache() {
119
71
  t.clear();
120
72
  }
121
73
  };
122
74
  }
123
- class _ {
75
+ class N {
124
76
  constructor(t = {}) {
125
- this.formatter = new f(), this.helper = p(t.storage), this.formatter = new f(), this.pluralFunc = t.plural || d, this.missingWarn = t.missingWarn ?? !0, this.missingHandler = t.missingHandler, this.getCustomMissingHandler = t.getCustomMissingHandler;
77
+ 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;
78
+ }
79
+ // --- Protected hooks (subclasses may override) ---
80
+ /**
81
+ * Called before translation lookup — subclasses use for reactivity tracking.
82
+ */
83
+ touch() {
84
+ }
85
+ /**
86
+ * Resolve route name from optional route context (string route name or adapter-specific object).
87
+ */
88
+ resolveRouteName(t) {
89
+ return typeof t == "string" ? t : this.getRoute();
90
+ }
91
+ /**
92
+ * Lookup translation value. Returns null when missing.
93
+ */
94
+ resolveLookup(t, e) {
95
+ const s = this.getLocale(), r = this.resolveRouteName(e);
96
+ let n = this.helper.getTranslation(s, r, String(t));
97
+ if (n === null) {
98
+ const i = this.getFallbackLocale();
99
+ s !== i && (n = this.helper.getTranslation(i, r, String(t)));
100
+ }
101
+ return n;
102
+ }
103
+ /**
104
+ * Check if translation exists in lookup source.
105
+ */
106
+ resolveHas(t, e) {
107
+ const s = this.getLocale(), r = this.resolveRouteName(e);
108
+ return this.helper.getTranslation(s, r, String(t)) !== null;
109
+ }
110
+ /**
111
+ * Context passed to missing-key handlers.
112
+ */
113
+ getMissingContext(t) {
114
+ return { locale: this.getLocale(), routeName: this.resolveRouteName(t) };
115
+ }
116
+ /**
117
+ * Warn or invoke handler when translation is missing.
118
+ */
119
+ warnMissing(t, e) {
120
+ const { locale: s, routeName: r } = this.getMissingContext(e), n = this.getCustomMissingHandler?.();
121
+ if (n) {
122
+ n(s, String(t), r);
123
+ return;
124
+ }
125
+ if (this.missingHandler) {
126
+ this.missingHandler(s, String(t), r);
127
+ return;
128
+ }
129
+ this.missingWarn && process.env.NODE_ENV !== "production" && typeof window < "u" && console.warn(`Not found '${t}' key in '${s}' locale messages for route '${r}'.`);
126
130
  }
127
131
  // --- Public methods (implemented in base class) ---
128
132
  /**
129
133
  * Get translation for a key
130
- * Based on logic from src/runtime/plugins/01.plugin.ts
131
134
  */
132
- t(t, e, n, r) {
135
+ t(t, e, s, r) {
133
136
  if (!t) return "";
134
- const a = this.getLocale(), i = r || this.getRoute();
135
- let o = this.helper.getTranslation(a, i, t);
136
- if (!o) {
137
- const l = this.getFallbackLocale();
138
- a !== l && (o = this.helper.getTranslation(l, i, t));
139
- }
140
- if (!o) {
141
- const l = this.getCustomMissingHandler?.();
142
- l ? l(a, t, i) : this.missingHandler ? this.missingHandler(a, t, i) : this.missingWarn && process.env.NODE_ENV !== "production" && typeof window < "u" && console.warn(`Not found '${t}' key in '${a}' locale messages for route '${i}'.`), o = n === void 0 ? t : n || t;
143
- }
144
- return typeof o == "string" && e ? m(o, e) : o;
137
+ this.touch();
138
+ const n = this.resolveLookup(t, r);
139
+ return n == null ? (this.warnMissing(t, r), s === void 0 ? t : s || t) : typeof n != "string" || !e ? n : v(n, e);
145
140
  }
146
141
  /**
147
142
  * Get translation as string
148
143
  */
149
- ts(t, e, n, r) {
150
- return this.t(t, e, n, r)?.toString() ?? n ?? t;
144
+ ts(t, e, s, r) {
145
+ return this.t(t, e, s, r)?.toString() ?? s ?? t;
151
146
  }
152
147
  /**
153
148
  * Plural translation
154
149
  */
155
- tc(t, e, n) {
156
- const { count: r, ...a } = typeof e == "number" ? { count: e } : e;
150
+ tc(t, e, s) {
151
+ this.touch();
152
+ const { count: r, ...n } = typeof e == "number" ? { count: e } : e;
157
153
  if (r === void 0)
158
- return n ?? t;
159
- const i = (l, u, c) => this.t(l, u, c);
160
- return this.pluralFunc(t, Number.parseInt(r.toString(), 10), a, this.getLocale(), i) ?? n ?? t;
154
+ return s ?? t;
155
+ const i = (u, h, l) => this.t(u, h, l);
156
+ return this.pluralFunc(t, Number.parseInt(r.toString(), 10), n, this.getLocale(), i) ?? s ?? t;
161
157
  }
162
158
  /**
163
159
  * Format number
164
160
  */
165
161
  tn(t, e) {
166
- return this.formatter.formatNumber(t, this.getLocale(), e);
162
+ return this.touch(), this.formatter.formatNumber(t, this.getLocale(), e);
167
163
  }
168
164
  /**
169
165
  * Format date
170
166
  */
171
167
  td(t, e) {
172
- return this.formatter.formatDate(t, this.getLocale(), e);
168
+ return this.touch(), this.formatter.formatDate(t, this.getLocale(), e);
173
169
  }
174
170
  /**
175
171
  * Format relative time
176
172
  */
177
173
  tdr(t, e) {
178
- return this.formatter.formatRelativeTime(t, this.getLocale(), e);
174
+ return this.touch(), this.formatter.formatRelativeTime(t, this.getLocale(), e);
179
175
  }
180
176
  /**
181
177
  * Check if translation exists
182
- * Based on logic from src/runtime/plugins/01.plugin.ts
183
178
  */
184
179
  has(t, e) {
185
- const n = e || this.getRoute(), r = this.getLocale();
186
- return !!this.helper.getTranslation(r, n, t);
180
+ return this.touch(), this.resolveHas(t, e);
187
181
  }
188
182
  /**
189
183
  * Clear cache
@@ -196,26 +190,31 @@ class _ {
196
190
  * Core translation loading logic (without reactivity)
197
191
  * Subclasses can override addTranslations/addRouteTranslations to add reactivity
198
192
  */
199
- loadTranslationsCore(t, e, n, r = "index") {
200
- n ? this.helper.mergeTranslation(t, r, e, !0) : this.helper.setTranslations(t, e, r);
193
+ loadTranslationsCore(t, e, s, r = "index") {
194
+ s ? this.helper.mergeTranslation(t, r, e, !0) : this.helper.setTranslations(t, e, r);
201
195
  }
202
196
  /**
203
197
  * Core route translation loading logic (without reactivity)
204
198
  * Subclasses can override addRouteTranslations to add reactivity
205
199
  */
206
- loadRouteTranslationsCore(t, e, n, r) {
207
- r ? this.helper.mergeTranslation(t, e, n, !0) : this.helper.loadPageTranslations(t, e, n);
200
+ loadRouteTranslationsCore(t, e, s, r) {
201
+ r ? this.helper.mergeTranslation(t, e, s, !0) : this.helper.loadPageTranslations(t, e, s);
208
202
  }
209
203
  }
210
204
  export {
211
- _ as BaseI18n,
212
- f as FormatService,
205
+ N as BaseI18n,
206
+ g as FormatService,
213
207
  d as defaultPlural,
214
- m as interpolate,
215
- T as isNoPrefixStrategy,
216
- w as isPrefixAndDefaultStrategy,
217
- v as isPrefixExceptDefaultStrategy,
218
- $ as isPrefixStrategy,
219
- p as useTranslationHelper,
220
- x as withPrefixStrategy
208
+ m as getByPath,
209
+ w as hasTranslationValue,
210
+ v as interpolate,
211
+ x as isNoPrefixStrategy,
212
+ S as isPrefixAndDefaultStrategy,
213
+ D as isPrefixExceptDefaultStrategy,
214
+ H as isPrefixStrategy,
215
+ L as mergeTranslationChunk,
216
+ f as resolveTranslation,
217
+ a as translationCacheKey,
218
+ T as useTranslationHelper,
219
+ M as withPrefixStrategy
221
220
  };