nuxt-i18n-micro 1.100.0 → 1.101.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.
@@ -8,5 +8,5 @@
8
8
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/T7szCJDf.js">
9
9
  <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DGwSTbEi.css">
10
10
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/DTIY11lm.js">
11
- <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/s7GTWWQo.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1759906445724,false]</script>
12
- <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"23d9342b-133b-4d2e-909f-71dd66a693ec",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
11
+ <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/s7GTWWQo.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1759936060690,false]</script>
12
+ <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"3c2f5e13-457a-416f-af4d-c74141ba3650",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
@@ -8,5 +8,5 @@
8
8
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/T7szCJDf.js">
9
9
  <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DGwSTbEi.css">
10
10
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/DTIY11lm.js">
11
- <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/s7GTWWQo.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1759906445725,false]</script>
12
- <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"23d9342b-133b-4d2e-909f-71dd66a693ec",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
11
+ <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/s7GTWWQo.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1759936060690,false]</script>
12
+ <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"3c2f5e13-457a-416f-af4d-c74141ba3650",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
@@ -1 +1 @@
1
- {"id":"23d9342b-133b-4d2e-909f-71dd66a693ec","timestamp":1759906439920}
1
+ {"id":"3c2f5e13-457a-416f-af4d-c74141ba3650","timestamp":1759936052722}
@@ -0,0 +1 @@
1
+ {"id":"3c2f5e13-457a-416f-af4d-c74141ba3650","timestamp":1759936052722,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
@@ -8,5 +8,5 @@
8
8
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/T7szCJDf.js">
9
9
  <link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DGwSTbEi.css">
10
10
  <link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/DTIY11lm.js">
11
- <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/s7GTWWQo.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1759906445725,false]</script>
12
- <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"23d9342b-133b-4d2e-909f-71dd66a693ec",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
11
+ <script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/s7GTWWQo.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1759936060691,false]</script>
12
+ <script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"3c2f5e13-457a-416f-af4d-c74141ba3650",buildAssetsDir:"/_nuxt/",cdnURL:""}}</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": "1.100.0",
4
+ "version": "1.101.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -110,92 +110,119 @@ function setupDevToolsUI(options, resolve2) {
110
110
  });
111
111
  }
112
112
 
113
- function extractDefineI18nRouteData(content, filePath) {
114
- const defineMatch = content.match(/\$?\bdefineI18nRoute\s*\(\s*\{[\s\S]*?\}\s*\)/);
115
- if (!defineMatch) {
116
- return { locales: null, localeRoutes: null };
117
- }
118
- const defineContent = defineMatch[0];
119
- let locales = null;
120
- let localeRoutes = null;
121
- let localesStr = "";
122
- const localesStart = defineContent.indexOf("locales:");
123
- if (localesStart !== -1) {
124
- const afterLocales = defineContent.substring(localesStart + 8);
125
- const trimmed = afterLocales.trim();
126
- if (trimmed.startsWith("[")) {
127
- let bracketCount = 0;
128
- let i = 0;
129
- for (; i < trimmed.length; i++) {
130
- if (trimmed[i] === "[") bracketCount++;
131
- if (trimmed[i] === "]") bracketCount--;
132
- if (bracketCount === 0) break;
133
- }
134
- localesStr = trimmed.substring(0, i + 1);
135
- } else if (trimmed.startsWith("{")) {
136
- let braceCount = 0;
137
- let i = 0;
138
- for (; i < trimmed.length; i++) {
139
- if (trimmed[i] === "{") braceCount++;
140
- if (trimmed[i] === "}") braceCount--;
141
- if (braceCount === 0) break;
113
+ function extractScriptContent(content) {
114
+ const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/);
115
+ return scriptMatch ? scriptMatch[1] : null;
116
+ }
117
+ function removeTypeScriptTypes(scriptContent) {
118
+ return scriptContent.replace(/\((\w+):[^)]*\)/g, "($1)");
119
+ }
120
+ function findDefineI18nRouteConfig(scriptContent) {
121
+ try {
122
+ const defineStart = scriptContent.indexOf("$defineI18nRoute(");
123
+ if (defineStart === -1) {
124
+ return null;
125
+ }
126
+ const openParen = scriptContent.indexOf("(", defineStart);
127
+ if (openParen === -1) {
128
+ return null;
129
+ }
130
+ let braceCount = 0;
131
+ let parenCount = 1;
132
+ let i = openParen + 1;
133
+ for (; i < scriptContent.length; i++) {
134
+ if (scriptContent[i] === "{") braceCount++;
135
+ if (scriptContent[i] === "}") braceCount--;
136
+ if (scriptContent[i] === "(") parenCount++;
137
+ if (scriptContent[i] === ")") {
138
+ parenCount--;
139
+ if (parenCount === 0 && braceCount === 0) break;
142
140
  }
143
- localesStr = trimmed.substring(0, i + 1);
144
141
  }
145
- }
146
- if (localesStr) {
142
+ if (i >= scriptContent.length) {
143
+ return null;
144
+ }
145
+ const configStr = scriptContent.substring(openParen + 1, i);
147
146
  try {
148
- const localesStrTrimmed = localesStr.trim();
149
- if (localesStrTrimmed.startsWith("[") && localesStrTrimmed.endsWith("]")) {
150
- const arrayMatch = localesStrTrimmed.match(/\[(.*?)\]/s);
151
- if (arrayMatch && arrayMatch[1]) {
152
- locales = arrayMatch[1].split(",").map((el) => el.trim().replace(/['"]/g, "")).filter((el) => el.length > 0);
147
+ const cleanConfigStr = removeTypeScriptTypes(configStr);
148
+ try {
149
+ const configObject = Function('"use strict";return (' + cleanConfigStr + ")")();
150
+ try {
151
+ const serialized = JSON.stringify(configObject);
152
+ return JSON.parse(serialized);
153
+ } catch {
154
+ return configObject;
153
155
  }
154
- }
155
- if (localesStrTrimmed.startsWith("{") && localesStrTrimmed.endsWith("}")) {
156
- const topLevelKeyMatches = localesStrTrimmed.match(/^\s*(\[?['"]?([\w-]+)['"]?\]?)\s*:\s*\{/gm);
157
- if (topLevelKeyMatches) {
158
- locales = topLevelKeyMatches.map((match) => {
159
- const keyMatch = match.match(/^\s*\[?['"]?([\w-]+)['"]?\]?\s*:/);
160
- return keyMatch ? keyMatch[1] : "";
161
- }).filter((key) => key.length > 0);
162
- } else {
163
- const fallbackMatches = localesStrTrimmed.match(/(\[?['"]?([\w-]+)['"]?\]?)\s*:\s*\{/g);
164
- if (fallbackMatches) {
165
- locales = fallbackMatches.map((match) => {
166
- const keyMatch = match.match(/(\[?['"]?([\w-]+)['"]?\]?)\s*:/);
167
- return keyMatch ? keyMatch[1] : "";
168
- }).filter((key) => key.length > 0);
169
- }
156
+ } catch {
157
+ const scriptWithoutImports = scriptContent.split("\n").filter((line) => !line.trim().startsWith("import ")).join("\n");
158
+ const cleanScript = removeTypeScriptTypes(scriptWithoutImports);
159
+ const safeScript = `
160
+ // Mock $defineI18nRoute to prevent errors
161
+ const $defineI18nRoute = () => {}
162
+ const defineI18nRoute = () => {}
163
+
164
+ // Mock process.env for conditional logic
165
+ const process = { env: { NODE_ENV: 'development' } }
166
+
167
+ // Execute the script content without imports and TypeScript types
168
+ ${cleanScript}
169
+
170
+ // Return the config object
171
+ return (${cleanConfigStr})
172
+ `;
173
+ const configObject = Function('"use strict";' + safeScript)();
174
+ try {
175
+ const serialized = JSON.stringify(configObject);
176
+ return JSON.parse(serialized);
177
+ } catch {
178
+ return configObject;
170
179
  }
171
180
  }
172
- } catch (error) {
173
- console.error("Failed to parse locales:", error, "in file:", filePath);
181
+ } catch {
182
+ return null;
174
183
  }
184
+ } catch {
185
+ return null;
175
186
  }
176
- const localeRoutesMatch = defineContent.match(/localeRoutes:\s*(\{[\s\S]*?\})/);
177
- if (localeRoutesMatch && localeRoutesMatch[1]) {
178
- try {
179
- const parsedLocaleRoutes = Function('"use strict";return (' + localeRoutesMatch[1] + ")")();
180
- if (typeof parsedLocaleRoutes === "object" && parsedLocaleRoutes !== null) {
181
- if (validateDefineI18nRouteConfig(parsedLocaleRoutes)) {
182
- localeRoutes = parsedLocaleRoutes;
183
- }
184
- } else {
185
- console.error("localeRoutes found but it is not a valid object in file:", filePath);
187
+ }
188
+ function extractDefineI18nRouteData(content, _filePath) {
189
+ try {
190
+ const scriptContent = extractScriptContent(content);
191
+ if (!scriptContent) {
192
+ return { locales: null, localeRoutes: null };
193
+ }
194
+ const configObject = findDefineI18nRouteConfig(scriptContent);
195
+ if (!configObject) {
196
+ return { locales: null, localeRoutes: null };
197
+ }
198
+ let locales = null;
199
+ if (configObject.locales) {
200
+ if (Array.isArray(configObject.locales)) {
201
+ locales = configObject.locales.map((item) => {
202
+ if (typeof item === "string") {
203
+ return item;
204
+ } else if (typeof item === "object" && item !== null && item.code) {
205
+ return item.code;
206
+ }
207
+ return null;
208
+ }).filter((item) => item !== null);
209
+ } else if (typeof configObject.locales === "object") {
210
+ locales = Object.keys(configObject.locales).filter(
211
+ (key) => key !== "meta" && key !== "path" && key !== "title" && key !== "description"
212
+ );
186
213
  }
187
- } catch (error) {
188
- console.error("Failed to parse localeRoutes:", error, "in file:", filePath);
189
214
  }
215
+ let localeRoutes = null;
216
+ if (configObject.localeRoutes && typeof configObject.localeRoutes === "object") {
217
+ const isValid = Object.values(configObject.localeRoutes).every((value) => typeof value === "string");
218
+ if (isValid) {
219
+ localeRoutes = configObject.localeRoutes;
220
+ }
221
+ }
222
+ return { locales, localeRoutes };
223
+ } catch {
224
+ return { locales: null, localeRoutes: null };
190
225
  }
191
- return { locales, localeRoutes };
192
- }
193
- function validateDefineI18nRouteConfig(obj) {
194
- if (typeof obj !== "object") return false;
195
- for (const routeKey in obj.localeRoutes) {
196
- if (typeof obj.localeRoutes[routeKey] !== "string") return false;
197
- }
198
- return true;
199
226
  }
200
227
  const normalizePath = (routePath) => {
201
228
  if (!routePath) {
@@ -1,6 +1,7 @@
1
1
  import { joinURL, parseURL, withQuery } from "ufo";
2
2
  import { isPrefixExceptDefaultStrategy, isNoPrefixStrategy } from "nuxt-i18n-micro-core";
3
3
  import { unref, useRoute, useRuntimeConfig, watch, onUnmounted, ref, useNuxtApp } from "#imports";
4
+ import { findAllowedLocalesForRoute } from "../utils/route-utils.js";
4
5
  export const useLocaleHead = async ({ addDirAttribute = true, identifierAttribute = "id", addSeoAttributes = true, baseUrl = "/" } = {}) => {
5
6
  const metaObject = ref({
6
7
  htmlAttrs: {},
@@ -28,7 +29,7 @@ export const useLocaleHead = async ({ addDirAttribute = true, identifierAttribut
28
29
  const routeName = (route.name ?? "").toString();
29
30
  const currentLocale = unref($getLocales().find((loc) => loc.code === locale));
30
31
  if (!currentLocale) return;
31
- const currentRouteLocales = routeLocales?.[routeName] || routeLocales?.[route.path];
32
+ const currentRouteLocales = findAllowedLocalesForRoute(route, routeLocales);
32
33
  const locales = currentRouteLocales ? allLocales.filter((loc) => currentRouteLocales.includes(loc.code)) : allLocales;
33
34
  const currentIso = currentLocale.iso || locale;
34
35
  const currentDir = currentLocale.dir || "auto";
@@ -1,5 +1,6 @@
1
1
  import { isNoPrefixStrategy, isPrefixStrategy } from "nuxt-i18n-micro-core";
2
2
  import { defineNuxtPlugin, useRuntimeConfig, useRoute, useRouter, navigateTo, createError } from "#imports";
3
+ import { findAllowedLocalesForRoute } from "../utils/route-utils.js";
3
4
  export default defineNuxtPlugin(async (nuxtApp) => {
4
5
  const config = useRuntimeConfig();
5
6
  const i18nConfig = config.public.i18nConfig;
@@ -7,15 +8,11 @@ export default defineNuxtPlugin(async (nuxtApp) => {
7
8
  const route = useRoute();
8
9
  const router = useRouter();
9
10
  const checkRouteLocales = (to) => {
10
- const routePath = to.path;
11
- const routeName = to.name?.toString();
12
- const normalizedRouteName = routeName?.replace("localized-", "");
13
- const normalizedRoutePath = normalizedRouteName ? `/${normalizedRouteName}` : void 0;
14
- const allowedLocales = routeName && routeLocales?.[routeName] || normalizedRouteName && routeLocales?.[normalizedRouteName] || normalizedRoutePath && routeLocales?.[normalizedRoutePath] || routeLocales?.[routePath];
11
+ const allowedLocales = findAllowedLocalesForRoute(to, routeLocales);
15
12
  if (!allowedLocales || allowedLocales.length === 0) {
16
13
  return;
17
14
  }
18
- const pathSegments = routePath.split("/").filter(Boolean);
15
+ const pathSegments = to.path.split("/").filter(Boolean);
19
16
  const firstSegment = pathSegments[0];
20
17
  const allLocales = i18nConfig.locales?.map((l) => l.code) || [];
21
18
  if (firstSegment && allLocales.includes(firstSegment) && !allowedLocales.includes(firstSegment)) {
@@ -0,0 +1,22 @@
1
+ import type { RouteLocationNormalizedLoaded } from 'vue-router';
2
+ /**
3
+ * Extracts the base route pattern from a matched route path
4
+ * Converts Vue Router dynamic route patterns to file-based route patterns
5
+ *
6
+ * @param matchedPath - The matched route path from route.matched[0].path
7
+ * @returns The base route pattern (e.g., "/test/[param]")
8
+ *
9
+ * @example
10
+ * extractBaseRoutePattern('/:locale(es)/test/:param()') // Returns '/test/[param]'
11
+ * extractBaseRoutePattern('/:locale(en)/static') // Returns '/static'
12
+ * extractBaseRoutePattern('/:locale(fr)/about/:id') // Returns '/about/[id]'
13
+ */
14
+ export declare function extractBaseRoutePattern(matchedPath: string): string;
15
+ /**
16
+ * Finds allowed locales for a route using various matching strategies
17
+ *
18
+ * @param route - The route object
19
+ * @param routeLocales - The routeLocales configuration object
20
+ * @returns Array of allowed locale codes or null if no restrictions
21
+ */
22
+ export declare function findAllowedLocalesForRoute(route: RouteLocationNormalizedLoaded, routeLocales: Record<string, string[]> | undefined): string[] | null;
@@ -0,0 +1,19 @@
1
+ export function extractBaseRoutePattern(matchedPath) {
2
+ return matchedPath.replace(/\/:locale\([^)]+\)/g, "").replace(/\/:([^()]+)\(\)/g, "/[$1]").replace(/\/:([^()]+)/g, "/[$1]");
3
+ }
4
+ export function findAllowedLocalesForRoute(route, routeLocales) {
5
+ const routePath = route.path;
6
+ const routeName = route.name?.toString();
7
+ const normalizedRouteName = routeName?.replace("localized-", "");
8
+ const normalizedRoutePath = normalizedRouteName ? `/${normalizedRouteName}` : void 0;
9
+ let allowedLocales = routeName && routeLocales?.[routeName] || normalizedRouteName && routeLocales?.[normalizedRouteName] || normalizedRoutePath && routeLocales?.[normalizedRoutePath] || routeLocales?.[routePath];
10
+ if (!allowedLocales && route.matched && route.matched.length > 0) {
11
+ const matchedRoute = route.matched[0];
12
+ const matchedPath = matchedRoute.path;
13
+ const baseRoutePattern = extractBaseRoutePattern(matchedPath);
14
+ if (routeLocales?.[baseRoutePattern]) {
15
+ allowedLocales = routeLocales[baseRoutePattern];
16
+ }
17
+ }
18
+ return allowedLocales || null;
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-i18n-micro",
3
- "version": "1.100.0",
3
+ "version": "1.101.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",
@@ -62,8 +62,8 @@
62
62
  "sirv": "^2.0.4",
63
63
  "ufo": "^1.5.4",
64
64
  "nuxt-i18n-micro-core": "1.0.18",
65
- "nuxt-i18n-micro-test-utils": "1.0.6",
66
- "nuxt-i18n-micro-types": "1.0.9"
65
+ "nuxt-i18n-micro-types": "1.0.9",
66
+ "nuxt-i18n-micro-test-utils": "1.0.6"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@nuxt/devtools": "^2.6.3",
@@ -1 +0,0 @@
1
- {"id":"23d9342b-133b-4d2e-909f-71dd66a693ec","timestamp":1759906439920,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}