@i18n-micro/core 1.1.2 → 1.1.4
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/dist/base.d.ts +0 -10
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +102 -109
- package/dist/translation.d.ts +2 -2
- package/package.json +2 -2
- package/src/base.ts +3 -25
- package/src/translation.ts +3 -3
- package/tests/base.test.ts +0 -17
package/dist/base.d.ts
CHANGED
|
@@ -6,12 +6,7 @@ export interface BaseI18nOptions {
|
|
|
6
6
|
plural?: PluralFunc;
|
|
7
7
|
missingWarn?: boolean;
|
|
8
8
|
missingHandler?: (locale: string, key: string, routeName: string) => void;
|
|
9
|
-
getPreviousPageInfo?: () => {
|
|
10
|
-
locale: string;
|
|
11
|
-
routeName: string;
|
|
12
|
-
} | null;
|
|
13
9
|
getCustomMissingHandler?: () => MissingHandler | null;
|
|
14
|
-
enablePreviousPageFallback?: boolean;
|
|
15
10
|
}
|
|
16
11
|
/**
|
|
17
12
|
* Abstract base class for i18n adapters
|
|
@@ -25,12 +20,7 @@ export declare abstract class BaseI18n {
|
|
|
25
20
|
pluralFunc: PluralFunc;
|
|
26
21
|
missingWarn: boolean;
|
|
27
22
|
missingHandler?: (locale: string, key: string, routeName: string) => void;
|
|
28
|
-
getPreviousPageInfo?: () => {
|
|
29
|
-
locale: string;
|
|
30
|
-
routeName: string;
|
|
31
|
-
} | null;
|
|
32
23
|
getCustomMissingHandler?: () => MissingHandler | null;
|
|
33
|
-
enablePreviousPageFallback: boolean;
|
|
34
24
|
constructor(options?: BaseI18nOptions);
|
|
35
25
|
/**
|
|
36
26
|
* Get current locale
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class g{formatNumber(e,
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class g{formatNumber(t,e,r){return new Intl.NumberFormat(e,r).format(t)}formatDate(t,e,r){const n=new Date(t);return Number.isNaN(n.getTime())?"Invalid Date":new Intl.DateTimeFormat(e,r).format(n)}formatRelativeTime(t,e,r){const n=new Date(t);if(Number.isNaN(n.getTime()))return new Intl.RelativeTimeFormat(e,r).format(0,"second");const i=Math.floor((new Date().getTime()-n.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,r).format(-u,l)}return new Intl.RelativeTimeFormat(e,r).format(0,"second")}}const p=/\{(\w+)\}/g;function h(s,t){return t?s.replace(p,(e,r)=>{const n=t[r];return n!==void 0?String(n):`{${r}}`}):s}function T(s){return s==="prefix"||s==="prefix_and_default"}function v(s){return s==="no_prefix"}function x(s){return s==="prefix"}function S(s){return s==="prefix_except_default"}function w(s){return s==="prefix_and_default"}const m=(s,t,e,r,n)=>{const a=n(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 f(s,t){if(!s||typeof t!="string")return null;if(t in s){const n=s[t];return n}const e=t.split(".");let r=s;for(const n of e)if(r&&typeof r=="object"&&n in r)r=r[n];else return null;return r??null}function d(s){const t=s?.translations??new Map;return{hasCache(e,r){const n=`${e}:${r}`;return t.has(n)||t.has(e)},getCache(e,r){const n=`${e}:${r}`;return t.get(n)},setCache(e,r,n){},hasTranslation(e,r){for(const[n,a]of t)if((n===e||n.startsWith(`${e}:`))&&f(a,r)!==null)return!0;return!1},hasGeneralTranslation(e){return t.has(e)},hasPageTranslation(e,r){return t.has(`${e}:${r}`)},getTranslation(e,r,n){const a=`${e}:${r}`,i=t.get(a),o=f(i,n);if(o!==null)return o;const l=t.get(e);return f(l,n)},loadTranslations(e,r){const n=t.get(e)??{};t.set(e,{...n,...r})},setTranslations(e,r){t.set(e,r)},loadPageTranslations(e,r,n){const a=`${e}:${r}`,i=t.get(a);!i||Object.keys(i).length===0?t.set(a,n):t.set(a,{...i,...n})},mergeTranslation(e,r,n,a=!1){const i=`${e}:${r}`,o=t.get(i)??{};t.set(i,{...o,...n})},mergeGlobalTranslation(e,r,n=!1){const a=t.get(e)??{};t.set(e,{...a,...r})},clearCache(){t.clear()}}}class ${constructor(t={}){this.formatter=new g,this.helper=d(t.storage),this.formatter=new g,this.pluralFunc=t.plural||m,this.missingWarn=t.missingWarn??!0,this.missingHandler=t.missingHandler,this.getCustomMissingHandler=t.getCustomMissingHandler}t(t,e,r,n){if(!t)return"";const a=this.getLocale(),i=n||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=r===void 0?t:r||t}return typeof o=="string"&&e?h(o,e):o}ts(t,e,r,n){return this.t(t,e,r,n)?.toString()??r??t}tc(t,e,r){const{count:n,...a}=typeof e=="number"?{count:e}:e;if(n===void 0)return r??t;const i=(l,c,u)=>this.t(l,c,u);return this.pluralFunc(t,Number.parseInt(n.toString(),10),a,this.getLocale(),i)??r??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 r=e||this.getRoute(),n=this.getLocale();return!!this.helper.getTranslation(n,r,t)}clearCache(){this.helper.clearCache()}loadTranslationsCore(t,e,r){r?this.helper.mergeGlobalTranslation(t,e,!0):this.helper.setTranslations(t,e)}loadRouteTranslationsCore(t,e,r,n){n?this.helper.mergeTranslation(t,e,r,!0):this.helper.loadPageTranslations(t,e,r)}}exports.BaseI18n=$;exports.FormatService=g;exports.defaultPlural=m;exports.interpolate=h;exports.isNoPrefixStrategy=v;exports.isPrefixAndDefaultStrategy=w;exports.isPrefixExceptDefaultStrategy=S;exports.isPrefixStrategy=x;exports.useTranslationHelper=d;exports.withPrefixStrategy=T;
|
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
class
|
|
2
|
-
formatNumber(
|
|
3
|
-
return new Intl.NumberFormat(
|
|
1
|
+
class h {
|
|
2
|
+
formatNumber(t, e, n) {
|
|
3
|
+
return new Intl.NumberFormat(e, n).format(t);
|
|
4
4
|
}
|
|
5
|
-
formatDate(
|
|
6
|
-
const r = new Date(
|
|
7
|
-
return Number.isNaN(r.getTime()) ? "Invalid Date" : new Intl.DateTimeFormat(
|
|
5
|
+
formatDate(t, e, n) {
|
|
6
|
+
const r = new Date(t);
|
|
7
|
+
return Number.isNaN(r.getTime()) ? "Invalid Date" : new Intl.DateTimeFormat(e, n).format(r);
|
|
8
8
|
}
|
|
9
|
-
formatRelativeTime(
|
|
10
|
-
const r = new Date(
|
|
9
|
+
formatRelativeTime(t, e, n) {
|
|
10
|
+
const r = new Date(t);
|
|
11
11
|
if (Number.isNaN(r.getTime()))
|
|
12
|
-
return new Intl.RelativeTimeFormat(
|
|
12
|
+
return new Intl.RelativeTimeFormat(e, n).format(0, "second");
|
|
13
13
|
const i = Math.floor(((/* @__PURE__ */ new Date()).getTime() - r.getTime()) / 1e3), o = [
|
|
14
14
|
{ unit: "year", seconds: 31536e3 },
|
|
15
15
|
{ unit: "month", seconds: 2592e3 },
|
|
@@ -18,54 +18,54 @@ class g {
|
|
|
18
18
|
{ unit: "minute", seconds: 60 },
|
|
19
19
|
{ unit: "second", seconds: 1 }
|
|
20
20
|
];
|
|
21
|
-
for (const { unit: l, seconds:
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
24
|
-
return new Intl.RelativeTimeFormat(
|
|
21
|
+
for (const { unit: l, seconds: c } of o) {
|
|
22
|
+
const u = Math.floor(i / c);
|
|
23
|
+
if (u >= 1)
|
|
24
|
+
return new Intl.RelativeTimeFormat(e, n).format(-u, l);
|
|
25
25
|
}
|
|
26
|
-
return new Intl.RelativeTimeFormat(
|
|
26
|
+
return new Intl.RelativeTimeFormat(e, n).format(0, "second");
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
const
|
|
30
|
-
function m(s,
|
|
31
|
-
return
|
|
32
|
-
const r =
|
|
29
|
+
const g = /\{(\w+)\}/g;
|
|
30
|
+
function m(s, t) {
|
|
31
|
+
return t ? s.replace(g, (e, n) => {
|
|
32
|
+
const r = t[n];
|
|
33
33
|
return r !== void 0 ? String(r) : `{${n}}`;
|
|
34
34
|
}) : s;
|
|
35
35
|
}
|
|
36
|
-
function
|
|
36
|
+
function T(s) {
|
|
37
37
|
return s === "prefix" || s === "prefix_and_default";
|
|
38
38
|
}
|
|
39
|
-
function
|
|
39
|
+
function v(s) {
|
|
40
40
|
return s === "no_prefix";
|
|
41
41
|
}
|
|
42
|
-
function
|
|
42
|
+
function w(s) {
|
|
43
43
|
return s === "prefix";
|
|
44
44
|
}
|
|
45
|
-
function
|
|
45
|
+
function x(s) {
|
|
46
46
|
return s === "prefix_except_default";
|
|
47
47
|
}
|
|
48
|
-
function
|
|
48
|
+
function $(s) {
|
|
49
49
|
return s === "prefix_and_default";
|
|
50
50
|
}
|
|
51
|
-
const d = (s,
|
|
52
|
-
const a = r(s,
|
|
51
|
+
const d = (s, t, e, n, r) => {
|
|
52
|
+
const a = r(s, e);
|
|
53
53
|
if (!a)
|
|
54
54
|
return null;
|
|
55
55
|
const i = a.toString().split("|");
|
|
56
56
|
if (i.length === 0) return null;
|
|
57
|
-
const o =
|
|
58
|
-
return o ? o.trim().replace("{count}",
|
|
57
|
+
const o = t < i.length ? i[t] : i[i.length - 1];
|
|
58
|
+
return o ? o.trim().replace("{count}", t.toString()) : null;
|
|
59
59
|
};
|
|
60
|
-
function f(s,
|
|
61
|
-
if (!s || typeof
|
|
62
|
-
if (
|
|
63
|
-
const r = s[
|
|
60
|
+
function f(s, t) {
|
|
61
|
+
if (!s || typeof t != "string") return null;
|
|
62
|
+
if (t in s) {
|
|
63
|
+
const r = s[t];
|
|
64
64
|
return r;
|
|
65
65
|
}
|
|
66
|
-
const
|
|
66
|
+
const e = t.split(".");
|
|
67
67
|
let n = s;
|
|
68
|
-
for (const r of
|
|
68
|
+
for (const r of e)
|
|
69
69
|
if (n && typeof n == "object" && r in n)
|
|
70
70
|
n = n[r];
|
|
71
71
|
else
|
|
@@ -73,131 +73,124 @@ function f(s, e) {
|
|
|
73
73
|
return n ?? null;
|
|
74
74
|
}
|
|
75
75
|
function p(s) {
|
|
76
|
-
const
|
|
76
|
+
const t = s?.translations ?? /* @__PURE__ */ new Map();
|
|
77
77
|
return {
|
|
78
|
-
hasCache(
|
|
79
|
-
const r = `${
|
|
80
|
-
return
|
|
78
|
+
hasCache(e, n) {
|
|
79
|
+
const r = `${e}:${n}`;
|
|
80
|
+
return t.has(r) || t.has(e);
|
|
81
81
|
},
|
|
82
|
-
getCache(
|
|
83
|
-
const r = `${
|
|
84
|
-
return
|
|
82
|
+
getCache(e, n) {
|
|
83
|
+
const r = `${e}:${n}`;
|
|
84
|
+
return t.get(r);
|
|
85
85
|
},
|
|
86
|
-
setCache(
|
|
86
|
+
setCache(e, n, r) {
|
|
87
87
|
},
|
|
88
|
-
hasTranslation(
|
|
89
|
-
for (const [r, a] of
|
|
90
|
-
if ((r ===
|
|
88
|
+
hasTranslation(e, n) {
|
|
89
|
+
for (const [r, a] of t)
|
|
90
|
+
if ((r === e || r.startsWith(`${e}:`)) && f(a, n) !== null)
|
|
91
91
|
return !0;
|
|
92
92
|
return !1;
|
|
93
93
|
},
|
|
94
|
-
hasGeneralTranslation(
|
|
95
|
-
return
|
|
94
|
+
hasGeneralTranslation(e) {
|
|
95
|
+
return t.has(e);
|
|
96
96
|
},
|
|
97
|
-
hasPageTranslation(
|
|
98
|
-
return
|
|
97
|
+
hasPageTranslation(e, n) {
|
|
98
|
+
return t.has(`${e}:${n}`);
|
|
99
99
|
},
|
|
100
|
-
getTranslation(
|
|
101
|
-
const a = `${
|
|
100
|
+
getTranslation(e, n, r) {
|
|
101
|
+
const a = `${e}:${n}`, i = t.get(a), o = f(i, r);
|
|
102
102
|
if (o !== null) return o;
|
|
103
|
-
const l =
|
|
103
|
+
const l = t.get(e);
|
|
104
104
|
return f(l, r);
|
|
105
105
|
},
|
|
106
|
-
loadTranslations(
|
|
107
|
-
const r =
|
|
108
|
-
|
|
106
|
+
loadTranslations(e, n) {
|
|
107
|
+
const r = t.get(e) ?? {};
|
|
108
|
+
t.set(e, { ...r, ...n });
|
|
109
109
|
},
|
|
110
|
-
setTranslations(
|
|
111
|
-
|
|
110
|
+
setTranslations(e, n) {
|
|
111
|
+
t.set(e, n);
|
|
112
112
|
},
|
|
113
|
-
loadPageTranslations(
|
|
114
|
-
const a = `${
|
|
115
|
-
!i || Object.keys(i).length === 0 ?
|
|
113
|
+
loadPageTranslations(e, n, r) {
|
|
114
|
+
const a = `${e}:${n}`, i = t.get(a);
|
|
115
|
+
!i || Object.keys(i).length === 0 ? t.set(a, r) : t.set(a, { ...i, ...r });
|
|
116
116
|
},
|
|
117
|
-
mergeTranslation(
|
|
118
|
-
const i = `${
|
|
119
|
-
|
|
117
|
+
mergeTranslation(e, n, r, a = !1) {
|
|
118
|
+
const i = `${e}:${n}`, o = t.get(i) ?? {};
|
|
119
|
+
t.set(i, { ...o, ...r });
|
|
120
120
|
},
|
|
121
|
-
mergeGlobalTranslation(
|
|
122
|
-
const a =
|
|
123
|
-
|
|
121
|
+
mergeGlobalTranslation(e, n, r = !1) {
|
|
122
|
+
const a = t.get(e) ?? {};
|
|
123
|
+
t.set(e, { ...a, ...n });
|
|
124
124
|
},
|
|
125
125
|
clearCache() {
|
|
126
|
-
|
|
126
|
+
t.clear();
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
|
-
class
|
|
131
|
-
constructor(
|
|
132
|
-
this.formatter = new
|
|
130
|
+
class _ {
|
|
131
|
+
constructor(t = {}) {
|
|
132
|
+
this.formatter = new h(), this.helper = p(t.storage), this.formatter = new h(), this.pluralFunc = t.plural || d, this.missingWarn = t.missingWarn ?? !0, this.missingHandler = t.missingHandler, this.getCustomMissingHandler = t.getCustomMissingHandler;
|
|
133
133
|
}
|
|
134
134
|
// --- Public methods (implemented in base class) ---
|
|
135
135
|
/**
|
|
136
136
|
* Get translation for a key
|
|
137
137
|
* Based on logic from src/runtime/plugins/01.plugin.ts
|
|
138
138
|
*/
|
|
139
|
-
t(
|
|
140
|
-
if (!
|
|
139
|
+
t(t, e, n, r) {
|
|
140
|
+
if (!t) return "";
|
|
141
141
|
const a = this.getLocale(), i = r || this.getRoute();
|
|
142
|
-
let o = this.helper.getTranslation(a, i,
|
|
143
|
-
if (!o && this.enablePreviousPageFallback && this.getPreviousPageInfo) {
|
|
144
|
-
const l = this.getPreviousPageInfo();
|
|
145
|
-
if (l) {
|
|
146
|
-
const u = this.helper.getTranslation(l.locale, l.routeName, e);
|
|
147
|
-
u && (o = u, process.env.NODE_ENV !== "production" && console.log(`Using fallback translation from previous route: ${l.routeName} -> ${e}`));
|
|
148
|
-
}
|
|
149
|
-
}
|
|
142
|
+
let o = this.helper.getTranslation(a, i, t);
|
|
150
143
|
if (!o) {
|
|
151
144
|
const l = this.getFallbackLocale();
|
|
152
|
-
a !== l && (o = this.helper.getTranslation(l, i,
|
|
145
|
+
a !== l && (o = this.helper.getTranslation(l, i, t));
|
|
153
146
|
}
|
|
154
147
|
if (!o) {
|
|
155
148
|
const l = this.getCustomMissingHandler?.();
|
|
156
|
-
l ? l(a,
|
|
149
|
+
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;
|
|
157
150
|
}
|
|
158
|
-
return typeof o == "string" &&
|
|
151
|
+
return typeof o == "string" && e ? m(o, e) : o;
|
|
159
152
|
}
|
|
160
153
|
/**
|
|
161
154
|
* Get translation as string
|
|
162
155
|
*/
|
|
163
|
-
ts(
|
|
164
|
-
return this.t(
|
|
156
|
+
ts(t, e, n, r) {
|
|
157
|
+
return this.t(t, e, n, r)?.toString() ?? n ?? t;
|
|
165
158
|
}
|
|
166
159
|
/**
|
|
167
160
|
* Plural translation
|
|
168
161
|
*/
|
|
169
|
-
tc(
|
|
170
|
-
const { count: r, ...a } = typeof
|
|
162
|
+
tc(t, e, n) {
|
|
163
|
+
const { count: r, ...a } = typeof e == "number" ? { count: e } : e;
|
|
171
164
|
if (r === void 0)
|
|
172
|
-
return n ??
|
|
173
|
-
const i = (l,
|
|
174
|
-
return this.pluralFunc(
|
|
165
|
+
return n ?? t;
|
|
166
|
+
const i = (l, c, u) => this.t(l, c, u);
|
|
167
|
+
return this.pluralFunc(t, Number.parseInt(r.toString(), 10), a, this.getLocale(), i) ?? n ?? t;
|
|
175
168
|
}
|
|
176
169
|
/**
|
|
177
170
|
* Format number
|
|
178
171
|
*/
|
|
179
|
-
tn(
|
|
180
|
-
return this.formatter.formatNumber(
|
|
172
|
+
tn(t, e) {
|
|
173
|
+
return this.formatter.formatNumber(t, this.getLocale(), e);
|
|
181
174
|
}
|
|
182
175
|
/**
|
|
183
176
|
* Format date
|
|
184
177
|
*/
|
|
185
|
-
td(
|
|
186
|
-
return this.formatter.formatDate(
|
|
178
|
+
td(t, e) {
|
|
179
|
+
return this.formatter.formatDate(t, this.getLocale(), e);
|
|
187
180
|
}
|
|
188
181
|
/**
|
|
189
182
|
* Format relative time
|
|
190
183
|
*/
|
|
191
|
-
tdr(
|
|
192
|
-
return this.formatter.formatRelativeTime(
|
|
184
|
+
tdr(t, e) {
|
|
185
|
+
return this.formatter.formatRelativeTime(t, this.getLocale(), e);
|
|
193
186
|
}
|
|
194
187
|
/**
|
|
195
188
|
* Check if translation exists
|
|
196
189
|
* Based on logic from src/runtime/plugins/01.plugin.ts
|
|
197
190
|
*/
|
|
198
|
-
has(
|
|
199
|
-
const n =
|
|
200
|
-
return !!this.helper.getTranslation(r, n,
|
|
191
|
+
has(t, e) {
|
|
192
|
+
const n = e || this.getRoute(), r = this.getLocale();
|
|
193
|
+
return !!this.helper.getTranslation(r, n, t);
|
|
201
194
|
}
|
|
202
195
|
/**
|
|
203
196
|
* Clear cache
|
|
@@ -210,26 +203,26 @@ class $ {
|
|
|
210
203
|
* Core translation loading logic (without reactivity)
|
|
211
204
|
* Subclasses can override addTranslations/addRouteTranslations to add reactivity
|
|
212
205
|
*/
|
|
213
|
-
loadTranslationsCore(
|
|
214
|
-
n ? this.helper.mergeGlobalTranslation(
|
|
206
|
+
loadTranslationsCore(t, e, n) {
|
|
207
|
+
n ? this.helper.mergeGlobalTranslation(t, e, !0) : this.helper.setTranslations(t, e);
|
|
215
208
|
}
|
|
216
209
|
/**
|
|
217
210
|
* Core route translation loading logic (without reactivity)
|
|
218
211
|
* Subclasses can override addRouteTranslations to add reactivity
|
|
219
212
|
*/
|
|
220
|
-
loadRouteTranslationsCore(
|
|
221
|
-
r ? this.helper.mergeTranslation(
|
|
213
|
+
loadRouteTranslationsCore(t, e, n, r) {
|
|
214
|
+
r ? this.helper.mergeTranslation(t, e, n, !0) : this.helper.loadPageTranslations(t, e, n);
|
|
222
215
|
}
|
|
223
216
|
}
|
|
224
217
|
export {
|
|
225
|
-
|
|
226
|
-
|
|
218
|
+
_ as BaseI18n,
|
|
219
|
+
h as FormatService,
|
|
227
220
|
d as defaultPlural,
|
|
228
221
|
m as interpolate,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
222
|
+
v as isNoPrefixStrategy,
|
|
223
|
+
$ as isPrefixAndDefaultStrategy,
|
|
224
|
+
x as isPrefixExceptDefaultStrategy,
|
|
225
|
+
w as isPrefixStrategy,
|
|
233
226
|
p as useTranslationHelper,
|
|
234
|
-
|
|
227
|
+
T as withPrefixStrategy
|
|
235
228
|
};
|
package/dist/translation.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Translations } from '@i18n-micro/types';
|
|
2
2
|
/**
|
|
3
|
-
* Bare Metal:
|
|
4
|
-
*
|
|
3
|
+
* Bare Metal: Simple translation storage without Ref, useState, devalue.
|
|
4
|
+
* Map key: locale (general) or locale:routeName (page-specific).
|
|
5
5
|
*/
|
|
6
6
|
export interface TranslationStorage {
|
|
7
7
|
translations: Map<string, Translations>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i18n-micro/core",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": "s00d/nuxt-i18n-micro",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"keywords": [],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@i18n-micro/types": "1.1.
|
|
25
|
+
"@i18n-micro/types": "1.1.4"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"vite": "^7.3.1",
|
package/src/base.ts
CHANGED
|
@@ -8,10 +8,7 @@ export interface BaseI18nOptions {
|
|
|
8
8
|
plural?: PluralFunc
|
|
9
9
|
missingWarn?: boolean
|
|
10
10
|
missingHandler?: (locale: string, key: string, routeName: string) => void
|
|
11
|
-
// Optional hooks for Nuxt runtime specific features
|
|
12
|
-
getPreviousPageInfo?: () => { locale: string; routeName: string } | null
|
|
13
11
|
getCustomMissingHandler?: () => MissingHandler | null
|
|
14
|
-
enablePreviousPageFallback?: boolean
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
/**
|
|
@@ -27,10 +24,7 @@ export abstract class BaseI18n {
|
|
|
27
24
|
public pluralFunc: PluralFunc
|
|
28
25
|
public missingWarn: boolean
|
|
29
26
|
public missingHandler?: (locale: string, key: string, routeName: string) => void
|
|
30
|
-
// Optional hooks for Nuxt runtime specific features
|
|
31
|
-
public getPreviousPageInfo?: () => { locale: string; routeName: string } | null
|
|
32
27
|
public getCustomMissingHandler?: () => MissingHandler | null
|
|
33
|
-
public enablePreviousPageFallback: boolean
|
|
34
28
|
|
|
35
29
|
constructor(options: BaseI18nOptions = {}) {
|
|
36
30
|
this.helper = useTranslationHelper(options.storage)
|
|
@@ -38,9 +32,7 @@ export abstract class BaseI18n {
|
|
|
38
32
|
this.pluralFunc = options.plural || defaultPlural
|
|
39
33
|
this.missingWarn = options.missingWarn ?? true
|
|
40
34
|
this.missingHandler = options.missingHandler
|
|
41
|
-
this.getPreviousPageInfo = options.getPreviousPageInfo
|
|
42
35
|
this.getCustomMissingHandler = options.getCustomMissingHandler
|
|
43
|
-
this.enablePreviousPageFallback = options.enablePreviousPageFallback ?? false
|
|
44
36
|
}
|
|
45
37
|
|
|
46
38
|
// --- Abstract methods (must be implemented by subclasses) ---
|
|
@@ -77,21 +69,7 @@ export abstract class BaseI18n {
|
|
|
77
69
|
// Note: In Nuxt runtime, server already merges global translations, so we don't need explicit fallback
|
|
78
70
|
let value = this.helper.getTranslation<string>(locale, route, key)
|
|
79
71
|
|
|
80
|
-
// 2.
|
|
81
|
-
if (!value && this.enablePreviousPageFallback && this.getPreviousPageInfo) {
|
|
82
|
-
const prev = this.getPreviousPageInfo()
|
|
83
|
-
if (prev) {
|
|
84
|
-
const prevValue = this.helper.getTranslation<string>(prev.locale, prev.routeName, key)
|
|
85
|
-
if (prevValue) {
|
|
86
|
-
value = prevValue
|
|
87
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
88
|
-
console.log(`Using fallback translation from previous route: ${prev.routeName} -> ${key}`)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 3. Fallback to fallbackLocale if not found and different (for non-Nuxt adapters)
|
|
72
|
+
// 2. Fallback to fallbackLocale if not found and different (for non-Nuxt adapters)
|
|
95
73
|
if (!value) {
|
|
96
74
|
const fallbackLocale = this.getFallbackLocale()
|
|
97
75
|
if (locale !== fallbackLocale) {
|
|
@@ -99,7 +77,7 @@ export abstract class BaseI18n {
|
|
|
99
77
|
}
|
|
100
78
|
}
|
|
101
79
|
|
|
102
|
-
//
|
|
80
|
+
// 3. Handle missing
|
|
103
81
|
if (!value) {
|
|
104
82
|
// Call custom handler if set (Nuxt runtime), otherwise use instance handler
|
|
105
83
|
const customHandler = this.getCustomMissingHandler?.()
|
|
@@ -117,7 +95,7 @@ export abstract class BaseI18n {
|
|
|
117
95
|
value = defaultValue === undefined ? key : defaultValue || key
|
|
118
96
|
}
|
|
119
97
|
|
|
120
|
-
//
|
|
98
|
+
// 4. Interpolate
|
|
121
99
|
return typeof value === 'string' && params ? interpolate(value, params) : (value as CleanTranslation)
|
|
122
100
|
}
|
|
123
101
|
|
package/src/translation.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Translations } from '@i18n-micro/types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Bare Metal:
|
|
5
|
-
*
|
|
4
|
+
* Bare Metal: Simple translation storage without Ref, useState, devalue.
|
|
5
|
+
* Map key: locale (general) or locale:routeName (page-specific).
|
|
6
6
|
*/
|
|
7
7
|
export interface TranslationStorage {
|
|
8
8
|
translations: Map<string, Translations>
|
|
@@ -81,7 +81,7 @@ export function useTranslationHelper(storage?: TranslationStorage) {
|
|
|
81
81
|
loadPageTranslations(locale: string, routeName: string, data: Translations): void {
|
|
82
82
|
const key = `${locale}:${routeName}`
|
|
83
83
|
const existing = translations.get(key)
|
|
84
|
-
// Perf:
|
|
84
|
+
// Perf: when existing is empty — keep reference, avoid O(n) copying of large objects
|
|
85
85
|
if (!existing || Object.keys(existing).length === 0) {
|
|
86
86
|
translations.set(key, data)
|
|
87
87
|
} else {
|
package/tests/base.test.ts
CHANGED
|
@@ -118,23 +118,6 @@ describe('BaseI18n', () => {
|
|
|
118
118
|
expect(i18n.t('title', undefined, undefined, 'about')).toBe('Route Title')
|
|
119
119
|
})
|
|
120
120
|
|
|
121
|
-
test('should use previousPageInfo fallback when enabled', async () => {
|
|
122
|
-
const storage = { translations: new Map<string, Translations>() }
|
|
123
|
-
const prevInfo = { locale: 'fr', routeName: 'previous' }
|
|
124
|
-
const i18n = new TestI18n('en', 'en', 'general', {
|
|
125
|
-
storage,
|
|
126
|
-
getPreviousPageInfo: () => prevInfo,
|
|
127
|
-
enablePreviousPageFallback: true,
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
const translations: Translations = { greeting: 'Bonjour' }
|
|
131
|
-
await i18n['helper'].loadPageTranslations('fr', 'previous', translations)
|
|
132
|
-
|
|
133
|
-
// Current locale is 'en', route is 'general', translation not found
|
|
134
|
-
// Should fallback to previous page info: locale 'fr', route 'previous'
|
|
135
|
-
expect(i18n.t('greeting')).toBe('Bonjour')
|
|
136
|
-
})
|
|
137
|
-
|
|
138
121
|
test('should call missingHandler when translation is missing', () => {
|
|
139
122
|
const handler = jest.fn()
|
|
140
123
|
const i18n = new TestI18n('en', 'en', 'general', { missingHandler: handler })
|