🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@i18n-micro/core

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@i18n-micro/core - npm Package Compare versions

Comparing version
1.1.4
to
1.2.0
+1
-1
dist/base.d.ts

@@ -75,3 +75,3 @@ import { CleanTranslation, MissingHandler, Params, PluralFunc, TranslationKey, Translations } from '@i18n-micro/types';

*/
loadTranslationsCore(locale: string, translations: Translations, merge: boolean): void;
loadTranslationsCore(locale: string, translations: Translations, merge: boolean, routeName?: string): void;
/**

@@ -78,0 +78,0 @@ * Core route translation loading logic (without reactivity)

@@ -1,1 +0,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;
"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,2 +0,2 @@

class h {
class f {
formatNumber(t, e, n) {

@@ -21,6 +21,6 @@ return new Intl.NumberFormat(e, n).format(t);

];
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);
for (const { unit: l, seconds: u } of o) {
const c = Math.floor(i / u);
if (c >= 1)
return new Intl.RelativeTimeFormat(e, n).format(-c, l);
}

@@ -37,15 +37,15 @@ return new Intl.RelativeTimeFormat(e, n).format(0, "second");

}
function T(s) {
function x(s) {
return s === "prefix" || s === "prefix_and_default";
}
function v(s) {
function T(s) {
return s === "no_prefix";
}
function w(s) {
function $(s) {
return s === "prefix";
}
function x(s) {
function v(s) {
return s === "prefix_except_default";
}
function $(s) {
function w(s) {
return s === "prefix_and_default";

@@ -62,3 +62,3 @@ }

};
function f(s, t) {
function h(s, t) {
if (!s || typeof t != "string") return null;

@@ -82,8 +82,8 @@ if (t in s) {

hasCache(e, n) {
const r = `${e}:${n}`;
return t.has(r) || t.has(e);
const r = n || "index";
return t.has(`${e}:${r}`);
},
getCache(e, n) {
const r = `${e}:${n}`;
return t.get(r);
const r = n || "index";
return t.get(`${e}:${r}`);
},

@@ -94,37 +94,30 @@ setCache(e, n, r) {

for (const [r, a] of t)
if ((r === e || r.startsWith(`${e}:`)) && f(a, n) !== null)
if (r.startsWith(`${e}:`) && h(a, n) !== null)
return !0;
return !1;
},
hasGeneralTranslation(e) {
return t.has(e);
},
hasPageTranslation(e, n) {
return t.has(`${e}:${n}`);
const r = n || "index";
return t.has(`${e}:${r}`);
},
getTranslation(e, n, r) {
const a = `${e}:${n}`, i = t.get(a), o = f(i, r);
if (o !== null) return o;
const l = t.get(e);
return f(l, r);
const a = n || "index";
return h(t.get(`${e}:${a}`), r);
},
loadTranslations(e, n) {
const r = t.get(e) ?? {};
t.set(e, { ...r, ...n });
loadTranslations(e, n, r = "index") {
const i = `${e}:${r || "index"}`, o = t.get(i) ?? {};
t.set(i, { ...o, ...n });
},
setTranslations(e, n) {
t.set(e, n);
setTranslations(e, n, r = "index") {
const a = r || "index";
t.set(`${e}:${a}`, n);
},
loadPageTranslations(e, n, r) {
const a = `${e}:${n}`, i = t.get(a);
!i || Object.keys(i).length === 0 ? t.set(a, r) : t.set(a, { ...i, ...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 i = `${e}:${n}`, o = t.get(i) ?? {};
t.set(i, { ...o, ...r });
const o = `${e}:${n || "index"}`, l = t.get(o) ?? {};
t.set(o, { ...l, ...r });
},
mergeGlobalTranslation(e, n, r = !1) {
const a = t.get(e) ?? {};
t.set(e, { ...a, ...n });
},
clearCache() {

@@ -137,3 +130,3 @@ t.clear();

constructor(t = {}) {
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;
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;
}

@@ -172,3 +165,3 @@ // --- Public methods (implemented in base class) ---

return n ?? t;
const i = (l, c, u) => this.t(l, c, u);
const i = (l, u, c) => this.t(l, u, c);
return this.pluralFunc(t, Number.parseInt(r.toString(), 10), a, this.getLocale(), i) ?? n ?? t;

@@ -213,4 +206,4 @@ }

*/
loadTranslationsCore(t, e, n) {
n ? this.helper.mergeGlobalTranslation(t, e, !0) : this.helper.setTranslations(t, e);
loadTranslationsCore(t, e, n, r = "index") {
n ? this.helper.mergeTranslation(t, r, e, !0) : this.helper.setTranslations(t, e, r);
}

@@ -227,11 +220,11 @@ /**

_ as BaseI18n,
h as FormatService,
f as FormatService,
d as defaultPlural,
m as interpolate,
v as isNoPrefixStrategy,
$ as isPrefixAndDefaultStrategy,
x as isPrefixExceptDefaultStrategy,
w as isPrefixStrategy,
T as isNoPrefixStrategy,
w as isPrefixAndDefaultStrategy,
v as isPrefixExceptDefaultStrategy,
$ as isPrefixStrategy,
p as useTranslationHelper,
T as withPrefixStrategy
x as withPrefixStrategy
};
import { Translations } from '@i18n-micro/types';
/**
* Bare Metal: Simple translation storage without Ref, useState, devalue.
* Map key: locale (general) or locale:routeName (page-specific).
* Map key: `${locale}:${routeName}` (page-specific).
*/

@@ -14,11 +14,9 @@ export interface TranslationStorage {

hasTranslation(locale: string, key: string): boolean;
hasGeneralTranslation(locale: string): boolean;
hasPageTranslation(locale: string, routeName: string): boolean;
getTranslation<T = unknown>(locale: string, routeName: string, key: string): T | null;
loadTranslations(locale: string, data: Translations): void;
setTranslations(locale: string, data: Translations): void;
loadTranslations(locale: string, data: Translations, routeName?: string): void;
setTranslations(locale: string, data: Translations, routeName?: string): void;
loadPageTranslations(locale: string, routeName: string, data: Translations): void;
mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force?: boolean): void;
mergeGlobalTranslation(locale: string, newTranslations: Translations, _force?: boolean): void;
clearCache(): void;
};
{
"name": "@i18n-micro/core",
"version": "1.1.4",
"version": "1.2.0",
"description": "",

@@ -25,3 +25,3 @@ "repository": "s00d/nuxt-i18n-micro",

"dependencies": {
"@i18n-micro/types": "1.1.4"
"@i18n-micro/types": "1.1.5"
},

@@ -28,0 +28,0 @@ "devDependencies": {

@@ -96,6 +96,2 @@ # @i18n-micro/core

Merges new translations into the cache for a specific route and locale.
- **`mergeGlobalTranslation(locale: string, newTranslations: Translations, force = false): void`**:
Merges new translations into the global cache for a specific locale.
- **`hasGeneralTranslation(locale: string): boolean`**:
Checks if global translations are loaded for the specified locale.
- **`hasPageTranslation(locale: string, routeName: string): boolean`**:

@@ -102,0 +98,0 @@ Checks if translations for a specific route and locale are loaded.

@@ -69,6 +69,5 @@ import type { CleanTranslation, Getter, MissingHandler, Params, PluralFunc, TranslationKey, Translations } from '@i18n-micro/types'

// 1. Try to find translation in current locale
// Note: In Nuxt runtime, server already merges global translations, so we don't need explicit fallback
let value = this.helper.getTranslation<string>(locale, route, key)
// 2. Fallback to fallbackLocale if not found and different (for non-Nuxt adapters)
// 2. Fallback to fallbackLocale if not found and different
if (!value) {

@@ -177,7 +176,7 @@ const fallbackLocale = this.getFallbackLocale()

*/
public loadTranslationsCore(locale: string, translations: Translations, merge: boolean): void {
public loadTranslationsCore(locale: string, translations: Translations, merge: boolean, routeName = 'index'): void {
if (merge) {
this.helper.mergeGlobalTranslation(locale, translations, true)
this.helper.mergeTranslation(locale, routeName, translations, true)
} else {
this.helper.setTranslations(locale, translations)
this.helper.setTranslations(locale, translations, routeName)
}

@@ -184,0 +183,0 @@ }

@@ -5,3 +5,3 @@ import type { Translations } from '@i18n-micro/types'

* Bare Metal: Simple translation storage without Ref, useState, devalue.
* Map key: locale (general) or locale:routeName (page-specific).
* Map key: `${locale}:${routeName}` (page-specific).
*/

@@ -40,8 +40,8 @@ export interface TranslationStorage {

hasCache(locale: string, page: string) {
const cacheKey = `${locale}:${page}`
return translations.has(cacheKey) || translations.has(locale)
const p = page || 'index'
return translations.has(`${locale}:${p}`)
},
getCache(locale: string, routeName: string) {
const cacheKey = `${locale}:${routeName}`
return translations.get(cacheKey)
const rn = routeName || 'index'
return translations.get(`${locale}:${rn}`)
},

@@ -53,3 +53,3 @@ setCache(_locale: string, _routeName: string, _cache: Map<string, unknown>) {

for (const [k, v] of translations) {
if ((k === locale || k.startsWith(`${locale}:`)) && findValue(v, key) !== null) {
if (k.startsWith(`${locale}:`) && findValue(v, key) !== null) {
return true

@@ -60,30 +60,24 @@ }

},
hasGeneralTranslation(locale: string): boolean {
return translations.has(locale)
},
hasPageTranslation(locale: string, routeName: string): boolean {
return translations.has(`${locale}:${routeName}`)
const rn = routeName || 'index'
return translations.has(`${locale}:${rn}`)
},
getTranslation<T = unknown>(locale: string, routeName: string, key: string): T | null {
const routeKey = `${locale}:${routeName}`
const routeData = translations.get(routeKey)
const val = findValue<T>(routeData, key)
if (val !== null) return val
const generalData = translations.get(locale)
return findValue<T>(generalData, key)
const rn = routeName || 'index'
return findValue<T>(translations.get(`${locale}:${rn}`), key)
},
loadTranslations(locale: string, data: Translations): void {
// Merge with existing, replacing duplicate keys
const existing = translations.get(locale) ?? {}
translations.set(locale, { ...existing, ...data })
loadTranslations(locale: string, data: Translations, routeName = 'index'): void {
const rn = routeName || 'index'
const key = `${locale}:${rn}`
const existing = translations.get(key) ?? {}
translations.set(key, { ...existing, ...data })
},
setTranslations(locale: string, data: Translations): void {
// Replace all translations for locale (no merge)
translations.set(locale, data)
setTranslations(locale: string, data: Translations, routeName = 'index'): void {
const rn = routeName || 'index'
translations.set(`${locale}:${rn}`, data)
},
loadPageTranslations(locale: string, routeName: string, data: Translations): void {
const key = `${locale}:${routeName}`
const rn = routeName || 'index'
const key = `${locale}:${rn}`
const existing = translations.get(key)
// Perf: when existing is empty — keep reference, avoid O(n) copying of large objects
if (!existing || Object.keys(existing).length === 0) {

@@ -96,10 +90,7 @@ translations.set(key, data)

mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force = false): void {
const key = `${locale}:${routeName}`
const rn = routeName || 'index'
const key = `${locale}:${rn}`
const existing = translations.get(key) ?? {}
translations.set(key, { ...existing, ...newTranslations })
},
mergeGlobalTranslation(locale: string, newTranslations: Translations, _force = false): void {
const existing = translations.get(locale) ?? {}
translations.set(locale, { ...existing, ...newTranslations })
},
clearCache(): void {

@@ -106,0 +97,0 @@ translations.clear()

@@ -41,6 +41,6 @@ import type { PluralFunc, Translations } from '@i18n-micro/types'

test('should initialize with default options', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.getLocale()).toBe('en')
expect(i18n.getFallbackLocale()).toBe('en')
expect(i18n.getRoute()).toBe('general')
expect(i18n.getRoute()).toBe('index')
})

@@ -50,3 +50,3 @@

const storage = { translations: new Map<string, Translations>() }
const i18n = new TestI18n('en', 'en', 'general', { storage })
const i18n = new TestI18n('en', 'en', 'index', { storage })
expect(i18n).toBeDefined()

@@ -57,3 +57,3 @@ })

const customPlural: PluralFunc = () => 'custom'
const i18n = new TestI18n('en', 'en', 'general', { plural: customPlural })
const i18n = new TestI18n('en', 'en', 'index', { plural: customPlural })
expect(i18n).toBeDefined()

@@ -63,3 +63,3 @@ })

test('should initialize with missingWarn option', () => {
const i18n = new TestI18n('en', 'en', 'general', { missingWarn: false })
const i18n = new TestI18n('en', 'en', 'index', { missingWarn: false })
expect(i18n).toBeDefined()

@@ -70,3 +70,3 @@ })

const handler = jest.fn()
const i18n = new TestI18n('en', 'en', 'general', { missingHandler: handler })
const i18n = new TestI18n('en', 'en', 'index', { missingHandler: handler })
expect(i18n).toBeDefined()

@@ -78,3 +78,3 @@ })

test('should return empty string for empty key', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.t('')).toBe('')

@@ -84,3 +84,3 @@ })

test('should return translation for existing key', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { greeting: 'Hello' }

@@ -94,3 +94,3 @@ i18n['helper'].loadTranslations('en', translations)

const storage = { translations: new Map<string, Translations>() }
const i18n = new TestI18n('en', 'en', 'general', { storage })
const i18n = new TestI18n('en', 'en', 'index', { storage })
const translations: Translations = { greeting: 'Hello, {name}!' }

@@ -103,3 +103,3 @@ await i18n['helper'].loadTranslations('en', translations)

test('should use defaultValue when translation is missing', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.t('missing.key', undefined, 'Default value')).toBe('Default value')

@@ -109,3 +109,3 @@ })

test('should return key when translation is missing and no defaultValue', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.t('missing.key')).toBe('missing.key')

@@ -116,3 +116,3 @@ })

const storage = { translations: new Map<string, Translations>() }
const i18n = new TestI18n('en', 'fr', 'general', { storage })
const i18n = new TestI18n('en', 'fr', 'index', { storage })
const translations: Translations = { greeting: 'Bonjour' }

@@ -125,3 +125,3 @@ await i18n['helper'].loadTranslations('fr', translations)

test('should use route-specific translation when routeName is provided', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const routeTranslations: Translations = { title: 'Route Title' }

@@ -135,7 +135,7 @@ await i18n['helper'].loadPageTranslations('en', 'about', routeTranslations)

const handler = jest.fn()
const i18n = new TestI18n('en', 'en', 'general', { missingHandler: handler })
const i18n = new TestI18n('en', 'en', 'index', { missingHandler: handler })
i18n.t('missing.key')
expect(handler).toHaveBeenCalledWith('en', 'missing.key', 'general')
expect(handler).toHaveBeenCalledWith('en', 'missing.key', 'index')
})

@@ -145,3 +145,3 @@

const customHandler = jest.fn()
const i18n = new TestI18n('en', 'en', 'general', {
const i18n = new TestI18n('en', 'en', 'index', {
getCustomMissingHandler: () => customHandler,

@@ -152,3 +152,3 @@ })

expect(customHandler).toHaveBeenCalledWith('en', 'missing.key', 'general')
expect(customHandler).toHaveBeenCalledWith('en', 'missing.key', 'index')
})

@@ -158,3 +158,3 @@

const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
const i18n = new TestI18n('en', 'en', 'general', { missingWarn: false })
const i18n = new TestI18n('en', 'en', 'index', { missingWarn: false })

@@ -170,3 +170,3 @@ i18n.t('missing.key')

test('should return translation as string', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { greeting: 'Hello' }

@@ -179,3 +179,3 @@ i18n['helper'].loadTranslations('en', translations)

test('should return defaultValue when translation is missing', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.ts('missing.key', undefined, 'Default')).toBe('Default')

@@ -185,3 +185,3 @@ })

test('should return key when translation is missing and no defaultValue', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.ts('missing.key')).toBe('missing.key')

@@ -191,3 +191,3 @@ })

test('should convert non-string values to string', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { count: 42 }

@@ -202,3 +202,3 @@ i18n['helper'].loadTranslations('en', translations)

test('should return defaultValue when count is undefined', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.tc('apples', { other: 'params' }, 'No count')).toBe('No count')

@@ -208,3 +208,3 @@ })

test('should use plural function with count', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { apples: 'apple|apples' }

@@ -221,3 +221,3 @@ await i18n['helper'].loadTranslations('en', translations)

test('should handle count as number', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { apples: 'apple|apples' }

@@ -230,3 +230,3 @@ i18n['helper'].loadTranslations('en', translations)

test('should handle count as Params object', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { apples: 'apple|apples' }

@@ -239,3 +239,3 @@ i18n['helper'].loadTranslations('en', translations)

test('should return defaultValue when plural function returns null', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
// When translation is missing, t() returns key, which is passed to pluralFunc

@@ -252,3 +252,3 @@ // pluralFunc tries to process 'missing.key' as translation, but since it doesn't contain '|',

test('should format number with default locale', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const result = i18n.tn(1234.56)

@@ -259,3 +259,3 @@ expect(result).toMatch(/1[,.]234[.,]56/)

test('should format number with custom options', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const result = i18n.tn(1234.56, { style: 'currency', currency: 'USD' })

@@ -266,3 +266,3 @@ expect(result).toContain('1,234.56')

test('should use current locale for formatting', () => {
const i18n = new TestI18n('ru', 'en', 'general')
const i18n = new TestI18n('ru', 'en', 'index')
const result = i18n.tn(1234.56)

@@ -276,3 +276,3 @@ // Russian locale uses different number formatting

test('should format date with default locale', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const date = new Date('2024-01-15')

@@ -285,3 +285,3 @@ const result = i18n.td(date)

test('should format date with custom options', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const date = new Date('2024-01-15')

@@ -294,3 +294,3 @@ const result = i18n.td(date, { year: 'numeric', month: 'long', day: 'numeric' })

test('should handle date as number (timestamp)', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const timestamp = new Date('2024-01-15').getTime()

@@ -303,3 +303,3 @@ const result = i18n.td(timestamp)

test('should handle date as string', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const result = i18n.td('2024-01-15')

@@ -313,3 +313,3 @@ expect(result).toBeDefined()

test('should format relative time', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const yesterday = new Date(Date.now() - 86400000)

@@ -322,3 +322,3 @@ const result = i18n.tdr(yesterday)

test('should format relative time with custom options', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const yesterday = new Date(Date.now() - 86400000)

@@ -330,3 +330,3 @@ const result = i18n.tdr(yesterday, { numeric: 'always' })

test('should handle invalid date gracefully', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const invalidDate = new Date('invalid')

@@ -340,3 +340,3 @@ const result = i18n.tdr(invalidDate)

test('should return true when translation exists', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { greeting: 'Hello' }

@@ -349,3 +349,3 @@ i18n['helper'].loadTranslations('en', translations)

test('should return false when translation does not exist', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.has('missing.key')).toBe(false)

@@ -355,3 +355,3 @@ })

test('should check route-specific translation when routeName is provided', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const routeTranslations: Translations = { title: 'Route Title' }

@@ -361,3 +361,3 @@ await i18n['helper'].loadPageTranslations('en', 'about', routeTranslations)

expect(i18n.has('title', 'about')).toBe(true)
expect(i18n.has('title', 'general')).toBe(false)
expect(i18n.has('title', 'index')).toBe(false)
})

@@ -376,3 +376,3 @@

test('should clear all translations from cache', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { greeting: 'Hello' }

@@ -391,3 +391,3 @@ i18n['helper'].loadTranslations('en', translations)

test('should load translations when merge is false', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { greeting: 'Hello' }

@@ -403,3 +403,3 @@

test('should merge translations when merge is true', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const initial: Translations = { greeting: 'Hello' }

@@ -420,3 +420,3 @@ const additional: Translations = { farewell: 'Goodbye' }

test('should load route translations when merge is false', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = { title: 'Page Title' }

@@ -432,3 +432,3 @@

test('should merge route translations when merge is true', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const initial: Translations = { title: 'Page Title' }

@@ -449,3 +449,3 @@ const additional: Translations = { description: 'Page Description' }

test('should handle nested translation keys', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = {

@@ -464,3 +464,3 @@ nested: {

test('should handle multiple params in interpolation', async () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
const translations: Translations = {

@@ -475,3 +475,3 @@ message: 'Hello, {name}! You are {age} years old.',

test('should handle null defaultValue', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
expect(i18n.t('missing.key', undefined, null)).toBe('missing.key')

@@ -481,3 +481,3 @@ })

test('should handle empty string defaultValue', () => {
const i18n = new TestI18n('en', 'en', 'general')
const i18n = new TestI18n('en', 'en', 'index')
// Empty string is falsy, so it will fall back to key (as per defaultValue || key logic)

@@ -489,7 +489,9 @@ expect(i18n.t('missing.key', undefined, '')).toBe('missing.key')

const storage = { translations: new Map<string, Translations>() }
const i18n = new TestI18n('en', 'en', 'general', { storage })
const generalTranslations: Translations = { greeting: 'Hello' }
const routeTranslations: Translations = { title: 'About Page' }
const i18n = new TestI18n('en', 'en', 'index', { storage })
const rootTranslations: Translations = { greeting: 'Hello' }
// Packages (vue, node, etc.) merge root into pages automatically.
// Core does not — so we simulate pre-merged data here.
const routeTranslations: Translations = { greeting: 'Hello', title: 'About Page' }
await i18n['helper'].loadTranslations('en', generalTranslations)
await i18n['helper'].loadTranslations('en', rootTranslations)
await i18n['helper'].loadPageTranslations('en', 'about', routeTranslations)

@@ -502,3 +504,2 @@

// Check route-specific translation with explicit routeName
// Note: getTranslation uses routeName parameter, so this should work
const titleValue = i18n.t('title', undefined, undefined, 'about')

@@ -510,5 +511,5 @@ expect(titleValue).toBe('About Page')

expect(i18n.t('title')).toBe('About Page')
expect(i18n.t('greeting')).toBe('Hello') // Should still work from general
expect(i18n.t('greeting')).toBe('Hello')
})
})
})

@@ -66,8 +66,8 @@ import { interpolate, useTranslationHelper } from '../src'

test('mergeGlobalTranslation updates general translations', () => {
test('mergeTranslation with index routeName updates index translations', () => {
const helper = useTranslationHelper()
helper.loadTranslations('en', translations.en)
helper.mergeGlobalTranslation('en', { newGlobalKey: 'Global value' })
expect(helper.getTranslation('en', 'index', 'newGlobalKey')).toBe('Global value')
helper.mergeTranslation('en', 'index', { newKey: 'New value' })
expect(helper.getTranslation('en', 'index', 'newKey')).toBe('New value')
})

@@ -74,0 +74,0 @@