nuxt-i18n-micro 3.17.0 → 3.18.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
- <!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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.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/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.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/CXlkzKTu.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:"04b81291-b744-424c-949f-38edb2f65714",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1775476313013,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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.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/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.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/CXlkzKTu.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:"bdfcba12-33f4-4120-bce9-f186efc3683a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1778844297940,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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.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/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.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/CXlkzKTu.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:"04b81291-b744-424c-949f-38edb2f65714",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1775476313014,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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.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/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.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/CXlkzKTu.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:"bdfcba12-33f4-4120-bce9-f186efc3683a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1778844297940,false]</script></body></html>
@@ -1 +1 @@
1
- {"id":"04b81291-b744-424c-949f-38edb2f65714","timestamp":1775476310207}
1
+ {"id":"bdfcba12-33f4-4120-bce9-f186efc3683a","timestamp":1778844295058}
@@ -0,0 +1 @@
1
+ {"id":"bdfcba12-33f4-4120-bce9-f186efc3683a","timestamp":1778844295058,"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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.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/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.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/CXlkzKTu.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:"04b81291-b744-424c-949f-38edb2f65714",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1775476313014,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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.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/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.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/CXlkzKTu.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:"bdfcba12-33f4-4120-bce9-f186efc3683a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1778844297940,false]</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": "3.17.0",
4
+ "version": "3.18.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -4,7 +4,7 @@ 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 { RouteGenerator, normalizePath, isInternalPath } from '@i18n-micro/route-strategy';
7
+ import { RouteGenerator, isLocaleAllowedForUnlocalizedRoute, normalizePath, isInternalPath } from '@i18n-micro/route-strategy';
8
8
  import { useNuxt, defineNuxtModule, useLogger, createResolver, addTemplate, addImportsDir, addPlugin, addServerHandler, addComponentsDir, addTypeTemplate, addPrerenderRoutes } from '@nuxt/kit';
9
9
  import { globby } from 'globby';
10
10
  import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
@@ -451,7 +451,7 @@ const module = defineNuxtModule({
451
451
  await preMergeLocales(rootDirs, translationDirName, mergedLocalesDir, localeInfos, options.fallbackLocale, options.disablePageLocales);
452
452
  logger.info(`Pre-merged translations from ${rootDirs.length} layer(s) into ${mergedLocalesDir}`);
453
453
  });
454
- const routeLocales = {};
454
+ const routeLocales = { ...options.routeLocales ?? {} };
455
455
  const globalLocaleRoutes = {};
456
456
  const routeDisableMeta = {};
457
457
  const pageFiles = await globby(["pages/**/*.vue", "app/pages/**/*.vue"], { cwd: nuxt.options.rootDir });
@@ -562,8 +562,6 @@ const module = defineNuxtModule({
562
562
  cacheMaxSize: options.cacheMaxSize ?? 0,
563
563
  cacheTtl: options.cacheTtl ?? 0
564
564
  };
565
- nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
566
- nuxt.options.runtimeConfig.public.i18nConfig = fullConfig;
567
565
  const fullConfigJson = JSON.stringify(fullConfig);
568
566
  const strategyTemplate = addTemplate({
569
567
  filename: "i18n.strategy.mjs",
@@ -811,6 +809,9 @@ declare module '#i18n-internal/plural' {
811
809
  if (originalPath.startsWith("/api")) continue;
812
810
  routeGenerator.locales.forEach((localeObj) => {
813
811
  const localeCode = localeObj.code;
812
+ if (!isLocaleAllowedForUnlocalizedRoute(routeGenerator.routeLocales, routeGenerator.locales, originalPath, localeCode)) {
813
+ return;
814
+ }
814
815
  const localizedPath = routeGenerator.resolveLocalizedPath(originalPath, localeCode);
815
816
  if (localizedPath === originalPath || localizedPath === normalizePath(originalPath)) {
816
817
  return;
@@ -860,6 +861,7 @@ declare module '#i18n-internal/plural' {
860
861
  const routesSet = prerenderRoutes.routes;
861
862
  const routeRules = nuxt.options.routeRules || {};
862
863
  const additionalRoutes = /* @__PURE__ */ new Set();
864
+ const localeCodes = new Set(routeGenerator.locales.map((locale) => locale.code));
863
865
  for (const route of routesSet) {
864
866
  if (isInternalPath(route, options.excludePatterns)) {
865
867
  routesSet.delete(route);
@@ -872,7 +874,14 @@ declare module '#i18n-internal/plural' {
872
874
  if (routeRules[route]?.prerender === false) {
873
875
  continue;
874
876
  }
877
+ const firstSegment = route.replace(/^\//, "").split("/")[0];
878
+ if (firstSegment && localeCodes.has(firstSegment)) {
879
+ continue;
880
+ }
875
881
  for (const locale of routeGenerator.locales) {
882
+ if (!isLocaleAllowedForUnlocalizedRoute(routeGenerator.routeLocales, routeGenerator.locales, route, locale.code)) {
883
+ continue;
884
+ }
876
885
  const localizedRoute = routeGenerator.resolveLocalizedPath(route, locale.code);
877
886
  if (localizedRoute === route) {
878
887
  continue;
@@ -890,7 +899,6 @@ declare module '#i18n-internal/plural' {
890
899
  routesSet.add(route);
891
900
  }
892
901
  if (withPrefixStrategy(options.strategy)) {
893
- const localeCodes = new Set(routeGenerator.locales.map((l) => l.code));
894
902
  const deleted = [];
895
903
  for (const route of routesSet) {
896
904
  if (route === "/" || route === "") continue;
@@ -1,10 +1,13 @@
1
- import { useState } from "#app";
1
+ import { useNuxtApp, useState } from "#app";
2
2
  import { getI18nConfig } from "#build/i18n.strategy.mjs";
3
3
  import { useCookie } from "#imports";
4
+ import { getEnabledLocaleCodes } from "../utils/active-locales.js";
4
5
  import { getHashCookieName, getLocaleCookieName, getLocaleCookieOptions } from "../utils/cookie.js";
6
+ import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
5
7
  export function useI18nLocale() {
6
- const i18nConfig = getI18nConfig();
7
- const validLocales = i18nConfig.locales?.map((l) => l.code) || [];
8
+ const nuxtApp = useNuxtApp();
9
+ const i18nConfig = resolveI18nConfigWithRuntimeOverrides(nuxtApp.$getI18nConfig?.() ?? getI18nConfig());
10
+ const validLocales = getEnabledLocaleCodes(i18nConfig.locales);
8
11
  const localeState = useState("i18n-locale", () => null);
9
12
  const localeCookieName = getLocaleCookieName(i18nConfig);
10
13
  const hashCookieName = getHashCookieName(i18nConfig);
@@ -2,35 +2,7 @@ import type { PathStrategy } from '@i18n-micro/path-strategy';
2
2
  import type { CleanTranslation, I18nRouteParams, Locale, MissingHandler, ModuleOptionsExtend, Params, Translations } from '@i18n-micro/types';
3
3
  import type { RouteLocationNamedRaw, RouteLocationNormalizedLoaded, RouteLocationRaw, RouteLocationResolved, RouteLocationResolvedGeneric } from 'vue-router';
4
4
  declare const _default: import("nuxt/app").Plugin<{
5
- getI18nConfig: () => {
6
- locales: never[];
7
- metaTrustForwardedHost: boolean;
8
- metaTrustForwardedProto: boolean;
9
- defaultLocale: string;
10
- localeCookie: null;
11
- autoDetectLanguage: boolean;
12
- autoDetectPath: string;
13
- strategy: string;
14
- dateBuild: number;
15
- hashMode: boolean;
16
- apiBaseUrl: string;
17
- isSSG: boolean;
18
- disablePageLocales: boolean;
19
- canonicalQueryWhitelist: string[];
20
- excludePatterns: never[];
21
- routeLocales: {};
22
- routeDisableMeta: {};
23
- globalLocaleRoutes: {};
24
- missingWarn: boolean;
25
- redirects: boolean;
26
- hmr: boolean;
27
- localizedRouteNamePrefix: string;
28
- routesLocaleLinks: {};
29
- noPrefixRedirect: boolean;
30
- debug: boolean;
31
- cacheMaxSize: number;
32
- cacheTtl: number;
33
- };
5
+ getI18nConfig: () => ModuleOptionsExtend;
34
6
  i18n: undefined;
35
7
  __micro: boolean;
36
8
  helper: {
@@ -63,35 +35,7 @@ declare const _default: import("nuxt/app").Plugin<{
63
35
  loadPageTranslations: (locale: string, routeName: string, translations: Translations) => Promise<void>;
64
36
  setMissingHandler: (handler: MissingHandler | null) => void;
65
37
  }> & import("nuxt/app").ObjectPlugin<{
66
- getI18nConfig: () => {
67
- locales: never[];
68
- metaTrustForwardedHost: boolean;
69
- metaTrustForwardedProto: boolean;
70
- defaultLocale: string;
71
- localeCookie: null;
72
- autoDetectLanguage: boolean;
73
- autoDetectPath: string;
74
- strategy: string;
75
- dateBuild: number;
76
- hashMode: boolean;
77
- apiBaseUrl: string;
78
- isSSG: boolean;
79
- disablePageLocales: boolean;
80
- canonicalQueryWhitelist: string[];
81
- excludePatterns: never[];
82
- routeLocales: {};
83
- routeDisableMeta: {};
84
- globalLocaleRoutes: {};
85
- missingWarn: boolean;
86
- redirects: boolean;
87
- hmr: boolean;
88
- localizedRouteNamePrefix: string;
89
- routesLocaleLinks: {};
90
- noPrefixRedirect: boolean;
91
- debug: boolean;
92
- cacheMaxSize: number;
93
- cacheTtl: number;
94
- };
38
+ getI18nConfig: () => ModuleOptionsExtend;
95
39
  i18n: undefined;
96
40
  __micro: boolean;
97
41
  helper: {
@@ -6,6 +6,7 @@ import { createI18nStrategy, getI18nConfig } from "#build/i18n.strategy.mjs";
6
6
  import { createError, defineNuxtPlugin, navigateTo, useHead, useRouter, useRuntimeConfig } from "#imports";
7
7
  import { useI18nLocale } from "../composables/useI18nLocale.js";
8
8
  import { deepMergeTranslations } from "../utils/deep-merge.js";
9
+ import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
9
10
  import { translationStorage } from "../utils/storage.js";
10
11
  const isDev = process.env.NODE_ENV !== "production";
11
12
  const RE_TOKEN = /\{(\w+)\}/g;
@@ -22,8 +23,11 @@ function getByPath(obj, path) {
22
23
  export default defineNuxtPlugin(async (nuxtApp) => {
23
24
  const router = useRouter();
24
25
  const i18nStrategy = createI18nStrategy(router);
25
- const i18nConfig = getI18nConfig();
26
26
  const runtimeConfig = useRuntimeConfig();
27
+ const i18nConfig = resolveI18nConfigWithRuntimeOverrides(
28
+ getI18nConfig(),
29
+ runtimeConfig.public
30
+ );
27
31
  translationStorage.configure({
28
32
  maxSize: i18nConfig.cacheMaxSize ?? 0,
29
33
  ttl: i18nConfig.cacheTtl ?? 0
@@ -379,7 +383,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
379
383
  return {
380
384
  provide: {
381
385
  ...provideData,
382
- getI18nConfig: () => getI18nConfig()
386
+ getI18nConfig: () => i18nConfig
383
387
  }
384
388
  };
385
389
  });
@@ -3,9 +3,13 @@ import { getI18nConfig } from "#build/i18n.strategy.mjs";
3
3
  import { defineNuxtPlugin, useHead, useRequestURL, useRoute } from "#imports";
4
4
  import { useLocaleHead } from "../composables/useLocaleHead.js";
5
5
  import { isMetaDisabledForRoute } from "../utils/route-utils.js";
6
+ import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
6
7
  export default defineNuxtPlugin((nuxtApp) => {
7
8
  const route = useRoute();
8
- const i18nConfig = getI18nConfig();
9
+ const getRuntimeConfig = nuxtApp.$getI18nConfig;
10
+ const i18nConfig = resolveI18nConfigWithRuntimeOverrides(
11
+ typeof getRuntimeConfig === "function" ? getRuntimeConfig() : getI18nConfig()
12
+ );
9
13
  const currentLocale = nuxtApp.$getLocale?.();
10
14
  if (isMetaDisabledForRoute(route, i18nConfig.routeDisableMeta, currentLocale)) {
11
15
  return;
@@ -1,9 +1,13 @@
1
1
  import { isNoPrefixStrategy } from "@i18n-micro/core";
2
2
  import { getI18nConfig } from "#build/i18n.strategy.mjs";
3
3
  import { defineNuxtPlugin, useNuxtApp, useRouter } from "#imports";
4
+ import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
4
5
  const isDev = process.env.NODE_ENV !== "production";
5
6
  export default defineNuxtPlugin(async (nuxtApp) => {
6
- const i18nConfig = getI18nConfig();
7
+ const getRuntimeConfig = nuxtApp.$getI18nConfig;
8
+ const i18nConfig = resolveI18nConfigWithRuntimeOverrides(
9
+ typeof getRuntimeConfig === "function" ? getRuntimeConfig() : getI18nConfig()
10
+ );
7
11
  const router = useRouter();
8
12
  const { $getLocale, $getRouteName } = useNuxtApp();
9
13
  const i18nHelper = nuxtApp.$i18n.helper;
@@ -2,7 +2,9 @@ import { getCookie, getHeader, getRequestURL, setCookie } from "h3";
2
2
  import { createI18nStrategy, getI18nConfig } from "#build/i18n.strategy.mjs";
3
3
  import { createError, defineNuxtPlugin, navigateTo, useRequestEvent, useRoute, useRouter, useState } from "#imports";
4
4
  import { useI18nLocale } from "../composables/useI18nLocale.js";
5
+ import { getEnabledLocaleCodes } from "../utils/active-locales.js";
5
6
  import { getLocaleCookieName, getLocaleCookieOptions } from "../utils/cookie.js";
7
+ import { resolveI18nConfigWithRuntimeOverrides } from "../utils/runtime-i18n-config.js";
6
8
  const DEBUG = process.env.NUXT_I18N_DEBUG_REDIRECT === "1";
7
9
  const DEFAULT_STATIC_PATTERNS = [
8
10
  /^\/sitemap.*\.xml$/,
@@ -53,8 +55,11 @@ export default defineNuxtPlugin({
53
55
  setup(nuxtApp) {
54
56
  const router = useRouter();
55
57
  const i18nStrategy = createI18nStrategy(router);
56
- const i18nConfig = getI18nConfig();
57
- const validLocales = i18nConfig.locales?.map((l) => l.code) || [];
58
+ const getRuntimeConfig = nuxtApp.$getI18nConfig;
59
+ const i18nConfig = resolveI18nConfigWithRuntimeOverrides(
60
+ typeof getRuntimeConfig === "function" ? getRuntimeConfig() : getI18nConfig()
61
+ );
62
+ const validLocales = getEnabledLocaleCodes(i18nConfig.locales);
58
63
  const defaultLocale = i18nConfig.defaultLocale || "en";
59
64
  const autoDetectPath = i18nConfig.autoDetectPath || "/";
60
65
  const cookieName = getLocaleCookieName(i18nConfig);
@@ -1,6 +1,9 @@
1
1
  import { defineEventHandler, getCookie, getHeader, getQuery, getRequestURL } from "h3";
2
2
  import { getI18nConfig } from "#i18n-internal/strategy";
3
+ import { useRuntimeConfig } from "#imports";
4
+ import { getEnabledLocaleCodes } from "../../utils/active-locales.js";
3
5
  import { getLocaleCookieName } from "../../utils/cookie.js";
6
+ import { resolveI18nConfigWithRuntimeOverrides } from "../../utils/runtime-i18n-config.js";
4
7
  function parseAcceptLanguage(header) {
5
8
  if (!header) return [];
6
9
  return header.split(",").map((entry) => {
@@ -12,8 +15,11 @@ export default defineEventHandler(async (event) => {
12
15
  const path = event.path || getRequestURL(event).pathname;
13
16
  if (path.startsWith("/api") || path.startsWith("/_nuxt") || path.startsWith("/_locales") || path.startsWith("/__")) return;
14
17
  if (path.includes(".") && !path.endsWith(".html")) return;
15
- const config = getI18nConfig();
16
- const validLocales = config.locales?.map((l) => l.code) || [];
18
+ const config = resolveI18nConfigWithRuntimeOverrides(
19
+ getI18nConfig(),
20
+ useRuntimeConfig(event).public
21
+ );
22
+ const validLocales = getEnabledLocaleCodes(config.locales);
17
23
  const defaultLocale = config.defaultLocale || "en";
18
24
  const pathSegments = path.split("/").filter(Boolean);
19
25
  const firstSegment = pathSegments[0];
@@ -1,5 +1,8 @@
1
1
  import { createError, defineEventHandler, getRouterParam, send, setResponseHeader } from "h3";
2
2
  import { getI18nConfig } from "#i18n-internal/strategy";
3
+ import { useRuntimeConfig } from "#imports";
4
+ import { isEnabledLocale } from "../../utils/active-locales.js";
5
+ import { resolveI18nConfigWithRuntimeOverrides } from "../../utils/runtime-i18n-config.js";
3
6
  import { loadTranslationsFromServer } from "../utils/server-loader.js";
4
7
  export default defineEventHandler(async (event) => {
5
8
  const page = getRouterParam(event, "page");
@@ -7,8 +10,11 @@ export default defineEventHandler(async (event) => {
7
10
  if (!locale || !page) {
8
11
  throw createError({ statusCode: 400, statusMessage: "Missing locale or page" });
9
12
  }
10
- const config = getI18nConfig();
11
- if (!config.locales?.find((l) => l.code === locale)) {
13
+ const config = resolveI18nConfigWithRuntimeOverrides(
14
+ getI18nConfig(),
15
+ useRuntimeConfig(event).public
16
+ );
17
+ if (!isEnabledLocale(config.locales, locale)) {
12
18
  throw createError({ statusCode: 404, statusMessage: "Locale not found" });
13
19
  }
14
20
  const { json } = await loadTranslationsFromServer(locale, page);
@@ -1,18 +1,26 @@
1
1
  import { getI18nConfig } from "#i18n-internal/strategy";
2
+ import { useRuntimeConfig } from "#imports";
3
+ import { getEnabledLocaleCodes, getEnabledLocales } from "../../utils/active-locales.js";
4
+ import { resolveI18nConfigWithRuntimeOverrides } from "../../utils/runtime-i18n-config.js";
2
5
  import { detectCurrentLocale } from "./locale-detector.js";
3
6
  export const useLocaleServerMiddleware = (event, defaultLocale, currentLocale) => {
4
- const { locales, defaultLocale: configDefaultLocale, fallbackLocale } = getI18nConfig();
7
+ const {
8
+ locales,
9
+ defaultLocale: configDefaultLocale,
10
+ fallbackLocale
11
+ } = resolveI18nConfigWithRuntimeOverrides(getI18nConfig(), useRuntimeConfig(event).public);
12
+ const enabledLocales = getEnabledLocales(locales);
5
13
  const detectedLocale = currentLocale || detectCurrentLocale(
6
14
  event,
7
15
  {
8
16
  fallbackLocale,
9
17
  defaultLocale: defaultLocale || configDefaultLocale,
10
- locales
18
+ locales: enabledLocales
11
19
  },
12
20
  defaultLocale
13
21
  );
14
- const localeConfig = locales?.find((l) => l.code === detectedLocale) ?? null;
15
- const availableLocales = locales?.map((l) => l.code) ?? [];
22
+ const localeConfig = enabledLocales.find((l) => l.code === detectedLocale) ?? null;
23
+ const availableLocales = getEnabledLocaleCodes(locales);
16
24
  const isDefault = detectedLocale === (defaultLocale || configDefaultLocale || "en");
17
25
  const isFallback = detectedLocale === (fallbackLocale || defaultLocale || configDefaultLocale || "en");
18
26
  return {
@@ -1,11 +1,13 @@
1
1
  import { useStorage } from "nitropack/runtime";
2
2
  import { getI18nConfig } from "#i18n-internal/strategy";
3
+ import { isEnabledLocale } from "../../utils/active-locales.js";
3
4
  import { CacheControl } from "../../utils/cache-control.js";
5
+ import { resolveI18nConfigWithRuntimeOverrides } from "../../utils/runtime-i18n-config.js";
4
6
  const CC_KEY = Symbol.for("__NUXT_I18N_SERVER_CACHE_CC__");
5
7
  function getServerCacheControl() {
6
8
  const g = globalThis;
7
9
  if (!g[CC_KEY]) {
8
- const cfg = getI18nConfig();
10
+ const cfg = resolveI18nConfigWithRuntimeOverrides(getI18nConfig());
9
11
  g[CC_KEY] = new CacheControl({ maxSize: cfg.cacheMaxSize ?? 0, ttl: cfg.cacheTtl ?? 0 });
10
12
  }
11
13
  return g[CC_KEY];
@@ -25,8 +27,8 @@ export async function loadTranslationsFromServer(locale, routeName) {
25
27
  if (cached) {
26
28
  return cached;
27
29
  }
28
- const config = getI18nConfig();
29
- if (!config.locales?.find((l) => l.code === locale)) {
30
+ const config = resolveI18nConfigWithRuntimeOverrides(getI18nConfig());
31
+ if (!isEnabledLocale(config.locales, locale)) {
30
32
  const empty = { data: {}, json: "{}" };
31
33
  cc.set(cacheKey, empty);
32
34
  return empty;
@@ -1,16 +1,23 @@
1
1
  import { interpolate } from "@i18n-micro/core";
2
2
  import { getI18nConfig } from "#i18n-internal/strategy";
3
+ import { useRuntimeConfig } from "#imports";
4
+ import { getEnabledLocales } from "../../utils/active-locales.js";
5
+ import { resolveI18nConfigWithRuntimeOverrides } from "../../utils/runtime-i18n-config.js";
3
6
  import { detectCurrentLocale } from "./locale-detector.js";
4
7
  import { loadTranslationsFromServer } from "./server-loader.js";
5
8
  const I18N_CONTEXT_KEY = "__i18n_translations__";
6
9
  export const useTranslationServerMiddleware = async (event, defaultLocale, currentLocale) => {
7
- const { locales, fallbackLocale, defaultLocale: configDefaultLocale } = getI18nConfig();
10
+ const {
11
+ locales,
12
+ fallbackLocale,
13
+ defaultLocale: configDefaultLocale
14
+ } = resolveI18nConfigWithRuntimeOverrides(getI18nConfig(), useRuntimeConfig(event).public);
8
15
  const locale = currentLocale || event.context.i18n?.locale || detectCurrentLocale(
9
16
  event,
10
17
  {
11
18
  fallbackLocale,
12
19
  defaultLocale: defaultLocale || configDefaultLocale,
13
- locales
20
+ locales: getEnabledLocales(locales)
14
21
  },
15
22
  defaultLocale
16
23
  );
@@ -0,0 +1,4 @@
1
+ import type { Locale } from '@i18n-micro/types';
2
+ export declare function getEnabledLocales(locales?: Locale[] | null): Locale[];
3
+ export declare function getEnabledLocaleCodes(locales?: Locale[] | null): string[];
4
+ export declare function isEnabledLocale(locales: Locale[] | null | undefined, code: string): boolean;
@@ -0,0 +1,9 @@
1
+ export function getEnabledLocales(locales) {
2
+ return (locales ?? []).filter((locale) => !locale.disabled);
3
+ }
4
+ export function getEnabledLocaleCodes(locales) {
5
+ return getEnabledLocales(locales).map((locale) => locale.code);
6
+ }
7
+ export function isEnabledLocale(locales, code) {
8
+ return getEnabledLocales(locales).some((locale) => locale.code === code);
9
+ }
@@ -0,0 +1,8 @@
1
+ import type { ModuleOptionsExtend } from '@i18n-micro/types';
2
+ export interface RuntimeI18nOverrides {
3
+ defaultLocale?: string;
4
+ fallbackLocale?: string;
5
+ disabledLocales?: string[];
6
+ strategy?: string;
7
+ }
8
+ export declare function resolveI18nConfigWithRuntimeOverrides(baseConfig: ModuleOptionsExtend, runtimePublic?: Record<string, unknown>, warn?: (message: string) => void): ModuleOptionsExtend;
@@ -0,0 +1,90 @@
1
+ function toNonEmptyString(value) {
2
+ if (typeof value !== "string") return void 0;
3
+ const normalized = value.trim();
4
+ return normalized.length > 0 ? normalized : void 0;
5
+ }
6
+ function parseLocalesList(value) {
7
+ if (Array.isArray(value)) {
8
+ const items = value.map((entry) => toNonEmptyString(entry)).filter((entry) => Boolean(entry));
9
+ return items.length > 0 ? items : void 0;
10
+ }
11
+ if (typeof value === "string") {
12
+ const items = value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
13
+ return items.length > 0 ? items : void 0;
14
+ }
15
+ return void 0;
16
+ }
17
+ function readEnvOverrides() {
18
+ return {
19
+ defaultLocale: toNonEmptyString(process.env.NUXT_I18N_DEFAULT_LOCALE) ?? toNonEmptyString(process.env.NUXT_PUBLIC_I18N_RUNTIME_DEFAULT_LOCALE),
20
+ fallbackLocale: toNonEmptyString(process.env.NUXT_I18N_FALLBACK_LOCALE) ?? toNonEmptyString(process.env.NUXT_PUBLIC_I18N_RUNTIME_FALLBACK_LOCALE),
21
+ disabledLocales: parseLocalesList(process.env.NUXT_I18N_DISABLED_LOCALES) ?? parseLocalesList(process.env.NUXT_PUBLIC_I18N_RUNTIME_DISABLED_LOCALES),
22
+ strategy: toNonEmptyString(process.env.NUXT_I18N_STRATEGY) ?? toNonEmptyString(process.env.NUXT_PUBLIC_I18N_RUNTIME_STRATEGY)
23
+ };
24
+ }
25
+ function readRuntimeOverrides(runtimePublic) {
26
+ const raw = runtimePublic?.i18nRuntime;
27
+ if (!raw || typeof raw !== "object") return {};
28
+ const runtime = raw;
29
+ return {
30
+ defaultLocale: toNonEmptyString(runtime.defaultLocale),
31
+ fallbackLocale: toNonEmptyString(runtime.fallbackLocale),
32
+ disabledLocales: parseLocalesList(runtime.disabledLocales),
33
+ strategy: toNonEmptyString(runtime.strategy)
34
+ };
35
+ }
36
+ function mergeOverrides(runtimePublic) {
37
+ const env = readEnvOverrides();
38
+ const runtime = readRuntimeOverrides(runtimePublic);
39
+ return {
40
+ defaultLocale: runtime.defaultLocale ?? env.defaultLocale,
41
+ fallbackLocale: runtime.fallbackLocale ?? env.fallbackLocale,
42
+ disabledLocales: runtime.disabledLocales ?? env.disabledLocales,
43
+ strategy: runtime.strategy ?? env.strategy
44
+ };
45
+ }
46
+ export function resolveI18nConfigWithRuntimeOverrides(baseConfig, runtimePublic, warn = (message) => console.warn(message)) {
47
+ const overrides = mergeOverrides(runtimePublic);
48
+ const locales = (baseConfig.locales ?? []).map((locale) => ({ ...locale }));
49
+ if (overrides.strategy && overrides.strategy !== baseConfig.strategy) {
50
+ warn(
51
+ `[nuxt-i18n-micro] runtime i18n strategy override is ignored: "${overrides.strategy}" (build strategy: "${baseConfig.strategy}"). Build a separate artifact for each strategy.`
52
+ );
53
+ }
54
+ if (overrides.disabledLocales && overrides.disabledLocales.length > 0) {
55
+ const disabledSet = new Set(overrides.disabledLocales);
56
+ for (const locale of locales) {
57
+ locale.disabled = disabledSet.has(locale.code);
58
+ }
59
+ }
60
+ const enabledLocales = locales.filter((locale) => !locale.disabled);
61
+ if (enabledLocales.length === 0) {
62
+ if (overrides.disabledLocales && overrides.disabledLocales.length > 0) {
63
+ warn("[nuxt-i18n-micro] runtime disabledLocales override would disable all locales; override ignored.");
64
+ }
65
+ return { ...baseConfig, locales: (baseConfig.locales ?? []).map((locale) => ({ ...locale })) };
66
+ }
67
+ const allLocaleCodes = new Set(locales.map((locale) => locale.code));
68
+ let defaultLocale = overrides.defaultLocale ?? baseConfig.defaultLocale;
69
+ let fallbackLocale = overrides.fallbackLocale ?? baseConfig.fallbackLocale;
70
+ if (fallbackLocale && !allLocaleCodes.has(fallbackLocale)) {
71
+ warn(`[nuxt-i18n-micro] runtime fallbackLocale "${fallbackLocale}" is not defined in locales; override ignored.`);
72
+ fallbackLocale = baseConfig.fallbackLocale;
73
+ }
74
+ const enabledLocaleCodes = new Set(enabledLocales.map((locale) => locale.code));
75
+ const firstEnabledLocale = enabledLocales[0]?.code ?? baseConfig.defaultLocale ?? "en";
76
+ if (!defaultLocale || !enabledLocaleCodes.has(defaultLocale)) {
77
+ if (overrides.defaultLocale) {
78
+ warn(`[nuxt-i18n-micro] runtime defaultLocale "${overrides.defaultLocale}" is not enabled; falling back to "${firstEnabledLocale}".`);
79
+ } else if (defaultLocale && !enabledLocaleCodes.has(defaultLocale)) {
80
+ warn(`[nuxt-i18n-micro] defaultLocale "${defaultLocale}" is disabled by runtime overrides; falling back to "${firstEnabledLocale}".`);
81
+ }
82
+ defaultLocale = firstEnabledLocale;
83
+ }
84
+ return {
85
+ ...baseConfig,
86
+ locales,
87
+ defaultLocale,
88
+ fallbackLocale
89
+ };
90
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-i18n-micro",
3
- "version": "3.17.0",
3
+ "version": "3.18.0",
4
4
  "description": "Nuxt I18n Micro is a lightweight, high-performance internationalization module for Nuxt, designed to handle multi-language support with minimal overhead, fast build times, and efficient runtime performance.",
5
5
  "repository": "s00d/nuxt-i18n-micro",
6
6
  "license": "MIT",
@@ -76,9 +76,9 @@
76
76
  "ufo": "^1.5.4",
77
77
  "@i18n-micro/core": "1.3.0",
78
78
  "@i18n-micro/path-strategy": "1.3.0",
79
- "@i18n-micro/route-strategy": "1.1.5",
80
- "@i18n-micro/test-utils": "1.2.0",
81
- "@i18n-micro/types": "1.2.1"
79
+ "@i18n-micro/route-strategy": "1.1.6",
80
+ "@i18n-micro/types": "1.2.1",
81
+ "@i18n-micro/test-utils": "1.2.0"
82
82
  },
83
83
  "devDependencies": {
84
84
  "@biomejs/biome": "^2.3.14",
@@ -120,8 +120,10 @@
120
120
  "dev:build": "nuxi build playground",
121
121
  "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
122
122
  "dev:generate": "nuxi generate playground",
123
- "release": "pnpm run format && pnpm run lint && pnpm run typecheck && pnpm run test:types && pnpm run test && pnpm run test:vitest && pnpm run test:workspaces && pnpm run prepack && changelogen --release && pnpm publish -r && git push --follow-tags",
124
- "release:lite": "pnpm run prepack && changelogen --release && pnpm publish -r && git push --follow-tags",
123
+ "release": "pnpm run format && pnpm run lint && pnpm run typecheck && pnpm run test:types && pnpm run test && pnpm run test:vitest && pnpm run test:workspaces && pnpm run prepack && node scripts/run-changelogen-release.mjs auto && pnpm publish -r && git push --follow-tags",
124
+ "release:lite": "pnpm run prepack && node scripts/run-changelogen-release.mjs patch && pnpm publish -r && git push --follow-tags",
125
+ "release:patch": "pnpm run prepack && node scripts/run-changelogen-release.mjs patch && pnpm publish -r && git push --follow-tags",
126
+ "release:minor": "pnpm run prepack && node scripts/run-changelogen-release.mjs minor && pnpm publish -r && git push --follow-tags",
125
127
  "lint": "biome check .",
126
128
  "lint:fix": "biome check --write .",
127
129
  "format": "biome format --write .",
@@ -1 +0,0 @@
1
- {"id":"04b81291-b744-424c-949f-38edb2f65714","timestamp":1775476310207,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}