nuxt-i18n-micro 3.18.2 → 3.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/client/200.html +1 -1
  2. package/dist/client/404.html +1 -1
  3. package/dist/client/_nuxt/builds/latest.json +1 -1
  4. package/dist/client/_nuxt/builds/meta/bbe443af-4371-4ec3-b41c-7a5e9deb9b9b.json +1 -0
  5. package/dist/client/index.html +1 -1
  6. package/dist/module.d.mts +1 -0
  7. package/dist/module.json +1 -1
  8. package/dist/module.mjs +57 -149
  9. package/dist/runtime/composables/useI18nLocale.js +3 -3
  10. package/dist/runtime/composables/useLocaleHead.js +1 -1
  11. package/dist/runtime/plugins/01.plugin.js +6 -3
  12. package/dist/runtime/plugins/02.meta.js +2 -2
  13. package/dist/runtime/plugins/05.hooks.js +1 -1
  14. package/dist/runtime/plugins/06.redirect.js +14 -36
  15. package/dist/runtime/server/middleware/i18n.global.js +17 -35
  16. package/dist/runtime/server/plugins/watcher.dev.js +36 -66
  17. package/dist/runtime/server/routes/i18n.js +2 -2
  18. package/dist/runtime/server/utils/locale-detector.js +11 -1
  19. package/dist/runtime/server/utils/locale-server-middleware.js +2 -2
  20. package/dist/runtime/server/utils/server-loader.d.ts +3 -3
  21. package/dist/runtime/server/utils/server-loader.js +32 -8
  22. package/dist/runtime/server/utils/translation-server-middleware.js +2 -2
  23. package/dist/runtime/utils/storage.d.ts +4 -6
  24. package/dist/runtime/utils/storage.js +20 -11
  25. package/dist/types.d.mts +2 -0
  26. package/package.json +11 -8
  27. package/dist/client/_nuxt/builds/meta/a653a17d-86f7-440a-a09c-9871b01bc761.json +0 -1
  28. package/dist/runtime/utils/active-locales.d.ts +0 -4
  29. package/dist/runtime/utils/active-locales.js +0 -9
  30. package/dist/runtime/utils/cache-control.d.ts +0 -50
  31. package/dist/runtime/utils/cache-control.js +0 -88
  32. package/dist/runtime/utils/cookie.d.ts +0 -24
  33. package/dist/runtime/utils/cookie.js +0 -22
  34. package/dist/runtime/utils/deep-merge.d.ts +0 -15
  35. package/dist/runtime/utils/deep-merge.js +0 -14
  36. package/dist/runtime/utils/route-utils.d.ts +0 -33
  37. package/dist/runtime/utils/route-utils.js +0 -63
  38. package/dist/runtime/utils/runtime-i18n-config.d.ts +0 -8
  39. package/dist/runtime/utils/runtime-i18n-config.js +0 -90
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BCTbvRLO.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D6byAye8.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BNXusRXY.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"a653a17d-86f7-440a-a09c-9871b01bc761",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1779781937006,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BCTbvRLO.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D6byAye8.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BNXusRXY.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"bbe443af-4371-4ec3-b41c-7a5e9deb9b9b",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1781425543266,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BCTbvRLO.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D6byAye8.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BNXusRXY.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"a653a17d-86f7-440a-a09c-9871b01bc761",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1779781937006,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BCTbvRLO.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D6byAye8.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BNXusRXY.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"bbe443af-4371-4ec3-b41c-7a5e9deb9b9b",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1781425543266,false]</script></body></html>
@@ -1 +1 @@
1
- {"id":"a653a17d-86f7-440a-a09c-9871b01bc761","timestamp":1779781934227}
1
+ {"id":"bbe443af-4371-4ec3-b41c-7a5e9deb9b9b","timestamp":1781425540376}
@@ -0,0 +1 @@
1
+ {"id":"bbe443af-4371-4ec3-b41c-7a5e9deb9b9b","timestamp":1781425540376,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BCTbvRLO.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D6byAye8.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BNXusRXY.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"a653a17d-86f7-440a-a09c-9871b01bc761",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1779781937006,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/B-cq5x_h.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BCTbvRLO.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D6byAye8.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BNXusRXY.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"bbe443af-4371-4ec3-b41c-7a5e9deb9b9b",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1781425543266,false]</script></body></html>
package/dist/module.d.mts CHANGED
@@ -3,6 +3,7 @@ import { HookResult } from '@nuxt/schema';
3
3
  import { ModuleOptions } from '@i18n-micro/types';
4
4
  export { Getter, GlobalLocaleRoutes, Locale, LocaleCode, ModuleOptions, PluralFunc, Strategies } from '@i18n-micro/types';
5
5
  export { PluginsInjections } from '../dist/runtime/plugins/01.plugin.js';
6
+ export { TranslationPayloadMode, resolveTranslationPayloadMode, resolveTranslationPayloadOptions, resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config';
6
7
 
7
8
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
8
9
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-i18n-micro",
3
3
  "configKey": "i18n",
4
- "version": "3.18.2",
4
+ "version": "3.19.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,10 +1,15 @@
1
1
  import * as fs from 'node:fs';
2
- import fs__default, { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import fs__default, { readFileSync, existsSync } from 'node:fs';
3
3
  import { createRequire } from 'node:module';
4
4
  import path, { resolve, join, dirname } from 'node:path';
5
5
  import { fileURLToPath, pathToFileURL } from 'node:url';
6
6
  import { defaultPlural, isNoPrefixStrategy, withPrefixStrategy } from '@i18n-micro/core';
7
+ import { generateHmrPlugin } from '@i18n-micro/hmr/generate-plugin';
7
8
  import { RouteGenerator, isLocaleAllowedForUnlocalizedRoute, normalizePath, isInternalPath } from '@i18n-micro/route-strategy';
9
+ import { buildTranslationSourceLayers, preMergeLocales } from '@i18n-micro/utils/build';
10
+ import { resolveTranslationPayloadOptions, getTranslationPayloadMisconfigurationWarnings, getTranslationPayloadSizeWarning, resolveTranslationPayloadWarningThresholds, resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config';
11
+ export { resolveTranslationPayloadMode, resolveTranslationPayloadOptions, resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config';
12
+ import { scanTranslationPayloadDirectory } from '@i18n-micro/utils/payload-stats';
8
13
  import { useNuxt, defineNuxtModule, useLogger, createResolver, addTemplate, addImportsDir, addPlugin, addServerHandler, addComponentsDir, addTypeTemplate, addPrerenderRoutes } from '@nuxt/kit';
9
14
  import { globby } from 'globby';
10
15
  import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
@@ -118,46 +123,6 @@ function setupDevToolsUI(options, resolve2, rootDirs) {
118
123
  });
119
124
  }
120
125
 
121
- function generateHmrPlugin(files) {
122
- const accepts = files.map((file) => {
123
- const isPage = /\/pages\//.test(file);
124
- let pageName = "";
125
- let locale = "";
126
- if (isPage) {
127
- const m = /\/pages\/(.+)\/([^/]+)\.json$/.exec(file);
128
- pageName = m?.[1] || "";
129
- locale = m?.[2] || "";
130
- } else {
131
- const m = /\/([^/]+)\.json$/.exec(file);
132
- locale = m?.[1] || "";
133
- }
134
- return `
135
- if (import.meta.hot) {
136
- import.meta.hot.accept('${file}', async (mod) => {
137
- const nuxtApp = useNuxtApp()
138
- const data = (mod && typeof mod === 'object' && Object.prototype.hasOwnProperty.call(mod, 'default'))
139
- ? mod.default
140
- : mod
141
- try {
142
- ${isPage ? `await nuxtApp.$loadPageTranslations('${locale}', '${pageName}', data)` : `await nuxtApp.$loadTranslations('${locale}', data)`}
143
- console.log('[i18n HMR] Translations reloaded:', '${isPage ? "page" : "global"}', '${locale}'${isPage ? `, '${pageName}'` : ""})
144
- }
145
- catch (e) {
146
- console.warn('[i18n HMR] Failed to reload translations for', '${file}', e)
147
- }
148
- })
149
- }
150
- `.trim();
151
- }).join("\n");
152
- return `
153
- import { defineNuxtPlugin, useNuxtApp } from '#imports'
154
-
155
- export default defineNuxtPlugin(() => {
156
- ${accepts}
157
- })
158
- `.trim();
159
- }
160
-
161
126
  function shouldLocalizeRouteRulePath(originalPath) {
162
127
  const routeRulePath = originalPath.trim();
163
128
  if (!routeRulePath) return true;
@@ -286,97 +251,7 @@ function extractDefineI18nRouteData(content, _filePath) {
286
251
  }
287
252
  }
288
253
 
289
- function deepMergeTranslations(target, source) {
290
- if (!target || Object.keys(target).length === 0) return { ...source };
291
- const output = { ...target };
292
- for (const key in source) {
293
- if (key === "__proto__" || key === "constructor") continue;
294
- const src = source[key];
295
- const dst = output[key];
296
- if (src && typeof src === "object" && !Array.isArray(src) && dst && typeof dst === "object" && !Array.isArray(dst)) {
297
- output[key] = deepMergeTranslations(dst, src);
298
- } else {
299
- output[key] = src;
300
- }
301
- }
302
- return output;
303
- }
304
254
  const DEFAULT_CANONICAL_QUERY_WHITELIST = ["page", "sort", "filter", "search", "q", "query", "tag"];
305
- async function preMergeLocales(rootDirs, translationDirName, outputDir, locales, globalFallbackLocale, disablePageLocales) {
306
- if (existsSync(outputDir)) fs__default.rmSync(outputDir, { recursive: true, force: true });
307
- mkdirSync(outputDir, { recursive: true });
308
- const layerPaths = rootDirs.map((dir) => join(dir, translationDirName));
309
- const allFiles = /* @__PURE__ */ new Set();
310
- for (const lp of layerPaths) {
311
- if (!existsSync(lp)) continue;
312
- const files = await globby("**/*.json", { cwd: lp });
313
- files.forEach((f) => allFiles.add(f));
314
- }
315
- const merged = /* @__PURE__ */ new Map();
316
- for (const file of allFiles) {
317
- let content = {};
318
- for (const lp of layerPaths) {
319
- const fp = join(lp, file);
320
- if (existsSync(fp)) {
321
- try {
322
- content = deepMergeTranslations(content, JSON.parse(readFileSync(fp, "utf-8")));
323
- } catch {
324
- }
325
- }
326
- }
327
- merged.set(file, content);
328
- }
329
- const rootMap = /* @__PURE__ */ new Map();
330
- const pageMap = /* @__PURE__ */ new Map();
331
- for (const [file, content] of merged) {
332
- const dir = dirname(file);
333
- const locale = file.slice(file.lastIndexOf("/") + 1).replace(".json", "");
334
- if (dir === ".") {
335
- rootMap.set(locale, content);
336
- } else {
337
- if (!pageMap.has(dir)) pageMap.set(dir, /* @__PURE__ */ new Map());
338
- pageMap.get(dir).set(locale, content);
339
- }
340
- }
341
- const knownCodes = new Set(locales.map((l) => l.code));
342
- const applyFallback = (map) => {
343
- for (const locale of locales) {
344
- const chain = [globalFallbackLocale, locale.fallbackLocale, locale.code].filter((l) => !!l && knownCodes.has(l)).filter((v, i, arr) => arr.indexOf(v) === i);
345
- if (chain.length <= 1) continue;
346
- let result = {};
347
- for (const code of chain) {
348
- const data = map.get(code);
349
- if (data) result = deepMergeTranslations(result, data);
350
- }
351
- map.set(locale.code, result);
352
- }
353
- };
354
- applyFallback(rootMap);
355
- for (const localeMap of pageMap.values()) {
356
- applyFallback(localeMap);
357
- }
358
- if (disablePageLocales || pageMap.size === 0) {
359
- const indexMap = /* @__PURE__ */ new Map();
360
- for (const [locale, data] of rootMap) {
361
- indexMap.set(locale, { ...data });
362
- }
363
- pageMap.set("pages/index", indexMap);
364
- } else {
365
- for (const [, localeMap] of pageMap) {
366
- for (const [locale, rootData] of rootMap) {
367
- const pageData = localeMap.get(locale);
368
- localeMap.set(locale, pageData ? deepMergeTranslations(rootData, pageData) : { ...rootData });
369
- }
370
- }
371
- }
372
- for (const [context, localeMap] of pageMap) {
373
- for (const [locale, data] of localeMap) {
374
- const targetPath = join(outputDir, context, `${locale}.json`);
375
- mkdirSync(dirname(targetPath), { recursive: true });
376
- writeFileSync(targetPath, JSON.stringify(data));
377
- }
378
- }
379
- }
380
255
  function generateI18nTypes() {
381
256
  return `
382
257
  import type {PluginsInjections} from "nuxt-i18n-micro";
@@ -425,6 +300,13 @@ const module = defineNuxtModule({
425
300
  apiBaseUrl: "_locales",
426
301
  apiBaseClientHost: void 0,
427
302
  apiBaseServerHost: void 0,
303
+ translationPayloads: {
304
+ mode: "premerged",
305
+ serverAssets: true,
306
+ serverHandler: true,
307
+ publicAssets: true,
308
+ prerenderRoutes: true
309
+ },
428
310
  routesLocaleLinks: {},
429
311
  globalLocaleRoutes: {},
430
312
  canonicalQueryWhitelist: void 0,
@@ -452,13 +334,33 @@ const module = defineNuxtModule({
452
334
  const resolver = createResolver(import.meta.url);
453
335
  const rootDirs = nuxt.options._layers.map((layer) => layer.config.rootDir).reverse();
454
336
  const mergedLocalesDir = resolve(nuxt.options.buildDir, "i18n-merged");
337
+ const sourceLocalesDir = resolve(nuxt.options.buildDir, "i18n-source");
455
338
  const translationDirName = options.translationDir || "locales";
339
+ const translationPayloads = resolveTranslationPayloadOptions(options);
340
+ const translationAssetsDir = translationPayloads.mode === "source" ? sourceLocalesDir : mergedLocalesDir;
341
+ for (const warning of getTranslationPayloadMisconfigurationWarnings({
342
+ translationPayloads,
343
+ apiBaseClientHost: options.apiBaseClientHost,
344
+ apiBaseServerHost: options.apiBaseServerHost
345
+ })) {
346
+ logger.warn(warning.replace("[nuxt-i18n-micro] ", ""));
347
+ }
456
348
  const localeInfos = (options.locales ?? []).map(
457
349
  (l) => typeof l === "string" ? { code: l } : { code: l.code, fallbackLocale: l.fallbackLocale }
458
350
  );
459
351
  nuxt.hook("build:before", async () => {
460
- await preMergeLocales(rootDirs, translationDirName, mergedLocalesDir, localeInfos, options.fallbackLocale, options.disablePageLocales);
461
- logger.info(`Pre-merged translations from ${rootDirs.length} layer(s) into ${mergedLocalesDir}`);
352
+ if (translationPayloads.mode === "source") {
353
+ await buildTranslationSourceLayers(rootDirs, translationDirName, sourceLocalesDir);
354
+ logger.info(`Built compact translation source from ${rootDirs.length} layer(s) into ${sourceLocalesDir}`);
355
+ } else {
356
+ await preMergeLocales(rootDirs, translationDirName, mergedLocalesDir, localeInfos, options.fallbackLocale, options.disablePageLocales);
357
+ logger.info(`Pre-merged translations from ${rootDirs.length} layer(s) into ${mergedLocalesDir}`);
358
+ }
359
+ const payloadStats = scanTranslationPayloadDirectory(translationAssetsDir);
360
+ const sizeWarning = getTranslationPayloadSizeWarning(payloadStats, resolveTranslationPayloadWarningThresholds(options.translationPayloads));
361
+ if (sizeWarning) {
362
+ logger.warn(sizeWarning.replace("[nuxt-i18n-micro] ", ""));
363
+ }
462
364
  });
463
365
  const routeLocales = { ...options.routeLocales ?? {} };
464
366
  const globalLocaleRoutes = {};
@@ -569,7 +471,8 @@ const module = defineNuxtModule({
569
471
  debug: options.debug ?? false,
570
472
  customRegexMatcher: options.customRegexMatcher instanceof RegExp ? options.customRegexMatcher.source : options.customRegexMatcher,
571
473
  cacheMaxSize: options.cacheMaxSize ?? 0,
572
- cacheTtl: options.cacheTtl ?? 0
474
+ cacheTtl: options.cacheTtl ?? 0,
475
+ translationPayloadMode: translationPayloads.mode
573
476
  };
574
477
  const fullConfigJson = JSON.stringify(fullConfig);
575
478
  const strategyTemplate = addTemplate({
@@ -679,10 +582,12 @@ export function getI18nPrivateConfig() { return __privateConfig }
679
582
  name: "i18n-plugin-redirect",
680
583
  order: 10
681
584
  });
682
- addServerHandler({
683
- route: `/${apiBaseUrl}/:page/:locale/data.json`,
684
- handler: resolver.resolve("./runtime/server/routes/i18n")
685
- });
585
+ if (translationPayloads.serverHandler) {
586
+ addServerHandler({
587
+ route: `/${apiBaseUrl}/:page/:locale/data.json`,
588
+ handler: resolver.resolve("./runtime/server/routes/i18n")
589
+ });
590
+ }
686
591
  if (options.components !== false) {
687
592
  addComponentsDir({
688
593
  path: resolver.resolve("./runtime/components"),
@@ -758,6 +663,7 @@ declare module '#i18n-internal/plural' {
758
663
  }
759
664
  });
760
665
  const addDataRoutes = (pages = []) => {
666
+ if (!translationPayloads.prerenderRoutes) return;
761
667
  const pagesForDataRoutes = pages.filter((p) => p.name !== void 0 && (!options.routesLocaleLinks || !options.routesLocaleLinks[p.name]));
762
668
  const dataRoutes = routeGenerator.generateDataRoutes(pagesForDataRoutes, apiBaseUrl, !!options.disablePageLocales);
763
669
  addPrerenderRoutes(dataRoutes);
@@ -794,11 +700,13 @@ declare module '#i18n-internal/plural' {
794
700
  nitroConfig.alias["#i18n-internal/plural"] = pluralTemplate.dst;
795
701
  nitroConfig.alias["#i18n-internal/strategy"] = strategyTemplate.dst;
796
702
  nitroConfig.alias["#i18n-internal/config"] = configTemplate.dst;
797
- nitroConfig.serverAssets = nitroConfig.serverAssets || [];
798
- nitroConfig.serverAssets.push({
799
- baseName: "i18n",
800
- dir: mergedLocalesDir
801
- });
703
+ if (translationPayloads.serverAssets) {
704
+ nitroConfig.serverAssets = nitroConfig.serverAssets || [];
705
+ nitroConfig.serverAssets.push({
706
+ baseName: "i18n",
707
+ dir: translationAssetsDir
708
+ });
709
+ }
802
710
  if (nitroConfig.imports) {
803
711
  nitroConfig.imports.presets = nitroConfig.imports.presets || [];
804
712
  nitroConfig.imports.presets.push({
@@ -838,14 +746,14 @@ declare module '#i18n-internal/plural' {
838
746
  });
839
747
  nuxt.hook("nitro:build:public-assets", (nitro) => {
840
748
  const isProd = nuxt.options.dev === false;
841
- if (isProd) {
842
- const publicDir = path.join(nitro.options.output.publicDir ?? "./dist", options.translationDir ?? "locales");
749
+ if (isProd && translationPayloads.publicAssets) {
750
+ const publicDir = resolveTranslationPayloadPublicDir(nitro.options.output.publicDir, options);
843
751
  try {
844
- if (existsSync(mergedLocalesDir)) {
845
- fs__default.cpSync(mergedLocalesDir, publicDir, { recursive: true });
846
- logger.log(`Pre-merged translations copied to public directory`);
752
+ if (existsSync(translationAssetsDir)) {
753
+ fs__default.cpSync(translationAssetsDir, publicDir, { recursive: true });
754
+ logger.log(`Translation payloads copied to public directory`);
847
755
  } else {
848
- logger.warn(`Pre-merged translations directory not found: ${mergedLocalesDir}`);
756
+ logger.warn(`Translation assets directory not found: ${translationAssetsDir}`);
849
757
  }
850
758
  } catch (err) {
851
759
  logger.error("Error copying translations:", err);
@@ -1,9 +1,9 @@
1
+ import { getEnabledLocaleCodes } from "@i18n-micro/utils/active-locales";
2
+ import { getHashCookieName, getLocaleCookieName, getLocaleCookieOptions } from "@i18n-micro/utils/cookie";
3
+ import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
1
4
  import { useNuxtApp, useState } from "#app";
2
5
  import { getI18nConfig } from "#build/i18n.strategy.mjs";
3
6
  import { useCookie } from "#imports";
4
- import { getEnabledLocaleCodes } from "../utils/active-locales.js";
5
- import { getHashCookieName, getLocaleCookieName, getLocaleCookieOptions } from "../utils/cookie.js";
6
- import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
7
7
  export function useI18nLocale() {
8
8
  const nuxtApp = useNuxtApp();
9
9
  const i18nConfig = resolveI18nConfigWithRuntimeOverrides(nuxtApp.$getI18nConfig?.() ?? getI18nConfig());
@@ -1,8 +1,8 @@
1
1
  import { isNoPrefixStrategy } from "@i18n-micro/core";
2
+ import { findAllowedLocalesForRoute } from "@i18n-micro/utils/route";
2
3
  import { joinURL, parseURL, withQuery } from "ufo";
3
4
  import { ref, unref, watch } from "vue";
4
5
  import { useNuxtApp, useRoute } from "#imports";
5
- import { findAllowedLocalesForRoute } from "../utils/route-utils.js";
6
6
  export const useLocaleHead = ({
7
7
  addDirAttribute = true,
8
8
  identifierAttribute = "id",
@@ -1,12 +1,12 @@
1
1
  import { FormatService, isNoPrefixStrategy } from "@i18n-micro/core";
2
+ import { deepMergeTranslations } from "@i18n-micro/utils/deep-merge";
3
+ import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
2
4
  import { shallowRef, triggerRef, unref } from "vue";
3
5
  import { useState } from "#app";
4
6
  import { plural } from "#build/i18n.plural.mjs";
5
7
  import { createI18nStrategy, getI18nConfig } from "#build/i18n.strategy.mjs";
6
8
  import { createError, defineNuxtPlugin, navigateTo, useHead, useRouter, useRuntimeConfig } from "#imports";
7
9
  import { useI18nLocale } from "../composables/useI18nLocale.js";
8
- import { deepMergeTranslations } from "../utils/deep-merge.js";
9
- import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
10
10
  import { translationStorage } from "../utils/storage.js";
11
11
  const isDev = process.env.NODE_ENV !== "production";
12
12
  const RE_TOKEN = /\{(\w+)\}/g;
@@ -81,7 +81,10 @@ export default defineNuxtPlugin(async (nuxtApp) => {
81
81
  const loadOptions = {
82
82
  apiBaseUrl: i18nConfig.apiBaseUrl ?? "_locales",
83
83
  baseURL: runtimeConfig.app.baseURL,
84
- dateBuild: i18nConfig.dateBuild
84
+ apiBaseClientHost: i18nConfig.apiBaseClientHost,
85
+ apiBaseServerHost: i18nConfig.apiBaseServerHost,
86
+ dateBuild: i18nConfig.dateBuild,
87
+ routesLocaleLinks: i18nConfig.routesLocaleLinks
85
88
  };
86
89
  const getCacheKey = (locale, routeName) => {
87
90
  return `${locale}:${routeName || "index"}`;
@@ -1,9 +1,9 @@
1
+ import { isMetaDisabledForRoute } from "@i18n-micro/utils/route";
2
+ import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
1
3
  import { watch } from "vue";
2
4
  import { getI18nConfig } from "#build/i18n.strategy.mjs";
3
5
  import { defineNuxtPlugin, useHead, useRequestURL, useRoute } from "#imports";
4
6
  import { useLocaleHead } from "../composables/useLocaleHead.js";
5
- import { isMetaDisabledForRoute } from "../utils/route-utils.js";
6
- import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
7
7
  export default defineNuxtPlugin((nuxtApp) => {
8
8
  const route = useRoute();
9
9
  const getRuntimeConfig = nuxtApp.$getI18nConfig;
@@ -1,7 +1,7 @@
1
1
  import { isNoPrefixStrategy } from "@i18n-micro/core";
2
+ import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
2
3
  import { getI18nConfig } from "#build/i18n.strategy.mjs";
3
4
  import { defineNuxtPlugin, useNuxtApp, useRouter } from "#imports";
4
- import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
5
5
  const isDev = process.env.NODE_ENV !== "production";
6
6
  export default defineNuxtPlugin(async (nuxtApp) => {
7
7
  const getRuntimeConfig = nuxtApp.$getI18nConfig;
@@ -1,10 +1,11 @@
1
+ import { getEnabledLocaleCodes } from "@i18n-micro/utils/active-locales";
2
+ import { getLocaleCookieName, getLocaleCookieOptions } from "@i18n-micro/utils/cookie";
3
+ import { resolvePreferredLocale } from "@i18n-micro/utils/resolve-locale";
4
+ import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
1
5
  import { getCookie, getHeader, getRequestURL, setCookie } from "h3";
2
6
  import { createI18nStrategy, getI18nConfig } from "#build/i18n.strategy.mjs";
3
7
  import { createError, defineNuxtPlugin, navigateTo, useRequestEvent, useRoute, useRouter, useState } from "#imports";
4
8
  import { useI18nLocale } from "../composables/useI18nLocale.js";
5
- import { getEnabledLocaleCodes } from "../utils/active-locales.js";
6
- import { getLocaleCookieName, getLocaleCookieOptions } from "../utils/cookie.js";
7
- import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
8
9
  const DEBUG = process.env.NUXT_I18N_DEBUG_REDIRECT === "1";
9
10
  const DEFAULT_STATIC_PATTERNS = [
10
11
  /^\/sitemap.*\.xml$/,
@@ -42,13 +43,6 @@ function isInternalPath(path, excludePatterns) {
42
43
  }
43
44
  return false;
44
45
  }
45
- function parseAcceptLanguage(header) {
46
- if (!header) return [];
47
- return header.split(",").map((entry) => {
48
- const [lang] = entry.split(";");
49
- return (lang ?? "").trim();
50
- }).filter((s) => s.length > 0);
51
- }
52
46
  export default defineNuxtPlugin({
53
47
  name: "i18n-redirect",
54
48
  enforce: "pre",
@@ -107,32 +101,16 @@ export default defineNuxtPlugin({
107
101
  const isPrerenderOrBot = !!(prerenderHeader || userAgent.includes("Nitro") || !userAgent);
108
102
  const skipRedirect = !isRootPath && autoDetectPath !== "*" && isPrerenderOrBot;
109
103
  if (!skipRedirect) {
110
- let preferredLocale = defaultLocale;
111
- if (autoDetectPath !== "*") {
112
- const localeState = useState("i18n-locale", () => null);
113
- if (localeState.value && validLocales.includes(localeState.value)) {
114
- preferredLocale = localeState.value;
115
- }
116
- }
117
- if (preferredLocale === defaultLocale && cookieName) {
118
- const cookieVal = getCookie(event, cookieName);
119
- if (cookieVal && validLocales.includes(cookieVal)) {
120
- preferredLocale = cookieVal;
121
- }
122
- }
123
- if (i18nConfig.autoDetectLanguage && preferredLocale === defaultLocale) {
124
- const acceptHeader = getHeader(event, "accept-language");
125
- const langs = parseAcceptLanguage(acceptHeader);
126
- for (const lang of langs) {
127
- const lowerCaseLanguage = lang.toLowerCase();
128
- const primaryLanguage = lowerCaseLanguage.split("-")[0];
129
- const found = validLocales.find((l) => l.toLowerCase() === lowerCaseLanguage || l.toLowerCase() === primaryLanguage);
130
- if (found) {
131
- preferredLocale = found;
132
- break;
133
- }
134
- }
135
- }
104
+ const localeState = autoDetectPath !== "*" ? useState("i18n-locale", () => null) : null;
105
+ let preferredLocale = resolvePreferredLocale({
106
+ defaultLocale,
107
+ validLocales,
108
+ autoDetectLanguage: i18nConfig.autoDetectLanguage,
109
+ stateLocale: localeState?.value ?? null,
110
+ cookieLocale: cookieName ? getCookie(event, cookieName) : null,
111
+ acceptLanguageHeader: getHeader(event, "accept-language"),
112
+ ignoreStateLocale: autoDetectPath === "*"
113
+ });
136
114
  if (autoDetectPath === "*" && !hasLocalePrefix) {
137
115
  preferredLocale = defaultLocale;
138
116
  }
@@ -1,16 +1,10 @@
1
+ import { getEnabledLocaleCodes } from "@i18n-micro/utils/active-locales";
2
+ import { getLocaleCookieName } from "@i18n-micro/utils/cookie";
3
+ import { resolveServerLocale } from "@i18n-micro/utils/resolve-locale";
4
+ import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
1
5
  import { defineEventHandler, getCookie, getHeader, getQuery, getRequestURL } from "h3";
2
6
  import { getI18nConfig } from "#i18n-internal/strategy";
3
7
  import { useRuntimeConfig } from "#imports";
4
- import { getEnabledLocaleCodes } from "../../utils/active-locales.js";
5
- import { getLocaleCookieName } from "../../utils/cookie.js";
6
- import { resolveI18nConfigWithRuntimeOverrides } from "../../utils/runtime-i18n-config.js";
7
- function parseAcceptLanguage(header) {
8
- if (!header) return [];
9
- return header.split(",").map((entry) => {
10
- const [lang] = entry.split(";");
11
- return (lang ?? "").trim();
12
- }).filter((s) => s.length > 0);
13
- }
14
8
  export default defineEventHandler(async (event) => {
15
9
  const path = event.path || getRequestURL(event).pathname;
16
10
  if (path.startsWith("/api") || path.startsWith("/_nuxt") || path.startsWith("/_locales") || path.startsWith("/__")) return;
@@ -24,31 +18,19 @@ export default defineEventHandler(async (event) => {
24
18
  const pathSegments = path.split("/").filter(Boolean);
25
19
  const firstSegment = pathSegments[0];
26
20
  const hasLocaleInUrl = Boolean(firstSegment && validLocales.includes(firstSegment));
27
- let locale = defaultLocale;
28
- if (hasLocaleInUrl) {
29
- locale = firstSegment;
30
- } else if (getQuery(event).locale && validLocales.includes(String(getQuery(event).locale))) {
31
- locale = String(getQuery(event).locale);
32
- } else {
33
- const cookieName = getLocaleCookieName(config);
34
- if (cookieName) {
35
- const cookieVal = getCookie(event, cookieName);
36
- if (cookieVal && validLocales.includes(cookieVal)) locale = cookieVal;
37
- }
38
- }
39
- if (config.autoDetectLanguage && locale === defaultLocale) {
40
- const acceptHeader = getHeader(event, "accept-language");
41
- const langs = parseAcceptLanguage(acceptHeader);
42
- for (const lang of langs) {
43
- const lowerCaseLanguage = lang.toLowerCase();
44
- const primaryLanguage = lowerCaseLanguage.split("-")[0];
45
- const found = validLocales.find((l) => l.toLowerCase() === lowerCaseLanguage || l.toLowerCase() === primaryLanguage);
46
- if (found) {
47
- locale = found;
48
- break;
49
- }
50
- }
51
- }
21
+ const cookieName = getLocaleCookieName(config);
22
+ const queryLocale = getQuery(event).locale ? String(getQuery(event).locale) : null;
23
+ const cookieLocale = cookieName ? getCookie(event, cookieName) : null;
24
+ const locale = resolveServerLocale({
25
+ defaultLocale,
26
+ validLocales,
27
+ autoDetectLanguage: config.autoDetectLanguage,
28
+ hasLocaleInUrl,
29
+ urlLocale: firstSegment ?? null,
30
+ queryLocale,
31
+ cookieLocale,
32
+ acceptLanguageHeader: getHeader(event, "accept-language")
33
+ });
52
34
  event.context.i18n = {
53
35
  locale,
54
36
  translations: {}