@i18n-micro/core 1.1.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,10 +93,6 @@ console.log(localizedRoute) // Localized route object
93
93
  Sets the cache for a specific route and locale.
94
94
  - **`mergeTranslation(locale: string, routeName: string, newTranslations: Translations, force = false): void`**:
95
95
  Merges new translations into the cache for a specific route and locale.
96
- - **`mergeGlobalTranslation(locale: string, newTranslations: Translations, force = false): void`**:
97
- Merges new translations into the global cache for a specific locale.
98
- - **`hasGeneralTranslation(locale: string): boolean`**:
99
- Checks if global translations are loaded for the specified locale.
100
96
  - **`hasPageTranslation(locale: string, routeName: string): boolean`**:
101
97
  Checks if translations for a specific route and locale are loaded.
102
98
  - **`hasTranslation(locale: string, key: string): boolean`**:
package/dist/base.d.ts CHANGED
@@ -72,7 +72,7 @@ export declare abstract class BaseI18n {
72
72
  * Core translation loading logic (without reactivity)
73
73
  * Subclasses can override addTranslations/addRouteTranslations to add reactivity
74
74
  */
75
- loadTranslationsCore(locale: string, translations: Translations, merge: boolean): void;
75
+ loadTranslationsCore(locale: string, translations: Translations, merge: boolean, routeName?: string): void;
76
76
  /**
77
77
  * Core route translation loading logic (without reactivity)
78
78
  * Subclasses can override addRouteTranslations to add reactivity
package/dist/index.cjs CHANGED
@@ -1 +1 @@
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;
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;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- class h {
1
+ class f {
2
2
  formatNumber(t, e, n) {
3
3
  return new Intl.NumberFormat(e, n).format(t);
4
4
  }
@@ -18,10 +18,10 @@ class h {
18
18
  { unit: "minute", seconds: 60 },
19
19
  { unit: "second", seconds: 1 }
20
20
  ];
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);
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);
25
25
  }
26
26
  return new Intl.RelativeTimeFormat(e, n).format(0, "second");
27
27
  }
@@ -33,19 +33,19 @@ function m(s, t) {
33
33
  return r !== void 0 ? String(r) : `{${n}}`;
34
34
  }) : s;
35
35
  }
36
- function T(s) {
36
+ function x(s) {
37
37
  return s === "prefix" || s === "prefix_and_default";
38
38
  }
39
- function v(s) {
39
+ function T(s) {
40
40
  return s === "no_prefix";
41
41
  }
42
- function w(s) {
42
+ function $(s) {
43
43
  return s === "prefix";
44
44
  }
45
- function x(s) {
45
+ function v(s) {
46
46
  return s === "prefix_except_default";
47
47
  }
48
- function $(s) {
48
+ function w(s) {
49
49
  return s === "prefix_and_default";
50
50
  }
51
51
  const d = (s, t, e, n, r) => {
@@ -57,7 +57,7 @@ const d = (s, t, e, n, r) => {
57
57
  const o = t < i.length ? i[t] : i[i.length - 1];
58
58
  return o ? o.trim().replace("{count}", t.toString()) : null;
59
59
  };
60
- function f(s, t) {
60
+ function h(s, t) {
61
61
  if (!s || typeof t != "string") return null;
62
62
  if (t in s) {
63
63
  const r = s[t];
@@ -76,51 +76,44 @@ function p(s) {
76
76
  const t = s?.translations ?? /* @__PURE__ */ new Map();
77
77
  return {
78
78
  hasCache(e, n) {
79
- const r = `${e}:${n}`;
80
- return t.has(r) || t.has(e);
79
+ const r = n || "index";
80
+ return t.has(`${e}:${r}`);
81
81
  },
82
82
  getCache(e, n) {
83
- const r = `${e}:${n}`;
84
- return t.get(r);
83
+ const r = n || "index";
84
+ return t.get(`${e}:${r}`);
85
85
  },
86
86
  setCache(e, n, r) {
87
87
  },
88
88
  hasTranslation(e, n) {
89
89
  for (const [r, a] of t)
90
- if ((r === e || r.startsWith(`${e}:`)) && f(a, n) !== null)
90
+ if (r.startsWith(`${e}:`) && h(a, n) !== null)
91
91
  return !0;
92
92
  return !1;
93
93
  },
94
- hasGeneralTranslation(e) {
95
- return t.has(e);
96
- },
97
94
  hasPageTranslation(e, n) {
98
- return t.has(`${e}:${n}`);
95
+ const r = n || "index";
96
+ return t.has(`${e}:${r}`);
99
97
  },
100
98
  getTranslation(e, n, r) {
101
- const a = `${e}:${n}`, i = t.get(a), o = f(i, r);
102
- if (o !== null) return o;
103
- const l = t.get(e);
104
- return f(l, r);
99
+ const a = n || "index";
100
+ return h(t.get(`${e}:${a}`), r);
105
101
  },
106
- loadTranslations(e, n) {
107
- const r = t.get(e) ?? {};
108
- t.set(e, { ...r, ...n });
102
+ loadTranslations(e, n, r = "index") {
103
+ const i = `${e}:${r || "index"}`, o = t.get(i) ?? {};
104
+ t.set(i, { ...o, ...n });
109
105
  },
110
- setTranslations(e, n) {
111
- t.set(e, n);
106
+ setTranslations(e, n, r = "index") {
107
+ const a = r || "index";
108
+ t.set(`${e}:${a}`, n);
112
109
  },
113
110
  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 });
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 });
116
113
  },
117
114
  mergeTranslation(e, n, r, a = !1) {
118
- const i = `${e}:${n}`, o = t.get(i) ?? {};
119
- t.set(i, { ...o, ...r });
120
- },
121
- mergeGlobalTranslation(e, n, r = !1) {
122
- const a = t.get(e) ?? {};
123
- t.set(e, { ...a, ...n });
115
+ const o = `${e}:${n || "index"}`, l = t.get(o) ?? {};
116
+ t.set(o, { ...l, ...r });
124
117
  },
125
118
  clearCache() {
126
119
  t.clear();
@@ -129,7 +122,7 @@ function p(s) {
129
122
  }
130
123
  class _ {
131
124
  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;
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;
133
126
  }
134
127
  // --- Public methods (implemented in base class) ---
135
128
  /**
@@ -163,7 +156,7 @@ class _ {
163
156
  const { count: r, ...a } = typeof e == "number" ? { count: e } : e;
164
157
  if (r === void 0)
165
158
  return n ?? t;
166
- const i = (l, c, u) => this.t(l, c, u);
159
+ const i = (l, u, c) => this.t(l, u, c);
167
160
  return this.pluralFunc(t, Number.parseInt(r.toString(), 10), a, this.getLocale(), i) ?? n ?? t;
168
161
  }
169
162
  /**
@@ -203,8 +196,8 @@ class _ {
203
196
  * Core translation loading logic (without reactivity)
204
197
  * Subclasses can override addTranslations/addRouteTranslations to add reactivity
205
198
  */
206
- loadTranslationsCore(t, e, n) {
207
- n ? this.helper.mergeGlobalTranslation(t, e, !0) : this.helper.setTranslations(t, e);
199
+ loadTranslationsCore(t, e, n, r = "index") {
200
+ n ? this.helper.mergeTranslation(t, r, e, !0) : this.helper.setTranslations(t, e, r);
208
201
  }
209
202
  /**
210
203
  * Core route translation loading logic (without reactivity)
@@ -216,13 +209,13 @@ class _ {
216
209
  }
217
210
  export {
218
211
  _ as BaseI18n,
219
- h as FormatService,
212
+ f as FormatService,
220
213
  d as defaultPlural,
221
214
  m as interpolate,
222
- v as isNoPrefixStrategy,
223
- $ as isPrefixAndDefaultStrategy,
224
- x as isPrefixExceptDefaultStrategy,
225
- w as isPrefixStrategy,
215
+ T as isNoPrefixStrategy,
216
+ w as isPrefixAndDefaultStrategy,
217
+ v as isPrefixExceptDefaultStrategy,
218
+ $ as isPrefixStrategy,
226
219
  p as useTranslationHelper,
227
- T as withPrefixStrategy
220
+ x as withPrefixStrategy
228
221
  };
@@ -1,7 +1,7 @@
1
1
  import { Translations } from '@i18n-micro/types';
2
2
  /**
3
3
  * Bare Metal: Simple translation storage without Ref, useState, devalue.
4
- * Map key: locale (general) or locale:routeName (page-specific).
4
+ * Map key: `${locale}:${routeName}` (page-specific).
5
5
  */
6
6
  export interface TranslationStorage {
7
7
  translations: Map<string, Translations>;
@@ -11,13 +11,11 @@ export declare function useTranslationHelper(storage?: TranslationStorage): {
11
11
  getCache(locale: string, routeName: string): Translations | undefined;
12
12
  setCache(_locale: string, _routeName: string, _cache: Map<string, unknown>): void;
13
13
  hasTranslation(locale: string, key: string): boolean;
14
- hasGeneralTranslation(locale: string): boolean;
15
14
  hasPageTranslation(locale: string, routeName: string): boolean;
16
15
  getTranslation<T = unknown>(locale: string, routeName: string, key: string): T | null;
17
- loadTranslations(locale: string, data: Translations): void;
18
- setTranslations(locale: string, data: Translations): void;
16
+ loadTranslations(locale: string, data: Translations, routeName?: string): void;
17
+ setTranslations(locale: string, data: Translations, routeName?: string): void;
19
18
  loadPageTranslations(locale: string, routeName: string, data: Translations): void;
20
19
  mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force?: boolean): void;
21
- mergeGlobalTranslation(locale: string, newTranslations: Translations, _force?: boolean): void;
22
20
  clearCache(): void;
23
21
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i18n-micro/core",
3
- "version": "1.1.4",
3
+ "version": "1.3.0",
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.4"
25
+ "@i18n-micro/types": "1.2.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "vite": "^7.3.1",
package/src/base.ts CHANGED
@@ -66,10 +66,9 @@ export abstract class BaseI18n {
66
66
  const route = routeName || this.getRoute()
67
67
 
68
68
  // 1. Try to find translation in current locale
69
- // Note: In Nuxt runtime, server already merges global translations, so we don't need explicit fallback
70
69
  let value = this.helper.getTranslation<string>(locale, route, key)
71
70
 
72
- // 2. Fallback to fallbackLocale if not found and different (for non-Nuxt adapters)
71
+ // 2. Fallback to fallbackLocale if not found and different
73
72
  if (!value) {
74
73
  const fallbackLocale = this.getFallbackLocale()
75
74
  if (locale !== fallbackLocale) {
@@ -173,11 +172,11 @@ export abstract class BaseI18n {
173
172
  * Core translation loading logic (without reactivity)
174
173
  * Subclasses can override addTranslations/addRouteTranslations to add reactivity
175
174
  */
176
- public loadTranslationsCore(locale: string, translations: Translations, merge: boolean): void {
175
+ public loadTranslationsCore(locale: string, translations: Translations, merge: boolean, routeName = 'index'): void {
177
176
  if (merge) {
178
- this.helper.mergeGlobalTranslation(locale, translations, true)
177
+ this.helper.mergeTranslation(locale, routeName, translations, true)
179
178
  } else {
180
- this.helper.setTranslations(locale, translations)
179
+ this.helper.setTranslations(locale, translations, routeName)
181
180
  }
182
181
  }
183
182
 
@@ -2,7 +2,7 @@ import type { Translations } from '@i18n-micro/types'
2
2
 
3
3
  /**
4
4
  * Bare Metal: Simple translation storage without Ref, useState, devalue.
5
- * Map key: locale (general) or locale:routeName (page-specific).
5
+ * Map key: `${locale}:${routeName}` (page-specific).
6
6
  */
7
7
  export interface TranslationStorage {
8
8
  translations: Map<string, Translations>
@@ -36,52 +36,46 @@ export function useTranslationHelper(storage?: TranslationStorage) {
36
36
 
37
37
  return {
38
38
  hasCache(locale: string, page: string) {
39
- const cacheKey = `${locale}:${page}`
40
- return translations.has(cacheKey) || translations.has(locale)
39
+ const p = page || 'index'
40
+ return translations.has(`${locale}:${p}`)
41
41
  },
42
42
  getCache(locale: string, routeName: string) {
43
- const cacheKey = `${locale}:${routeName}`
44
- return translations.get(cacheKey)
43
+ const rn = routeName || 'index'
44
+ return translations.get(`${locale}:${rn}`)
45
45
  },
46
46
  setCache(_locale: string, _routeName: string, _cache: Map<string, unknown>) {
47
47
  // No-op for bare metal
48
48
  },
49
49
  hasTranslation(locale: string, key: string): boolean {
50
50
  for (const [k, v] of translations) {
51
- if ((k === locale || k.startsWith(`${locale}:`)) && findValue(v, key) !== null) {
51
+ if (k.startsWith(`${locale}:`) && findValue(v, key) !== null) {
52
52
  return true
53
53
  }
54
54
  }
55
55
  return false
56
56
  },
57
- hasGeneralTranslation(locale: string): boolean {
58
- return translations.has(locale)
59
- },
60
57
  hasPageTranslation(locale: string, routeName: string): boolean {
61
- return translations.has(`${locale}:${routeName}`)
58
+ const rn = routeName || 'index'
59
+ return translations.has(`${locale}:${rn}`)
62
60
  },
63
61
  getTranslation<T = unknown>(locale: string, routeName: string, key: string): T | null {
64
- const routeKey = `${locale}:${routeName}`
65
- const routeData = translations.get(routeKey)
66
- const val = findValue<T>(routeData, key)
67
- if (val !== null) return val
68
-
69
- const generalData = translations.get(locale)
70
- return findValue<T>(generalData, key)
62
+ const rn = routeName || 'index'
63
+ return findValue<T>(translations.get(`${locale}:${rn}`), key)
71
64
  },
72
- loadTranslations(locale: string, data: Translations): void {
73
- // Merge with existing, replacing duplicate keys
74
- const existing = translations.get(locale) ?? {}
75
- translations.set(locale, { ...existing, ...data })
65
+ loadTranslations(locale: string, data: Translations, routeName = 'index'): void {
66
+ const rn = routeName || 'index'
67
+ const key = `${locale}:${rn}`
68
+ const existing = translations.get(key) ?? {}
69
+ translations.set(key, { ...existing, ...data })
76
70
  },
77
- setTranslations(locale: string, data: Translations): void {
78
- // Replace all translations for locale (no merge)
79
- translations.set(locale, data)
71
+ setTranslations(locale: string, data: Translations, routeName = 'index'): void {
72
+ const rn = routeName || 'index'
73
+ translations.set(`${locale}:${rn}`, data)
80
74
  },
81
75
  loadPageTranslations(locale: string, routeName: string, data: Translations): void {
82
- const key = `${locale}:${routeName}`
76
+ const rn = routeName || 'index'
77
+ const key = `${locale}:${rn}`
83
78
  const existing = translations.get(key)
84
- // Perf: when existing is empty — keep reference, avoid O(n) copying of large objects
85
79
  if (!existing || Object.keys(existing).length === 0) {
86
80
  translations.set(key, data)
87
81
  } else {
@@ -89,14 +83,11 @@ export function useTranslationHelper(storage?: TranslationStorage) {
89
83
  }
90
84
  },
91
85
  mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force = false): void {
92
- const key = `${locale}:${routeName}`
86
+ const rn = routeName || 'index'
87
+ const key = `${locale}:${rn}`
93
88
  const existing = translations.get(key) ?? {}
94
89
  translations.set(key, { ...existing, ...newTranslations })
95
90
  },
96
- mergeGlobalTranslation(locale: string, newTranslations: Translations, _force = false): void {
97
- const existing = translations.get(locale) ?? {}
98
- translations.set(locale, { ...existing, ...newTranslations })
99
- },
100
91
  clearCache(): void {
101
92
  translations.clear()
102
93
  },
@@ -38,44 +38,44 @@ class TestI18n extends BaseI18n {
38
38
  describe('BaseI18n', () => {
39
39
  describe('Constructor', () => {
40
40
  test('should initialize with default options', () => {
41
- const i18n = new TestI18n('en', 'en', 'general')
41
+ const i18n = new TestI18n('en', 'en', 'index')
42
42
  expect(i18n.getLocale()).toBe('en')
43
43
  expect(i18n.getFallbackLocale()).toBe('en')
44
- expect(i18n.getRoute()).toBe('general')
44
+ expect(i18n.getRoute()).toBe('index')
45
45
  })
46
46
 
47
47
  test('should initialize with custom storage', () => {
48
48
  const storage = { translations: new Map<string, Translations>() }
49
- const i18n = new TestI18n('en', 'en', 'general', { storage })
49
+ const i18n = new TestI18n('en', 'en', 'index', { storage })
50
50
  expect(i18n).toBeDefined()
51
51
  })
52
52
 
53
53
  test('should initialize with custom plural function', () => {
54
54
  const customPlural: PluralFunc = () => 'custom'
55
- const i18n = new TestI18n('en', 'en', 'general', { plural: customPlural })
55
+ const i18n = new TestI18n('en', 'en', 'index', { plural: customPlural })
56
56
  expect(i18n).toBeDefined()
57
57
  })
58
58
 
59
59
  test('should initialize with missingWarn option', () => {
60
- const i18n = new TestI18n('en', 'en', 'general', { missingWarn: false })
60
+ const i18n = new TestI18n('en', 'en', 'index', { missingWarn: false })
61
61
  expect(i18n).toBeDefined()
62
62
  })
63
63
 
64
64
  test('should initialize with missingHandler', () => {
65
65
  const handler = jest.fn()
66
- const i18n = new TestI18n('en', 'en', 'general', { missingHandler: handler })
66
+ const i18n = new TestI18n('en', 'en', 'index', { missingHandler: handler })
67
67
  expect(i18n).toBeDefined()
68
68
  })
69
69
  })
70
70
 
71
71
  describe('t() method', () => {
72
72
  test('should return empty string for empty key', () => {
73
- const i18n = new TestI18n('en', 'en', 'general')
73
+ const i18n = new TestI18n('en', 'en', 'index')
74
74
  expect(i18n.t('')).toBe('')
75
75
  })
76
76
 
77
77
  test('should return translation for existing key', async () => {
78
- const i18n = new TestI18n('en', 'en', 'general')
78
+ const i18n = new TestI18n('en', 'en', 'index')
79
79
  const translations: Translations = { greeting: 'Hello' }
80
80
  i18n['helper'].loadTranslations('en', translations)
81
81
 
@@ -84,7 +84,7 @@ describe('BaseI18n', () => {
84
84
 
85
85
  test('should interpolate params in translation', async () => {
86
86
  const storage = { translations: new Map<string, Translations>() }
87
- const i18n = new TestI18n('en', 'en', 'general', { storage })
87
+ const i18n = new TestI18n('en', 'en', 'index', { storage })
88
88
  const translations: Translations = { greeting: 'Hello, {name}!' }
89
89
  await i18n['helper'].loadTranslations('en', translations)
90
90
 
@@ -92,18 +92,18 @@ describe('BaseI18n', () => {
92
92
  })
93
93
 
94
94
  test('should use defaultValue when translation is missing', () => {
95
- const i18n = new TestI18n('en', 'en', 'general')
95
+ const i18n = new TestI18n('en', 'en', 'index')
96
96
  expect(i18n.t('missing.key', undefined, 'Default value')).toBe('Default value')
97
97
  })
98
98
 
99
99
  test('should return key when translation is missing and no defaultValue', () => {
100
- const i18n = new TestI18n('en', 'en', 'general')
100
+ const i18n = new TestI18n('en', 'en', 'index')
101
101
  expect(i18n.t('missing.key')).toBe('missing.key')
102
102
  })
103
103
 
104
104
  test('should fallback to fallbackLocale when translation is missing', async () => {
105
105
  const storage = { translations: new Map<string, Translations>() }
106
- const i18n = new TestI18n('en', 'fr', 'general', { storage })
106
+ const i18n = new TestI18n('en', 'fr', 'index', { storage })
107
107
  const translations: Translations = { greeting: 'Bonjour' }
108
108
  await i18n['helper'].loadTranslations('fr', translations)
109
109
 
@@ -111,7 +111,7 @@ describe('BaseI18n', () => {
111
111
  })
112
112
 
113
113
  test('should use route-specific translation when routeName is provided', async () => {
114
- const i18n = new TestI18n('en', 'en', 'general')
114
+ const i18n = new TestI18n('en', 'en', 'index')
115
115
  const routeTranslations: Translations = { title: 'Route Title' }
116
116
  await i18n['helper'].loadPageTranslations('en', 'about', routeTranslations)
117
117
 
@@ -120,27 +120,27 @@ describe('BaseI18n', () => {
120
120
 
121
121
  test('should call missingHandler when translation is missing', () => {
122
122
  const handler = jest.fn()
123
- const i18n = new TestI18n('en', 'en', 'general', { missingHandler: handler })
123
+ const i18n = new TestI18n('en', 'en', 'index', { missingHandler: handler })
124
124
 
125
125
  i18n.t('missing.key')
126
126
 
127
- expect(handler).toHaveBeenCalledWith('en', 'missing.key', 'general')
127
+ expect(handler).toHaveBeenCalledWith('en', 'missing.key', 'index')
128
128
  })
129
129
 
130
130
  test('should call customMissingHandler when set (Nuxt runtime)', () => {
131
131
  const customHandler = jest.fn()
132
- const i18n = new TestI18n('en', 'en', 'general', {
132
+ const i18n = new TestI18n('en', 'en', 'index', {
133
133
  getCustomMissingHandler: () => customHandler,
134
134
  })
135
135
 
136
136
  i18n.t('missing.key')
137
137
 
138
- expect(customHandler).toHaveBeenCalledWith('en', 'missing.key', 'general')
138
+ expect(customHandler).toHaveBeenCalledWith('en', 'missing.key', 'index')
139
139
  })
140
140
 
141
141
  test('should not warn when missingWarn is false', () => {
142
142
  const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
143
- const i18n = new TestI18n('en', 'en', 'general', { missingWarn: false })
143
+ const i18n = new TestI18n('en', 'en', 'index', { missingWarn: false })
144
144
 
145
145
  i18n.t('missing.key')
146
146
 
@@ -151,7 +151,7 @@ describe('BaseI18n', () => {
151
151
 
152
152
  describe('ts() method', () => {
153
153
  test('should return translation as string', async () => {
154
- const i18n = new TestI18n('en', 'en', 'general')
154
+ const i18n = new TestI18n('en', 'en', 'index')
155
155
  const translations: Translations = { greeting: 'Hello' }
156
156
  i18n['helper'].loadTranslations('en', translations)
157
157
 
@@ -159,17 +159,17 @@ describe('BaseI18n', () => {
159
159
  })
160
160
 
161
161
  test('should return defaultValue when translation is missing', () => {
162
- const i18n = new TestI18n('en', 'en', 'general')
162
+ const i18n = new TestI18n('en', 'en', 'index')
163
163
  expect(i18n.ts('missing.key', undefined, 'Default')).toBe('Default')
164
164
  })
165
165
 
166
166
  test('should return key when translation is missing and no defaultValue', () => {
167
- const i18n = new TestI18n('en', 'en', 'general')
167
+ const i18n = new TestI18n('en', 'en', 'index')
168
168
  expect(i18n.ts('missing.key')).toBe('missing.key')
169
169
  })
170
170
 
171
171
  test('should convert non-string values to string', async () => {
172
- const i18n = new TestI18n('en', 'en', 'general')
172
+ const i18n = new TestI18n('en', 'en', 'index')
173
173
  const translations: Translations = { count: 42 }
174
174
  i18n['helper'].loadTranslations('en', translations)
175
175
 
@@ -179,12 +179,12 @@ describe('BaseI18n', () => {
179
179
 
180
180
  describe('tc() method', () => {
181
181
  test('should return defaultValue when count is undefined', () => {
182
- const i18n = new TestI18n('en', 'en', 'general')
182
+ const i18n = new TestI18n('en', 'en', 'index')
183
183
  expect(i18n.tc('apples', { other: 'params' }, 'No count')).toBe('No count')
184
184
  })
185
185
 
186
186
  test('should use plural function with count', async () => {
187
- const i18n = new TestI18n('en', 'en', 'general')
187
+ const i18n = new TestI18n('en', 'en', 'index')
188
188
  const translations: Translations = { apples: 'apple|apples' }
189
189
  await i18n['helper'].loadTranslations('en', translations)
190
190
 
@@ -196,7 +196,7 @@ describe('BaseI18n', () => {
196
196
  })
197
197
 
198
198
  test('should handle count as number', async () => {
199
- const i18n = new TestI18n('en', 'en', 'general')
199
+ const i18n = new TestI18n('en', 'en', 'index')
200
200
  const translations: Translations = { apples: 'apple|apples' }
201
201
  i18n['helper'].loadTranslations('en', translations)
202
202
 
@@ -204,7 +204,7 @@ describe('BaseI18n', () => {
204
204
  })
205
205
 
206
206
  test('should handle count as Params object', async () => {
207
- const i18n = new TestI18n('en', 'en', 'general')
207
+ const i18n = new TestI18n('en', 'en', 'index')
208
208
  const translations: Translations = { apples: 'apple|apples' }
209
209
  i18n['helper'].loadTranslations('en', translations)
210
210
 
@@ -212,7 +212,7 @@ describe('BaseI18n', () => {
212
212
  })
213
213
 
214
214
  test('should return defaultValue when plural function returns null', () => {
215
- const i18n = new TestI18n('en', 'en', 'general')
215
+ const i18n = new TestI18n('en', 'en', 'index')
216
216
  // When translation is missing, t() returns key, which is passed to pluralFunc
217
217
  // pluralFunc tries to process 'missing.key' as translation, but since it doesn't contain '|',
218
218
  // it returns the key itself. So tc returns the key, not defaultValue.
@@ -224,19 +224,19 @@ describe('BaseI18n', () => {
224
224
 
225
225
  describe('tn() method', () => {
226
226
  test('should format number with default locale', () => {
227
- const i18n = new TestI18n('en', 'en', 'general')
227
+ const i18n = new TestI18n('en', 'en', 'index')
228
228
  const result = i18n.tn(1234.56)
229
229
  expect(result).toMatch(/1[,.]234[.,]56/)
230
230
  })
231
231
 
232
232
  test('should format number with custom options', () => {
233
- const i18n = new TestI18n('en', 'en', 'general')
233
+ const i18n = new TestI18n('en', 'en', 'index')
234
234
  const result = i18n.tn(1234.56, { style: 'currency', currency: 'USD' })
235
235
  expect(result).toContain('1,234.56')
236
236
  })
237
237
 
238
238
  test('should use current locale for formatting', () => {
239
- const i18n = new TestI18n('ru', 'en', 'general')
239
+ const i18n = new TestI18n('ru', 'en', 'index')
240
240
  const result = i18n.tn(1234.56)
241
241
  // Russian locale uses different number formatting
242
242
  expect(result).toBeDefined()
@@ -245,7 +245,7 @@ describe('BaseI18n', () => {
245
245
 
246
246
  describe('td() method', () => {
247
247
  test('should format date with default locale', () => {
248
- const i18n = new TestI18n('en', 'en', 'general')
248
+ const i18n = new TestI18n('en', 'en', 'index')
249
249
  const date = new Date('2024-01-15')
250
250
  const result = i18n.td(date)
251
251
  expect(result).toBeDefined()
@@ -253,7 +253,7 @@ describe('BaseI18n', () => {
253
253
  })
254
254
 
255
255
  test('should format date with custom options', () => {
256
- const i18n = new TestI18n('en', 'en', 'general')
256
+ const i18n = new TestI18n('en', 'en', 'index')
257
257
  const date = new Date('2024-01-15')
258
258
  const result = i18n.td(date, { year: 'numeric', month: 'long', day: 'numeric' })
259
259
  expect(result).toContain('2024')
@@ -261,7 +261,7 @@ describe('BaseI18n', () => {
261
261
  })
262
262
 
263
263
  test('should handle date as number (timestamp)', () => {
264
- const i18n = new TestI18n('en', 'en', 'general')
264
+ const i18n = new TestI18n('en', 'en', 'index')
265
265
  const timestamp = new Date('2024-01-15').getTime()
266
266
  const result = i18n.td(timestamp)
267
267
  expect(result).toBeDefined()
@@ -269,7 +269,7 @@ describe('BaseI18n', () => {
269
269
  })
270
270
 
271
271
  test('should handle date as string', () => {
272
- const i18n = new TestI18n('en', 'en', 'general')
272
+ const i18n = new TestI18n('en', 'en', 'index')
273
273
  const result = i18n.td('2024-01-15')
274
274
  expect(result).toBeDefined()
275
275
  expect(result).not.toBe('Invalid Date')
@@ -278,7 +278,7 @@ describe('BaseI18n', () => {
278
278
 
279
279
  describe('tdr() method', () => {
280
280
  test('should format relative time', () => {
281
- const i18n = new TestI18n('en', 'en', 'general')
281
+ const i18n = new TestI18n('en', 'en', 'index')
282
282
  const yesterday = new Date(Date.now() - 86400000)
283
283
  const result = i18n.tdr(yesterday)
284
284
  expect(result).toBeDefined()
@@ -286,14 +286,14 @@ describe('BaseI18n', () => {
286
286
  })
287
287
 
288
288
  test('should format relative time with custom options', () => {
289
- const i18n = new TestI18n('en', 'en', 'general')
289
+ const i18n = new TestI18n('en', 'en', 'index')
290
290
  const yesterday = new Date(Date.now() - 86400000)
291
291
  const result = i18n.tdr(yesterday, { numeric: 'always' })
292
292
  expect(result).toBeDefined()
293
293
  })
294
294
 
295
295
  test('should handle invalid date gracefully', () => {
296
- const i18n = new TestI18n('en', 'en', 'general')
296
+ const i18n = new TestI18n('en', 'en', 'index')
297
297
  const invalidDate = new Date('invalid')
298
298
  const result = i18n.tdr(invalidDate)
299
299
  expect(result).toBeDefined()
@@ -302,7 +302,7 @@ describe('BaseI18n', () => {
302
302
 
303
303
  describe('has() method', () => {
304
304
  test('should return true when translation exists', async () => {
305
- const i18n = new TestI18n('en', 'en', 'general')
305
+ const i18n = new TestI18n('en', 'en', 'index')
306
306
  const translations: Translations = { greeting: 'Hello' }
307
307
  i18n['helper'].loadTranslations('en', translations)
308
308
 
@@ -310,17 +310,17 @@ describe('BaseI18n', () => {
310
310
  })
311
311
 
312
312
  test('should return false when translation does not exist', () => {
313
- const i18n = new TestI18n('en', 'en', 'general')
313
+ const i18n = new TestI18n('en', 'en', 'index')
314
314
  expect(i18n.has('missing.key')).toBe(false)
315
315
  })
316
316
 
317
317
  test('should check route-specific translation when routeName is provided', async () => {
318
- const i18n = new TestI18n('en', 'en', 'general')
318
+ const i18n = new TestI18n('en', 'en', 'index')
319
319
  const routeTranslations: Translations = { title: 'Route Title' }
320
320
  await i18n['helper'].loadPageTranslations('en', 'about', routeTranslations)
321
321
 
322
322
  expect(i18n.has('title', 'about')).toBe(true)
323
- expect(i18n.has('title', 'general')).toBe(false)
323
+ expect(i18n.has('title', 'index')).toBe(false)
324
324
  })
325
325
 
326
326
  test('should use current route when routeName is not provided', async () => {
@@ -334,7 +334,7 @@ describe('BaseI18n', () => {
334
334
 
335
335
  describe('clearCache() method', () => {
336
336
  test('should clear all translations from cache', async () => {
337
- const i18n = new TestI18n('en', 'en', 'general')
337
+ const i18n = new TestI18n('en', 'en', 'index')
338
338
  const translations: Translations = { greeting: 'Hello' }
339
339
  i18n['helper'].loadTranslations('en', translations)
340
340
 
@@ -348,7 +348,7 @@ describe('BaseI18n', () => {
348
348
 
349
349
  describe('loadTranslationsCore() method', () => {
350
350
  test('should load translations when merge is false', async () => {
351
- const i18n = new TestI18n('en', 'en', 'general')
351
+ const i18n = new TestI18n('en', 'en', 'index')
352
352
  const translations: Translations = { greeting: 'Hello' }
353
353
 
354
354
  i18n['loadTranslationsCore']('en', translations, false)
@@ -359,7 +359,7 @@ describe('BaseI18n', () => {
359
359
  })
360
360
 
361
361
  test('should merge translations when merge is true', async () => {
362
- const i18n = new TestI18n('en', 'en', 'general')
362
+ const i18n = new TestI18n('en', 'en', 'index')
363
363
  const initial: Translations = { greeting: 'Hello' }
364
364
  const additional: Translations = { farewell: 'Goodbye' }
365
365
 
@@ -375,7 +375,7 @@ describe('BaseI18n', () => {
375
375
 
376
376
  describe('loadRouteTranslationsCore() method', () => {
377
377
  test('should load route translations when merge is false', async () => {
378
- const i18n = new TestI18n('en', 'en', 'general')
378
+ const i18n = new TestI18n('en', 'en', 'index')
379
379
  const translations: Translations = { title: 'Page Title' }
380
380
 
381
381
  i18n['loadRouteTranslationsCore']('en', 'about', translations, false)
@@ -386,7 +386,7 @@ describe('BaseI18n', () => {
386
386
  })
387
387
 
388
388
  test('should merge route translations when merge is true', async () => {
389
- const i18n = new TestI18n('en', 'en', 'general')
389
+ const i18n = new TestI18n('en', 'en', 'index')
390
390
  const initial: Translations = { title: 'Page Title' }
391
391
  const additional: Translations = { description: 'Page Description' }
392
392
 
@@ -402,7 +402,7 @@ describe('BaseI18n', () => {
402
402
 
403
403
  describe('Edge cases', () => {
404
404
  test('should handle nested translation keys', async () => {
405
- const i18n = new TestI18n('en', 'en', 'general')
405
+ const i18n = new TestI18n('en', 'en', 'index')
406
406
  const translations: Translations = {
407
407
  nested: {
408
408
  deep: {
@@ -416,7 +416,7 @@ describe('BaseI18n', () => {
416
416
  })
417
417
 
418
418
  test('should handle multiple params in interpolation', async () => {
419
- const i18n = new TestI18n('en', 'en', 'general')
419
+ const i18n = new TestI18n('en', 'en', 'index')
420
420
  const translations: Translations = {
421
421
  message: 'Hello, {name}! You are {age} years old.',
422
422
  }
@@ -426,23 +426,25 @@ describe('BaseI18n', () => {
426
426
  })
427
427
 
428
428
  test('should handle null defaultValue', () => {
429
- const i18n = new TestI18n('en', 'en', 'general')
429
+ const i18n = new TestI18n('en', 'en', 'index')
430
430
  expect(i18n.t('missing.key', undefined, null)).toBe('missing.key')
431
431
  })
432
432
 
433
433
  test('should handle empty string defaultValue', () => {
434
- const i18n = new TestI18n('en', 'en', 'general')
434
+ const i18n = new TestI18n('en', 'en', 'index')
435
435
  // Empty string is falsy, so it will fall back to key (as per defaultValue || key logic)
436
436
  expect(i18n.t('missing.key', undefined, '')).toBe('missing.key')
437
437
  })
438
438
 
439
439
  test('should handle route change', async () => {
440
440
  const storage = { translations: new Map<string, Translations>() }
441
- const i18n = new TestI18n('en', 'en', 'general', { storage })
442
- const generalTranslations: Translations = { greeting: 'Hello' }
443
- const routeTranslations: Translations = { title: 'About Page' }
441
+ const i18n = new TestI18n('en', 'en', 'index', { storage })
442
+ const rootTranslations: Translations = { greeting: 'Hello' }
443
+ // Packages (vue, node, etc.) merge root into pages automatically.
444
+ // Core does not — so we simulate pre-merged data here.
445
+ const routeTranslations: Translations = { greeting: 'Hello', title: 'About Page' }
444
446
 
445
- await i18n['helper'].loadTranslations('en', generalTranslations)
447
+ await i18n['helper'].loadTranslations('en', rootTranslations)
446
448
  await i18n['helper'].loadPageTranslations('en', 'about', routeTranslations)
447
449
 
448
450
  // Verify translations are loaded
@@ -450,14 +452,13 @@ describe('BaseI18n', () => {
450
452
  expect(i18n.has('title', 'about')).toBe(true)
451
453
 
452
454
  // Check route-specific translation with explicit routeName
453
- // Note: getTranslation uses routeName parameter, so this should work
454
455
  const titleValue = i18n.t('title', undefined, undefined, 'about')
455
456
  expect(titleValue).toBe('About Page')
456
457
 
457
458
  // Change route and check
458
459
  i18n.setRoute('about')
459
460
  expect(i18n.t('title')).toBe('About Page')
460
- expect(i18n.t('greeting')).toBe('Hello') // Should still work from general
461
+ expect(i18n.t('greeting')).toBe('Hello')
461
462
  })
462
463
  })
463
464
  })
@@ -63,12 +63,12 @@ describe('Translation Helper', () => {
63
63
  expect(helper.getTranslation('en', 'home', 'newKey')).toBe('New value')
64
64
  })
65
65
 
66
- test('mergeGlobalTranslation updates general translations', () => {
66
+ test('mergeTranslation with index routeName updates index translations', () => {
67
67
  const helper = useTranslationHelper()
68
68
  helper.loadTranslations('en', translations.en)
69
69
 
70
- helper.mergeGlobalTranslation('en', { newGlobalKey: 'Global value' })
71
- expect(helper.getTranslation('en', 'index', 'newGlobalKey')).toBe('Global value')
70
+ helper.mergeTranslation('en', 'index', { newKey: 'New value' })
71
+ expect(helper.getTranslation('en', 'index', 'newKey')).toBe('New value')
72
72
  })
73
73
 
74
74
  test('deepClone creates a deep copy of objects', () => {