nuxt-i18n-micro 1.85.1 → 1.87.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.
@@ -8,5 +8,5 @@
8
8
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BBbCCCx2.js">
9
9
  <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.BRj5A2lo.css">
10
10
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CfVvufsh.js">
11
- <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/MjlAM4Ld.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1745917005102,false]</script>
12
- <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"73fe5312-b8b8-43c9-8321-d410f5f66e5c",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
11
+ <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/MjlAM4Ld.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1746387320983,false]</script>
12
+ <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"8378fa7a-5fb5-44d9-986d-f34c5db00a22",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
@@ -8,5 +8,5 @@
8
8
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BBbCCCx2.js">
9
9
  <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.BRj5A2lo.css">
10
10
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CfVvufsh.js">
11
- <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/MjlAM4Ld.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1745917005102,false]</script>
12
- <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"73fe5312-b8b8-43c9-8321-d410f5f66e5c",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
11
+ <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/MjlAM4Ld.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1746387320984,false]</script>
12
+ <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"8378fa7a-5fb5-44d9-986d-f34c5db00a22",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
@@ -1 +1 @@
1
- {"id":"73fe5312-b8b8-43c9-8321-d410f5f66e5c","timestamp":1745916998724}
1
+ {"id":"8378fa7a-5fb5-44d9-986d-f34c5db00a22","timestamp":1746387315515}
@@ -0,0 +1 @@
1
+ {"id":"8378fa7a-5fb5-44d9-986d-f34c5db00a22","timestamp":1746387315515,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
@@ -8,5 +8,5 @@
8
8
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BBbCCCx2.js">
9
9
  <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.BRj5A2lo.css">
10
10
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CfVvufsh.js">
11
- <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/MjlAM4Ld.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1745917005102,false]</script>
12
- <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"73fe5312-b8b8-43c9-8321-d410f5f66e5c",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
11
+ <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/MjlAM4Ld.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1746387320984,false]</script>
12
+ <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"8378fa7a-5fb5-44d9-986d-f34c5db00a22",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-i18n-micro",
3
3
  "configKey": "i18n",
4
- "version": "1.85.1",
4
+ "version": "1.87.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -493,6 +493,7 @@ const module = defineNuxtModule({
493
493
  meta: true,
494
494
  debug: false,
495
495
  define: true,
496
+ redirects: true,
496
497
  plugin: true,
497
498
  hooks: true,
498
499
  types: true,
@@ -511,6 +512,7 @@ const module = defineNuxtModule({
511
512
  apiBaseUrl: "_locales",
512
513
  routesLocaleLinks: {},
513
514
  globalLocaleRoutes: {},
515
+ canonicalQueryWhitelist: ["page", "sort", "filter", "search", "q", "query", "tag"],
514
516
  plural: (key, count, params, _locale, getTranslation) => {
515
517
  const translation = getTranslation(key, params);
516
518
  if (!translation) {
@@ -560,7 +562,8 @@ const module = defineNuxtModule({
560
562
  hashMode: nuxt.options?.router?.options?.hashMode ?? false,
561
563
  apiBaseUrl,
562
564
  isSSG,
563
- disablePageLocales: options.disablePageLocales ?? false
565
+ disablePageLocales: options.disablePageLocales ?? false,
566
+ canonicalQueryWhitelist: options.canonicalQueryWhitelist ?? []
564
567
  };
565
568
  if (typeof options.customRegexMatcher !== "undefined") {
566
569
  const localeCodes = localeManager.locales.map((l) => l.code);
@@ -619,6 +622,14 @@ const module = defineNuxtModule({
619
622
  order: 4
620
623
  });
621
624
  }
625
+ if (options.redirects) {
626
+ addPlugin({
627
+ src: resolver.resolve("./runtime/plugins/06.redirect"),
628
+ name: "i18n-plugin-redirect",
629
+ mode: "all",
630
+ order: 6
631
+ });
632
+ }
622
633
  addServerHandler({
623
634
  route: `/${apiBaseUrl}/:page/:locale/data.json`,
624
635
  handler: resolver.resolve("./runtime/server/routes/get")
@@ -1,4 +1,4 @@
1
- import { joinURL } from "ufo";
1
+ import { joinURL, parseURL, withQuery } from "ufo";
2
2
  import { isPrefixExceptDefaultStrategy, isNoPrefixStrategy } from "nuxt-i18n-micro-core";
3
3
  import { unref, useRoute, useRuntimeConfig, watch, onUnmounted, ref, useNuxtApp } from "#imports";
4
4
  export const useLocaleHead = ({ addDirAttribute = true, identifierAttribute = "id", addSeoAttributes = true, baseUrl = "/" } = {}) => {
@@ -7,28 +7,43 @@ export const useLocaleHead = ({ addDirAttribute = true, identifierAttribute = "i
7
7
  link: [],
8
8
  meta: []
9
9
  });
10
+ function filterQuery(fullPath, whitelist) {
11
+ const { pathname, search } = parseURL(fullPath);
12
+ const params = new URLSearchParams(search);
13
+ const filtered = {};
14
+ for (const key of whitelist) {
15
+ if (params.has(key)) {
16
+ filtered[key] = params.get(key);
17
+ }
18
+ }
19
+ return withQuery(pathname, filtered);
20
+ }
10
21
  function updateMeta() {
11
- const { defaultLocale, strategy } = useRuntimeConfig().public.i18nConfig;
22
+ const { defaultLocale, strategy, canonicalQueryWhitelist } = useRuntimeConfig().public.i18nConfig;
12
23
  const { $getLocales, $getLocale } = useNuxtApp();
13
24
  const route = useRoute();
14
25
  const locale = unref($getLocale());
15
26
  const locales = unref($getLocales());
16
27
  const routeName = (route.name ?? "").toString();
17
28
  const currentLocale = unref($getLocales().find((loc) => loc.code === locale));
18
- if (!currentLocale) {
19
- return;
20
- }
29
+ if (!currentLocale) return;
21
30
  const currentIso = currentLocale.iso || locale;
22
31
  const currentDir = currentLocale.dir || "auto";
23
32
  let fullPath = unref(route.fullPath);
24
- let ogUrl = joinURL(unref(baseUrl), fullPath);
25
33
  if (!fullPath.startsWith("/")) {
26
34
  fullPath = `/${fullPath}`;
27
35
  }
28
36
  const matchedLocale = locales.find((locale2) => fullPath.startsWith(`/${locale2.code}`));
37
+ let localizedPath = fullPath;
38
+ let ogUrl;
39
+ let canonicalPath;
29
40
  if (routeName.startsWith("localized-") && matchedLocale) {
30
- fullPath = fullPath.slice(matchedLocale.code.length + 1);
31
- ogUrl = joinURL(unref(baseUrl), locale, fullPath);
41
+ localizedPath = fullPath.slice(matchedLocale.code.length + 1);
42
+ canonicalPath = filterQuery(localizedPath, canonicalQueryWhitelist ?? []);
43
+ ogUrl = joinURL(unref(baseUrl), locale, canonicalPath);
44
+ } else {
45
+ canonicalPath = filterQuery(fullPath, canonicalQueryWhitelist ?? []);
46
+ ogUrl = joinURL(unref(baseUrl), canonicalPath);
32
47
  }
33
48
  metaObject.value = {
34
49
  htmlAttrs: {
@@ -61,7 +76,8 @@ export const useLocaleHead = ({ addDirAttribute = true, identifierAttribute = "i
61
76
  href: ogUrl
62
77
  };
63
78
  const alternateLinks = isNoPrefixStrategy(strategy) ? [] : alternateLocales.flatMap((loc) => {
64
- const href = defaultLocale === loc.code && isPrefixExceptDefaultStrategy(strategy) ? joinURL(unref(baseUrl), fullPath) : joinURL(unref(baseUrl), loc.code, fullPath);
79
+ const localizedPath2 = defaultLocale === loc.code && isPrefixExceptDefaultStrategy(strategy) ? canonicalPath : joinURL(loc.code, canonicalPath);
80
+ const href = joinURL(unref(baseUrl), localizedPath2);
65
81
  const links = [{
66
82
  [identifierAttribute]: `i18n-alternate-${loc.code}`,
67
83
  rel: "alternate",
@@ -1,68 +1,24 @@
1
- import { isNoPrefixStrategy, isPrefixStrategy } from "nuxt-i18n-micro-core";
2
- import { unref, useRoute, useRouter, useNuxtApp, watch, computed, onUnmounted, defineNuxtPlugin, navigateTo, useRuntimeConfig } from "#imports";
3
- export default defineNuxtPlugin(async (nuxtApp) => {
4
- const config = useRuntimeConfig();
5
- const route = useRoute();
6
- const router = useRouter();
7
- const i18nConfig = config.public.i18nConfig;
8
- const normalizeLocales = (locales) => {
9
- if (Array.isArray(locales)) {
10
- return locales.reduce((acc, locale) => {
11
- acc[locale] = {};
12
- return acc;
13
- }, {});
14
- } else if (typeof locales === "object" && locales !== null) {
15
- return locales;
16
- }
17
- return {};
18
- };
19
- const handleRedirect = async (to) => {
20
- const currentLocale = nuxtApp.$getLocale().toString();
21
- const { name } = to;
22
- let defaultRouteName = name?.toString().replace("localized-", "").replace(new RegExp(`-${currentLocale}$`), "");
23
- if (!to.params.locale) {
24
- if (router.hasRoute(`localized-${to.name?.toString()}-${currentLocale}`)) {
25
- defaultRouteName = `localized-${to.name?.toString()}-${currentLocale}`;
26
- } else {
27
- defaultRouteName = `localized-${to.name?.toString()}`;
28
- }
29
- if (!router.hasRoute(defaultRouteName)) {
30
- return;
31
- }
32
- const newParams = { ...to.params };
33
- if (!isNoPrefixStrategy(i18nConfig.strategy)) {
34
- newParams.locale = i18nConfig.defaultLocale;
35
- }
36
- return navigateTo({ name: defaultRouteName, params: newParams }, { redirectCode: 301, external: true });
37
- }
38
- };
39
- if (import.meta.server) {
40
- if (isPrefixStrategy(i18nConfig.strategy) || isNoPrefixStrategy(i18nConfig.strategy)) {
41
- await handleRedirect(route);
42
- }
1
+ import { defineNuxtPlugin, useNuxtApp, computed, watch, onUnmounted, unref } from "#imports";
2
+ const normalizeLocales = (locales) => {
3
+ if (Array.isArray(locales)) {
4
+ return locales.reduce((acc, locale) => {
5
+ acc[locale] = {};
6
+ return acc;
7
+ }, {});
8
+ } else if (typeof locales === "object" && locales !== null) {
9
+ return locales;
43
10
  }
44
- router.beforeEach(async (to, from, next) => {
45
- if (isPrefixStrategy(i18nConfig.strategy) || isNoPrefixStrategy(i18nConfig.strategy)) {
46
- await handleRedirect(to);
47
- }
48
- if (next) {
49
- next();
50
- }
51
- });
11
+ return {};
12
+ };
13
+ export default defineNuxtPlugin(() => {
52
14
  const defineI18nRoute = async (routeDefinition) => {
53
- const { $getLocale } = useNuxtApp();
15
+ const { $getLocale, $mergeGlobalTranslations } = useNuxtApp();
54
16
  let currentLocale = computed(() => $getLocale());
55
17
  const normalizedLocales = normalizeLocales(routeDefinition.locales);
56
18
  const updateTranslations = () => {
57
- const currentLocaleValue = unref(currentLocale);
58
- if (currentLocaleValue && Object.values(normalizedLocales).length) {
59
- if (normalizedLocales[currentLocaleValue]) {
60
- const translation = normalizedLocales[currentLocaleValue];
61
- const { $mergeGlobalTranslations } = useNuxtApp();
62
- if ($mergeGlobalTranslations) {
63
- $mergeGlobalTranslations(translation);
64
- }
65
- }
19
+ const localeValue = unref(currentLocale);
20
+ if (localeValue && normalizedLocales[localeValue]) {
21
+ $mergeGlobalTranslations?.(normalizedLocales[localeValue]);
66
22
  }
67
23
  };
68
24
  updateTranslations();
@@ -71,8 +27,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
71
27
  onUnmounted(() => {
72
28
  if (stopWatcher) {
73
29
  stopWatcher();
74
- currentLocale = null;
75
30
  stopWatcher = null;
31
+ currentLocale = null;
76
32
  }
77
33
  });
78
34
  }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -0,0 +1,38 @@
1
+ import { isNoPrefixStrategy, isPrefixStrategy } from "nuxt-i18n-micro-core";
2
+ import { defineNuxtPlugin, useRuntimeConfig, useRoute, useRouter, navigateTo } from "#imports";
3
+ export default defineNuxtPlugin(async (nuxtApp) => {
4
+ const config = useRuntimeConfig();
5
+ const i18nConfig = config.public.i18nConfig;
6
+ const route = useRoute();
7
+ const router = useRouter();
8
+ const handleRedirect = async (to) => {
9
+ const currentLocale = nuxtApp.$getLocale().toString();
10
+ const name = to.name?.toString();
11
+ let defaultRouteName = name?.toString().replace("localized-", "").replace(new RegExp(`-${currentLocale}$`), "");
12
+ if (!to.params.locale) {
13
+ if (router.hasRoute(`localized-${name}-${currentLocale}`)) {
14
+ defaultRouteName = `localized-${name}-${currentLocale}`;
15
+ } else {
16
+ defaultRouteName = `localized-${name}`;
17
+ }
18
+ if (!router.hasRoute(defaultRouteName)) return;
19
+ const newParams = { ...to.params };
20
+ if (!isNoPrefixStrategy(i18nConfig.strategy)) {
21
+ newParams.locale = i18nConfig.defaultLocale;
22
+ }
23
+ return navigateTo({ name: defaultRouteName, params: newParams }, {
24
+ redirectCode: 301,
25
+ external: true
26
+ });
27
+ }
28
+ };
29
+ if (import.meta.server && (isPrefixStrategy(i18nConfig.strategy) || isNoPrefixStrategy(i18nConfig.strategy))) {
30
+ await handleRedirect(route);
31
+ }
32
+ router.beforeEach(async (to, from, next) => {
33
+ if (isPrefixStrategy(i18nConfig.strategy) || isNoPrefixStrategy(i18nConfig.strategy)) {
34
+ await handleRedirect(to);
35
+ }
36
+ next?.();
37
+ });
38
+ });
@@ -36,7 +36,7 @@ export default defineEventHandler(async (event) => {
36
36
  const cacheName = join("_locales", getTranslationPath(locale, page));
37
37
  const isThereAsset = await serverStorage.hasItem(cacheName);
38
38
  if (isThereAsset) {
39
- const rawContent = await serverStorage.getItem(cacheName) ?? {};
39
+ const rawContent = await serverStorage.getItemRaw(cacheName) ?? {};
40
40
  return typeof rawContent === "string" ? JSON.parse(rawContent) : rawContent;
41
41
  }
42
42
  const createPaths = (locale2) => rootDirs.map((dir) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-i18n-micro",
3
- "version": "1.85.1",
3
+ "version": "1.87.0",
4
4
  "description": "Nuxt I18n Micro is a lightweight, high-performance internationalization module for Nuxt, designed to handle multi-language support with minimal overhead, fast build times, and efficient runtime performance.",
5
5
  "repository": "s00d/nuxt-i18n-micro",
6
6
  "license": "MIT",
@@ -59,8 +59,8 @@
59
59
  "globby": "^14.1.0",
60
60
  "sirv": "^2.0.4",
61
61
  "ufo": "^1.5.4",
62
+ "nuxt-i18n-micro-types": "1.0.6",
62
63
  "nuxt-i18n-micro-core": "1.0.18",
63
- "nuxt-i18n-micro-types": "1.0.4",
64
64
  "nuxt-i18n-micro-test-utils": "1.0.6"
65
65
  },
66
66
  "devDependencies": {
@@ -108,6 +108,7 @@
108
108
  "lint": "eslint .",
109
109
  "lint:fix": "eslint . --fix",
110
110
  "test": "playwright test",
111
+ "test:vitest": "vitest run",
111
112
  "test:watch": "vitest watch",
112
113
  "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
113
114
  "test:workspaces": "pnpm recursive run test",
@@ -1 +0,0 @@
1
- {"id":"73fe5312-b8b8-43c9-8321-d410f5f66e5c","timestamp":1745916998724,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}