nuxt-i18n-micro 3.19.0 → 3.20.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.
Files changed (39) hide show
  1. package/README.md +16 -16
  2. package/dist/client/200.html +1 -1
  3. package/dist/client/404.html +1 -1
  4. package/dist/client/_nuxt/{D6byAye8.js → BA2bGsrZ.js} +1 -1
  5. package/dist/client/_nuxt/{BNXusRXY.js → C_HK2snF.js} +1 -1
  6. package/dist/client/_nuxt/{BCTbvRLO.js → Dx6dmpFi.js} +1 -1
  7. package/dist/client/_nuxt/SDQMalAT.js +14 -0
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/5a55317c-1ad8-41ca-a7f6-2fe58e16b2f6.json +1 -0
  10. package/dist/client/index.html +1 -1
  11. package/dist/module.json +1 -1
  12. package/dist/module.mjs +147 -89
  13. package/dist/runtime/components/i18n-group.vue +1 -4
  14. package/dist/runtime/components/i18n-link.vue +2 -12
  15. package/dist/runtime/components/i18n-switcher.vue +7 -30
  16. package/dist/runtime/composables/useI18n.js +1 -1
  17. package/dist/runtime/composables/useI18nLocale.js +2 -3
  18. package/dist/runtime/composables/useLocaleHead.js +1 -1
  19. package/dist/runtime/middleware/i18n-redirect.global.d.ts +6 -0
  20. package/dist/runtime/middleware/i18n-redirect.global.js +43 -0
  21. package/dist/runtime/plugins/01.plugin.d.ts +21 -21
  22. package/dist/runtime/plugins/01.plugin.js +65 -312
  23. package/dist/runtime/plugins/05.hooks.js +31 -31
  24. package/dist/runtime/plugins/06.redirect.d.ts +1 -0
  25. package/dist/runtime/plugins/06.redirect.js +12 -83
  26. package/dist/runtime/server/middleware/i18n.global.js +13 -1
  27. package/dist/runtime/server/utils/locale-detector.d.ts +9 -12
  28. package/dist/runtime/server/utils/locale-detector.js +25 -28
  29. package/dist/runtime/server/utils/locale-server-middleware.js +8 -6
  30. package/dist/runtime/server/utils/server-loader.js +10 -9
  31. package/dist/runtime/server/utils/translation-server-middleware.js +8 -6
  32. package/dist/runtime/utils/nuxt-i18n.d.ts +122 -0
  33. package/dist/runtime/utils/nuxt-i18n.js +335 -0
  34. package/dist/runtime/utils/storage.d.ts +7 -6
  35. package/dist/runtime/utils/storage.js +18 -11
  36. package/internals.d.mts +1 -0
  37. package/package.json +16 -13
  38. package/dist/client/_nuxt/B-cq5x_h.js +0 -14
  39. package/dist/client/_nuxt/builds/meta/bbe443af-4371-4ec3-b41c-7a5e9deb9b9b.json +0 -1
@@ -3,32 +3,32 @@ import type { CleanTranslation, I18nRouteParams, Locale, MissingHandler, ModuleO
3
3
  import type { RouteLocationNamedRaw, RouteLocationNormalizedLoaded, RouteLocationRaw, RouteLocationResolved, RouteLocationResolvedGeneric } from 'vue-router';
4
4
  declare const _default: import("nuxt/app").Plugin<{
5
5
  getI18nConfig: () => ModuleOptionsExtend;
6
- i18n: undefined;
7
- __micro: boolean;
8
6
  helper: {
9
7
  mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force?: boolean): Promise<void>;
10
8
  };
11
- i18nStrategy: import("~/packages/path-strategy/dist/prefix-except-default-strategy.mjs").PrefixExceptDefaultPathStrategy;
9
+ i18n: unknown;
10
+ __micro: boolean;
11
+ i18nStrategy: PathStrategy;
12
12
  getLocale: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => string;
13
- getLocaleName: () => any;
13
+ getLocaleName: () => string | null;
14
14
  defaultLocale: () => string | undefined;
15
15
  getLocales: () => Locale[];
16
- getRouteName: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric, locale?: string) => any;
17
- t: (key: string, params?: Params, defaultValue?: string | null, route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => CleanTranslation;
16
+ getRouteName: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric, locale?: string) => string;
17
+ t: (key: import("@i18n-micro/types").TranslationKey, params?: Params, defaultValue?: string | null, routeContext?: unknown) => CleanTranslation;
18
18
  ts: (key: string, params?: Params, defaultValue?: string, route?: RouteLocationNormalizedLoaded) => string;
19
19
  _t: (route: RouteLocationNormalizedLoaded) => (key: string, params?: Params, defaultValue?: string | null) => CleanTranslation;
20
20
  _ts: (route: RouteLocationNormalizedLoaded) => (key: string, params?: Params, defaultValue?: string) => string;
21
- tc: (key: string, params: number | Params, defaultValue?: string) => string;
21
+ tc: (key: import("@i18n-micro/types").TranslationKey, count: number | Params, defaultValue?: string) => string;
22
22
  tn: (value: number, options?: Intl.NumberFormatOptions) => string;
23
23
  td: (value: Date | number | string, options?: Intl.DateTimeFormatOptions) => string;
24
24
  tdr: (value: Date | number | string, options?: Intl.RelativeTimeFormatOptions) => string;
25
- has: (key: string) => boolean;
25
+ has: (key: import("@i18n-micro/types").TranslationKey, routeContext?: unknown) => boolean;
26
26
  mergeTranslations: (newTranslations: Translations) => void;
27
27
  switchLocaleRoute: (toLocale: string) => RouteLocationRaw;
28
28
  clearCache: () => void;
29
29
  switchLocalePath: (toLocale: string) => string;
30
- switchLocale: (toLocale: string) => Promise<string | false | void | import("vue-router").RouteLocationAsRelativeGeneric | import("vue-router").RouteLocationAsPathGeneric | import("vue-router").NavigationFailure>;
31
- switchRoute: (route: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, toLocale?: string) => string | false | void | import("vue-router").RouteLocationAsRelativeGeneric | import("vue-router").RouteLocationAsPathGeneric | Promise<false | void | import("vue-router").NavigationFailure>;
30
+ switchLocale: (toLocale: string) => Promise<unknown>;
31
+ switchRoute: (route: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, toLocale?: string) => unknown;
32
32
  localeRoute(to: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, locale?: string): RouteLocationResolved;
33
33
  localePath(to: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, locale?: string): string;
34
34
  setI18nRouteParams: (value: I18nRouteParams) => I18nRouteParams;
@@ -36,32 +36,32 @@ declare const _default: import("nuxt/app").Plugin<{
36
36
  setMissingHandler: (handler: MissingHandler | null) => void;
37
37
  }> & import("nuxt/app").ObjectPlugin<{
38
38
  getI18nConfig: () => ModuleOptionsExtend;
39
- i18n: undefined;
40
- __micro: boolean;
41
39
  helper: {
42
40
  mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force?: boolean): Promise<void>;
43
41
  };
44
- i18nStrategy: import("~/packages/path-strategy/dist/prefix-except-default-strategy.mjs").PrefixExceptDefaultPathStrategy;
42
+ i18n: unknown;
43
+ __micro: boolean;
44
+ i18nStrategy: PathStrategy;
45
45
  getLocale: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => string;
46
- getLocaleName: () => any;
46
+ getLocaleName: () => string | null;
47
47
  defaultLocale: () => string | undefined;
48
48
  getLocales: () => Locale[];
49
- getRouteName: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric, locale?: string) => any;
50
- t: (key: string, params?: Params, defaultValue?: string | null, route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => CleanTranslation;
49
+ getRouteName: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric, locale?: string) => string;
50
+ t: (key: import("@i18n-micro/types").TranslationKey, params?: Params, defaultValue?: string | null, routeContext?: unknown) => CleanTranslation;
51
51
  ts: (key: string, params?: Params, defaultValue?: string, route?: RouteLocationNormalizedLoaded) => string;
52
52
  _t: (route: RouteLocationNormalizedLoaded) => (key: string, params?: Params, defaultValue?: string | null) => CleanTranslation;
53
53
  _ts: (route: RouteLocationNormalizedLoaded) => (key: string, params?: Params, defaultValue?: string) => string;
54
- tc: (key: string, params: number | Params, defaultValue?: string) => string;
54
+ tc: (key: import("@i18n-micro/types").TranslationKey, count: number | Params, defaultValue?: string) => string;
55
55
  tn: (value: number, options?: Intl.NumberFormatOptions) => string;
56
56
  td: (value: Date | number | string, options?: Intl.DateTimeFormatOptions) => string;
57
57
  tdr: (value: Date | number | string, options?: Intl.RelativeTimeFormatOptions) => string;
58
- has: (key: string) => boolean;
58
+ has: (key: import("@i18n-micro/types").TranslationKey, routeContext?: unknown) => boolean;
59
59
  mergeTranslations: (newTranslations: Translations) => void;
60
60
  switchLocaleRoute: (toLocale: string) => RouteLocationRaw;
61
61
  clearCache: () => void;
62
62
  switchLocalePath: (toLocale: string) => string;
63
- switchLocale: (toLocale: string) => Promise<string | false | void | import("vue-router").RouteLocationAsRelativeGeneric | import("vue-router").RouteLocationAsPathGeneric | import("vue-router").NavigationFailure>;
64
- switchRoute: (route: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, toLocale?: string) => string | false | void | import("vue-router").RouteLocationAsRelativeGeneric | import("vue-router").RouteLocationAsPathGeneric | Promise<false | void | import("vue-router").NavigationFailure>;
63
+ switchLocale: (toLocale: string) => Promise<unknown>;
64
+ switchRoute: (route: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, toLocale?: string) => unknown;
65
65
  localeRoute(to: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, locale?: string): RouteLocationResolved;
66
66
  localePath(to: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, locale?: string): string;
67
67
  setI18nRouteParams: (value: I18nRouteParams) => I18nRouteParams;
@@ -74,8 +74,8 @@ export interface PluginsInjections {
74
74
  $getI18nConfig: () => ModuleOptionsExtend;
75
75
  $getLocale: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => string;
76
76
  $getLocaleName: () => string | null;
77
- $getLocales: () => Locale[];
78
77
  $defaultLocale: () => string | undefined;
78
+ $getLocales: () => Locale[];
79
79
  $getRouteName: (route?: RouteLocationNamedRaw | RouteLocationResolvedGeneric, locale?: string) => string;
80
80
  $t: (key: string, params?: Params, defaultValue?: string | null) => CleanTranslation;
81
81
  $_t: (route: RouteLocationNormalizedLoaded) => (key: string, params?: Params, defaultValue?: string | null) => CleanTranslation;
@@ -1,25 +1,13 @@
1
- import { FormatService, isNoPrefixStrategy } from "@i18n-micro/core";
2
- import { deepMergeTranslations } from "@i18n-micro/utils/deep-merge";
1
+ import { isNoPrefixStrategy } from "@i18n-micro/core";
3
2
  import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
4
- import { shallowRef, triggerRef, unref } from "vue";
5
3
  import { useState } from "#app";
6
4
  import { plural } from "#build/i18n.plural.mjs";
7
5
  import { createI18nStrategy, getI18nConfig } from "#build/i18n.strategy.mjs";
8
- import { createError, defineNuxtPlugin, navigateTo, useHead, useRouter, useRuntimeConfig } from "#imports";
6
+ import { createError, defineNuxtPlugin, navigateTo, useRouter, useRuntimeConfig } from "#imports";
9
7
  import { useI18nLocale } from "../composables/useI18nLocale.js";
8
+ import { createNuxtI18nPluginApi, NuxtI18n, NuxtTranslationLoader } from "../utils/nuxt-i18n.js";
10
9
  import { translationStorage } from "../utils/storage.js";
11
10
  const isDev = process.env.NODE_ENV !== "production";
12
- const RE_TOKEN = /\{(\w+)\}/g;
13
- function getByPath(obj, path) {
14
- if (obj[path] !== void 0) return obj[path];
15
- const parts = path.split(".");
16
- let v = obj;
17
- for (const p of parts) {
18
- if (v == null || typeof v !== "object") return void 0;
19
- v = v[p];
20
- }
21
- return v;
22
- }
23
11
  export default defineNuxtPlugin(async (nuxtApp) => {
24
12
  const router = useRouter();
25
13
  const i18nStrategy = createI18nStrategy(router);
@@ -32,14 +20,15 @@ export default defineNuxtPlugin(async (nuxtApp) => {
32
20
  maxSize: i18nConfig.cacheMaxSize ?? 0,
33
21
  ttl: i18nConfig.cacheTtl ?? 0
34
22
  });
35
- const loadedChunks = /* @__PURE__ */ new Map();
36
- let currentLocale = "";
37
- let currentRouteName = "";
38
- let cachedTranslations = {};
39
- let pendingCleanState = null;
40
- const contextSignal = shallowRef(0);
41
23
  const { locale: localeState, setLocale, getLocale, getEffectiveLocale, resolveInitialLocale, isValidLocale } = useI18nLocale();
42
- const translationService = new FormatService();
24
+ const i18nRouteParams = useState("i18n-route-params", () => ({}));
25
+ const customMissingHandler = useState("i18n-missing-handler", () => null);
26
+ const ssrChunks = useState("i18n-ssr-chunks", () => ({}));
27
+ const i18n = new NuxtI18n({
28
+ plural,
29
+ missingWarn: i18nConfig.missingWarn ?? true,
30
+ getCustomMissingHandler: () => customMissingHandler.value
31
+ });
43
32
  const getCurrentLocale = (route) => {
44
33
  const r = route ?? router.currentRoute.value;
45
34
  return i18nStrategy.getCurrentLocale(r, getLocale() ?? null);
@@ -47,103 +36,36 @@ export default defineNuxtPlugin(async (nuxtApp) => {
47
36
  const getPluginRouteName = (route, locale) => {
48
37
  return i18nStrategy.getPluginRouteName(route, locale);
49
38
  };
50
- const switchLocaleLogic = (toLocale, i18nParams, to) => {
51
- const fromLocale = getCurrentLocale();
52
- const currentRoute = router.currentRoute.value;
53
- let resolvedTarget;
54
- if (typeof to === "string") {
55
- const formattedPath = i18nStrategy.formatPathForResolve(to, fromLocale, toLocale);
56
- resolvedTarget = router.resolve(formattedPath);
57
- } else {
58
- resolvedTarget = to ?? currentRoute;
59
- }
60
- const switchedRoute = i18nStrategy.switchLocaleRoute(fromLocale, toLocale, resolvedTarget, {
61
- i18nRouteParams: i18nParams
62
- });
63
- if (typeof switchedRoute === "string" && (switchedRoute.startsWith("http://") || switchedRoute.startsWith("https://"))) {
64
- return navigateTo(switchedRoute, { redirectCode: 200, external: true });
65
- }
66
- if (isNoPrefixStrategy(i18nConfig.strategy)) {
67
- ;
68
- switchedRoute.force = true;
69
- }
70
- return router.push(switchedRoute);
71
- };
39
+ i18n.setRouteContextResolver((route) => {
40
+ const resolvedRoute = route ?? router.currentRoute.value;
41
+ const locale = getCurrentLocale(resolvedRoute);
42
+ return {
43
+ locale,
44
+ routeName: getPluginRouteName(resolvedRoute, locale)
45
+ };
46
+ });
72
47
  nuxtApp.hook("page:transition:finish", () => {
73
- if (pendingCleanState) {
74
- cachedTranslations = pendingCleanState;
75
- pendingCleanState = null;
76
- }
48
+ i18n.finishTransition();
77
49
  });
78
- const i18nRouteParams = useState("i18n-route-params", () => ({}));
79
- const customMissingHandler = useState("i18n-missing-handler", () => null);
80
- const missingWarn = i18nConfig.missingWarn ?? true;
81
50
  const loadOptions = {
82
51
  apiBaseUrl: i18nConfig.apiBaseUrl ?? "_locales",
83
52
  baseURL: runtimeConfig.app.baseURL,
84
53
  apiBaseClientHost: i18nConfig.apiBaseClientHost,
85
- apiBaseServerHost: i18nConfig.apiBaseServerHost,
86
54
  dateBuild: i18nConfig.dateBuild,
87
55
  routesLocaleLinks: i18nConfig.routesLocaleLinks
88
56
  };
89
- const getCacheKey = (locale, routeName) => {
90
- return `${locale}:${routeName || "index"}`;
91
- };
92
- const loadFromCacheSync = (locale, routeName) => {
93
- const cacheKey = getCacheKey(locale, routeName);
94
- if (loadedChunks.has(cacheKey)) {
95
- return loadedChunks.get(cacheKey);
96
- }
97
- const cached = translationStorage.getFromCache(locale, routeName);
98
- if (cached) {
99
- loadedChunks.set(cacheKey, cached.data);
100
- return cached.data;
101
- }
102
- return null;
103
- };
104
- const pendingLoads = /* @__PURE__ */ new Map();
105
- const loadAsync = (locale, routeName) => {
106
- const cacheKey = getCacheKey(locale, routeName);
107
- const pending = pendingLoads.get(cacheKey);
108
- if (pending) return pending;
109
- const promise = (async () => {
110
- try {
111
- const result = await translationStorage.load(locale, routeName, loadOptions);
112
- const existing = loadedChunks.get(cacheKey);
113
- const mergedChunk = existing ? { ...result.data, ...existing } : result.data;
114
- loadedChunks.set(cacheKey, mergedChunk);
115
- if (import.meta.server && result.json) {
116
- const ctx = nuxtApp.ssrContext.event.context;
117
- if (!ctx._i18n) ctx._i18n = {};
118
- ctx._i18n[cacheKey] = result.json;
119
- }
120
- return mergedChunk;
121
- } catch (e) {
122
- if (isDev) console.error("[i18n] Load error:", e);
123
- return {};
124
- } finally {
125
- pendingLoads.delete(cacheKey);
126
- }
127
- })();
128
- pendingLoads.set(cacheKey, promise);
129
- return promise;
130
- };
131
- const switchContext = async (locale, routeName) => {
132
- let data = loadFromCacheSync(locale, routeName);
133
- if (data === null) {
134
- data = await loadAsync(locale, routeName);
135
- }
136
- if (currentLocale === locale) {
137
- cachedTranslations = deepMergeTranslations(cachedTranslations, data);
138
- pendingCleanState = data;
139
- } else {
140
- cachedTranslations = deepMergeTranslations(cachedTranslations, data);
141
- pendingCleanState = null;
142
- }
143
- currentLocale = locale;
144
- currentRouteName = routeName || "";
145
- triggerRef(contextSignal);
146
- };
57
+ if (import.meta.client && Object.keys(ssrChunks.value).length > 0) {
58
+ translationStorage.seedFromSsrChunks(ssrChunks.value);
59
+ }
60
+ const loader = new NuxtTranslationLoader({
61
+ i18n,
62
+ loadOptions,
63
+ getSsrChunks: () => ssrChunks.value,
64
+ setSsrChunk: (cacheKey, data) => {
65
+ ssrChunks.value = { ...ssrChunks.value, [cacheKey]: data };
66
+ },
67
+ isDev
68
+ });
147
69
  const serverLocale = import.meta.server ? nuxtApp.ssrContext?.event?.context?.i18n?.locale : void 0;
148
70
  const initialLocale = resolveInitialLocale({
149
71
  route: router.currentRoute.value,
@@ -152,35 +74,50 @@ export default defineNuxtPlugin(async (nuxtApp) => {
152
74
  });
153
75
  const initialRouteName = getPluginRouteName(router.currentRoute.value, initialLocale);
154
76
  try {
155
- await switchContext(initialLocale, initialRouteName);
77
+ await loader.switchContext(initialLocale, initialRouteName);
156
78
  } catch (e) {
157
79
  if (isDev) console.error("[i18n] Initial load error:", e);
158
80
  throw createError({ statusCode: 404, statusMessage: "Page Not Found" });
159
81
  }
160
- if (import.meta.server) {
161
- const ctx = nuxtApp.ssrContext?.event.context;
162
- if (ctx?._i18n && Object.keys(ctx._i18n).length > 0) {
163
- let script = "window.__I18N__={};";
164
- for (const [key, value] of Object.entries(ctx._i18n)) {
165
- const safeKey = JSON.stringify(key);
166
- script += `window.__I18N__[${safeKey}]=${value};`;
167
- }
168
- useHead({
169
- script: [{ innerHTML: script, type: "text/javascript", tagPosition: "bodyClose" }]
170
- });
82
+ const getRouteName = (route, locale) => {
83
+ const selectedRoute = route ?? router.currentRoute.value;
84
+ const selectedLocale = locale ?? getCurrentLocale(selectedRoute);
85
+ return i18nStrategy.getRouteBaseName(selectedRoute, selectedLocale) ?? "";
86
+ };
87
+ const {
88
+ helper,
89
+ switchContext,
90
+ provide: provideApi
91
+ } = createNuxtI18nPluginApi({
92
+ i18n,
93
+ loader,
94
+ i18nStrategy,
95
+ i18nConfig,
96
+ router,
97
+ getCurrentLocale,
98
+ getEffectiveLocale,
99
+ getPluginRouteName,
100
+ getRouteName,
101
+ i18nRouteParams,
102
+ setLocale,
103
+ isValidLocale,
104
+ navigateTo,
105
+ setMissingHandler: (handler) => {
106
+ customMissingHandler.value = handler;
171
107
  }
172
- }
108
+ });
173
109
  router.beforeEach(async (to, from) => {
174
110
  if (to.name !== from.name) {
175
111
  i18nRouteParams.value = {};
176
112
  }
177
- if (to.path === from.path && !isNoPrefixStrategy(i18nConfig.strategy)) {
113
+ const shouldSwitchContext = to.path !== from.path || isNoPrefixStrategy(i18nConfig.strategy);
114
+ if (!shouldSwitchContext) {
178
115
  return;
179
116
  }
180
117
  try {
181
118
  const targetLocale = getEffectiveLocale(to, (r) => getCurrentLocale(r));
182
119
  const targetRouteName = getPluginRouteName(to, targetLocale);
183
- if (targetLocale !== currentLocale || targetRouteName !== currentRouteName) {
120
+ if (targetLocale !== i18n.getCurrentLocale() || targetRouteName !== i18n.getCurrentRouteName()) {
184
121
  await switchContext(targetLocale, targetRouteName);
185
122
  }
186
123
  if (targetLocale && isValidLocale(targetLocale) && localeState.value !== targetLocale) {
@@ -191,195 +128,11 @@ export default defineNuxtPlugin(async (nuxtApp) => {
191
128
  }
192
129
  return;
193
130
  });
194
- const tFast = (key, params, defaultValue, route) => {
195
- if (!key) return "";
196
- contextSignal.value;
197
- const translations = route ? loadedChunks.get(
198
- getCacheKey(
199
- getCurrentLocale(route),
200
- getPluginRouteName(route, getCurrentLocale(route))
201
- )
202
- ) || {} : cachedTranslations;
203
- let val = translations[key];
204
- if (val === void 0 && key.includes(".")) {
205
- val = getByPath(translations, key);
206
- }
207
- if (val === void 0) {
208
- if (customMissingHandler.value) {
209
- customMissingHandler.value(currentLocale, key, currentRouteName);
210
- } else if (missingWarn && isDev && import.meta.client) {
211
- console.warn(`[i18n] Missing key '${key}' in '${currentLocale}' for route '${currentRouteName}'`);
212
- }
213
- return defaultValue === void 0 ? key : defaultValue;
214
- }
215
- if (typeof val !== "string") return val;
216
- if (!params) return val;
217
- return val.replace(RE_TOKEN, (_, k) => {
218
- return params[k] !== void 0 ? String(params[k]) : `{${k}}`;
219
- });
220
- };
221
- const getRouteName = (route, locale) => {
222
- const selectedRoute = route ?? router.currentRoute.value;
223
- const selectedLocale = locale ?? getCurrentLocale(selectedRoute);
224
- return i18nStrategy.getRouteBaseName(selectedRoute, selectedLocale) ?? "";
225
- };
226
- const hasTranslation = (key) => {
227
- contextSignal.value;
228
- if (cachedTranslations[key] !== void 0) return true;
229
- if (key.includes(".") && getByPath(cachedTranslations, key) !== void 0) return true;
230
- return false;
231
- };
232
- const mergeTranslations = (newTranslations) => {
233
- const cacheKey = getCacheKey(currentLocale, currentRouteName);
234
- const current = loadedChunks.get(cacheKey) || {};
235
- const merged = { ...current, ...newTranslations };
236
- loadedChunks.set(cacheKey, merged);
237
- cachedTranslations = deepMergeTranslations(cachedTranslations, merged);
238
- if (pendingCleanState) pendingCleanState = merged;
239
- triggerRef(contextSignal);
240
- };
241
- const helper = {
242
- async mergeTranslation(locale, routeName, newTranslations, _force = false) {
243
- const cacheKey = getCacheKey(locale, routeName);
244
- if (!loadedChunks.has(cacheKey)) {
245
- await loadAsync(locale, routeName || void 0);
246
- }
247
- const existing = loadedChunks.get(cacheKey) || {};
248
- const mergedChunk = { ...existing, ...newTranslations };
249
- loadedChunks.set(cacheKey, mergedChunk);
250
- if (locale === currentLocale && routeName === currentRouteName) {
251
- cachedTranslations = deepMergeTranslations(cachedTranslations, mergedChunk);
252
- if (pendingCleanState) pendingCleanState = mergedChunk;
253
- triggerRef(contextSignal);
254
- }
255
- }
256
- };
257
131
  const provideData = {
258
- i18n: void 0,
259
- __micro: true,
132
+ ...provideApi,
260
133
  helper,
261
- i18nStrategy,
262
- getLocale: (route) => getEffectiveLocale(route, (r) => getCurrentLocale(r)),
263
- getLocaleName: () => i18nStrategy.getCurrentLocaleName(router.currentRoute.value, getLocale() ?? null),
264
- defaultLocale: () => i18nConfig.defaultLocale,
265
- getLocales: () => i18nConfig.locales || [],
266
- getRouteName,
267
- t: tFast,
268
- ts: (key, params, defaultValue, route) => {
269
- const value = tFast(key, params, defaultValue, route);
270
- return value?.toString() ?? defaultValue ?? key;
271
- },
272
- _t: (route) => {
273
- return (key, params, defaultValue) => {
274
- return tFast(key, params, defaultValue, route);
275
- };
276
- },
277
- _ts: (route) => {
278
- return (key, params, defaultValue) => {
279
- const value = tFast(key, params, defaultValue, route);
280
- return value?.toString() ?? defaultValue ?? key;
281
- };
282
- },
283
- tc: (key, params, defaultValue) => {
284
- contextSignal.value;
285
- const { count, ..._params } = typeof params === "number" ? { count: params } : params;
286
- if (count === void 0) return defaultValue ?? key;
287
- return plural(key, Number.parseInt(count.toString(), 10), _params, currentLocale, tFast) ?? defaultValue ?? key;
288
- },
289
- tn: (value, options) => {
290
- contextSignal.value;
291
- return translationService.formatNumber(value, currentLocale, options);
292
- },
293
- td: (value, options) => {
294
- contextSignal.value;
295
- return translationService.formatDate(value, currentLocale, options);
296
- },
297
- tdr: (value, options) => {
298
- contextSignal.value;
299
- return translationService.formatRelativeTime(value, currentLocale, options);
300
- },
301
- has: hasTranslation,
302
- mergeTranslations,
303
- switchLocaleRoute: (toLocale) => {
304
- const route = router.currentRoute.value;
305
- const fromLocale = getCurrentLocale(route);
306
- return i18nStrategy.switchLocaleRoute(fromLocale, toLocale, route, { i18nRouteParams: unref(i18nRouteParams.value) });
307
- },
308
- clearCache: () => {
309
- translationStorage.clear();
310
- loadedChunks.clear();
311
- cachedTranslations = {};
312
- triggerRef(contextSignal);
313
- },
314
- switchLocalePath: (toLocale) => {
315
- const route = router.currentRoute.value;
316
- const fromLocale = getCurrentLocale(route);
317
- const localeRoute = i18nStrategy.switchLocaleRoute(fromLocale, toLocale, route, { i18nRouteParams: unref(i18nRouteParams.value) });
318
- if (!localeRoute || typeof localeRoute !== "object") return String(localeRoute ?? "");
319
- if ("fullPath" in localeRoute && localeRoute.fullPath) return localeRoute.fullPath;
320
- if ("path" in localeRoute && localeRoute.path) return localeRoute.path;
321
- if ("name" in localeRoute && localeRoute.name && router.hasRoute(String(localeRoute.name))) {
322
- return router.resolve(localeRoute).fullPath;
323
- }
324
- return "";
325
- },
326
- switchLocale: async (toLocale) => {
327
- if (!isValidLocale(toLocale)) {
328
- if (isDev) {
329
- console.warn(`[i18n] Invalid locale '${toLocale}'`);
330
- }
331
- return;
332
- }
333
- setLocale(toLocale);
334
- if (isNoPrefixStrategy(i18nConfig.strategy) || i18nConfig.hashMode) {
335
- const route = router.currentRoute.value;
336
- const routeName = getPluginRouteName(route, toLocale);
337
- await switchContext(toLocale, routeName);
338
- }
339
- return switchLocaleLogic(toLocale, unref(i18nRouteParams.value));
340
- },
341
- switchRoute: (route, toLocale) => {
342
- return switchLocaleLogic(
343
- toLocale ?? getCurrentLocale(),
344
- unref(i18nRouteParams.value),
345
- route
346
- );
347
- },
348
- localeRoute(to, locale) {
349
- const targetLocale = locale !== void 0 && locale !== "" ? String(locale) : getCurrentLocale();
350
- const currentRoute = router.currentRoute.value;
351
- const result = i18nStrategy.localeRoute(targetLocale, to, currentRoute);
352
- const fullPath = result.fullPath ?? result.path ?? "";
353
- const path = result.path ?? fullPath.split("?")[0]?.split("#")[0] ?? fullPath;
354
- const out = { path, fullPath, href: fullPath };
355
- if (result.query && Object.keys(result.query).length) out.query = result.query;
356
- if (result.hash) out.hash = result.hash;
357
- return out;
358
- },
359
- localePath(to, locale) {
360
- const targetLocale = locale !== void 0 && locale !== "" ? String(locale) : getCurrentLocale();
361
- const currentRoute = router.currentRoute.value;
362
- const result = i18nStrategy.localeRoute(targetLocale, to, currentRoute);
363
- return result.fullPath ?? result.path ?? "";
364
- },
365
- setI18nRouteParams: (value) => {
366
- i18nRouteParams.value = value;
367
- return i18nRouteParams.value;
368
- },
369
- loadPageTranslations: async (locale, routeName, translations) => {
370
- const cacheKey = getCacheKey(locale, routeName);
371
- const current = loadedChunks.get(cacheKey) || {};
372
- const mergedChunk = { ...current, ...translations };
373
- loadedChunks.set(cacheKey, mergedChunk);
374
- if (locale === currentLocale && routeName === currentRouteName) {
375
- cachedTranslations = deepMergeTranslations(cachedTranslations, mergedChunk);
376
- if (pendingCleanState) pendingCleanState = mergedChunk;
377
- triggerRef(contextSignal);
378
- }
379
- },
380
- setMissingHandler: (handler) => {
381
- customMissingHandler.value = handler;
382
- }
134
+ i18n: void 0,
135
+ __micro: true
383
136
  };
384
137
  const $provideData = Object.fromEntries(Object.entries(provideData).map(([key, value]) => [`$${key}`, value]));
385
138
  provideData.i18n = { ...provideData, ...$provideData };
@@ -3,43 +3,43 @@ import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime
3
3
  import { getI18nConfig } from "#build/i18n.strategy.mjs";
4
4
  import { defineNuxtPlugin, useNuxtApp, useRouter } from "#imports";
5
5
  const isDev = process.env.NODE_ENV !== "production";
6
- export default defineNuxtPlugin(async (nuxtApp) => {
7
- const getRuntimeConfig = nuxtApp.$getI18nConfig;
8
- const i18nConfig = resolveI18nConfigWithRuntimeOverrides(
9
- typeof getRuntimeConfig === "function" ? getRuntimeConfig() : getI18nConfig()
10
- );
11
- const router = useRouter();
12
- const { $getLocale, $getRouteName } = useNuxtApp();
13
- const i18nHelper = nuxtApp.$i18n.helper;
14
- if (!i18nHelper) {
15
- if (isDev) {
16
- console.warn("[i18n] Helper is not available. Skipping hooks plugin.");
6
+ export default defineNuxtPlugin({
7
+ name: "i18n-plugin-hooks",
8
+ dependsOn: ["i18n-plugin-loader"],
9
+ async setup(nuxtApp) {
10
+ const getRuntimeConfig = nuxtApp.$getI18nConfig;
11
+ const i18nConfig = resolveI18nConfigWithRuntimeOverrides(
12
+ typeof getRuntimeConfig === "function" ? getRuntimeConfig() : getI18nConfig()
13
+ );
14
+ const router = useRouter();
15
+ const { $getLocale, $getRouteName } = useNuxtApp();
16
+ const i18nHelper = nuxtApp.$i18n?.helper;
17
+ if (!i18nHelper) {
18
+ if (isDev) {
19
+ console.warn("[i18n] Helper is not available. Skipping hooks plugin.");
20
+ }
21
+ return;
17
22
  }
18
- return;
19
- }
20
- const locale = $getLocale();
21
- const routeName = $getRouteName();
22
- await nuxtApp.callHook(
23
- // @ts-expect-error i18n:register is custom hook
24
- "i18n:register",
25
- (translations, selectedLocale) => {
26
- i18nHelper.mergeTranslation(selectedLocale ?? locale, routeName, translations, true);
27
- },
28
- locale
29
- );
30
- router.beforeEach(async (to, from) => {
31
- if (to.path !== from.path || isNoPrefixStrategy(i18nConfig.strategy)) {
32
- const locale2 = $getLocale(to);
33
- const routeName2 = $getRouteName(to);
23
+ const callRegister = async (route) => {
24
+ const locale = $getLocale(route);
25
+ const routeName = $getRouteName(route);
34
26
  await nuxtApp.callHook(
35
27
  // @ts-expect-error i18n:register is custom hook
36
28
  "i18n:register",
37
29
  (translations, selectedLocale) => {
38
- i18nHelper.mergeTranslation(selectedLocale ?? locale2, routeName2, translations, true);
30
+ void i18nHelper.mergeTranslation(selectedLocale ?? locale, routeName, translations, true);
39
31
  },
40
- locale2
32
+ locale
41
33
  );
34
+ };
35
+ if (i18nConfig.hooks !== false) {
36
+ await callRegister();
42
37
  }
43
- return;
44
- });
38
+ router.beforeEach(async (to, from) => {
39
+ if (i18nConfig.hooks === false) return;
40
+ if (to.path !== from.path || isNoPrefixStrategy(i18nConfig.strategy)) {
41
+ await callRegister(to);
42
+ }
43
+ });
44
+ }
45
45
  });
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Universal redirect plugin for i18n routes (works on both server and client).
3
3
  * Handles locale detection, 404 checks, and redirects.
4
+ * Client SPA redirects are handled by i18n-redirect route middleware.
4
5
  */
5
6
  declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
6
7
  export default _default;