nuxt-i18n-micro 3.17.5 → 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:"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:"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:"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:"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":"27df88ba-c559-40f5-a972-a7bb2444b9fc","timestamp":1777382527247}
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:"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:"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.5",
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;
@@ -878,6 +879,9 @@ declare module '#i18n-internal/plural' {
878
879
  continue;
879
880
  }
880
881
  for (const locale of routeGenerator.locales) {
882
+ if (!isLocaleAllowedForUnlocalizedRoute(routeGenerator.routeLocales, routeGenerator.locales, route, locale.code)) {
883
+ continue;
884
+ }
881
885
  const localizedRoute = routeGenerator.resolveLocalizedPath(route, locale.code);
882
886
  if (localizedRoute === route) {
883
887
  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.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/test-utils": "1.2.0",
79
+ "@i18n-micro/route-strategy": "1.1.6",
80
80
  "@i18n-micro/types": "1.2.1",
81
- "@i18n-micro/route-strategy": "1.1.5"
81
+ "@i18n-micro/test-utils": "1.2.0"
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":[]}