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.
- package/README.md +16 -16
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/{D6byAye8.js → BA2bGsrZ.js} +1 -1
- package/dist/client/_nuxt/{BNXusRXY.js → C_HK2snF.js} +1 -1
- package/dist/client/_nuxt/{BCTbvRLO.js → Dx6dmpFi.js} +1 -1
- package/dist/client/_nuxt/SDQMalAT.js +14 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/5a55317c-1ad8-41ca-a7f6-2fe58e16b2f6.json +1 -0
- package/dist/client/index.html +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +147 -89
- package/dist/runtime/components/i18n-group.vue +1 -4
- package/dist/runtime/components/i18n-link.vue +2 -12
- package/dist/runtime/components/i18n-switcher.vue +7 -30
- package/dist/runtime/composables/useI18n.js +1 -1
- package/dist/runtime/composables/useI18nLocale.js +2 -3
- package/dist/runtime/composables/useLocaleHead.js +1 -1
- package/dist/runtime/middleware/i18n-redirect.global.d.ts +6 -0
- package/dist/runtime/middleware/i18n-redirect.global.js +43 -0
- package/dist/runtime/plugins/01.plugin.d.ts +21 -21
- package/dist/runtime/plugins/01.plugin.js +65 -312
- package/dist/runtime/plugins/05.hooks.js +31 -31
- package/dist/runtime/plugins/06.redirect.d.ts +1 -0
- package/dist/runtime/plugins/06.redirect.js +12 -83
- package/dist/runtime/server/middleware/i18n.global.js +13 -1
- package/dist/runtime/server/utils/locale-detector.d.ts +9 -12
- package/dist/runtime/server/utils/locale-detector.js +25 -28
- package/dist/runtime/server/utils/locale-server-middleware.js +8 -6
- package/dist/runtime/server/utils/server-loader.js +10 -9
- package/dist/runtime/server/utils/translation-server-middleware.js +8 -6
- package/dist/runtime/utils/nuxt-i18n.d.ts +122 -0
- package/dist/runtime/utils/nuxt-i18n.js +335 -0
- package/dist/runtime/utils/storage.d.ts +7 -6
- package/dist/runtime/utils/storage.js +18 -11
- package/internals.d.mts +1 -0
- package/package.json +16 -13
- package/dist/client/_nuxt/B-cq5x_h.js +0 -14
- 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
|
-
|
|
9
|
+
i18n: unknown;
|
|
10
|
+
__micro: boolean;
|
|
11
|
+
i18nStrategy: PathStrategy;
|
|
12
12
|
getLocale: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => string;
|
|
13
|
-
getLocaleName: () =>
|
|
13
|
+
getLocaleName: () => string | null;
|
|
14
14
|
defaultLocale: () => string | undefined;
|
|
15
15
|
getLocales: () => Locale[];
|
|
16
|
-
getRouteName: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric, locale?: string) =>
|
|
17
|
-
t: (key:
|
|
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:
|
|
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:
|
|
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<
|
|
31
|
-
switchRoute: (route: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, toLocale?: string) =>
|
|
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
|
-
|
|
42
|
+
i18n: unknown;
|
|
43
|
+
__micro: boolean;
|
|
44
|
+
i18nStrategy: PathStrategy;
|
|
45
45
|
getLocale: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => string;
|
|
46
|
-
getLocaleName: () =>
|
|
46
|
+
getLocaleName: () => string | null;
|
|
47
47
|
defaultLocale: () => string | undefined;
|
|
48
48
|
getLocales: () => Locale[];
|
|
49
|
-
getRouteName: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric, locale?: string) =>
|
|
50
|
-
t: (key:
|
|
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:
|
|
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:
|
|
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<
|
|
64
|
-
switchRoute: (route: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, toLocale?: string) =>
|
|
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 {
|
|
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,
|
|
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
|
|
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
|
-
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
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 !==
|
|
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
|
-
|
|
259
|
-
__micro: true,
|
|
132
|
+
...provideApi,
|
|
260
133
|
helper,
|
|
261
|
-
|
|
262
|
-
|
|
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(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 ??
|
|
30
|
+
void i18nHelper.mergeTranslation(selectedLocale ?? locale, routeName, translations, true);
|
|
39
31
|
},
|
|
40
|
-
|
|
32
|
+
locale
|
|
41
33
|
);
|
|
34
|
+
};
|
|
35
|
+
if (i18nConfig.hooks !== false) {
|
|
36
|
+
await callRegister();
|
|
42
37
|
}
|
|
43
|
-
|
|
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;
|