@comvi/vue 0.2.0 → 0.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
@@ -32,7 +32,7 @@ Comvi i18n is a modern, framework-agnostic internationalization library built on
32
32
  - **Rich text without XSS.** Embed components inside translation strings (`Click <link>here</link>`) — translators see clean markup, you decide what each tag renders to. No raw HTML, no unsafe DOM injection, no splitting a sentence across template fragments.
33
33
  - **Real ICU MessageFormat.** Plurals, ordinals, and select all follow locale-correct grammar via `Intl.PluralRules` — Polish, Ukrainian, Arabic, Welsh, and the rest. Same syntax every major TMS (Crowdin, Lokalise, Phrase) already speaks.
34
34
  - **Locale-aware formatters built in.** `formatNumber`, `formatDate`, `formatCurrency`, and `formatRelativeTime` follow the active locale via native `Intl`, with reactive updates in every framework binding.
35
- - **~8 kB gzipped, zero runtime dependencies.** No `eval` or `new Function` anywhere — runs under a strict CSP without `unsafe-eval`. Safe for Chrome extensions, Cloudflare Workers, and locked-down enterprise apps.
35
+ - **~8 kB minified + gzipped (as bundled by your app), zero runtime dependencies.** No `eval` or `new Function` anywhere — runs under a strict CSP without `unsafe-eval`. Safe for Chrome extensions, Cloudflare Workers, and locked-down enterprise apps.
36
36
  - **Pluggable, not monolithic.** Translation loading (CDN/API), locale detection, and in-context editing are opt-in plugins via `@comvi/plugin-fetch-loader`, `@comvi/plugin-locale-detector`, and `@comvi/plugin-in-context-editor`. You only ship what you use.
37
37
  - **Same API across 6 frameworks.** `useI18n()` and `<T>` look the same in [Vue](https://www.npmjs.com/package/@comvi/vue), [React](https://www.npmjs.com/package/@comvi/react), [SolidJS](https://www.npmjs.com/package/@comvi/solid), [Svelte](https://www.npmjs.com/package/@comvi/svelte), [Next.js](https://www.npmjs.com/package/@comvi/next), and [Nuxt](https://www.npmjs.com/package/@comvi/nuxt) — switch frameworks without relearning your i18n layer.
38
38
 
package/dist/VueI18n.d.ts CHANGED
@@ -1,17 +1,15 @@
1
1
  import { I18n, I18nOptions, FlattenedTranslations, TranslationParams, TranslationResult, TranslationValue, I18nPlugin, I18nEvent, I18nEventData } from '@comvi/core';
2
- import { Ref, App } from 'vue';
2
+ import { Ref, ComputedRef, App } from 'vue';
3
3
  /**
4
4
  * Vue-specific i18n options extending core options
5
5
  */
6
6
  export interface VueI18nOptions extends I18nOptions {
7
- /** @deprecated Use locale. */
8
- language?: string;
9
7
  /**
10
8
  * Initial locale for SSR hydration.
11
9
  * Use this to prevent hydration mismatches when server renders with a different
12
10
  * locale than what the client would detect.
13
11
  */
14
- ssrLanguage?: string;
12
+ ssrLocale?: string;
15
13
  }
16
14
  /**
17
15
  * Vue-specific wrapper around the core I18n using composition
@@ -24,7 +22,8 @@ export declare class VueI18n {
24
22
  private _isLoading;
25
23
  private _isInitializing;
26
24
  private _cacheRevision;
27
- private _translationCacheRef;
25
+ private _configRevision;
26
+ private _translationCacheComputed?;
28
27
  private _unsubscribers;
29
28
  private _requestedLocale;
30
29
  private _localeQueue;
@@ -40,13 +39,8 @@ export declare class VueI18n {
40
39
  onMissingKey: (callback: (key: string, locale: string, namespace: string) => TranslationResult | void) => () => void;
41
40
  onLoadError: (callback: (locale: string, namespace: string, error: Error) => void) => () => void;
42
41
  on: <E extends I18nEvent>(event: E, callback: (payload: I18nEventData[E]) => void) => () => void;
43
- hasLocale: (locale: string, namespace?: string) => boolean;
44
- hasTranslation: (key: string, locale?: string, namespace?: string, checkFallbacks?: boolean) => boolean;
45
- getLoadedLocales: () => string[];
46
42
  setFallbackLocale: (locales: string | string[]) => void;
47
- getDefaultNamespace: () => string;
48
43
  reportError: (error: unknown, context?: Parameters<I18n["reportError"]>[1]) => void;
49
- getActiveNamespaces: () => string[];
50
44
  formatNumber: I18n["formatNumber"];
51
45
  formatDate: I18n["formatDate"];
52
46
  formatCurrency: I18n["formatCurrency"];
@@ -56,9 +50,40 @@ export declare class VueI18n {
56
50
  set locale(value: string);
57
51
  private _dirComputed?;
58
52
  /** Text direction for the current locale, as a reactive computed ref */
59
- get dir(): import('vue').ComputedRef<"ltr" | "rtl">;
53
+ get dir(): ComputedRef<"ltr" | "rtl">;
54
+ private _loadedLocalesComputed?;
55
+ get loadedLocales(): ComputedRef<string[]>;
56
+ private _activeNamespacesComputed?;
57
+ get activeNamespaces(): ComputedRef<string[]>;
58
+ private _defaultNamespaceComputed?;
59
+ /** Current default namespace as a reactive ComputedRef */
60
+ get defaultNamespace(): ComputedRef<string>;
61
+ /**
62
+ * Reactive check for translation existence. Returns a ComputedRef that
63
+ * re-evaluates when locale, cache, or config changes. Call inside component setup
64
+ * (or an effectScope) — the underlying `computed()` registers with the
65
+ * active scope and disposes automatically.
66
+ */
67
+ hasTranslation(key: string, opts?: {
68
+ locale?: string;
69
+ namespace?: string;
70
+ checkFallbacks?: boolean;
71
+ }): ComputedRef<boolean>;
72
+ /**
73
+ * Reactive check for locale availability. Returns a ComputedRef that
74
+ * re-evaluates when the translation cache changes.
75
+ */
76
+ hasLocale(locale: string, namespace?: string): ComputedRef<boolean>;
77
+ /** Imperative (non-reactive) translation-existence check — plain boolean, for use outside a reactive scope. */
78
+ hasTranslationNow(key: string, opts?: {
79
+ locale?: string;
80
+ namespace?: string;
81
+ checkFallbacks?: boolean;
82
+ }): boolean;
83
+ /** Imperative (non-reactive) locale-availability check — plain boolean, for use outside a reactive scope. */
84
+ hasLocaleNow(locale: string, namespace?: string): boolean;
60
85
  setLocale(locale: string): Promise<void>;
61
- get translationCache(): Readonly<Ref<Readonly<ReadonlyMap<string, FlattenedTranslations>>>>;
86
+ get translationCache(): ComputedRef<ReadonlyMap<string, FlattenedTranslations>>;
62
87
  get isLoading(): Readonly<Ref<boolean>>;
63
88
  get isInitializing(): Readonly<Ref<boolean>>;
64
89
  /** Raw translation result for rich text renderers and advanced integrations. */
@@ -83,6 +108,19 @@ export declare class VueI18n {
83
108
  use(plugin: I18nPlugin, options?: Parameters<I18n["use"]>[1]): this;
84
109
  destroy(): void;
85
110
  private _installedApps;
111
+ /**
112
+ * Install the i18n plugin into a Vue app.
113
+ *
114
+ * Side effects:
115
+ * - Provides the i18n instance via `I18N_INJECTION_KEY` so `useI18n()` works.
116
+ * - Registers `$t`, `$tRaw`, `$i18n` global properties for Options API + templates.
117
+ * - If the core isn't initialized yet, kicks off `init()` asynchronously (fire-and-forget).
118
+ *
119
+ * SSR note: on server-side rendering, call `await i18n.init()` BEFORE
120
+ * `renderToString(app)`. The fire-and-forget `init()` here is for client-side
121
+ * convenience only — on the server, rendering races against translation loading
122
+ * and you may serialize stale/empty caches.
123
+ */
86
124
  install(app: App): void;
87
125
  }
88
126
  export declare function createI18n(options: VueI18nOptions): VueI18n;
@@ -1 +1 @@
1
- {"version":3,"file":"VueI18n.d.ts","sourceRoot":"","sources":["../src/VueI18n.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VueI18n.d.ts","sourceRoot":"","sources":["../src/VueI18n.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Ref } from 'vue';
1
+ import { Ref, ComputedRef } from 'vue';
2
2
  import { TranslationParams, TranslationResult, FlattenedTranslations, TranslationValue, I18nEvent, I18nEventData, I18n } from '@comvi/core';
3
3
  export interface UseI18nReturn {
4
4
  /** Translation function - namespaced keys (when ns is provided). Always returns plain text. */
@@ -17,8 +17,8 @@ export interface UseI18nReturn {
17
17
  locale: Ref<string>;
18
18
  /** Set locale asynchronously */
19
19
  setLocale: (locale: string) => Promise<void>;
20
- /** Translation cache (stable readonly ref with manual triggers, no cloning) */
21
- translationCache: Readonly<Ref<Readonly<ReadonlyMap<string, FlattenedTranslations>>>>;
20
+ /** Translation cache as a reactive ComputedRef (stable identity, re-evaluates on cache mutation) */
21
+ translationCache: ComputedRef<ReadonlyMap<string, FlattenedTranslations>>;
22
22
  /** Loading state (readonly reactive Vue Ref) */
23
23
  isLoading: Readonly<Ref<boolean>>;
24
24
  /** Initializing state (readonly reactive Vue Ref) */
@@ -37,16 +37,35 @@ export interface UseI18nReturn {
37
37
  clearTranslations: (locale?: string, namespace?: string) => void;
38
38
  /** Force reload translations from loader */
39
39
  reloadTranslations: (locale?: string, namespace?: string) => Promise<void>;
40
- /** Check if a locale is loaded for a namespace */
41
- hasLocale: (locale: string, namespace?: string) => boolean;
42
- /** Check if a translation exists */
43
- hasTranslation: (key: string, locale?: string, namespace?: string, checkFallbacks?: boolean) => boolean;
44
- /** Get list of all loaded locale codes */
45
- getLoadedLocales: () => string[];
46
- /** Get list of active namespaces */
47
- getActiveNamespaces: () => string[];
48
- /** Get default namespace */
49
- getDefaultNamespace: () => string;
40
+ /** Reactive list of all loaded locale codes */
41
+ loadedLocales: ComputedRef<string[]>;
42
+ /** Reactive list of active namespaces */
43
+ activeNamespaces: ComputedRef<string[]>;
44
+ /** Reactive default namespace */
45
+ defaultNamespace: ComputedRef<string>;
46
+ /**
47
+ * Reactive check for translation existence. Returns a ComputedRef<boolean>
48
+ * that re-evaluates when locale or cache changes. Call inside `setup()`
49
+ * (or an `effectScope`) so the underlying `computed()` disposes with the scope.
50
+ */
51
+ hasTranslation: (key: string, opts?: {
52
+ locale?: string;
53
+ namespace?: string;
54
+ checkFallbacks?: boolean;
55
+ }) => ComputedRef<boolean>;
56
+ /**
57
+ * Reactive check for locale availability. Returns a ComputedRef<boolean>
58
+ * that re-evaluates when the translation cache changes.
59
+ */
60
+ hasLocale: (locale: string, namespace?: string) => ComputedRef<boolean>;
61
+ /** Imperative (non-reactive) translation-existence check — plain boolean, for use outside a reactive scope. */
62
+ hasTranslationNow: (key: string, opts?: {
63
+ locale?: string;
64
+ namespace?: string;
65
+ checkFallbacks?: boolean;
66
+ }) => boolean;
67
+ /** Imperative (non-reactive) locale-availability check — returns a plain `boolean`. */
68
+ hasLocaleNow: (locale: string, namespace?: string) => boolean;
50
69
  /** Subscribe to i18n events */
51
70
  on: <E extends I18nEvent>(event: E, callback: (payload: I18nEventData[E]) => void) => () => void;
52
71
  /** Report an error to the configured onError handler */
@@ -60,15 +79,20 @@ export interface UseI18nReturn {
60
79
  /** Format a relative time ("2 hours ago", "in 3 days") using the current language locale */
61
80
  formatRelativeTime: I18n["formatRelativeTime"];
62
81
  /** Text direction for the current language, as a reactive computed ref */
63
- dir: import('vue').ComputedRef<"ltr" | "rtl">;
82
+ dir: ComputedRef<"ltr" | "rtl">;
64
83
  /** Cleanup resources (call when i18n instance is no longer needed) */
65
84
  destroy: () => void;
66
85
  }
67
86
  /**
68
- * Vue composable to access the i18n instance
69
- * Must be used within a component that has access to the i18n plugin
87
+ * Vue composable to access the i18n instance.
88
+ * Must be called within a component that has access to the i18n plugin
89
+ * (i.e. after `app.use(i18n)`).
70
90
  *
71
- * @param ns - Optional namespace to scope translations to
91
+ * @param ns - Optional namespace to scope the returned `t` / `tRaw` functions to.
92
+ * When provided, key lookups default to this namespace instead of the
93
+ * configured `defaultNs`. Other returned methods (e.g. `hasTranslation`,
94
+ * `addActiveNamespace`) are NOT scoped — they accept explicit `namespace`
95
+ * arguments where applicable.
72
96
  * @returns Object with translation function, reactive state, and i18n methods
73
97
  */
74
98
  export declare function useI18n(ns?: string): UseI18nReturn;
@@ -1 +1 @@
1
- {"version":3,"file":"useI18n.d.ts","sourceRoot":"","sources":["../../src/composables/useI18n.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useI18n.d.ts","sourceRoot":"","sources":["../../src/composables/useI18n.ts"],"names":[],"mappings":"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"}
package/dist/comvi-vue.js CHANGED
@@ -1 +1,507 @@
1
- import{I18n as e,createBoundTranslation as t,createElement as n}from"@comvi/core";import{Fragment as r,computed as i,defineComponent as a,h as o,inject as s,readonly as c,shallowRef as l,triggerRef as u}from"vue";export*from"@comvi/core";var d=Symbol(`i18n`);function f(e){if(e.type===`text`)return e.text;let t=``;for(let n of e.children)t+=typeof n==`string`?n:f(n);return t}function p(e){if(typeof e==`string`)return e;let t=``;for(let n of e)t+=typeof n==`string`?n:f(n);return t}var m=[`addTranslations`,`addActiveNamespace`,`clearTranslations`,`reloadTranslations`,`registerLoader`,`registerPostProcessor`,`onMissingKey`,`onLoadError`,`on`,`hasLocale`,`hasTranslation`,`setFallbackLocale`,`getDefaultNamespace`,`reportError`,`getActiveNamespaces`,`formatNumber`,`formatDate`,`formatCurrency`,`formatRelativeTime`],h=class{constructor(t){this._unsubscribers=[],this._localeQueue=Promise.resolve(),this._isLocaleQueueIdle=!0,this._isDestroyed=!1,this._installedApps=new WeakSet;let n=t.ssrLanguage??t.locale??t.language;this._core=new e({...t,locale:n}),this._locale=l(n),this._requestedLocale=n,this._isLoading=l(this._core.isLoading),this._isInitializing=l(this._core.isInitializing),this._cacheRevision=l(this._core.translationCache.getRevision()),this._translationCacheRef=l(this._core.translationCache.getInternalMap());let r=()=>{this._cacheRevision.value=this._core.translationCache.getRevision(),this._translationCacheRef.value=this._core.translationCache.getInternalMap(),u(this._translationCacheRef)};this._unsubscribers.push(this._core.on(`localeChanged`,({to:e})=>{this._locale.value=e,this._requestedLocale=e}),this._core.on(`namespaceLoaded`,r),this._core.on(`loadingStateChanged`,({isLoading:e,isInitializing:t})=>{this._isLoading.value=e,this._isInitializing.value=t}),this._core.on(`initialized`,()=>{this._locale.value=this._core.locale,r(),this._isLoading.value=this._core.isLoading,this._isInitializing.value=this._core.isInitializing}),this._core.on(`translationsCleared`,r));let i=this._core;for(let e of m)this[e]=(...t)=>i[e](...t);this.registerLocaleDetector=e=>i.registerLocaleDetector(e),this.getLoadedLocales=()=>i.getLoadedLocales(),this.t=this.t.bind(this),this.tRaw=this.tRaw.bind(this),this.setLocale=this.setLocale.bind(this),this.destroy=this.destroy.bind(this)}get locale(){return this._localeComputed||(this._localeComputed=i({get:()=>this._locale.value,set:e=>{this._requestedLocale!==e&&this.setLocale(e).catch(e=>{})}})),this._localeComputed}set locale(e){this.setLocale(e).catch(e=>{})}get dir(){return this._dirComputed||(this._dirComputed=i(()=>this._core.dir)),this._dirComputed}async setLocale(e){let t=e;this._requestedLocale=t;let n=async()=>{this._core.locale!==t&&await this._core.setLocaleAsync(t)},r=this._isLocaleQueueIdle?n():this._localeQueue.then(n,n);this._isLocaleQueueIdle=!1;let i=r.catch(()=>{});this._localeQueue=i,i.finally(()=>{this._localeQueue===i&&(this._isLocaleQueueIdle=!0)});try{await r}catch(e){throw this._requestedLocale===t&&(this._requestedLocale=this._core.locale),e}}get translationCache(){return this._translationCacheRef}get isLoading(){return c(this._isLoading)}get isInitializing(){return c(this._isInitializing)}tRaw(e,...t){return this._core.tRaw(e,...t)}t(e,...t){return p(this.tRaw(e,...t))}async init(){return await this._core.init(),this}use(e,t){return this._core.use(e,t),this}destroy(){this._isDestroyed||(this._isDestroyed=!0,this._unsubscribers.reverse().forEach(e=>e()),this._unsubscribers.length=0,this._core.destroy().catch(e=>{this._core.reportError(e,{source:`plugin-cleanup`})}))}install(e){this._installedApps.has(e)||(this._installedApps.add(e),!this._core.isInitialized&&!this._core.isInitializing&&this.init().catch(e=>{this._core.reportError(e instanceof Error?e:Error(String(e)),{source:`init`})}),e.provide(d,this),e.config.globalProperties.$i18n=this,e.config.globalProperties.$t=this.t,e.config.globalProperties.$tRaw=this.tRaw)}};function g(e){return new h(e)}var _=[`locale`,`setLocale`,`translationCache`,`isLoading`,`isInitializing`,`addTranslations`,`addActiveNamespace`,`setFallbackLocale`,`onMissingKey`,`onLoadError`,`clearTranslations`,`reloadTranslations`,`hasLocale`,`hasTranslation`,`getLoadedLocales`,`getActiveNamespaces`,`getDefaultNamespace`,`on`,`reportError`,`formatNumber`,`formatDate`,`formatCurrency`,`formatRelativeTime`,`dir`,`destroy`];function v(e){let n=s(d);if(!n)throw Error(`[i18n] useI18n must be used within a Vue app with i18n plugin installed. Make sure you called app.use(i18n) before using this composable.`);let r=t(n,e),i={t:((e,t)=>p(r(e,t))),tRaw:r};for(let e of _)i[e]=n[e];return i}var y=`__vue_handler_`,b=`__`;function x(e){return typeof e==`string`?e?[e]:[]:e}var S=a({name:`T`,props:{i18nKey:{type:String,required:!0},params:{type:Object,default:()=>({})},ns:{type:String,default:void 0},locale:{type:String,default:void 0},fallback:{type:String,default:void 0},raw:{type:Boolean,default:void 0},components:{type:Object,default:void 0}},setup(e,{slots:t}){let i=s(d);if(!i)throw Error(`[i18n] <T> component must be used within a Vue app with i18n plugin installed`);return()=>{let a=e.i18nKey,s=new Map,c={},l=(e,t)=>{s.set(e,n=>{try{return t(n)}catch(t){return i.reportError(t,{source:`translation`,tagName:e}),o(`span`,{},n)}}),c[e]=({children:t})=>n(`${y}${e}${b}`,{},x(t))},u=e=>e.length===1&&typeof e[0]==`string`?e[0]:e;if(e.components)for(let[t,r]of Object.entries(e.components))if(typeof r==`string`)c[t]=({children:e})=>n(r,{},x(e));else if(typeof r==`object`&&r&&`component`in r){let e=r.component,i=r.props||{};typeof e==`string`?c[t]=({children:t})=>n(e,i,x(t)):l(t,t=>o(e,i,{default:()=>u(t)}))}else l(t,e=>o(r,{},{default:()=>u(e)}));for(let[e,n]of Object.entries(t))n&&!(e in c)&&l(e,e=>{let t=n({children:u(e)}),i=Array.isArray(t)?t:[t];return i.length<=1?i.length===0?o(r,{},[]):i[0]:o(r,{},i)});let d={...e.params,...c};e.ns!==void 0&&(d.ns=e.ns),e.locale!==void 0&&(d.locale=e.locale),e.fallback!==void 0&&(d.fallback=e.fallback),e.raw!==void 0&&(d.raw=e.raw);let f=i.tRaw(a,d);if(typeof f==`string`)return f;let p=e=>typeof e==`string`?e?[e]:[]:e.map(e=>typeof e==`string`?e:m(e)),m=e=>{if(e.type===`text`)return e.text;if(e.type===`fragment`)return o(r,{key:e.key},p(e.children));let t=e.tag,n=p(e.children);if(t.startsWith(y)&&t.endsWith(b)){let e=t.slice(14,-2),r=s.get(e);if(r)try{return r(n)}catch(t){return i.reportError(t,{source:`translation`,tagName:e}),o(`span`,{},n)}}return o(t,e.props,n)};return f.map(e=>typeof e==`string`?e:m(e))}}});export{d as I18N_INJECTION_KEY,S as T,h as VueI18n,g as createI18n,v as useI18n};
1
+ import { I18n, createBoundTranslation, createElement } from "@comvi/core";
2
+ import { Fragment, computed, defineComponent, h, inject, readonly, shallowRef } from "vue";
3
+ export * from "@comvi/core";
4
+ //#region src/keys.ts
5
+ /**
6
+ * Injection key for Vue's provide/inject pattern
7
+ * Used to inject the i18n instance into Vue components
8
+ */
9
+ var I18N_INJECTION_KEY = Symbol("i18n");
10
+ //#endregion
11
+ //#region src/utils.ts
12
+ function virtualNodeToText(node) {
13
+ if (node.type === "text") return node.text;
14
+ let text = "";
15
+ for (const child of node.children) text += typeof child === "string" ? child : virtualNodeToText(child);
16
+ return text;
17
+ }
18
+ function translationResultToString(result) {
19
+ if (typeof result === "string") return result;
20
+ let text = "";
21
+ for (const part of result) text += typeof part === "string" ? part : virtualNodeToText(part);
22
+ return text;
23
+ }
24
+ //#endregion
25
+ //#region src/VueI18n.ts
26
+ /**
27
+ * Vue-specific wrapper around the core I18n using composition
28
+ * Provides Vue reactivity integration and plugin installation
29
+ */
30
+ var VueI18n = class {
31
+ constructor(options) {
32
+ this._unsubscribers = [];
33
+ this._localeQueue = Promise.resolve();
34
+ this._isLocaleQueueIdle = true;
35
+ this._isDestroyed = false;
36
+ this._installedApps = /* @__PURE__ */ new WeakSet();
37
+ const initialLocale = options.ssrLocale ?? options.locale;
38
+ this._core = new I18n({
39
+ ...options,
40
+ locale: initialLocale
41
+ });
42
+ this._locale = shallowRef(initialLocale);
43
+ this._requestedLocale = initialLocale;
44
+ this._isLoading = shallowRef(this._core.isLoading);
45
+ this._isInitializing = shallowRef(this._core.isInitializing);
46
+ this._cacheRevision = shallowRef(this._core.translationCache.getRevision());
47
+ this._configRevision = shallowRef(0);
48
+ const syncCache = () => {
49
+ this._cacheRevision.value = this._core.translationCache.getRevision();
50
+ };
51
+ this._unsubscribers.push(this._core.on("localeChanged", ({ to }) => {
52
+ this._locale.value = to;
53
+ this._requestedLocale = to;
54
+ }), this._core.on("namespaceLoaded", syncCache), this._core.on("loadingStateChanged", ({ isLoading, isInitializing }) => {
55
+ this._isLoading.value = isLoading;
56
+ this._isInitializing.value = isInitializing;
57
+ }), this._core.on("initialized", () => {
58
+ this._locale.value = this._core.locale;
59
+ syncCache();
60
+ this._isLoading.value = this._core.isLoading;
61
+ this._isInitializing.value = this._core.isInitializing;
62
+ }), this._core.on("translationsCleared", syncCache), this._core.on("defaultNamespaceChanged", () => {
63
+ syncCache();
64
+ this._configRevision.value++;
65
+ }), this._core.on("configChanged", () => {
66
+ this._configRevision.value++;
67
+ }));
68
+ const core = this._core;
69
+ this.addTranslations = core.addTranslations.bind(core);
70
+ this.addActiveNamespace = core.addActiveNamespace.bind(core);
71
+ this.clearTranslations = core.clearTranslations.bind(core);
72
+ this.reloadTranslations = core.reloadTranslations.bind(core);
73
+ this.registerLoader = core.registerLoader.bind(core);
74
+ this.registerPostProcessor = core.registerPostProcessor.bind(core);
75
+ this.onMissingKey = core.onMissingKey.bind(core);
76
+ this.onLoadError = core.onLoadError.bind(core);
77
+ this.on = core.on.bind(core);
78
+ this.setFallbackLocale = core.setFallbackLocale.bind(core);
79
+ this.reportError = core.reportError.bind(core);
80
+ this.formatNumber = core.formatNumber.bind(core);
81
+ this.formatDate = core.formatDate.bind(core);
82
+ this.formatCurrency = core.formatCurrency.bind(core);
83
+ this.formatRelativeTime = core.formatRelativeTime.bind(core);
84
+ this.registerLocaleDetector = core.registerLocaleDetector.bind(core);
85
+ this.t = this.t.bind(this);
86
+ this.tRaw = this.tRaw.bind(this);
87
+ this.setLocale = this.setLocale.bind(this);
88
+ this.destroy = this.destroy.bind(this);
89
+ this.hasTranslation = this.hasTranslation.bind(this);
90
+ this.hasLocale = this.hasLocale.bind(this);
91
+ this.hasTranslationNow = this.hasTranslationNow.bind(this);
92
+ this.hasLocaleNow = this.hasLocaleNow.bind(this);
93
+ }
94
+ get locale() {
95
+ if (!this._localeComputed) this._localeComputed = computed({
96
+ get: () => this._locale.value,
97
+ set: (newLocale) => {
98
+ if (this._requestedLocale !== newLocale) this.setLocale(newLocale).catch((error) => {
99
+ this._core.reportError(error, { source: "setLocale" });
100
+ });
101
+ }
102
+ });
103
+ return this._localeComputed;
104
+ }
105
+ set locale(value) {
106
+ this.setLocale(value).catch((error) => {
107
+ this._core.reportError(error, { source: "setLocale" });
108
+ });
109
+ }
110
+ /** Text direction for the current locale, as a reactive computed ref */
111
+ get dir() {
112
+ if (!this._dirComputed) this._dirComputed = computed(() => {
113
+ this._locale.value;
114
+ return this._core.dir;
115
+ });
116
+ return this._dirComputed;
117
+ }
118
+ get loadedLocales() {
119
+ if (!this._loadedLocalesComputed) this._loadedLocalesComputed = computed(() => {
120
+ this._cacheRevision.value;
121
+ return this._core.getLoadedLocales();
122
+ });
123
+ return this._loadedLocalesComputed;
124
+ }
125
+ get activeNamespaces() {
126
+ if (!this._activeNamespacesComputed) this._activeNamespacesComputed = computed(() => {
127
+ this._cacheRevision.value;
128
+ this._configRevision.value;
129
+ return this._core.getActiveNamespaces();
130
+ });
131
+ return this._activeNamespacesComputed;
132
+ }
133
+ /** Current default namespace as a reactive ComputedRef */
134
+ get defaultNamespace() {
135
+ if (!this._defaultNamespaceComputed) this._defaultNamespaceComputed = computed(() => {
136
+ this._cacheRevision.value;
137
+ this._configRevision.value;
138
+ return this._core.getDefaultNamespace();
139
+ });
140
+ return this._defaultNamespaceComputed;
141
+ }
142
+ /**
143
+ * Reactive check for translation existence. Returns a ComputedRef that
144
+ * re-evaluates when locale, cache, or config changes. Call inside component setup
145
+ * (or an effectScope) — the underlying `computed()` registers with the
146
+ * active scope and disposes automatically.
147
+ */
148
+ hasTranslation(key, opts) {
149
+ return computed(() => {
150
+ this._locale.value;
151
+ this._cacheRevision.value;
152
+ this._configRevision.value;
153
+ return this._core.hasTranslation(key, opts?.locale, opts?.namespace, opts?.checkFallbacks);
154
+ });
155
+ }
156
+ /**
157
+ * Reactive check for locale availability. Returns a ComputedRef that
158
+ * re-evaluates when the translation cache changes.
159
+ */
160
+ hasLocale(locale, namespace) {
161
+ return computed(() => {
162
+ this._cacheRevision.value;
163
+ return this._core.hasLocale(locale, namespace);
164
+ });
165
+ }
166
+ /** Imperative (non-reactive) translation-existence check — plain boolean, for use outside a reactive scope. */
167
+ hasTranslationNow(key, opts) {
168
+ return this._core.hasTranslation(key, opts?.locale, opts?.namespace, opts?.checkFallbacks);
169
+ }
170
+ /** Imperative (non-reactive) locale-availability check — plain boolean, for use outside a reactive scope. */
171
+ hasLocaleNow(locale, namespace) {
172
+ return this._core.hasLocale(locale, namespace);
173
+ }
174
+ async setLocale(locale) {
175
+ const target = locale;
176
+ this._requestedLocale = target;
177
+ const run = async () => {
178
+ if (this._core.locale !== target) await this._core.setLocaleAsync(target);
179
+ };
180
+ const task = this._isLocaleQueueIdle ? run() : this._localeQueue.then(run, run);
181
+ this._isLocaleQueueIdle = false;
182
+ const tail = task.catch(() => {});
183
+ this._localeQueue = tail;
184
+ tail.finally(() => {
185
+ if (this._localeQueue === tail) this._isLocaleQueueIdle = true;
186
+ });
187
+ try {
188
+ await task;
189
+ } catch (error) {
190
+ if (this._requestedLocale === target) this._requestedLocale = this._core.locale;
191
+ throw error;
192
+ }
193
+ }
194
+ get translationCache() {
195
+ if (!this._translationCacheComputed) this._translationCacheComputed = computed(() => {
196
+ this._cacheRevision.value;
197
+ return this._core.translationCache.getInternalMap();
198
+ });
199
+ return this._translationCacheComputed;
200
+ }
201
+ get isLoading() {
202
+ return readonly(this._isLoading);
203
+ }
204
+ get isInitializing() {
205
+ return readonly(this._isInitializing);
206
+ }
207
+ tRaw(key, ...params) {
208
+ this._locale.value;
209
+ this._cacheRevision.value;
210
+ this._configRevision.value;
211
+ return this._core.tRaw(key, ...params);
212
+ }
213
+ t(key, ...params) {
214
+ return translationResultToString(this.tRaw(key, ...params));
215
+ }
216
+ async init() {
217
+ await this._core.init();
218
+ return this;
219
+ }
220
+ use(plugin, options) {
221
+ this._core.use(plugin, options);
222
+ return this;
223
+ }
224
+ destroy() {
225
+ if (this._isDestroyed) return;
226
+ this._isDestroyed = true;
227
+ this._unsubscribers.slice().reverse().forEach((unsub) => unsub());
228
+ this._unsubscribers.length = 0;
229
+ this._core.destroy().catch((error) => {
230
+ this._core.reportError(error, { source: "plugin-cleanup" });
231
+ });
232
+ }
233
+ /**
234
+ * Install the i18n plugin into a Vue app.
235
+ *
236
+ * Side effects:
237
+ * - Provides the i18n instance via `I18N_INJECTION_KEY` so `useI18n()` works.
238
+ * - Registers `$t`, `$tRaw`, `$i18n` global properties for Options API + templates.
239
+ * - If the core isn't initialized yet, kicks off `init()` asynchronously (fire-and-forget).
240
+ *
241
+ * SSR note: on server-side rendering, call `await i18n.init()` BEFORE
242
+ * `renderToString(app)`. The fire-and-forget `init()` here is for client-side
243
+ * convenience only — on the server, rendering races against translation loading
244
+ * and you may serialize stale/empty caches.
245
+ */
246
+ install(app) {
247
+ if (this._installedApps.has(app)) return;
248
+ this._installedApps.add(app);
249
+ if (!this._core.isInitialized && !this._core.isInitializing) this.init().catch((error) => {
250
+ this._core.reportError(error instanceof Error ? error : new Error(String(error)), { source: "init" });
251
+ });
252
+ app.provide(I18N_INJECTION_KEY, this);
253
+ app.config.globalProperties.$i18n = this;
254
+ app.config.globalProperties.$t = this.t;
255
+ app.config.globalProperties.$tRaw = this.tRaw;
256
+ }
257
+ };
258
+ function createI18n(options) {
259
+ return new VueI18n(options);
260
+ }
261
+ //#endregion
262
+ //#region src/composables/useI18n.ts
263
+ /** Keys copied from the i18n instance as direct references */
264
+ var PASSTHROUGH_KEYS = [
265
+ "locale",
266
+ "setLocale",
267
+ "translationCache",
268
+ "isLoading",
269
+ "isInitializing",
270
+ "addTranslations",
271
+ "addActiveNamespace",
272
+ "setFallbackLocale",
273
+ "onMissingKey",
274
+ "onLoadError",
275
+ "clearTranslations",
276
+ "reloadTranslations",
277
+ "hasLocale",
278
+ "hasTranslation",
279
+ "hasLocaleNow",
280
+ "hasTranslationNow",
281
+ "loadedLocales",
282
+ "activeNamespaces",
283
+ "defaultNamespace",
284
+ "on",
285
+ "reportError",
286
+ "formatNumber",
287
+ "formatDate",
288
+ "formatCurrency",
289
+ "formatRelativeTime",
290
+ "dir",
291
+ "destroy"
292
+ ];
293
+ /**
294
+ * Vue composable to access the i18n instance.
295
+ * Must be called within a component that has access to the i18n plugin
296
+ * (i.e. after `app.use(i18n)`).
297
+ *
298
+ * @param ns - Optional namespace to scope the returned `t` / `tRaw` functions to.
299
+ * When provided, key lookups default to this namespace instead of the
300
+ * configured `defaultNs`. Other returned methods (e.g. `hasTranslation`,
301
+ * `addActiveNamespace`) are NOT scoped — they accept explicit `namespace`
302
+ * arguments where applicable.
303
+ * @returns Object with translation function, reactive state, and i18n methods
304
+ */
305
+ function useI18n(ns) {
306
+ const i18n = inject(I18N_INJECTION_KEY);
307
+ if (!i18n) throw new Error("[i18n] useI18n must be used within a Vue app with i18n plugin installed. Make sure you called app.use(i18n) before using this composable.");
308
+ const tRaw = createBoundTranslation(i18n, ns);
309
+ const t = ((key, params) => translationResultToString(tRaw(key, params)));
310
+ const result = {
311
+ t,
312
+ tRaw
313
+ };
314
+ for (const k of PASSTHROUGH_KEYS) result[k] = i18n[k];
315
+ return result;
316
+ }
317
+ //#endregion
318
+ //#region src/components/T.ts
319
+ /**
320
+ * Marker prefix for Vue component/slot handling in tag interpolation
321
+ * Used to identify VirtualNodes that should be converted to Vue-specific elements
322
+ */
323
+ var MARKER_PREFIX = "__vue_handler_";
324
+ var MARKER_SUFFIX = "__";
325
+ /**
326
+ * Convert TranslationResult children to format suitable for slot/component
327
+ */
328
+ function childrenToArray(children) {
329
+ if (typeof children === "string") return children ? [children] : [];
330
+ return children;
331
+ }
332
+ /**
333
+ * Translation component for Vue
334
+ * Renders translated content with support for slots and components prop as tag handlers
335
+ *
336
+ * @example
337
+ * ```vue
338
+ * <!-- Simple usage -->
339
+ * <T i18nKey="greeting" />
340
+ *
341
+ * <!-- With parameters -->
342
+ * <T i18nKey="welcome" :params="{ name: 'John' }" />
343
+ *
344
+ * <!-- With tag interpolation using slots -->
345
+ * <T i18nKey="welcome_link">
346
+ * <template #link="{ children }">
347
+ * <a href="/help">{{ children }}</a>
348
+ * </template>
349
+ * </T>
350
+ *
351
+ * <!-- With tag interpolation using components prop -->
352
+ * <T
353
+ * i18nKey="welcome_link"
354
+ * :components="{
355
+ * link: { component: 'a', props: { href: '/help' } },
356
+ * bold: 'strong'
357
+ * }"
358
+ * />
359
+ *
360
+ * <!-- With specific namespace -->
361
+ * <T i18nKey="button.submit" ns="forms" />
362
+ *
363
+ * <!-- With specific locale -->
364
+ * <T i18nKey="greeting" locale="fr" />
365
+ * ```
366
+ */
367
+ var T = defineComponent({
368
+ name: "T",
369
+ props: {
370
+ /**
371
+ * Translation key to look up
372
+ */
373
+ i18nKey: {
374
+ type: String,
375
+ required: true
376
+ },
377
+ /**
378
+ * Parameters for interpolation
379
+ * These will be merged with slot content
380
+ */
381
+ params: {
382
+ type: Object,
383
+ default: () => ({})
384
+ },
385
+ /**
386
+ * Namespace to use (optional)
387
+ * If not specified, uses the default namespace
388
+ */
389
+ ns: {
390
+ type: String,
391
+ default: void 0
392
+ },
393
+ /**
394
+ * Specific locale to use (optional)
395
+ * If not specified, uses the current locale
396
+ */
397
+ locale: {
398
+ type: String,
399
+ default: void 0
400
+ },
401
+ /**
402
+ * Fallback text to display if translation is missing (optional)
403
+ * If not specified, returns the key itself
404
+ */
405
+ fallback: {
406
+ type: String,
407
+ default: void 0
408
+ },
409
+ /**
410
+ * Skip post-processing (optional)
411
+ * When true, prevents post-processors like IncontextEditor from adding invisible marker characters
412
+ */
413
+ raw: {
414
+ type: Boolean,
415
+ default: void 0
416
+ },
417
+ /**
418
+ * Components map for tag interpolation (optional)
419
+ * Maps tag names to their handlers (string tag name, component, or config object)
420
+ *
421
+ * @example
422
+ * {
423
+ * bold: 'strong', // HTML tag name
424
+ * link: { component: 'a', props: { href: '#' } }, // With props
425
+ * btn: MyButton // Vue component
426
+ * }
427
+ */
428
+ components: {
429
+ type: Object,
430
+ default: void 0
431
+ }
432
+ },
433
+ setup(props, { slots }) {
434
+ const i18n = inject(I18N_INJECTION_KEY);
435
+ if (!i18n) throw new Error("[i18n] <T> component must be used within a Vue app with i18n plugin installed");
436
+ return () => {
437
+ const key = props.i18nKey;
438
+ const vueHandlers = /* @__PURE__ */ new Map();
439
+ const tagHandlers = {};
440
+ const registerVueHandler = (tagName, vueHandler) => {
441
+ vueHandlers.set(tagName, (children) => {
442
+ try {
443
+ return vueHandler(children);
444
+ } catch (error) {
445
+ i18n.reportError(error, {
446
+ source: "translation",
447
+ tagName
448
+ });
449
+ return h("span", {}, children);
450
+ }
451
+ });
452
+ tagHandlers[tagName] = ({ children }) => createElement(`${MARKER_PREFIX}${tagName}${MARKER_SUFFIX}`, {}, childrenToArray(children));
453
+ };
454
+ const flattenChildren = (children) => children.length === 1 && typeof children[0] === "string" ? children[0] : children;
455
+ if (props.components) for (const [tagName, handler] of Object.entries(props.components)) if (typeof handler === "string") tagHandlers[tagName] = ({ children }) => createElement(handler, {}, childrenToArray(children));
456
+ else if (typeof handler === "object" && handler !== null && "component" in handler) {
457
+ const component = handler.component;
458
+ const componentProps = handler.props || {};
459
+ if (typeof component === "string") tagHandlers[tagName] = ({ children }) => createElement(component, componentProps, childrenToArray(children));
460
+ else registerVueHandler(tagName, (children) => h(component, componentProps, { default: () => flattenChildren(children) }));
461
+ } else registerVueHandler(tagName, (children) => h(handler, {}, { default: () => flattenChildren(children) }));
462
+ for (const [slotName, slot] of Object.entries(slots)) if (slot && !(slotName in tagHandlers)) registerVueHandler(slotName, (children) => {
463
+ const rendered = slot({ children: flattenChildren(children) });
464
+ const nodes = Array.isArray(rendered) ? rendered : [rendered];
465
+ if (nodes.length <= 1) return nodes.length === 0 ? h(Fragment, {}, []) : nodes[0];
466
+ return h(Fragment, {}, nodes);
467
+ });
468
+ const translationParams = {
469
+ ...props.params,
470
+ ...tagHandlers
471
+ };
472
+ if (props.ns !== void 0) translationParams.ns = props.ns;
473
+ if (props.locale !== void 0) translationParams.locale = props.locale;
474
+ if (props.fallback !== void 0) translationParams.fallback = props.fallback;
475
+ if (props.raw !== void 0) translationParams.raw = props.raw;
476
+ const content = i18n.tRaw(key, translationParams);
477
+ if (typeof content === "string") return content;
478
+ const convertChildren = (childResult) => {
479
+ if (typeof childResult === "string") return childResult ? [childResult] : [];
480
+ return childResult.map((child) => typeof child === "string" ? child : convertNode(child));
481
+ };
482
+ const convertNode = (node) => {
483
+ if (node.type === "text") return node.text;
484
+ if (node.type === "fragment") return h(Fragment, { key: node.key }, convertChildren(node.children));
485
+ const tag = node.tag;
486
+ const convertedChildren = convertChildren(node.children);
487
+ if (tag.startsWith(MARKER_PREFIX) && tag.endsWith(MARKER_SUFFIX)) {
488
+ const handlerName = tag.slice(14, -2);
489
+ const handler = vueHandlers.get(handlerName);
490
+ if (handler) try {
491
+ return handler(convertedChildren);
492
+ } catch (error) {
493
+ i18n.reportError(error, {
494
+ source: "translation",
495
+ tagName: handlerName
496
+ });
497
+ return h("span", {}, convertedChildren);
498
+ }
499
+ }
500
+ return h(tag, node.props, convertedChildren);
501
+ };
502
+ return content.map((item) => typeof item === "string" ? item : convertNode(item));
503
+ };
504
+ }
505
+ });
506
+ //#endregion
507
+ export { I18N_INJECTION_KEY, T, VueI18n, createI18n, useI18n };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from '@comvi/core';
2
2
  export { VueI18n, createI18n } from './VueI18n';
3
3
  export { useI18n } from './composables/useI18n';
4
+ export type { UseI18nReturn } from './composables/useI18n';
4
5
  export { T } from './components/T';
5
6
  export { I18N_INJECTION_KEY } from './keys';
6
7
  export type { VueI18n as I18nInstance, VueI18nOptions } from './VueI18n';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAG5B,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,gBAAgB,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAG5C,YAAY,EAAE,OAAO,IAAI,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAG5B,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,CAAC,EAAE,MAAM,gBAAgB,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAG5C,YAAY,EAAE,OAAO,IAAI,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@comvi/vue",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Vue 3 integration for Comvi — composables, components, and type-safe translations",
6
6
  "license": "MIT",
7
7
  "author": "Comvi <hello@comvi.io> (https://comvi.io)",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/comvi-io/comvi-js.git",
10
+ "url": "git+https://github.com/comvi-io/comvi-js.git",
11
11
  "directory": "packages/vue"
12
12
  },
13
13
  "homepage": "https://comvi.io",
@@ -33,19 +33,20 @@
33
33
  "files": [
34
34
  "dist"
35
35
  ],
36
- "main": "./dist/comvi-vue.cjs",
37
36
  "module": "./dist/comvi-vue.js",
38
37
  "types": "./dist/index.d.ts",
39
38
  "exports": {
40
39
  ".": {
41
40
  "types": "./dist/index.d.ts",
42
- "import": "./dist/comvi-vue.js",
43
- "require": "./dist/comvi-vue.cjs"
41
+ "default": "./dist/comvi-vue.js"
44
42
  }
45
43
  },
44
+ "engines": {
45
+ "node": ">=18"
46
+ },
46
47
  "sideEffects": false,
47
48
  "dependencies": {
48
- "@comvi/core": "0.2.0"
49
+ "@comvi/core": "0.3.0"
49
50
  },
50
51
  "peerDependencies": {
51
52
  "vue": "^3.0.0"
@@ -1 +0,0 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@comvi/core`),t=require(`vue`);var n=Symbol(`i18n`);function r(e){if(e.type===`text`)return e.text;let t=``;for(let n of e.children)t+=typeof n==`string`?n:r(n);return t}function i(e){if(typeof e==`string`)return e;let t=``;for(let n of e)t+=typeof n==`string`?n:r(n);return t}var a=[`addTranslations`,`addActiveNamespace`,`clearTranslations`,`reloadTranslations`,`registerLoader`,`registerPostProcessor`,`onMissingKey`,`onLoadError`,`on`,`hasLocale`,`hasTranslation`,`setFallbackLocale`,`getDefaultNamespace`,`reportError`,`getActiveNamespaces`,`formatNumber`,`formatDate`,`formatCurrency`,`formatRelativeTime`],o=class{constructor(n){this._unsubscribers=[],this._localeQueue=Promise.resolve(),this._isLocaleQueueIdle=!0,this._isDestroyed=!1,this._installedApps=new WeakSet;let r=n.ssrLanguage??n.locale??n.language;this._core=new e.I18n({...n,locale:r}),this._locale=(0,t.shallowRef)(r),this._requestedLocale=r,this._isLoading=(0,t.shallowRef)(this._core.isLoading),this._isInitializing=(0,t.shallowRef)(this._core.isInitializing),this._cacheRevision=(0,t.shallowRef)(this._core.translationCache.getRevision()),this._translationCacheRef=(0,t.shallowRef)(this._core.translationCache.getInternalMap());let i=()=>{this._cacheRevision.value=this._core.translationCache.getRevision(),this._translationCacheRef.value=this._core.translationCache.getInternalMap(),(0,t.triggerRef)(this._translationCacheRef)};this._unsubscribers.push(this._core.on(`localeChanged`,({to:e})=>{this._locale.value=e,this._requestedLocale=e}),this._core.on(`namespaceLoaded`,i),this._core.on(`loadingStateChanged`,({isLoading:e,isInitializing:t})=>{this._isLoading.value=e,this._isInitializing.value=t}),this._core.on(`initialized`,()=>{this._locale.value=this._core.locale,i(),this._isLoading.value=this._core.isLoading,this._isInitializing.value=this._core.isInitializing}),this._core.on(`translationsCleared`,i));let o=this._core;for(let e of a)this[e]=(...t)=>o[e](...t);this.registerLocaleDetector=e=>o.registerLocaleDetector(e),this.getLoadedLocales=()=>o.getLoadedLocales(),this.t=this.t.bind(this),this.tRaw=this.tRaw.bind(this),this.setLocale=this.setLocale.bind(this),this.destroy=this.destroy.bind(this)}get locale(){return this._localeComputed||(this._localeComputed=(0,t.computed)({get:()=>this._locale.value,set:e=>{this._requestedLocale!==e&&this.setLocale(e).catch(e=>{})}})),this._localeComputed}set locale(e){this.setLocale(e).catch(e=>{})}get dir(){return this._dirComputed||(this._dirComputed=(0,t.computed)(()=>this._core.dir)),this._dirComputed}async setLocale(e){let t=e;this._requestedLocale=t;let n=async()=>{this._core.locale!==t&&await this._core.setLocaleAsync(t)},r=this._isLocaleQueueIdle?n():this._localeQueue.then(n,n);this._isLocaleQueueIdle=!1;let i=r.catch(()=>{});this._localeQueue=i,i.finally(()=>{this._localeQueue===i&&(this._isLocaleQueueIdle=!0)});try{await r}catch(e){throw this._requestedLocale===t&&(this._requestedLocale=this._core.locale),e}}get translationCache(){return this._translationCacheRef}get isLoading(){return(0,t.readonly)(this._isLoading)}get isInitializing(){return(0,t.readonly)(this._isInitializing)}tRaw(e,...t){return this._core.tRaw(e,...t)}t(e,...t){return i(this.tRaw(e,...t))}async init(){return await this._core.init(),this}use(e,t){return this._core.use(e,t),this}destroy(){this._isDestroyed||(this._isDestroyed=!0,this._unsubscribers.reverse().forEach(e=>e()),this._unsubscribers.length=0,this._core.destroy().catch(e=>{this._core.reportError(e,{source:`plugin-cleanup`})}))}install(e){this._installedApps.has(e)||(this._installedApps.add(e),!this._core.isInitialized&&!this._core.isInitializing&&this.init().catch(e=>{this._core.reportError(e instanceof Error?e:Error(String(e)),{source:`init`})}),e.provide(n,this),e.config.globalProperties.$i18n=this,e.config.globalProperties.$t=this.t,e.config.globalProperties.$tRaw=this.tRaw)}};function s(e){return new o(e)}var c=[`locale`,`setLocale`,`translationCache`,`isLoading`,`isInitializing`,`addTranslations`,`addActiveNamespace`,`setFallbackLocale`,`onMissingKey`,`onLoadError`,`clearTranslations`,`reloadTranslations`,`hasLocale`,`hasTranslation`,`getLoadedLocales`,`getActiveNamespaces`,`getDefaultNamespace`,`on`,`reportError`,`formatNumber`,`formatDate`,`formatCurrency`,`formatRelativeTime`,`dir`,`destroy`];function l(r){let a=(0,t.inject)(n);if(!a)throw Error(`[i18n] useI18n must be used within a Vue app with i18n plugin installed. Make sure you called app.use(i18n) before using this composable.`);let o=(0,e.createBoundTranslation)(a,r),s={t:((e,t)=>i(o(e,t))),tRaw:o};for(let e of c)s[e]=a[e];return s}var u=`__vue_handler_`,d=`__`;function f(e){return typeof e==`string`?e?[e]:[]:e}var p=(0,t.defineComponent)({name:`T`,props:{i18nKey:{type:String,required:!0},params:{type:Object,default:()=>({})},ns:{type:String,default:void 0},locale:{type:String,default:void 0},fallback:{type:String,default:void 0},raw:{type:Boolean,default:void 0},components:{type:Object,default:void 0}},setup(r,{slots:i}){let a=(0,t.inject)(n);if(!a)throw Error(`[i18n] <T> component must be used within a Vue app with i18n plugin installed`);return()=>{let n=r.i18nKey,o=new Map,s={},c=(n,r)=>{o.set(n,e=>{try{return r(e)}catch(r){return a.reportError(r,{source:`translation`,tagName:n}),(0,t.h)(`span`,{},e)}}),s[n]=({children:t})=>(0,e.createElement)(`${u}${n}${d}`,{},f(t))},l=e=>e.length===1&&typeof e[0]==`string`?e[0]:e;if(r.components)for(let[n,i]of Object.entries(r.components))if(typeof i==`string`)s[n]=({children:t})=>(0,e.createElement)(i,{},f(t));else if(typeof i==`object`&&i&&`component`in i){let r=i.component,a=i.props||{};typeof r==`string`?s[n]=({children:t})=>(0,e.createElement)(r,a,f(t)):c(n,e=>(0,t.h)(r,a,{default:()=>l(e)}))}else c(n,e=>(0,t.h)(i,{},{default:()=>l(e)}));for(let[e,n]of Object.entries(i))n&&!(e in s)&&c(e,e=>{let r=n({children:l(e)}),i=Array.isArray(r)?r:[r];return i.length<=1?i.length===0?(0,t.h)(t.Fragment,{},[]):i[0]:(0,t.h)(t.Fragment,{},i)});let p={...r.params,...s};r.ns!==void 0&&(p.ns=r.ns),r.locale!==void 0&&(p.locale=r.locale),r.fallback!==void 0&&(p.fallback=r.fallback),r.raw!==void 0&&(p.raw=r.raw);let m=a.tRaw(n,p);if(typeof m==`string`)return m;let h=e=>typeof e==`string`?e?[e]:[]:e.map(e=>typeof e==`string`?e:g(e)),g=e=>{if(e.type===`text`)return e.text;if(e.type===`fragment`)return(0,t.h)(t.Fragment,{key:e.key},h(e.children));let n=e.tag,r=h(e.children);if(n.startsWith(u)&&n.endsWith(d)){let e=n.slice(14,-2),i=o.get(e);if(i)try{return i(r)}catch(n){return a.reportError(n,{source:`translation`,tagName:e}),(0,t.h)(`span`,{},r)}}return(0,t.h)(n,e.props,r)};return m.map(e=>typeof e==`string`?e:g(e))}}});exports.I18N_INJECTION_KEY=n,exports.T=p,exports.VueI18n=o,exports.createI18n=s,exports.useI18n=l,Object.keys(e).forEach(function(t){t!==`default`&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});