nuxt-i18n-micro 3.17.5 → 3.18.1

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:"27df88ba-c559-40f5-a972-a7bb2444b9fc",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777382530223,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:"e5c47a47-2baa-48ed-a966-2bd443ccf75f",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1779651295704,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:"27df88ba-c559-40f5-a972-a7bb2444b9fc",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777382530223,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:"e5c47a47-2baa-48ed-a966-2bd443ccf75f",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1779651295704,false]</script></body></html>
@@ -1 +1 @@
1
- {"id":"27df88ba-c559-40f5-a972-a7bb2444b9fc","timestamp":1777382527247}
1
+ {"id":"e5c47a47-2baa-48ed-a966-2bd443ccf75f","timestamp":1779651292725}
@@ -0,0 +1 @@
1
+ {"id":"e5c47a47-2baa-48ed-a966-2bd443ccf75f","timestamp":1779651292725,"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:"27df88ba-c559-40f5-a972-a7bb2444b9fc",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777382530223,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:"e5c47a47-2baa-48ed-a966-2bd443ccf75f",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1779651295704,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.5",
4
+ "version": "3.18.1",
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';
@@ -158,6 +158,15 @@ ${accepts}
158
158
  `.trim();
159
159
  }
160
160
 
161
+ function shouldLocalizeRouteRulePath(originalPath) {
162
+ const routeRulePath = originalPath.trim();
163
+ if (!routeRulePath) return true;
164
+ const path = routeRulePath.startsWith("/") ? routeRulePath : `/${routeRulePath}`;
165
+ if (path === "/api" || path.startsWith("/api/")) return false;
166
+ const firstSegment = path.replace(/^\/+/, "").split("/")[0] ?? "";
167
+ return !firstSegment.startsWith("_");
168
+ }
169
+
161
170
  function extractScriptContent(content) {
162
171
  const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/);
163
172
  return scriptMatch?.[1] ? scriptMatch[1] : null;
@@ -451,7 +460,7 @@ const module = defineNuxtModule({
451
460
  await preMergeLocales(rootDirs, translationDirName, mergedLocalesDir, localeInfos, options.fallbackLocale, options.disablePageLocales);
452
461
  logger.info(`Pre-merged translations from ${rootDirs.length} layer(s) into ${mergedLocalesDir}`);
453
462
  });
454
- const routeLocales = {};
463
+ const routeLocales = { ...options.routeLocales ?? {} };
455
464
  const globalLocaleRoutes = {};
456
465
  const routeDisableMeta = {};
457
466
  const pageFiles = await globby(["pages/**/*.vue", "app/pages/**/*.vue"], { cwd: nuxt.options.rootDir });
@@ -562,8 +571,6 @@ const module = defineNuxtModule({
562
571
  cacheMaxSize: options.cacheMaxSize ?? 0,
563
572
  cacheTtl: options.cacheTtl ?? 0
564
573
  };
565
- nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
566
- nuxt.options.runtimeConfig.public.i18nConfig = fullConfig;
567
574
  const fullConfigJson = JSON.stringify(fullConfig);
568
575
  const strategyTemplate = addTemplate({
569
576
  filename: "i18n.strategy.mjs",
@@ -808,9 +815,12 @@ declare module '#i18n-internal/plural' {
808
815
  if (routeRules && Object.keys(routeRules).length && !isNoPrefixStrategy(strategy)) {
809
816
  nitroConfig.routeRules = nitroConfig.routeRules || {};
810
817
  for (const [originalPath, ruleValue] of Object.entries(routeRules)) {
811
- if (originalPath.startsWith("/api")) continue;
818
+ if (!shouldLocalizeRouteRulePath(originalPath)) continue;
812
819
  routeGenerator.locales.forEach((localeObj) => {
813
820
  const localeCode = localeObj.code;
821
+ if (!isLocaleAllowedForUnlocalizedRoute(routeGenerator.routeLocales, routeGenerator.locales, originalPath, localeCode)) {
822
+ return;
823
+ }
814
824
  const localizedPath = routeGenerator.resolveLocalizedPath(originalPath, localeCode);
815
825
  if (localizedPath === originalPath || localizedPath === normalizePath(originalPath)) {
816
826
  return;
@@ -878,6 +888,9 @@ declare module '#i18n-internal/plural' {
878
888
  continue;
879
889
  }
880
890
  for (const locale of routeGenerator.locales) {
891
+ if (!isLocaleAllowedForUnlocalizedRoute(routeGenerator.routeLocales, routeGenerator.locales, route, locale.code)) {
892
+ continue;
893
+ }
881
894
  const localizedRoute = routeGenerator.resolveLocalizedPath(route, locale.code);
882
895
  if (localizedRoute === route) {
883
896
  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.5",
3
+ "version": "3.18.1",
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",
@@ -77,8 +77,8 @@
77
77
  "@i18n-micro/core": "1.3.0",
78
78
  "@i18n-micro/path-strategy": "1.3.0",
79
79
  "@i18n-micro/test-utils": "1.2.0",
80
- "@i18n-micro/types": "1.2.1",
81
- "@i18n-micro/route-strategy": "1.1.5"
80
+ "@i18n-micro/route-strategy": "1.1.6",
81
+ "@i18n-micro/types": "1.2.1"
82
82
  },
83
83
  "devDependencies": {
84
84
  "@biomejs/biome": "^2.3.14",
@@ -1 +0,0 @@
1
- {"id":"27df88ba-c559-40f5-a972-a7bb2444b9fc","timestamp":1777382527247,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}