nuxt-i18n-micro 3.30.2 → 4.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.
Files changed (111) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -2
  3. package/dist/client/200.html +1 -1
  4. package/dist/client/404.html +1 -1
  5. package/dist/client/_nuxt/BVna_KKx.js +3 -0
  6. package/dist/client/_nuxt/Bw81RhAv.js +1 -0
  7. package/dist/client/_nuxt/CPz_jhsD.js +1 -0
  8. package/dist/client/_nuxt/CmvObVGq.js +1 -0
  9. package/dist/client/_nuxt/DKUOBR6F.js +5 -0
  10. package/dist/client/_nuxt/builds/latest.json +1 -1
  11. package/dist/client/_nuxt/builds/meta/f0c8d4bf-40d2-4915-8b4a-ebe46bde61c8.json +1 -0
  12. package/dist/client/_nuxt/{entry.BM-mexAv.css → entry.PSWtHhay.css} +1 -1
  13. package/dist/client/_nuxt/error-404.xs3fxhVJ.css +1 -0
  14. package/dist/client/_nuxt/error-500.C2S-4iLg.css +1 -0
  15. package/dist/client/index.html +1 -1
  16. package/dist/module.d.mts +14 -5
  17. package/dist/module.json +4 -1
  18. package/dist/module.mjs +850 -475
  19. package/dist/runtime/components/i18n-group.d.vue.ts +17 -9
  20. package/dist/runtime/components/i18n-group.vue +2 -4
  21. package/dist/runtime/components/i18n-group.vue.d.ts +17 -9
  22. package/dist/runtime/components/i18n-link.d.vue.ts +9 -6
  23. package/dist/runtime/components/i18n-link.vue +7 -28
  24. package/dist/runtime/components/i18n-link.vue.d.ts +9 -6
  25. package/dist/runtime/components/i18n-switcher.d.vue.ts +128 -54
  26. package/dist/runtime/components/i18n-switcher.vue +56 -156
  27. package/dist/runtime/components/i18n-switcher.vue.d.ts +128 -54
  28. package/dist/runtime/components/i18n-t.d.vue.ts +59 -116
  29. package/dist/runtime/components/i18n-t.vue +24 -71
  30. package/dist/runtime/components/i18n-t.vue.d.ts +59 -116
  31. package/dist/runtime/composables/useI18n.js +2 -38
  32. package/dist/runtime/composables/useI18nHead.js +21 -6
  33. package/dist/runtime/composables/useI18nLocale.d.ts +4 -1
  34. package/dist/runtime/composables/useI18nLocale.js +20 -22
  35. package/dist/runtime/composables/useLocaleHead.d.ts +26 -31
  36. package/dist/runtime/composables/useLocaleHead.js +47 -167
  37. package/dist/runtime/devtools/bridge.d.ts +2 -2
  38. package/dist/runtime/devtools/vue-devtools.d.ts +2 -2
  39. package/dist/runtime/devtools/vue-devtools.js +5 -5
  40. package/dist/runtime/middleware/i18n-redirect.global.d.ts +3 -2
  41. package/dist/runtime/middleware/i18n-redirect.global.js +30 -41
  42. package/dist/runtime/plugins/01.plugin.d.ts +156 -34
  43. package/dist/runtime/plugins/01.plugin.js +184 -138
  44. package/dist/runtime/plugins/02.meta.js +36 -43
  45. package/dist/runtime/plugins/04.vue-devtools.js +1 -1
  46. package/dist/runtime/plugins/06.redirect.d.ts +3 -3
  47. package/dist/runtime/plugins/06.redirect.js +65 -91
  48. package/dist/runtime/server/{utils → internal}/dev-live-translations.js +4 -3
  49. package/dist/runtime/server/internal/server-translation-cache.d.ts +9 -0
  50. package/dist/runtime/server/internal/server-translation-cache.js +10 -0
  51. package/dist/runtime/server/internal/shared-cache.d.ts +24 -0
  52. package/dist/runtime/server/internal/shared-cache.js +71 -0
  53. package/dist/runtime/server/internal/translation-watcher.d.ts +79 -0
  54. package/dist/runtime/server/internal/translation-watcher.js +105 -0
  55. package/dist/runtime/server/middleware/i18n.global.js +24 -37
  56. package/dist/runtime/server/payload-source.assets.d.ts +1 -0
  57. package/dist/runtime/server/payload-source.assets.js +3 -8
  58. package/dist/runtime/server/payload-source.fs.d.ts +1 -0
  59. package/dist/runtime/server/payload-source.fs.js +18 -7
  60. package/dist/runtime/server/plugins/failed-read.d.ts +7 -0
  61. package/dist/runtime/server/plugins/failed-read.js +23 -0
  62. package/dist/runtime/server/plugins/payload-cache.d.ts +6 -0
  63. package/dist/runtime/server/plugins/payload-cache.js +16 -0
  64. package/dist/runtime/server/plugins/watcher.dev.js +15 -79
  65. package/dist/runtime/server/routes/i18n.js +13 -14
  66. package/dist/runtime/server/utils/locale-detector.d.ts +32 -2
  67. package/dist/runtime/server/utils/locale-detector.js +48 -27
  68. package/dist/runtime/server/utils/locale-server-middleware.js +14 -33
  69. package/dist/runtime/server/utils/payload-cache.d.ts +4 -0
  70. package/dist/runtime/server/utils/payload-cache.js +13 -0
  71. package/dist/runtime/server/utils/server-loader.d.ts +17 -6
  72. package/dist/runtime/server/utils/server-loader.js +105 -51
  73. package/dist/runtime/server/utils/translation-server-middleware.js +18 -35
  74. package/dist/runtime/utils/cache-keys.d.ts +6 -0
  75. package/dist/runtime/utils/cache-keys.js +2 -0
  76. package/dist/runtime/utils/i18n-api.d.ts +185 -0
  77. package/dist/runtime/utils/i18n-api.js +196 -0
  78. package/dist/runtime/utils/i18n-config.d.ts +18 -0
  79. package/dist/runtime/utils/i18n-config.js +47 -0
  80. package/dist/runtime/utils/i18n-navigation.d.ts +26 -0
  81. package/dist/runtime/utils/i18n-navigation.js +96 -0
  82. package/dist/runtime/utils/nuxt-i18n-server.d.ts +26 -0
  83. package/dist/runtime/utils/nuxt-i18n-server.js +83 -0
  84. package/dist/runtime/utils/nuxt-i18n.d.ts +81 -96
  85. package/dist/runtime/utils/nuxt-i18n.js +180 -252
  86. package/dist/runtime/utils/page-head.d.ts +12 -0
  87. package/dist/runtime/utils/page-head.js +6 -0
  88. package/dist/runtime/utils/request-url.d.ts +6 -0
  89. package/dist/runtime/utils/request-url.js +11 -0
  90. package/dist/runtime/utils/storage.d.ts +31 -19
  91. package/dist/runtime/utils/storage.js +100 -49
  92. package/dist/runtime/utils/translation-entry.d.ts +6 -0
  93. package/dist/runtime/utils/translation-entry.js +16 -0
  94. package/dist/types.d.mts +4 -6
  95. package/internals.d.mts +9 -2
  96. package/package.json +78 -79
  97. package/dist/client/_nuxt/BOxWElQP.js +0 -21
  98. package/dist/client/_nuxt/CUWB4CwN.js +0 -3
  99. package/dist/client/_nuxt/CmUbe-uv.js +0 -1
  100. package/dist/client/_nuxt/DAzShkZF.js +0 -1
  101. package/dist/client/_nuxt/MokPP5zi.js +0 -1
  102. package/dist/client/_nuxt/builds/meta/f020fec8-462b-480b-82d8-6b0cb65617e8.json +0 -1
  103. package/dist/client/_nuxt/error-404.D9Svp6Hk.css +0 -1
  104. package/dist/client/_nuxt/error-500.B-mjI9_9.css +0 -1
  105. package/dist/runtime/plugins/03.define.d.ts +0 -7
  106. package/dist/runtime/plugins/03.define.js +0 -42
  107. package/dist/runtime/plugins/05.hooks.d.ts +0 -2
  108. package/dist/runtime/plugins/05.hooks.js +0 -47
  109. package/dist/runtime/server/utils/server-translation-cache.d.ts +0 -11
  110. package/dist/runtime/server/utils/server-translation-cache.js +0 -20
  111. /package/dist/runtime/server/{utils → internal}/dev-live-translations.d.ts +0 -0
@@ -3,41 +3,53 @@ export interface LoadOptions {
3
3
  apiBaseUrl: string;
4
4
  baseURL: string;
5
5
  apiBaseClientHost?: string;
6
- apiBaseServerHost?: string;
7
6
  dateBuild?: string | number;
8
7
  routesLocaleLinks?: Record<string, string>;
9
- }
10
- export interface LoadResult {
11
- data: Record<string, unknown>;
12
- cacheKey: string;
8
+ /** A page's file needs its root, so the two start together. A delta from a host still finds its root without it. */
9
+ rootDelta?: boolean;
10
+ /**
11
+ * Server only: the Nitro loader of this request (`event.context.i18n.load`), called directly. It reads a host too, named at
12
+ * build time or in the environment, so a render has one source. `null` is a host's "no such file", as a 404 is for a browser.
13
+ */
14
+ serverLoad?: (locale: string, routeName: string) => Promise<Record<string, unknown> | null>;
13
15
  }
14
16
  declare class TranslationStorage {
15
17
  private cc;
18
+ /** Roots on their way: every page of a first burst waits for one request. */
19
+ private readonly rootLoads;
20
+ /** The mark of each root held, to tell a delta made against another one. */
21
+ private readonly rootMarks;
16
22
  constructor();
17
23
  /**
18
- * Configure cache limits. Call once from plugin with config values.
24
+ * Configure cache limits: the plugin's config values, on every request on the server, where this copy is process-wide.
19
25
  */
20
26
  configure(options: CacheControlOptions): void;
21
27
  /**
22
- * Freeze a shallow copy before it enters the cache so a consumer cannot mutate
23
- * what other callers will read. Nested values are shared on purpose — cheap, and
24
- * no caller mutates a chunk in place.
28
+ * On the server the cache is shared by every request, so its root is frozen, once, in place: nested values are shared
29
+ * on purpose, and no caller mutates a chunk. A browser fetched this object for itself and has one reader: nothing to guard.
25
30
  */
26
- private freezeChunk;
27
- private getCacheKey;
31
+ private shared;
28
32
  private fetchTranslations;
29
- /** Seed cache without fetching (e.g. `serverTranslationPreload` for index). */
30
- seed(locale: string, routeName: string | undefined, data: Record<string, unknown>): void;
31
33
  /**
32
- * Synchronous cache check and retrieval.
33
- * Returns data if cached (and not expired), otherwise null.
34
+ * A chunk, or `null` when the host has no file for it: a 404, a server loader's `null`, or a static host's SPA shell
35
+ * in a page's place. Anything else is a failed load, and the next try can work. A `null` body is one of those: a
36
+ * dictionary it is not, and read as "no file" on the root it would be kept as an empty one until a reload.
34
37
  */
35
- getFromCache(locale: string, routeName?: string): LoadResult | null;
38
+ private fetchChunk;
39
+ private request;
40
+ /** The URL the browser fetches a chunk from: what SSR preloads for the first page. */
41
+ clientUrl(locale: string, routeName: string | undefined, options: LoadOptions): string;
42
+ /** The cached chunk (not expired), or null. */
43
+ getFromCache(locale: string, routeName?: string): Record<string, unknown> | null;
44
+ /** Load a chunk, with caching. */
45
+ load(locale: string, routeName: string | undefined, options: LoadOptions): Promise<Record<string, unknown>>;
46
+ private loadRoot;
36
47
  /**
37
- * Load translations (with caching).
38
- * Returns data, cache key, and JSON for injection (server only).
48
+ * A delta on the root it was made against. A root held since an older deploy lacks what moved into it, so a stale tab
49
+ * asks for the delta's: `?v=` of the mark, a URL no cache holds. One try, whatever comes: a CDN mid-deploy may have
50
+ * neither side new yet, and the root it gives is still the best text there is. Both arrive before anything is shown.
39
51
  */
40
- load(locale: string, routeName: string | undefined, options: LoadOptions): Promise<LoadResult>;
52
+ private layered;
41
53
  /**
42
54
  * Clear cache and metadata.
43
55
  */
@@ -1,95 +1,146 @@
1
+ import { joinURL, withQuery } from "ufo";
1
2
  import { translationCacheKey } from "@i18n-micro/core/helpers";
2
- import { STORAGE_CC_KEY } from "@i18n-micro/hmr/cache-keys";
3
+ import { STORAGE_CC_KEY } from "./cache-keys.js";
4
+ import { takeRootMark } from "./translation-entry.js";
3
5
  import { CacheControl } from "@i18n-micro/utils/cache-control";
6
+ import { mergeOverRoot, PREMERGED_ROOT_PAGE } from "@i18n-micro/utils/merge-source";
4
7
  import { buildTranslationPayloadFetchRequest } from "@i18n-micro/utils/payload-url";
8
+ const NO_ROOT = Object.freeze({});
9
+ const NO_FILE = Object.freeze({});
10
+ class ChunkCache extends CacheControl {
11
+ /** A response requested before a clear may predate the edit the clear is for. */
12
+ clears = 0;
13
+ clear() {
14
+ this.clears++;
15
+ super.clear();
16
+ }
17
+ }
5
18
  function getStorageCacheControl() {
6
19
  const g = globalThis;
7
20
  if (!g[STORAGE_CC_KEY]) {
8
- g[STORAGE_CC_KEY] = new CacheControl();
21
+ g[STORAGE_CC_KEY] = new ChunkCache();
9
22
  }
10
23
  return g[STORAGE_CC_KEY];
11
24
  }
12
25
  class TranslationStorage {
13
26
  cc;
27
+ /** Roots on their way: every page of a first burst waits for one request. */
28
+ rootLoads = /* @__PURE__ */ new Map();
29
+ /** The mark of each root held, to tell a delta made against another one. */
30
+ rootMarks = /* @__PURE__ */ new Map();
14
31
  constructor() {
15
32
  this.cc = getStorageCacheControl();
16
33
  }
17
34
  /**
18
- * Configure cache limits. Call once from plugin with config values.
35
+ * Configure cache limits: the plugin's config values, on every request on the server, where this copy is process-wide.
19
36
  */
20
37
  configure(options) {
21
38
  this.cc.configure(options);
22
39
  }
23
- // ==========================================================================
24
- // HELPERS
25
- // ==========================================================================
26
40
  /**
27
- * Freeze a shallow copy before it enters the cache so a consumer cannot mutate
28
- * what other callers will read. Nested values are shared on purpose — cheap, and
29
- * no caller mutates a chunk in place.
41
+ * On the server the cache is shared by every request, so its root is frozen, once, in place: nested values are shared
42
+ * on purpose, and no caller mutates a chunk. A browser fetched this object for itself and has one reader: nothing to guard.
30
43
  */
31
- freezeChunk(data) {
32
- return Object.freeze({ ...data });
33
- }
34
- getCacheKey(locale, routeName) {
35
- return translationCacheKey(locale, routeName);
44
+ shared(data) {
45
+ return import.meta.server ? Object.freeze(data) : data;
36
46
  }
37
- // ==========================================================================
38
- // FETCH LOADER
39
- // ==========================================================================
40
47
  async fetchTranslations(locale, routeName, options) {
41
- const request = buildTranslationPayloadFetchRequest({
48
+ if (import.meta.server && options.serverLoad) return await options.serverLoad(locale, routeName ?? "");
49
+ const request = this.request(locale, routeName, options, import.meta.server);
50
+ return await $fetch(request.path, {
51
+ baseURL: request.baseURL,
52
+ params: request.params,
53
+ // after a clear a browser's copy may be what the clear is for: `max-age` would answer with it
54
+ cache: import.meta.client && this.cc.clears ? "no-cache" : void 0
55
+ });
56
+ }
57
+ /**
58
+ * A chunk, or `null` when the host has no file for it: a 404, a server loader's `null`, or a static host's SPA shell
59
+ * in a page's place. Anything else is a failed load, and the next try can work. A `null` body is one of those: a
60
+ * dictionary it is not, and read as "no file" on the root it would be kept as an empty one until a reload.
61
+ */
62
+ async fetchChunk(locale, routeName, options) {
63
+ const data = await this.fetchTranslations(locale, routeName, options).catch((error) => {
64
+ if (error.status === 404) return NO_FILE;
65
+ throw error;
66
+ });
67
+ if (data === NO_FILE || data === null && import.meta.server && options.serverLoad) return null;
68
+ if (data !== null && typeof data === "object" && !Array.isArray(data)) return data;
69
+ if (routeName !== PREMERGED_ROOT_PAGE) return null;
70
+ throw new TypeError(`[i18n] Invalid translation payload: ${translationCacheKey(locale, routeName)}`);
71
+ }
72
+ request(locale, routeName, options, isServer) {
73
+ return buildTranslationPayloadFetchRequest({
42
74
  apiBaseUrl: options.apiBaseUrl,
43
75
  routeName,
44
76
  locale,
45
- isServer: import.meta.server,
77
+ isServer,
46
78
  baseURL: options.baseURL,
47
79
  apiBaseClientHost: options.apiBaseClientHost,
48
- apiBaseServerHost: options.apiBaseServerHost ?? (import.meta.server ? process.env.NUXT_I18N_APP_BASE_SERVER_HOST : void 0),
49
80
  dateBuild: options.dateBuild,
50
81
  routesLocaleLinks: options.routesLocaleLinks
51
82
  });
52
- return await $fetch(request.path, {
53
- baseURL: request.baseURL,
54
- params: request.params
55
- });
56
83
  }
57
- /** Seed cache without fetching (e.g. `serverTranslationPreload` for index). */
58
- seed(locale, routeName, data) {
59
- const cacheKey = this.getCacheKey(locale, routeName);
60
- this.cc.set(cacheKey, this.freezeChunk(data));
84
+ /** The URL the browser fetches a chunk from: what SSR preloads for the first page. */
85
+ clientUrl(locale, routeName, options) {
86
+ const request = this.request(locale, routeName, options, false);
87
+ return withQuery(joinURL(request.baseURL, request.path), request.params ?? {});
61
88
  }
62
- // ==========================================================================
63
- // PUBLIC API
64
- // ==========================================================================
65
- /**
66
- * Synchronous cache check and retrieval.
67
- * Returns data if cached (and not expired), otherwise null.
68
- */
89
+ /** The cached chunk (not expired), or null. */
69
90
  getFromCache(locale, routeName) {
70
- const cacheKey = this.getCacheKey(locale, routeName);
71
- const cached = this.cc.get(cacheKey);
72
- if (cached) {
73
- return { data: cached, cacheKey };
91
+ return this.cc.get(translationCacheKey(locale, routeName)) ?? null;
92
+ }
93
+ /** Load a chunk, with caching. */
94
+ async load(locale, routeName, options) {
95
+ const direct = import.meta.server && Boolean(options.serverLoad);
96
+ const cached = direct ? null : this.getFromCache(locale, routeName);
97
+ if (cached) return cached;
98
+ const clears = this.cc.clears;
99
+ const page = routeName !== PREMERGED_ROOT_PAGE;
100
+ const root = page && options.rootDelta && !direct ? this.loadRoot(locale, options) : void 0;
101
+ root?.catch(() => {
102
+ });
103
+ const file = await this.fetchChunk(locale, routeName, options);
104
+ const mark = file ? takeRootMark(file) : void 0;
105
+ const layers = file && mark && page ? await this.layered(locale, mark, file, root, options) : null;
106
+ const data = layers ? layers.data : file;
107
+ if (clears !== this.cc.clears) return this.load(locale, routeName, options);
108
+ if (!page) this.rootMarks.set(locale, mark);
109
+ if (data === null && routeName === PREMERGED_ROOT_PAGE) {
110
+ if (import.meta.server && import.meta.prerender) throw new Error("[i18n] The host has no dictionary for this page");
111
+ return NO_ROOT;
74
112
  }
75
- return null;
113
+ if (data === null) return this.load(locale, PREMERGED_ROOT_PAGE, options);
114
+ if (!direct && !layers?.partial) this.cc.set(translationCacheKey(locale, routeName), this.shared(data));
115
+ return data;
116
+ }
117
+ loadRoot(locale, options) {
118
+ const pending = this.rootLoads.get(locale);
119
+ if (pending) return pending;
120
+ const load = this.load(locale, PREMERGED_ROOT_PAGE, options).finally(() => this.rootLoads.delete(locale));
121
+ this.rootLoads.set(locale, load);
122
+ return load;
76
123
  }
77
124
  /**
78
- * Load translations (with caching).
79
- * Returns data, cache key, and JSON for injection (server only).
125
+ * A delta on the root it was made against. A root held since an older deploy lacks what moved into it, so a stale tab
126
+ * asks for the delta's: `?v=` of the mark, a URL no cache holds. One try, whatever comes: a CDN mid-deploy may have
127
+ * neither side new yet, and the root it gives is still the best text there is. Both arrive before anything is shown.
80
128
  */
81
- async load(locale, routeName, options) {
82
- const cached = this.getFromCache(locale, routeName);
83
- if (cached) return cached;
84
- const cacheKey = this.getCacheKey(locale, routeName);
85
- const data = await this.fetchTranslations(locale, routeName, options);
86
- this.cc.set(cacheKey, this.freezeChunk(data));
87
- return { data: this.cc.get(cacheKey), cacheKey };
129
+ async layered(locale, mark, delta, started, options) {
130
+ let root = await (started ?? this.loadRoot(locale, options));
131
+ const held = this.rootMarks.get(locale);
132
+ if (held && held !== mark) {
133
+ this.cc.delete(translationCacheKey(locale, PREMERGED_ROOT_PAGE));
134
+ root = await this.load(locale, PREMERGED_ROOT_PAGE, { ...options, dateBuild: mark });
135
+ }
136
+ return { data: mergeOverRoot(root, delta), partial: root === NO_ROOT };
88
137
  }
89
138
  /**
90
139
  * Clear cache and metadata.
91
140
  */
92
141
  clear() {
142
+ this.rootLoads.clear();
143
+ this.rootMarks.clear();
93
144
  this.cc.clear();
94
145
  }
95
146
  }
@@ -0,0 +1,6 @@
1
+ import type { Translations } from '@i18n-micro/types';
2
+ import type { ServerTranslationCacheEntry } from '../server/internal/server-translation-cache.js';
3
+ /** `json` on demand: where the public dir answers `/_locales` the string would only hold memory (a chunk's size, per cached entry). */
4
+ export declare function toEntry(data: Translations): ServerTranslationCacheEntry;
5
+ /** The root mark of a file just read, taken off it: nothing above the loader sees one. `undefined`: a whole chunk. */
6
+ export declare function takeRootMark(file: Record<string, unknown>): string | undefined;
@@ -0,0 +1,16 @@
1
+ import { PREMERGED_ROOT_MARK } from "@i18n-micro/utils/merge-source";
2
+ export function toEntry(data) {
3
+ let json;
4
+ return {
5
+ // every request reads this tree: its root is frozen, once, in place
6
+ data: Object.freeze(data),
7
+ get json() {
8
+ return json ??= JSON.stringify(data).replaceAll("<", "\\u003c");
9
+ }
10
+ };
11
+ }
12
+ export function takeRootMark(file) {
13
+ const mark = file[PREMERGED_ROOT_MARK];
14
+ delete file[PREMERGED_ROOT_MARK];
15
+ return typeof mark === "string" ? mark : void 0;
16
+ }
package/dist/types.d.mts CHANGED
@@ -1,15 +1,13 @@
1
- import type { ModuleHooks } from './module.mjs'
1
+ import type { ModuleRuntimeHooks } from './module.mjs'
2
2
 
3
- declare module '@nuxt/schema' {
4
- interface NuxtHooks extends ModuleHooks {}
3
+ declare module '#app' {
4
+ interface RuntimeNuxtHooks extends ModuleRuntimeHooks {}
5
5
  }
6
6
 
7
7
  export { type Getter, type GlobalLocaleRoutes, type Locale, type LocaleCode, type ModuleOptions, type PluralFunc, type Strategies } from '@i18n-micro/types'
8
8
 
9
9
  export { type PluginsInjections } from '../dist/runtime/plugins/01.plugin.js'
10
10
 
11
- export { type TranslationPayloadMode, type resolveTranslationPayloadMode, type resolveTranslationPayloadOptions, type resolveTranslationPayloadPublicDir, type resolveTranslationPayloadPublicRel, type shouldCopyTranslationPayloadsToPublic, type shouldRegisterNitroServerAssets } from '@i18n-micro/utils/payload-config'
12
-
13
11
  export { default } from './module.mjs'
14
12
 
15
- export { type ModuleHooks } from './module.mjs'
13
+ export { type ModuleRuntimeHooks, type TranslationPayloadMode } from './module.mjs'
package/internals.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { Getter, Params } from '@i18n-micro/types'
2
+
1
3
  declare module '#build/i18n.plural.mjs' {
2
4
  export function plural(key: string, count: number, params: Params, locale: string, getter: Getter): string | null
3
5
  }
@@ -17,6 +19,8 @@ interface I18nPrivateConfig {
17
19
  customRegexMatcher?: string
18
20
  routesLocaleLinks?: Record<string, string>
19
21
  serverTranslationPreload?: boolean
22
+ cacheStorage?: string
23
+ cacheStorageBuild?: string
20
24
  /** Absolute buildDir payload dir for Node fs reads in dev/prerender. */
21
25
  payloadFsDir: string
22
26
  /** Relative dir under Nitro `public/` for prod payload files. */
@@ -63,8 +67,8 @@ export interface I18nStrategyInstance {
63
67
  getRedirect(currentPath: string, targetLocale: string): string | null
64
68
  getLocaleFromPath(path: string): string | null
65
69
  resolveLocaleFromPath(path: string): string | null
66
- localeRoute(targetLocale: string, routeOrPath: I18nStrategyResolvedRoute | string, currentRoute?: I18nStrategyResolvedRoute): I18nStrategyResolvedRoute | string
67
- switchLocaleRoute(fromLocale: string, toLocale: string, route: I18nStrategyResolvedRoute, options?: { i18nRouteParams?: Record<string, Record<string, unknown>> }): I18nStrategyResolvedRoute | string
70
+ localeRoute(targetLocale: string, routeOrPath: I18nStrategyResolvedRoute | string): I18nStrategyResolvedRoute | string
71
+ switchLocaleRoute(toLocale: string, route: I18nStrategyResolvedRoute, options?: { i18nRouteParams?: Record<string, Record<string, unknown>>, named?: boolean }): I18nStrategyResolvedRoute | string
68
72
  getCanonicalPath(route: I18nStrategyResolvedRoute, targetLocale: string): string | null
69
73
  setRouter(router: I18nStrategyRouterLike): void
70
74
  getCurrentLocale(route: I18nStrategyResolvedRoute, getDefaultLocale?: () => string | null | undefined): string
@@ -87,6 +91,7 @@ interface I18nStrategyRouterLike {
87
91
  }
88
92
 
89
93
  declare module '#build/i18n.strategy.mjs' {
94
+ export const domains: boolean
90
95
  /** Создаёт инстанс стратегии: контекст (build-time) уже в файле, при импорте передаётся только router. */
91
96
  export function createI18nStrategy(router: I18nStrategyRouterLike): I18nStrategyInstance
92
97
  export function getI18nConfig(): unknown
@@ -98,10 +103,12 @@ declare module '#i18n-internal/config' {
98
103
  }
99
104
 
100
105
  declare module '#i18n-internal/strategy' {
106
+ export const domains: boolean
101
107
  export function getI18nConfig(): unknown
102
108
  export function createI18nStrategy(router: I18nStrategyRouterLike): I18nStrategyInstance
103
109
  }
104
110
 
105
111
  declare module '#i18n-internal/payload-source' {
112
+ export const embeddedPayloads: boolean
106
113
  export function readPayload(relPath: string): Promise<Record<string, unknown>>
107
114
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-i18n-micro",
3
- "version": "3.30.2",
3
+ "version": "4.0.0",
4
4
  "description": "Fast, lightweight i18n for Nuxt with strategy-based routing and minimal overhead.",
5
5
  "keywords": [
6
6
  "i18n",
@@ -24,13 +24,10 @@
24
24
  "email": "Virus191288@gmail.com",
25
25
  "url": "https://s00d.github.io/"
26
26
  },
27
- "repository": "s00d/nuxt-i18n-micro",
28
- "workspaces": [
29
- "client",
30
- "docs",
31
- "packages/**",
32
- "test/fixtures/**/*"
33
- ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/s00d/nuxt-i18n-micro.git"
30
+ },
34
31
  "files": [
35
32
  "dist",
36
33
  "internals.d.mts"
@@ -74,53 +71,58 @@
74
71
  },
75
72
  "dependencies": {
76
73
  "@nuxt/devtools-kit": "^3.4.2",
77
- "@nuxt/kit": "^3.18.1 || ^4.5.2",
78
- "@vue/compiler-sfc": "^3.5.43",
74
+ "@nuxt/kit": "^3.18.0 || ^4.0.0",
75
+ "@vue/compiler-sfc": "^3.5.44",
79
76
  "chokidar": "^5.0.0",
80
77
  "globby": "^16.2.4",
81
78
  "h3": "^1.15.11",
82
79
  "sirv": "^3.0.2",
83
80
  "ufo": "^1.6.4",
84
- "unplugin": "^1.16.1 || ^2.0.0 || ^3.4.0",
85
- "@i18n-micro/core": "^1.3.22",
86
- "@i18n-micro/path-strategy": "^1.3.22",
87
- "@i18n-micro/hmr": "^1.0.20",
88
- "@i18n-micro/route-strategy": "^1.1.30",
89
- "@i18n-micro/test-utils": "^1.2.19",
90
- "@i18n-micro/types": "^1.2.18",
91
- "@i18n-micro/utils": "^1.0.27"
81
+ "@i18n-micro/path-strategy": "^2.0.0",
82
+ "@i18n-micro/route-strategy": "^2.0.0",
83
+ "@i18n-micro/types": "^2.0.0",
84
+ "@i18n-micro/utils": "^2.0.0",
85
+ "@i18n-micro/core": "^2.0.0"
92
86
  },
93
87
  "devDependencies": {
94
88
  "@nuxt/devtools": "^3.4.2",
95
89
  "@nuxt/devtools-ui-kit": "^3.4.2",
96
90
  "@nuxt/module-builder": "^1.0.3",
97
- "@nuxt/schema": "^3.18.1 || ^4.5.2",
98
- "@nuxt/test-utils": "^4.3.2",
99
- "@playwright/test": "^1.63.0",
91
+ "@nuxt/schema": "^3.18.0 || ^4.0.0",
92
+ "@nuxt/test-utils": "^4.3.4",
93
+ "@playwright/test": "^1.64.1",
94
+ "@stryker-mutator/core": "^10.1.1",
95
+ "@stryker-mutator/vitest-runner": "^10.1.1",
100
96
  "@types/node": "^22.19.0",
101
- "@untestutils/nuxt": "^0.7.1",
102
- "@untestutils/perf": "^0.7.1",
97
+ "@untestutils/nuxt": "^0.8.1",
98
+ "@untestutils/perf": "^0.8.1",
103
99
  "@vue/devtools-api": "^8.1.5",
104
100
  "changelogen": "^0.6.2",
105
- "oxfmt": "^0.70.0",
106
- "oxlint": "^1.85.0",
101
+ "fast-check": "^4.10.2",
102
+ "oxfmt": "^0.76.0",
103
+ "oxlint": "^1.90.0",
107
104
  "typescript": "6.0.3",
108
- "untestutils": "^0.7.1",
109
- "vitest": "^5.0.2",
110
- "vue-tsc": "^3.3.11"
105
+ "untestutils": "^0.8.1",
106
+ "vitest": "^5.1.0",
107
+ "vue-tsc": "^3.3.14"
111
108
  },
112
109
  "peerDependencies": {
113
110
  "@vue/devtools-api": "^8.1.5",
114
- "vue-router": "^4.6.3 || ^5.0.0"
111
+ "vue-router": "^4.5.1 || ^5.0.0"
115
112
  },
116
113
  "peerDependenciesMeta": {
117
114
  "@vue/devtools-api": {
118
115
  "optional": true
119
116
  }
120
117
  },
121
- "optionalDependencies": {
122
- "@rollup/rollup-linux-x64-gnu": "^4.63.5",
123
- "@rollup/rollup-win32-x64-msvc": "^4.63.5"
118
+ "engines": {
119
+ "node": ">=20"
120
+ },
121
+ "devEngines": {
122
+ "packageManager": {
123
+ "name": "pnpm",
124
+ "onFail": "warn"
125
+ }
124
126
  },
125
127
  "stackblitz": {
126
128
  "startCommand": "pnpm install && pnpm --filter \"./packages/*\" run build && pnpm run dev:prepare && pnpm run dev",
@@ -129,70 +131,67 @@
129
131
  }
130
132
  },
131
133
  "scripts": {
132
- "preinstall": "node -e \"const{existsSync}=require('fs');if(!existsSync('pnpm-workspace.yaml'))process.exit(0);const ua=process.env.npm_config_user_agent||'';if(!ua.includes('pnpm')){console.error('This repo requires pnpm (npm/yarn corrupt the TypeScript hoist). Use: pnpm install');process.exit(1)}\"",
133
134
  "dev": "nuxi dev playground",
134
135
  "dev:build": "nuxi build playground",
135
- "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
136
136
  "dev:generate": "nuxi generate playground",
137
- "release": "pnpm run release:check && pnpm run release:patch",
138
- "release:check": "pnpm run format && pnpm run lint && pnpm run typecheck && pnpm run check:versions -- --npm && pnpm run release:source && pnpm run compare:published && pnpm run test",
139
- "release:auth": "pnpm -C scripts cli ensure-npm-auth",
140
- "release:source": "pnpm -C scripts cli ensure-release-source",
141
- "release:patch": "pnpm run release:auth && pnpm run prepack && pnpm -C scripts cli release --bump patch && pnpm publish -r && git push --follow-tags",
142
- "release:minor": "pnpm run release:auth && pnpm run prepack && pnpm -C scripts cli release --bump minor && pnpm publish -r && git push --follow-tags",
143
- "release:major": "pnpm run release:auth && pnpm run prepack && pnpm -C scripts cli release --bump major && pnpm publish -r && git push --follow-tags",
144
- "lint": "oxlint src packages test client playground scripts/src build.config.ts",
145
- "lint:fix": "oxlint src packages test client playground scripts/src build.config.ts --fix",
146
- "format": "oxfmt --write src packages test client playground scripts/src build.config.ts",
147
- "format:check": "oxfmt --check src packages test client playground scripts/src build.config.ts",
137
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
138
+ "bench": "node playground/bench/matrix.mjs",
139
+ "client:build": "nuxi generate client",
140
+ "client:dev": "nuxi dev client --port 3300",
141
+ "build:packages": "pnpm --filter \"./packages/*\" run build",
142
+ "lint": "oxlint src packages test client playground scripts/src build.config.ts docs/app docs/server docs/nuxt.config.ts docs/content.config.ts docs/redirects.ts docs/preview.mjs",
143
+ "lint:fix": "oxlint src packages test client playground scripts/src build.config.ts docs/app docs/server docs/nuxt.config.ts docs/content.config.ts docs/redirects.ts docs/preview.mjs --fix",
144
+ "format": "oxfmt --write src packages test client playground scripts/src build.config.ts docs/app docs/server docs/nuxt.config.ts docs/content.config.ts docs/redirects.ts docs/preview.mjs",
145
+ "format:check": "oxfmt --check src packages test client playground scripts/src build.config.ts docs/app docs/server docs/nuxt.config.ts docs/content.config.ts docs/redirects.ts docs/preview.mjs",
146
+ "typecheck": "pnpm run typecheck:root && pnpm run typecheck:playground && pnpm run typecheck:scripts && pnpm run typecheck:packages && pnpm run typecheck:package-playgrounds",
147
+ "typecheck:root": "vue-tsc --noEmit -p tsconfig.nuxt.json",
148
+ "typecheck:playground": "vue-tsc --noEmit -p playground/tsconfig.json",
149
+ "typecheck:scripts": "pnpm -C scripts typecheck",
150
+ "typecheck:packages": "pnpm --filter \"./packages/**\" --filter \"!./packages/*/playground\" run typecheck",
151
+ "typecheck:package-playgrounds": "pnpm --filter \"./packages/*/playground\" run typecheck",
148
152
  "test": "vitest run",
153
+ "test:watch": "vitest watch --config vitest.unit.config.ts",
149
154
  "test:unit": "vitest run --config vitest.unit.config.ts",
155
+ "test:unit:types": "vitest run --config vitest.unit.config.ts --typecheck.only",
150
156
  "test:integration": "vitest run --config vitest.integration.config.ts",
157
+ "test:strategies": "vitest run --config vitest.integration.config.ts test/strategies-",
151
158
  "test:e2e": "vitest run --config vitest.e2e.config.ts",
159
+ "test:e2e:browsers": "rc=0; for b in chromium firefox webkit; do echo \"== $b\"; E2E_BROWSER=$b vitest run --config vitest.e2e.config.ts || rc=1; done; exit $rc",
152
160
  "test:packages": "vitest run --config vitest.packages.config.ts",
161
+ "test:dist:packages": "pnpm --filter \"./packages/*\" run --if-present test:dist",
162
+ "test:presets": "pnpm -C scripts cli preset-smoke --preset all",
153
163
  "test:performance": "pnpm -C scripts cli performance",
154
- "test:strategies": "vitest run --config vitest.integration.config.ts test/strategies-",
155
- "test:watch": "vitest watch --config vitest.unit.config.ts",
156
- "test:types": "pnpm run typecheck",
157
- "typecheck:root": "vue-tsc --noEmit -p tsconfig.nuxt.json",
158
- "typecheck:playground": "vue-tsc --noEmit -p playground/tsconfig.json",
159
- "typecheck:scripts": "pnpm -C scripts typecheck",
160
- "typecheck:packages": "pnpm --filter \"./packages/**\" --filter \"!./packages/*/playground\" run typecheck",
161
- "typecheck:package-playgrounds": "pnpm --filter \"./packages/*/playground\" run typecheck",
162
- "typecheck": "pnpm run typecheck:root && pnpm run typecheck:playground && pnpm run typecheck:scripts && pnpm run typecheck:packages && pnpm run typecheck:package-playgrounds",
163
- "build:packages": "pnpm --filter \"./packages/*\" run build",
164
- "client:build": "nuxi generate client",
165
- "client:prepare": "nuxi client:prepare client",
166
- "client:dev": "nuxi dev client --port 3300",
167
- "clean": "find . \\( -name 'node_modules' -o -name '.nuxt' -o -name '.output' -o -name 'dist' \\) -type d -prune -exec rm -rf '{}' + && find . \\( -name 'pnpm-lock.yaml' -o -name 'package-lock.json' \\) -type f -exec rm -f '{}' +",
168
- "clean:test": "rm -rf test/.fixture-builds test/.untestutils && find test/fixtures -type d \\( -name '.nuxt' -o -name '.output-shared' -o -name '.nuxt-test' -o -name 'test-results' \\) -prune -exec rm -rf '{}' + 2>/dev/null || true",
169
- "check:versions": "pnpm -C scripts cli check-versions",
170
- "verify:packages": "pnpm -C scripts cli verify-packages",
171
- "verify:packages:publint": "pnpm -C scripts cli verify-packages --publint",
172
- "compare:published": "pnpm -C scripts cli compare-published",
173
- "test:dist:packages": "pnpm --filter \"@i18n-micro/core\" --filter \"@i18n-micro/utils\" --filter \"@i18n-micro/hmr\" --filter \"@i18n-micro/vue\" --filter \"@i18n-micro/path-strategy\" --filter \"@i18n-micro/route-strategy\" --filter \"@i18n-micro/devtools-ui\" --filter \"@i18n-micro/astro\" run test:dist",
174
- "typecheck:nuxt": "nuxt typecheck --no-emit",
164
+ "test:mutation": "stryker run stryker.config.mjs",
165
+ "test:fuzz": "pnpm -C scripts cli http-fuzz",
175
166
  "docs:dev": "pnpm --filter @i18n-micro/docs dev",
176
- "docs:build": "pnpm --filter @i18n-micro/docs build",
177
- "docs:serve": "pnpm --filter @i18n-micro/docs preview",
178
167
  "docs:clean-dev": "pnpm --filter @i18n-micro/docs clean-dev",
168
+ "docs:build": "pnpm --filter @i18n-micro/docs build",
179
169
  "docs:preview": "pnpm --filter @i18n-micro/docs preview",
180
- "smoke:pack": "pnpm -C scripts cli smoke-pack",
181
- "smoke:verify": "pnpm -C scripts cli smoke-verify",
182
- "smoke:browser": "pnpm -C scripts cli smoke-browser",
183
- "test:presets": "pnpm -C scripts cli preset-smoke --preset all",
184
- "test:presets:vercel": "pnpm -C scripts cli preset-smoke --preset vercel",
185
- "test:presets:cloudflare": "pnpm -C scripts cli preset-smoke --preset cloudflare_pages",
186
- "test:presets:netlify": "pnpm -C scripts cli preset-smoke --preset netlify",
170
+ "docs:generate": "pnpm -C scripts cli docs-generate",
171
+ "docs:generate:check": "pnpm -C scripts cli docs-generate --check",
172
+ "api:surface": "pnpm -C scripts cli api-surface",
173
+ "api:surface:update": "pnpm -C scripts cli api-surface --update",
187
174
  "audit:deps": "pnpm -C scripts cli deps-audit",
188
175
  "audit:docs": "pnpm -C scripts cli docs-audit",
189
176
  "audit:fixtures": "pnpm -C scripts cli fixtures-audit",
190
- "api:surface": "pnpm -C scripts cli api-surface",
191
- "api:surface:update": "pnpm -C scripts cli api-surface --update",
192
177
  "budget:payload": "pnpm -C scripts cli payload-budget",
193
178
  "budget:payload:update": "pnpm -C scripts cli payload-budget --update",
179
+ "clean": "find . -path ./.claude -prune -o \\( -name 'node_modules' -o -name '.nuxt' -o -name '.output' -o -name 'dist' \\) -type d -prune -exec rm -rf '{}' +",
180
+ "clean:test": "rm -rf test/.fixture-builds test/.untestutils && find test/fixtures -type d \\( -name '.nuxt' -o -name '.output-shared' -o -name '.nuxt-test' -o -name 'test-results' \\) -prune -exec rm -rf '{}' + 2>/dev/null || true",
181
+ "check:versions": "pnpm -C scripts cli check-versions",
182
+ "verify:packages": "pnpm -C scripts cli verify-packages",
183
+ "verify:packages:publint": "pnpm -C scripts cli verify-packages --publint",
184
+ "compare:published": "pnpm -C scripts cli compare-published",
185
+ "check:consumer": "pnpm -C scripts cli consumer-types",
186
+ "check:oldest": "pnpm -C scripts cli oldest-versions",
194
187
  "preflight": "pnpm -C scripts cli preflight",
195
- "docs:generate": "pnpm -C scripts cli docs-generate",
196
- "docs:generate:check": "pnpm -C scripts cli docs-generate --check"
188
+ "bump": "pnpm -C scripts cli bump",
189
+ "release:check": "pnpm -C scripts cli publish --check-only",
190
+ "release:auth": "pnpm -C scripts cli ensure-npm-auth",
191
+ "release:source": "pnpm -C scripts cli ensure-release-source",
192
+ "release:patch": "pnpm -C scripts cli publish --bump patch",
193
+ "release:minor": "pnpm -C scripts cli publish --bump minor",
194
+ "release:major": "pnpm -C scripts cli publish --bump major",
195
+ "release:packages": "pnpm -C scripts cli publish"
197
196
  }
198
197
  }