nuxt-i18n-micro 1.102.0 → 2.0.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.
@@ -1 +1 @@
1
- {"id":"3b71a52e-40e0-4913-a13a-e722e382d06d","timestamp":1760083211792}
1
+ {"id":"9577efa7-2d15-4fca-9d0d-3cd305cdd931","timestamp":1761831387055}
@@ -0,0 +1 @@
1
+ {"id":"9577efa7-2d15-4fca-9d0d-3cd305cdd931","timestamp":1761831387055,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
@@ -0,0 +1 @@
1
+ .spotlight[data-v-f75a8935]{background:linear-gradient(45deg,#00dc82,#36e4da 50%,#0047e1);bottom:-30vh;filter:blur(20vh);height:40vh}.gradient-border[data-v-f75a8935]{-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border-radius:.5rem;position:relative}@media (prefers-color-scheme:light){.gradient-border[data-v-f75a8935]{background-color:#ffffff4d}.gradient-border[data-v-f75a8935]:before{background:linear-gradient(90deg,#e2e2e2,#e2e2e2 25%,#00dc82,#36e4da 75%,#0047e1)}}@media (prefers-color-scheme:dark){.gradient-border[data-v-f75a8935]{background-color:#1414144d}.gradient-border[data-v-f75a8935]:before{background:linear-gradient(90deg,#303030,#303030 25%,#00dc82,#36e4da 75%,#0047e1)}}.gradient-border[data-v-f75a8935]:before{background-size:400% auto;border-radius:.5rem;content:"";inset:0;-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:.5;padding:2px;position:absolute;transition:background-position .3s ease-in-out,opacity .2s ease-in-out;width:100%}.gradient-border[data-v-f75a8935]:hover:before{background-position:-50% 0;opacity:1}.fixed[data-v-f75a8935]{position:fixed}.left-0[data-v-f75a8935]{left:0}.right-0[data-v-f75a8935]{right:0}.z-10[data-v-f75a8935]{z-index:10}.z-20[data-v-f75a8935]{z-index:20}.grid[data-v-f75a8935]{display:grid}.mb-16[data-v-f75a8935]{margin-bottom:4rem}.mb-8[data-v-f75a8935]{margin-bottom:2rem}.max-w-520px[data-v-f75a8935]{max-width:520px}.min-h-screen[data-v-f75a8935]{min-height:100vh}.w-full[data-v-f75a8935]{width:100%}.flex[data-v-f75a8935]{display:flex}.cursor-pointer[data-v-f75a8935]{cursor:pointer}.place-content-center[data-v-f75a8935]{place-content:center}.items-center[data-v-f75a8935]{align-items:center}.justify-center[data-v-f75a8935]{justify-content:center}.overflow-hidden[data-v-f75a8935]{overflow:hidden}.bg-white[data-v-f75a8935]{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-4[data-v-f75a8935]{padding-left:1rem;padding-right:1rem}.px-8[data-v-f75a8935]{padding-left:2rem;padding-right:2rem}.py-2[data-v-f75a8935]{padding-bottom:.5rem;padding-top:.5rem}.text-center[data-v-f75a8935]{text-align:center}.text-8xl[data-v-f75a8935]{font-size:6rem;line-height:1}.text-xl[data-v-f75a8935]{font-size:1.25rem;line-height:1.75rem}.text-black[data-v-f75a8935]{--un-text-opacity:1;color:rgb(0 0 0/var(--un-text-opacity))}.font-light[data-v-f75a8935]{font-weight:300}.font-medium[data-v-f75a8935]{font-weight:500}.leading-tight[data-v-f75a8935]{line-height:1.25}.font-sans[data-v-f75a8935]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.antialiased[data-v-f75a8935]{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){.dark\:bg-black[data-v-f75a8935]{--un-bg-opacity:1;background-color:rgb(0 0 0/var(--un-bg-opacity))}.dark\:text-white[data-v-f75a8935]{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media (min-width:640px){.sm\:px-0[data-v-f75a8935]{padding-left:0;padding-right:0}.sm\:px-6[data-v-f75a8935]{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-3[data-v-f75a8935]{padding-bottom:.75rem;padding-top:.75rem}.sm\:text-4xl[data-v-f75a8935]{font-size:2.25rem;line-height:2.5rem}.sm\:text-xl[data-v-f75a8935]{font-size:1.25rem;line-height:1.75rem}}
@@ -0,0 +1 @@
1
+ .spotlight[data-v-8bb04cde]{background:linear-gradient(45deg,#00dc82,#36e4da 50%,#0047e1);filter:blur(20vh)}.fixed[data-v-8bb04cde]{position:fixed}.-bottom-1\/2[data-v-8bb04cde]{bottom:-50%}.left-0[data-v-8bb04cde]{left:0}.right-0[data-v-8bb04cde]{right:0}.grid[data-v-8bb04cde]{display:grid}.mb-16[data-v-8bb04cde]{margin-bottom:4rem}.mb-8[data-v-8bb04cde]{margin-bottom:2rem}.h-1\/2[data-v-8bb04cde]{height:50%}.max-w-520px[data-v-8bb04cde]{max-width:520px}.min-h-screen[data-v-8bb04cde]{min-height:100vh}.place-content-center[data-v-8bb04cde]{place-content:center}.overflow-hidden[data-v-8bb04cde]{overflow:hidden}.bg-white[data-v-8bb04cde]{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-8[data-v-8bb04cde]{padding-left:2rem;padding-right:2rem}.text-center[data-v-8bb04cde]{text-align:center}.text-8xl[data-v-8bb04cde]{font-size:6rem;line-height:1}.text-xl[data-v-8bb04cde]{font-size:1.25rem;line-height:1.75rem}.text-black[data-v-8bb04cde]{--un-text-opacity:1;color:rgb(0 0 0/var(--un-text-opacity))}.font-light[data-v-8bb04cde]{font-weight:300}.font-medium[data-v-8bb04cde]{font-weight:500}.leading-tight[data-v-8bb04cde]{line-height:1.25}.font-sans[data-v-8bb04cde]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.antialiased[data-v-8bb04cde]{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){.dark\:bg-black[data-v-8bb04cde]{--un-bg-opacity:1;background-color:rgb(0 0 0/var(--un-bg-opacity))}.dark\:text-white[data-v-8bb04cde]{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media (min-width:640px){.sm\:px-0[data-v-8bb04cde]{padding-left:0;padding-right:0}.sm\:text-4xl[data-v-8bb04cde]{font-size:2.25rem;line-height:2.5rem}}
@@ -1,12 +1,12 @@
1
1
  <!DOCTYPE html><html><head><meta charset="utf-8">
2
2
  <meta name="viewport" content="width=device-width, initial-scale=1">
3
3
  <link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Cq9tULRb.css" crossorigin>
4
- <link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/s7GTWWQo.js">
4
+ <link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/XZXfxmri.js">
5
5
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/l5rcX3cq.js">
6
- <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.B2VbLYbY.css">
7
- <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Df3aIBNA.js">
8
- <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/T7szCJDf.js">
9
- <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DGwSTbEi.css">
10
- <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/DTIY11lm.js">
11
- <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/s7GTWWQo.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},1760083218234,false]</script>
12
- <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"3b71a52e-40e0-4913-a13a-e722e382d06d",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
6
+ <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.Ba2SLv0l.css">
7
+ <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CQupjgaS.js">
8
+ <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CBxwnKtU.js">
9
+ <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.BqKd8Zt-.css">
10
+ <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BYEpoBUk.js">
11
+ <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/XZXfxmri.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},1761831392702,false]</script>
12
+ <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"9577efa7-2d15-4fca-9d0d-3cd305cdd931",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.102.0",
4
+ "version": "2.0.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -2,7 +2,6 @@ import path, { resolve, join } from 'node:path';
2
2
  import * as fs from 'node:fs';
3
3
  import fs__default, { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
4
4
  import { useNuxt, defineNuxtModule, useLogger, createResolver, addTemplate, addImportsDir, addPlugin, addServerHandler, addComponentsDir, addTypeTemplate, addPrerenderRoutes } from '@nuxt/kit';
5
- import { watch } from 'chokidar';
6
5
  import { isPrefixAndDefaultStrategy, isPrefixStrategy, isNoPrefixStrategy, isPrefixExceptDefaultStrategy, withPrefixStrategy } from 'nuxt-i18n-micro-core';
7
6
  import { fileURLToPath } from 'node:url';
8
7
  import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
@@ -576,7 +575,14 @@ class PageManager {
576
575
  ...page,
577
576
  children: this.createLocalizedChildren(originalChildren, page.path, localeCodes, true, false, parentLocale, { [firstLocale]: customPath }),
578
577
  path: routePath,
579
- name: routeName
578
+ name: routeName,
579
+ alias: [],
580
+ // remove alias to prevent infinite recursion
581
+ meta: {
582
+ ...page.meta,
583
+ alias: []
584
+ // remove alias to prevent infinite recursion
585
+ }
580
586
  };
581
587
  }
582
588
  buildLocalizedRouteName(baseName, locale, modifyName, forceLocaleSuffixOrCustom = false) {
@@ -685,6 +691,7 @@ const module = defineNuxtModule({
685
691
  disablePageLocales: false,
686
692
  disableWatcher: false,
687
693
  disableUpdater: false,
694
+ // experimental kept in runtimeConfig only to avoid type drift here
688
695
  noPrefixRedirect: false,
689
696
  includeDefaultLocaleRoute: void 0,
690
697
  fallbackLocale: void 0,
@@ -768,9 +775,6 @@ const module = defineNuxtModule({
768
775
  localeCookie: options.localeCookie ?? "user-locale",
769
776
  autoDetectPath: options.autoDetectPath ?? "/",
770
777
  strategy: options.strategy ?? "prefix_except_default",
771
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
772
- // @ts-ignore
773
- routesLocaleLinks: options.routesLocaleLinks ?? {},
774
778
  dateBuild: Date.now(),
775
779
  hashMode: nuxt.options?.router?.options?.hashMode ?? false,
776
780
  apiBaseUrl,
@@ -785,7 +789,10 @@ const module = defineNuxtModule({
785
789
  // @ts-ignore
786
790
  routeDisableMeta,
787
791
  experimental: {
788
- i18nPreviousPageFallback: options.experimental?.i18nPreviousPageFallback ?? false
792
+ i18nPreviousPageFallback: options.experimental?.i18nPreviousPageFallback ?? false,
793
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
794
+ // @ts-ignore
795
+ hmr: options.experimental?.hmr ?? true
789
796
  }
790
797
  };
791
798
  if (typeof options.customRegexMatcher !== "undefined") {
@@ -802,7 +809,10 @@ const module = defineNuxtModule({
802
809
  // @ts-ignore
803
810
  fallbackLocale: options.fallbackLocale ?? void 0,
804
811
  translationDir: options.translationDir ?? "locales",
805
- customRegexMatcher: options.customRegexMatcher
812
+ customRegexMatcher: options.customRegexMatcher,
813
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
814
+ // @ts-ignore
815
+ routesLocaleLinks: options.routesLocaleLinks ?? {}
806
816
  };
807
817
  addImportsDir(resolver.resolve("./runtime/composables"));
808
818
  if (process.env && process.env.TEST) {
@@ -862,12 +872,66 @@ const module = defineNuxtModule({
862
872
  pathPrefix: false,
863
873
  extensions: ["vue"]
864
874
  });
875
+ if (nuxt.options.dev && (options.experimental?.hmr ?? true)) {
876
+ const translationsDir = join(nuxt.options.rootDir, options.translationDir || "locales");
877
+ const files = await globby(["**/*.json"], { cwd: translationsDir, absolute: true });
878
+ const tpl = addTemplate({
879
+ filename: "i18n-hmr-plugin.mjs",
880
+ write: true,
881
+ getContents: () => generateHmrPlugin(files.map((f) => f.replace(/\\/g, "/")))
882
+ });
883
+ addPlugin({
884
+ src: tpl.dst,
885
+ mode: "client",
886
+ name: "i18n-hmr-plugin",
887
+ order: 10
888
+ });
889
+ }
865
890
  if (options.types) {
866
891
  addTypeTemplate({
867
892
  filename: "types/i18n-plugin.d.ts",
868
893
  getContents: () => generateI18nTypes()
869
894
  });
870
895
  }
896
+ function generateHmrPlugin(files) {
897
+ const accepts = files.map((file) => {
898
+ const isPage = /\/pages\//.test(file);
899
+ let pageName = "";
900
+ let locale = "";
901
+ if (isPage) {
902
+ const m = /\/pages\/([^/]+)\/([^/]+)\.json$/.exec(file);
903
+ pageName = m?.[1] || "";
904
+ locale = m?.[2] || "";
905
+ } else {
906
+ const m = /\/([^/]+)\.json$/.exec(file);
907
+ locale = m?.[1] || "";
908
+ }
909
+ return `
910
+ if (import.meta.hot) {
911
+ import.meta.hot.accept('${file}', async (mod) => {
912
+ const nuxtApp = useNuxtApp()
913
+ const data = (mod && typeof mod === 'object' && Object.prototype.hasOwnProperty.call(mod, 'default'))
914
+ ? mod.default
915
+ : mod
916
+ try {
917
+ ${isPage ? `await nuxtApp.$loadPageTranslations('${locale}', '${pageName}', data)` : `await nuxtApp.$loadTranslations('${locale}', data)`}
918
+ console.log('[i18n HMR] Translations reloaded:', '${isPage ? "page" : "global"}', '${locale}'${isPage ? `, '${pageName}'` : ""})
919
+ }
920
+ catch (e) {
921
+ console.warn('[i18n HMR] Failed to reload translations for', '${file}', e)
922
+ }
923
+ })
924
+ }
925
+ `.trim();
926
+ }).join("\n");
927
+ return `
928
+ import { defineNuxtPlugin, useNuxtApp } from '#imports'
929
+
930
+ export default defineNuxtPlugin(() => {
931
+ ${accepts}
932
+ })
933
+ `.trim();
934
+ }
871
935
  nuxt.hook("pages:resolved", (pages) => {
872
936
  const prerenderRoutes = [];
873
937
  const routeRules = nuxt.options.routeRules || {};
@@ -1024,24 +1088,12 @@ const module = defineNuxtModule({
1024
1088
  }
1025
1089
  }
1026
1090
  });
1027
- if (!options.disableUpdater) {
1028
- nuxt.hook("nitro:build:before", async (_nitro) => {
1029
- const isProd = nuxt.options.dev === false;
1030
- if (!isProd) {
1031
- const translationPath = path.resolve(nuxt.options.rootDir, options.translationDir);
1032
- logger.log("\u2139 add file watcher: " + translationPath);
1033
- const watcherEvent = async (path2) => {
1034
- await watcher.close();
1035
- logger.log("\u21BB update store item: " + path2);
1036
- nuxt.callHook("restart");
1037
- };
1038
- const watcher = watch(translationPath, { depth: 2, persistent: true }).on("change", watcherEvent);
1039
- nuxt.hook("close", () => {
1040
- watcher.close();
1041
- });
1042
- }
1043
- });
1044
- }
1091
+ nuxt.hook("nitro:config", (nitroConfig) => {
1092
+ if (nuxt.options.dev && (options.experimental?.hmr ?? true) && !options.disableUpdater) {
1093
+ nitroConfig.plugins = nitroConfig.plugins || [];
1094
+ nitroConfig.plugins.push(resolver.resolve("./runtime/server/plugins/watcher.dev"));
1095
+ }
1096
+ });
1045
1097
  nuxt.hook("prerender:routes", async (prerenderRoutes) => {
1046
1098
  if (isNoPrefixStrategy(options.strategy)) {
1047
1099
  return;
@@ -22,37 +22,40 @@ export declare const useLocaleHead: ({ addDirAttribute, identifierAttribute, add
22
22
  identifierAttribute?: string | undefined;
23
23
  addSeoAttributes?: boolean | undefined;
24
24
  baseUrl?: string | undefined;
25
- }) => import("vue").Ref<{
26
- htmlAttrs: {
27
- lang?: string | undefined;
28
- dir?: "ltr" | "rtl" | "auto" | undefined;
29
- };
30
- link: {
31
- [x: string]: string | undefined;
32
- rel: string;
33
- href: string;
34
- hreflang?: string | undefined;
35
- }[];
36
- meta: {
37
- [x: string]: string;
38
- property: string;
39
- content: string;
40
- }[];
41
- }, MetaObject | {
42
- htmlAttrs: {
43
- lang?: string | undefined;
44
- dir?: "ltr" | "rtl" | "auto" | undefined;
45
- };
46
- link: {
47
- [x: string]: string | undefined;
48
- rel: string;
49
- href: string;
50
- hreflang?: string | undefined;
51
- }[];
52
- meta: {
53
- [x: string]: string;
54
- property: string;
55
- content: string;
56
- }[];
57
- }>;
25
+ }) => {
26
+ metaObject: import("vue").Ref<{
27
+ htmlAttrs: {
28
+ lang?: string | undefined;
29
+ dir?: "ltr" | "rtl" | "auto" | undefined;
30
+ };
31
+ link: {
32
+ [x: string]: string | undefined;
33
+ rel: string;
34
+ href: string;
35
+ hreflang?: string | undefined;
36
+ }[];
37
+ meta: {
38
+ [x: string]: string;
39
+ property: string;
40
+ content: string;
41
+ }[];
42
+ }, MetaObject | {
43
+ htmlAttrs: {
44
+ lang?: string | undefined;
45
+ dir?: "ltr" | "rtl" | "auto" | undefined;
46
+ };
47
+ link: {
48
+ [x: string]: string | undefined;
49
+ rel: string;
50
+ href: string;
51
+ hreflang?: string | undefined;
52
+ }[];
53
+ meta: {
54
+ [x: string]: string;
55
+ property: string;
56
+ content: string;
57
+ }[];
58
+ }>;
59
+ updateMeta: () => void;
60
+ };
58
61
  export {};
@@ -1,6 +1,6 @@
1
1
  import { joinURL, parseURL, withQuery } from "ufo";
2
2
  import { isPrefixExceptDefaultStrategy, isNoPrefixStrategy } from "nuxt-i18n-micro-core";
3
- import { unref, useRoute, useRuntimeConfig, watch, onUnmounted, ref, useNuxtApp } from "#imports";
3
+ import { unref, useRoute, useRuntimeConfig, ref, useNuxtApp } from "#imports";
4
4
  import { findAllowedLocalesForRoute } from "../utils/route-utils.js";
5
5
  export const useLocaleHead = ({ addDirAttribute = true, identifierAttribute = "id", addSeoAttributes = true, baseUrl = "/" } = {}) => {
6
6
  const metaObject = ref({
@@ -101,16 +101,5 @@ export const useLocaleHead = ({ addDirAttribute = true, identifierAttribute = "i
101
101
  metaObject.value.meta = [ogLocaleMeta, ogUrlMeta, ...alternateOgLocalesMeta];
102
102
  metaObject.value.link = [canonicalLink, ...alternateLinks];
103
103
  }
104
- if (import.meta.client) {
105
- const route = useRoute();
106
- const stop = watch(
107
- () => route.fullPath,
108
- () => updateMeta(),
109
- { immediate: true }
110
- );
111
- onUnmounted(() => stop());
112
- } else {
113
- updateMeta();
114
- }
115
- return metaObject;
104
+ return { metaObject, updateMeta };
116
105
  };
@@ -1,4 +1,5 @@
1
1
  import type { RouteLocationNormalizedLoaded, RouteLocationRaw, RouteLocationResolved, RouteLocationResolvedGeneric, RouteLocationNamedRaw } from 'vue-router';
2
+ import { useTranslationHelper } from 'nuxt-i18n-micro-core';
2
3
  import type { Locale, I18nRouteParams, Params, Translations, CleanTranslation } from 'nuxt-i18n-micro-types';
3
4
  declare const _default: import("nuxt/app").Plugin<{
4
5
  i18n: undefined;
@@ -28,6 +29,20 @@ declare const _default: import("nuxt/app").Plugin<{
28
29
  localePath: (to: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, locale?: string) => string;
29
30
  setI18nRouteParams: (value: I18nRouteParams) => I18nRouteParams;
30
31
  loadPageTranslations: (locale: string, routeName: string, translations: Translations) => Promise<void>;
32
+ helper: {
33
+ hasCache(locale: string, page: string): boolean;
34
+ getCache(locale: string, routeName: string): Map<string, unknown> | undefined;
35
+ setCache(locale: string, routeName: string, cache: Map<string, Translations | unknown>): void;
36
+ mergeTranslation(locale: string, routeName: string, newTranslations: Translations, force?: boolean): void;
37
+ mergeGlobalTranslation(locale: string, newTranslations: Translations, force?: boolean): void;
38
+ hasGeneralTranslation(locale: string): boolean;
39
+ hasPageTranslation(locale: string, routeName: string): boolean;
40
+ hasTranslation: (locale: string, key: string) => boolean;
41
+ getTranslation: <T = unknown>(locale: string, routeName: string, key: string) => T | null;
42
+ loadPageTranslations(locale: string, routeName: string, translations: Translations): Promise<void>;
43
+ loadTranslations(locale: string, translations: Translations): Promise<void>;
44
+ clearCache(): void;
45
+ };
31
46
  }> & import("nuxt/app").ObjectPlugin<{
32
47
  i18n: undefined;
33
48
  __micro: boolean;
@@ -56,6 +71,20 @@ declare const _default: import("nuxt/app").Plugin<{
56
71
  localePath: (to: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, locale?: string) => string;
57
72
  setI18nRouteParams: (value: I18nRouteParams) => I18nRouteParams;
58
73
  loadPageTranslations: (locale: string, routeName: string, translations: Translations) => Promise<void>;
74
+ helper: {
75
+ hasCache(locale: string, page: string): boolean;
76
+ getCache(locale: string, routeName: string): Map<string, unknown> | undefined;
77
+ setCache(locale: string, routeName: string, cache: Map<string, Translations | unknown>): void;
78
+ mergeTranslation(locale: string, routeName: string, newTranslations: Translations, force?: boolean): void;
79
+ mergeGlobalTranslation(locale: string, newTranslations: Translations, force?: boolean): void;
80
+ hasGeneralTranslation(locale: string): boolean;
81
+ hasPageTranslation(locale: string, routeName: string): boolean;
82
+ hasTranslation: (locale: string, key: string) => boolean;
83
+ getTranslation: <T = unknown>(locale: string, routeName: string, key: string) => T | null;
84
+ loadPageTranslations(locale: string, routeName: string, translations: Translations): Promise<void>;
85
+ loadTranslations(locale: string, translations: Translations): Promise<void>;
86
+ clearCache(): void;
87
+ };
59
88
  }>;
60
89
  export default _default;
61
90
  export interface PluginsInjections {
@@ -83,4 +112,5 @@ export interface PluginsInjections {
83
112
  $localePath: (to: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string, locale?: string) => string;
84
113
  $setI18nRouteParams: (value: I18nRouteParams) => I18nRouteParams;
85
114
  $loadPageTranslations: (locale: string, routeName: string, translations: Translations) => Promise<void>;
115
+ helper: ReturnType<typeof useTranslationHelper>;
86
116
  }
@@ -1,7 +1,7 @@
1
1
  import { useTranslationHelper, interpolate, isNoPrefixStrategy, RouteService, FormatService } from "nuxt-i18n-micro-core";
2
- import { useRouter, useCookie, useState, unref, navigateTo, defineNuxtPlugin, useRuntimeConfig } from "#imports";
2
+ import { useRouter, useCookie, unref, navigateTo, defineNuxtPlugin, useRuntimeConfig, createError } from "#imports";
3
+ import { useState } from "#app";
3
4
  import { plural } from "#build/i18n.plural.mjs";
4
- const i18nHelper = useTranslationHelper();
5
5
  const isDev = process.env.NODE_ENV !== "production";
6
6
  export default defineNuxtPlugin(async (nuxtApp) => {
7
7
  const config = useRuntimeConfig();
@@ -9,6 +9,17 @@ export default defineNuxtPlugin(async (nuxtApp) => {
9
9
  const apiBaseUrl = i18nConfig.apiBaseUrl ?? "_locales";
10
10
  const router = useRouter();
11
11
  const runtimeConfig = useRuntimeConfig();
12
+ const generalLocaleCache = useState("i18n-general-cache", () => ({}));
13
+ const routeLocaleCache = useState("i18n-route-cache", () => ({}));
14
+ const dynamicTranslationsCaches = useState("i18n-dynamic-caches", () => []);
15
+ const serverTranslationCache = useState("i18n-server-cache", () => ({}));
16
+ const translationCaches = {
17
+ generalLocaleCache,
18
+ routeLocaleCache,
19
+ dynamicTranslationsCaches,
20
+ serverTranslationCache
21
+ };
22
+ const i18nHelper = useTranslationHelper(translationCaches);
12
23
  let hashLocaleDefault = null;
13
24
  let noPrefixDefault = null;
14
25
  if (i18nConfig.hashMode) {
@@ -39,75 +50,56 @@ export default defineNuxtPlugin(async (nuxtApp) => {
39
50
  previousPageInfo.value = null;
40
51
  }
41
52
  });
42
- const loadTranslationsIfNeeded = async (locale, routeName, path) => {
43
- try {
44
- if (!i18nHelper.hasPageTranslation(locale, routeName)) {
45
- let fRouteName = routeName;
46
- if (i18nConfig.routesLocaleLinks && fRouteName && i18nConfig.routesLocaleLinks[fRouteName]) {
47
- const newRouteName = i18nConfig.routesLocaleLinks[fRouteName];
48
- if (newRouteName) {
49
- fRouteName = newRouteName;
50
- }
51
- }
52
- if (!fRouteName || fRouteName === "") {
53
- console.warn(`[nuxt-i18n-next] The page name is missing in the path: ${path}. Please ensure that definePageMeta({ name: 'pageName' }) is set.`);
54
- return;
55
- }
56
- const url = `/${apiBaseUrl}/${fRouteName}/${locale}/data.json`.replace(/\/{2,}/g, "/");
57
- const data = await $fetch(url, {
58
- baseURL: runtimeConfig.app.baseURL,
59
- params: {
60
- v: i18nConfig.dateBuild
61
- }
62
- });
63
- await i18nHelper.loadPageTranslations(locale, routeName, data ?? {});
64
- }
65
- } catch (_error) {
66
- }
67
- };
68
- async function loadGlobalTranslations(to) {
53
+ async function loadPageAndGlobalTranslations(to) {
69
54
  let locale = routeService.getCurrentLocale(to);
70
55
  if (i18nConfig.hashMode) {
71
- locale = await nuxtApp.runWithContext(() => {
72
- return useCookie("hash-locale", { default: () => locale }).value;
73
- });
56
+ locale = await nuxtApp.runWithContext(() => useCookie("hash-locale", { default: () => locale }).value);
74
57
  }
75
58
  if (isNoPrefixStrategy(i18nConfig.strategy)) {
76
- locale = await nuxtApp.runWithContext(() => {
77
- return useCookie("no-prefix-locale", { default: () => locale }).value;
78
- });
59
+ locale = await nuxtApp.runWithContext(() => useCookie("no-prefix-locale", { default: () => locale }).value);
60
+ }
61
+ const routeName = routeService.getRouteName(to, locale);
62
+ if (!routeName) {
63
+ return;
64
+ }
65
+ if (i18nHelper.hasPageTranslation(locale, routeName)) {
66
+ if (isDev) {
67
+ console.log(`[DEBUG] Cache HIT for '${locale}:${routeName}'. Skipping fetch.`);
68
+ }
69
+ return;
79
70
  }
80
- if (!i18nHelper.hasGeneralTranslation(locale)) {
81
- const url = `/${apiBaseUrl}/general/${locale}/data.json`.replace(/\/{2,}/g, "/");
71
+ const url = `/${apiBaseUrl}/${routeName}/${locale}/data.json`.replace(/\/{2,}/g, "/");
72
+ try {
82
73
  const data = await $fetch(url, {
83
74
  baseURL: runtimeConfig.app.baseURL,
84
- params: {
85
- v: i18nConfig.dateBuild
86
- }
75
+ params: { v: i18nConfig.dateBuild }
87
76
  });
88
- await i18nHelper.loadTranslations(locale, data ?? {});
89
- }
90
- if (!i18nConfig.disablePageLocales) {
91
- const locale2 = routeService.getCurrentLocale(to);
92
- const routeName = routeService.getRouteName(to, locale2);
93
- await loadTranslationsIfNeeded(locale2, routeName, to.fullPath);
77
+ await i18nHelper.loadPageTranslations(locale, routeName, data ?? {});
78
+ } catch (e) {
79
+ if (isDev) {
80
+ console.error(`[i18n] Failed to load translations for ${routeName}/${locale}`, e);
81
+ }
82
+ throw createError({ statusCode: 404, statusMessage: "Page Not Found" });
94
83
  }
95
84
  }
96
85
  router.beforeEach(async (to, from, next) => {
97
- if (to.path !== from.path || isNoPrefixStrategy(i18nConfig.strategy)) {
98
- if (import.meta.client && from.path !== to.path && enablePreviousPageFallback) {
99
- const fromLocale = routeService.getCurrentLocale(from);
100
- const fromRouteName = routeService.getRouteName(from, fromLocale);
101
- previousPageInfo.value = { locale: fromLocale, routeName: fromRouteName };
102
- console.log(`Saved previous page info for cleanup: ${fromRouteName}`);
103
- }
104
- await loadGlobalTranslations(to);
86
+ if (to.path === from.path && !isNoPrefixStrategy(i18nConfig.strategy)) {
87
+ if (next) next();
88
+ return;
105
89
  }
106
- if (next) {
107
- next();
90
+ if (import.meta.client && enablePreviousPageFallback) {
91
+ const fromLocale = routeService.getCurrentLocale(from);
92
+ const fromRouteName = routeService.getRouteName(from, fromLocale);
93
+ previousPageInfo.value = { locale: fromLocale, routeName: fromRouteName };
108
94
  }
95
+ try {
96
+ await loadPageAndGlobalTranslations(to);
97
+ } catch (e) {
98
+ console.error("[i18n] Error loading translations:", e);
99
+ }
100
+ if (next) next();
109
101
  });
110
- await loadGlobalTranslations(router.currentRoute.value);
102
+ await loadPageAndGlobalTranslations(router.currentRoute.value);
111
103
  const provideData = {
112
104
  i18n: void 0,
113
105
  __micro: true,
@@ -134,9 +126,6 @@ export default defineNuxtPlugin(async (nuxtApp) => {
134
126
  console.log(`Using fallback translation from previous route: ${prev.routeName} -> ${key}`);
135
127
  }
136
128
  }
137
- if (!value) {
138
- value = i18nHelper.getTranslation(locale, "", key);
139
- }
140
129
  if (!value) {
141
130
  if (isDev && import.meta.client) {
142
131
  console.warn(`Not found '${key}' key in '${locale}' locale messages for route '${routeName}'.`);
@@ -243,7 +232,9 @@ export default defineNuxtPlugin(async (nuxtApp) => {
243
232
  },
244
233
  loadPageTranslations: async (locale, routeName, translations) => {
245
234
  await i18nHelper.loadPageTranslations(locale, routeName, translations);
246
- }
235
+ },
236
+ helper: i18nHelper
237
+ // Оставляем helper, он может быть полезен для продвинутых пользователей
247
238
  };
248
239
  const $provideData = Object.fromEntries(
249
240
  Object.entries(provideData).map(([key, value]) => [`$${key}`, value])
@@ -1,5 +1,5 @@
1
1
  import { useLocaleHead } from "../composables/useLocaleHead.js";
2
- import { useRequestURL, useHead, defineNuxtPlugin, useRuntimeConfig, useRoute } from "#imports";
2
+ import { useRequestURL, useHead, defineNuxtPlugin, useRuntimeConfig, useRoute, watch } from "#imports";
3
3
  import { isMetaDisabledForRoute } from "../utils/route-utils.js";
4
4
  const host = process.env.HOST ?? "localhost";
5
5
  const port = process.env.PORT ?? "host";
@@ -15,11 +15,20 @@ export default defineNuxtPlugin((nuxtApp) => {
15
15
  const defaultUrl = port === "80" || port === "443" ? `${schema}://${host}` : `${schema}://${host}:${port}`;
16
16
  const url = useRequestURL();
17
17
  const baseUrl = (i18nConfig.metaBaseUrl || url.origin || defaultUrl).toString();
18
- const head = useLocaleHead({
18
+ const { metaObject, updateMeta } = useLocaleHead({
19
19
  addDirAttribute: true,
20
20
  identifierAttribute: "id",
21
21
  addSeoAttributes: true,
22
22
  baseUrl
23
23
  });
24
- useHead(head);
24
+ useHead(metaObject);
25
+ if (import.meta.server) {
26
+ updateMeta();
27
+ } else if (import.meta.client) {
28
+ watch(
29
+ () => useRoute().fullPath,
30
+ () => updateMeta(),
31
+ { immediate: true }
32
+ );
33
+ }
25
34
  });
@@ -1,19 +1,27 @@
1
- import { isNoPrefixStrategy, useTranslationHelper } from "nuxt-i18n-micro-core";
2
- import { defineNuxtPlugin, useRuntimeConfig, useRouter } from "#imports";
3
- const i18nHelper = useTranslationHelper();
1
+ import { isNoPrefixStrategy } from "nuxt-i18n-micro-core";
2
+ import { defineNuxtPlugin, useRuntimeConfig, useRouter, useNuxtApp } from "#imports";
3
+ const isDev = process.env.NODE_ENV !== "production";
4
4
  export default defineNuxtPlugin(async (nuxtApp) => {
5
5
  const config = useRuntimeConfig();
6
6
  const i18nConfig = config.public.i18nConfig;
7
7
  const router = useRouter();
8
- const locale = nuxtApp.$getLocale();
9
- const routeName = nuxtApp.$getRouteName();
8
+ const { $getLocale, $getRouteName } = useNuxtApp();
9
+ const i18nHelper = nuxtApp.$i18n.helper;
10
+ if (!i18nHelper) {
11
+ if (isDev) {
12
+ console.warn("[i18n] Helper is not available. Skipping hooks plugin.");
13
+ }
14
+ return;
15
+ }
16
+ const locale = $getLocale();
17
+ const routeName = $getRouteName();
10
18
  await nuxtApp.callHook("i18n:register", (translations, selectedLocale) => {
11
19
  i18nHelper.mergeTranslation(selectedLocale ?? locale, routeName, translations, true);
12
20
  }, locale);
13
21
  router.beforeEach(async (to, from, next) => {
14
22
  if (to.path !== from.path || isNoPrefixStrategy(i18nConfig.strategy)) {
15
- const locale2 = nuxtApp.$getLocale(to);
16
- const routeName2 = nuxtApp.$getRouteName(to);
23
+ const locale2 = $getLocale(to);
24
+ const routeName2 = $getRouteName(to);
17
25
  await nuxtApp.callHook("i18n:register", (translations, selectedLocale) => {
18
26
  i18nHelper.mergeTranslation(selectedLocale ?? locale2, routeName2, translations, true);
19
27
  }, locale2);
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;