nuxt-i18n-micro 1.97.0 → 1.98.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.
- package/dist/client/200.html +2 -2
- package/dist/client/404.html +2 -2
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/9bdb882b-a145-42be-bbc5-f7c9185266d5.json +1 -0
- package/dist/client/index.html +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +105 -60
- package/dist/runtime/components/locale-redirect.vue +1 -1
- package/dist/runtime/composables/useLocaleHead.d.ts +2 -2
- package/dist/runtime/composables/useLocaleHead.js +6 -4
- package/dist/runtime/plugins/02.meta.js +9 -11
- package/dist/runtime/plugins/06.redirect.js +30 -3
- package/dist/runtime/utils/path-utils.d.ts +7 -0
- package/dist/runtime/utils/path-utils.js +40 -0
- package/package.json +3 -3
- package/dist/client/_nuxt/builds/meta/6376a03b-f005-4f0f-b051-3b017577666c.json +0 -1
package/dist/client/200.html
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
<link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BrUpQP6I.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/C0zq22yw.js">
|
|
11
|
-
<script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/ivw-WKkv.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},
|
|
12
|
-
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"
|
|
11
|
+
<script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/ivw-WKkv.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},1759229335371,false]</script>
|
|
12
|
+
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"9bdb882b-a145-42be-bbc5-f7c9185266d5",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
|
package/dist/client/404.html
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
<link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BrUpQP6I.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/C0zq22yw.js">
|
|
11
|
-
<script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/ivw-WKkv.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},
|
|
12
|
-
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"
|
|
11
|
+
<script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/ivw-WKkv.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},1759229335372,false]</script>
|
|
12
|
+
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"9bdb882b-a145-42be-bbc5-f7c9185266d5",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"
|
|
1
|
+
{"id":"9bdb882b-a145-42be-bbc5-f7c9185266d5","timestamp":1759229326396}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id":"9bdb882b-a145-42be-bbc5-f7c9185266d5","timestamp":1759229326396,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|
package/dist/client/index.html
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
<link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BrUpQP6I.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/C0zq22yw.js">
|
|
11
|
-
<script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/ivw-WKkv.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},
|
|
12
|
-
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"
|
|
11
|
+
<script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/ivw-WKkv.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},1759229335372,false]</script>
|
|
12
|
+
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"9bdb882b-a145-42be-bbc5-f7c9185266d5",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import path, { resolve } from 'node:path';
|
|
1
|
+
import path, { resolve, join } from 'node:path';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import fs__default, {
|
|
3
|
+
import fs__default, { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { useNuxt, defineNuxtModule, useLogger, createResolver, addTemplate, addImportsDir, addPlugin, addServerHandler, addComponentsDir, addTypeTemplate, addPrerenderRoutes } from '@nuxt/kit';
|
|
5
5
|
import { watch } from 'chokidar';
|
|
6
6
|
import { isPrefixAndDefaultStrategy, isPrefixStrategy, isNoPrefixStrategy, isPrefixExceptDefaultStrategy, withPrefixStrategy } from 'nuxt-i18n-micro-core';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
9
9
|
import sirv from 'sirv';
|
|
10
|
+
import { isInternalPath } from '../dist/runtime/utils/path-utils.js';
|
|
11
|
+
import { globby } from 'globby';
|
|
10
12
|
|
|
11
13
|
const DEVTOOLS_UI_PORT = 3030;
|
|
12
14
|
const DEVTOOLS_UI_ROUTE = "/__nuxt-i18n-micro";
|
|
@@ -108,66 +110,88 @@ function setupDevToolsUI(options, resolve2) {
|
|
|
108
110
|
});
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
/^\/favicon\.ico$/,
|
|
116
|
-
/^\/apple-touch-icon.*\.png$/,
|
|
117
|
-
/^\/manifest\.json$/,
|
|
118
|
-
/^\/sw\.js$/,
|
|
119
|
-
/^\/workbox-.*\.js$/,
|
|
120
|
-
/\.(xml|txt|ico|json|js|css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/
|
|
121
|
-
];
|
|
122
|
-
const isInternalPath = (path2, excludePatterns) => {
|
|
123
|
-
if (/(?:^|\/)__[^/]+/.test(path2)) {
|
|
124
|
-
return true;
|
|
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 };
|
|
125
117
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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;
|
|
142
|
+
}
|
|
143
|
+
localesStr = trimmed.substring(0, i + 1);
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
} else if (path2 === pattern || path2.startsWith(pattern)) {
|
|
140
|
-
return true;
|
|
146
|
+
if (localesStr) {
|
|
147
|
+
try {
|
|
148
|
+
const localesStrTrimmed = localesStr.trim();
|
|
149
|
+
if (localesStrTrimmed.startsWith("[") && localesStrTrimmed.endsWith("]")) {
|
|
150
|
+
const arrayMatch = localesStrTrimmed.match(/\[(.*?)\]/s);
|
|
151
|
+
if (arrayMatch && arrayMatch[1]) {
|
|
152
|
+
const elements = arrayMatch[1].split(",").map((el) => el.trim().replace(/['"]/g, "")).filter((el) => el.length > 0);
|
|
153
|
+
locales = elements;
|
|
141
154
|
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
155
|
+
}
|
|
156
|
+
if (localesStrTrimmed.startsWith("{") && localesStrTrimmed.endsWith("}")) {
|
|
157
|
+
const topLevelKeyMatches = localesStrTrimmed.match(/^\s*(\w+)\s*:\s*\{/gm);
|
|
158
|
+
if (topLevelKeyMatches) {
|
|
159
|
+
const keys = topLevelKeyMatches.map((match) => {
|
|
160
|
+
const keyMatch = match.match(/^\s*(\w+)\s*:/);
|
|
161
|
+
return keyMatch ? keyMatch[1] : "";
|
|
162
|
+
}).filter((key) => key.length > 0);
|
|
163
|
+
locales = keys;
|
|
164
|
+
} else {
|
|
165
|
+
const fallbackMatches = localesStrTrimmed.match(/(\w+)\s*:\s*\{/g);
|
|
166
|
+
if (fallbackMatches) {
|
|
167
|
+
const keys = fallbackMatches.map((match) => {
|
|
168
|
+
const keyMatch = match.match(/(\w+)\s*:/);
|
|
169
|
+
return keyMatch ? keyMatch[1] : "";
|
|
170
|
+
}).filter((key) => key.length > 0);
|
|
171
|
+
locales = keys;
|
|
172
|
+
}
|
|
145
173
|
}
|
|
146
174
|
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error("Failed to parse locales:", error, "in file:", filePath);
|
|
147
177
|
}
|
|
148
178
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
const parsedLocaleRoutes = Function('"use strict";return (' + localeRoutesMatch[1] + ")")();
|
|
158
|
-
if (typeof parsedLocaleRoutes === "object" && parsedLocaleRoutes !== null) {
|
|
159
|
-
if (validateDefineI18nRouteConfig(parsedLocaleRoutes)) {
|
|
160
|
-
return parsedLocaleRoutes;
|
|
161
|
-
}
|
|
162
|
-
} else {
|
|
163
|
-
console.error("localeRoutes found but it is not a valid object in file:", filePath);
|
|
179
|
+
const localeRoutesMatch = defineContent.match(/localeRoutes:\s*(\{[\s\S]*?\})/);
|
|
180
|
+
if (localeRoutesMatch && localeRoutesMatch[1]) {
|
|
181
|
+
try {
|
|
182
|
+
const parsedLocaleRoutes = Function('"use strict";return (' + localeRoutesMatch[1] + ")")();
|
|
183
|
+
if (typeof parsedLocaleRoutes === "object" && parsedLocaleRoutes !== null) {
|
|
184
|
+
if (validateDefineI18nRouteConfig(parsedLocaleRoutes)) {
|
|
185
|
+
localeRoutes = parsedLocaleRoutes;
|
|
164
186
|
}
|
|
165
|
-
}
|
|
166
|
-
console.error("
|
|
187
|
+
} else {
|
|
188
|
+
console.error("localeRoutes found but it is not a valid object in file:", filePath);
|
|
167
189
|
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error("Failed to parse localeRoutes:", error, "in file:", filePath);
|
|
168
192
|
}
|
|
169
193
|
}
|
|
170
|
-
return
|
|
194
|
+
return { locales, localeRoutes };
|
|
171
195
|
}
|
|
172
196
|
function validateDefineI18nRouteConfig(obj) {
|
|
173
197
|
if (typeof obj !== "object") return false;
|
|
@@ -215,9 +239,10 @@ class PageManager {
|
|
|
215
239
|
localizedPaths = {};
|
|
216
240
|
activeLocaleCodes;
|
|
217
241
|
globalLocaleRoutes;
|
|
242
|
+
filesLocaleRoutes;
|
|
218
243
|
noPrefixRedirect;
|
|
219
244
|
excludePatterns;
|
|
220
|
-
constructor(locales, defaultLocaleCode, strategy, globalLocaleRoutes, noPrefixRedirect, excludePatterns) {
|
|
245
|
+
constructor(locales, defaultLocaleCode, strategy, globalLocaleRoutes, filesLocaleRoutes, noPrefixRedirect, excludePatterns) {
|
|
221
246
|
this.locales = locales;
|
|
222
247
|
this.defaultLocale = this.findLocaleByCode(defaultLocaleCode) || { code: defaultLocaleCode };
|
|
223
248
|
this.strategy = strategy;
|
|
@@ -225,6 +250,7 @@ class PageManager {
|
|
|
225
250
|
this.excludePatterns = excludePatterns;
|
|
226
251
|
this.activeLocaleCodes = this.computeActiveLocaleCodes();
|
|
227
252
|
this.globalLocaleRoutes = globalLocaleRoutes || {};
|
|
253
|
+
this.filesLocaleRoutes = filesLocaleRoutes || {};
|
|
228
254
|
}
|
|
229
255
|
findLocaleByCode(code) {
|
|
230
256
|
return this.locales.find((locale) => locale.code === code);
|
|
@@ -278,13 +304,10 @@ class PageManager {
|
|
|
278
304
|
const pageName = buildRouteNameFromRoute(page.name, page.path);
|
|
279
305
|
const globalLocalePath = this.globalLocaleRoutes[pageName];
|
|
280
306
|
if (!globalLocalePath) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
const normalizedFullPath = normalizePath(path.posix.join(parentPath, page.path));
|
|
286
|
-
localizedPaths[normalizedFullPath] = localeRoutes;
|
|
287
|
-
}
|
|
307
|
+
const filesLocalePath = this.filesLocaleRoutes[pageName];
|
|
308
|
+
if (filesLocalePath && typeof filesLocalePath === "object") {
|
|
309
|
+
const normalizedFullPath = normalizePath(path.posix.join(parentPath, page.path));
|
|
310
|
+
localizedPaths[normalizedFullPath] = filesLocalePath;
|
|
288
311
|
}
|
|
289
312
|
} else if (typeof globalLocalePath === "object") {
|
|
290
313
|
const normalizedFullPath = normalizePath(path.posix.join(parentPath, page.path));
|
|
@@ -638,7 +661,26 @@ const module = defineNuxtModule({
|
|
|
638
661
|
const resolver = createResolver(import.meta.url);
|
|
639
662
|
const rootDirs = nuxt.options._layers.map((layer) => layer.config.rootDir).reverse();
|
|
640
663
|
const localeManager = new LocaleManager(options, rootDirs);
|
|
641
|
-
const
|
|
664
|
+
const routeLocales = {};
|
|
665
|
+
const globalLocaleRoutes = {};
|
|
666
|
+
const pageFiles = await globby("pages/**/*.vue", { cwd: nuxt.options.rootDir });
|
|
667
|
+
for (const pageFile of pageFiles) {
|
|
668
|
+
const fullPath = join(nuxt.options.rootDir, pageFile);
|
|
669
|
+
try {
|
|
670
|
+
const fileContent = readFileSync(fullPath, "utf-8");
|
|
671
|
+
const { locales: extractedLocales, localeRoutes } = extractDefineI18nRouteData(fileContent, fullPath);
|
|
672
|
+
const routePath = pageFile.replace(/^pages\//, "/").replace(/\/index\.vue$/, "").replace(/\.vue$/, "").replace(/\/$/, "") || "/";
|
|
673
|
+
const pageName = routePath.replace(/[^a-z0-9]/gi, "-").replace(/^-+|-+$/g, "");
|
|
674
|
+
if (extractedLocales) {
|
|
675
|
+
routeLocales[routePath] = extractedLocales;
|
|
676
|
+
}
|
|
677
|
+
if (localeRoutes) {
|
|
678
|
+
globalLocaleRoutes[pageName] = localeRoutes;
|
|
679
|
+
}
|
|
680
|
+
} catch {
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
const pageManager = new PageManager(localeManager.locales, defaultLocale, options.strategy, options.globalLocaleRoutes, globalLocaleRoutes, options.noPrefixRedirect, options.excludePatterns);
|
|
642
684
|
addTemplate({
|
|
643
685
|
filename: "i18n.plural.mjs",
|
|
644
686
|
write: true,
|
|
@@ -663,7 +705,10 @@ const module = defineNuxtModule({
|
|
|
663
705
|
isSSG,
|
|
664
706
|
disablePageLocales: options.disablePageLocales ?? false,
|
|
665
707
|
canonicalQueryWhitelist: options.canonicalQueryWhitelist ?? [],
|
|
666
|
-
excludePatterns: options.excludePatterns ?? []
|
|
708
|
+
excludePatterns: options.excludePatterns ?? [],
|
|
709
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
710
|
+
// @ts-ignore
|
|
711
|
+
routeLocales
|
|
667
712
|
};
|
|
668
713
|
if (typeof options.customRegexMatcher !== "undefined") {
|
|
669
714
|
const localeCodes = localeManager.locales.map((l) => l.code);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<script setup>
|
|
6
6
|
import { useRoute, useI18n, createError, navigateTo, useRuntimeConfig } from "#imports";
|
|
7
|
-
import { isInternalPath } from "
|
|
7
|
+
import { isInternalPath } from "../utils/path-utils";
|
|
8
8
|
const route = useRoute();
|
|
9
9
|
const { $getLocales, $defaultLocale } = useI18n();
|
|
10
10
|
const config = useRuntimeConfig();
|
|
@@ -22,7 +22,7 @@ export declare const useLocaleHead: ({ addDirAttribute, identifierAttribute, add
|
|
|
22
22
|
identifierAttribute?: string | undefined;
|
|
23
23
|
addSeoAttributes?: boolean | undefined;
|
|
24
24
|
baseUrl?: string | undefined;
|
|
25
|
-
}) => import("vue").Ref<{
|
|
25
|
+
}) => Promise<import("vue").Ref<{
|
|
26
26
|
htmlAttrs: {
|
|
27
27
|
lang?: string | undefined;
|
|
28
28
|
dir?: "ltr" | "rtl" | "auto" | undefined;
|
|
@@ -54,5 +54,5 @@ export declare const useLocaleHead: ({ addDirAttribute, identifierAttribute, add
|
|
|
54
54
|
property: string;
|
|
55
55
|
content: string;
|
|
56
56
|
}[];
|
|
57
|
-
}
|
|
57
|
+
}>>;
|
|
58
58
|
export {};
|
|
@@ -1,7 +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
|
-
export const useLocaleHead = ({ addDirAttribute = true, identifierAttribute = "id", addSeoAttributes = true, baseUrl = "/" } = {}) => {
|
|
4
|
+
export const useLocaleHead = async ({ addDirAttribute = true, identifierAttribute = "id", addSeoAttributes = true, baseUrl = "/" } = {}) => {
|
|
5
5
|
const metaObject = ref({
|
|
6
6
|
htmlAttrs: {},
|
|
7
7
|
link: [],
|
|
@@ -19,15 +19,17 @@ export const useLocaleHead = ({ addDirAttribute = true, identifierAttribute = "i
|
|
|
19
19
|
return withQuery(pathname, filtered);
|
|
20
20
|
}
|
|
21
21
|
function updateMeta() {
|
|
22
|
-
const { defaultLocale, strategy, canonicalQueryWhitelist } = useRuntimeConfig().public.i18nConfig;
|
|
22
|
+
const { defaultLocale, strategy, canonicalQueryWhitelist, routeLocales } = useRuntimeConfig().public.i18nConfig;
|
|
23
23
|
const { $getLocales, $getLocale } = useNuxtApp();
|
|
24
24
|
if (!$getLocale || !$getLocales) return;
|
|
25
25
|
const route = useRoute();
|
|
26
26
|
const locale = unref($getLocale());
|
|
27
|
-
const
|
|
27
|
+
const allLocales = unref($getLocales());
|
|
28
28
|
const routeName = (route.name ?? "").toString();
|
|
29
29
|
const currentLocale = unref($getLocales().find((loc) => loc.code === locale));
|
|
30
30
|
if (!currentLocale) return;
|
|
31
|
+
const currentRouteLocales = routeLocales?.[routeName] || routeLocales?.[route.path];
|
|
32
|
+
const locales = currentRouteLocales ? allLocales.filter((loc) => currentRouteLocales.includes(loc.code)) : allLocales;
|
|
31
33
|
const currentIso = currentLocale.iso || locale;
|
|
32
34
|
const currentDir = currentLocale.dir || "auto";
|
|
33
35
|
let fullPath = unref(route.fullPath);
|
|
@@ -55,7 +57,7 @@ export const useLocaleHead = ({ addDirAttribute = true, identifierAttribute = "i
|
|
|
55
57
|
meta: []
|
|
56
58
|
};
|
|
57
59
|
if (!addSeoAttributes) return;
|
|
58
|
-
const alternateLocales =
|
|
60
|
+
const alternateLocales = locales;
|
|
59
61
|
const ogLocaleMeta = {
|
|
60
62
|
[identifierAttribute]: "i18n-og",
|
|
61
63
|
property: "og:locale",
|
|
@@ -2,20 +2,18 @@ import { useLocaleHead } from "../composables/useLocaleHead.js";
|
|
|
2
2
|
import { useRequestURL, useHead, defineNuxtPlugin, useRuntimeConfig } from "#imports";
|
|
3
3
|
const host = process.env.HOST ?? "localhost";
|
|
4
4
|
const port = process.env.PORT ?? "host";
|
|
5
|
-
export default defineNuxtPlugin((
|
|
5
|
+
export default defineNuxtPlugin(async (_nuxtApp) => {
|
|
6
6
|
const config = useRuntimeConfig();
|
|
7
7
|
const i18nConfig = config.public.i18nConfig;
|
|
8
8
|
const schema = port === "443" ? "https" : "http";
|
|
9
9
|
const defaultUrl = port === "80" || port === "443" ? `${schema}://${host}` : `${schema}://${host}:${port}`;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
baseUrl
|
|
18
|
-
});
|
|
19
|
-
useHead(head);
|
|
10
|
+
const url = useRequestURL();
|
|
11
|
+
const baseUrl = (i18nConfig.metaBaseUrl || url.origin || defaultUrl).toString();
|
|
12
|
+
const head = await useLocaleHead({
|
|
13
|
+
addDirAttribute: true,
|
|
14
|
+
identifierAttribute: "id",
|
|
15
|
+
addSeoAttributes: true,
|
|
16
|
+
baseUrl
|
|
20
17
|
});
|
|
18
|
+
useHead(head);
|
|
21
19
|
});
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import { isNoPrefixStrategy, isPrefixStrategy } from "nuxt-i18n-micro-core";
|
|
2
|
-
import { defineNuxtPlugin, useRuntimeConfig, useRoute, useRouter, navigateTo } from "#imports";
|
|
2
|
+
import { defineNuxtPlugin, useRuntimeConfig, useRoute, useRouter, navigateTo, createError } from "#imports";
|
|
3
3
|
export default defineNuxtPlugin(async (nuxtApp) => {
|
|
4
4
|
const config = useRuntimeConfig();
|
|
5
5
|
const i18nConfig = config.public.i18nConfig;
|
|
6
|
+
const { routeLocales } = useRuntimeConfig().public.i18nConfig;
|
|
6
7
|
const route = useRoute();
|
|
7
8
|
const router = useRouter();
|
|
9
|
+
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];
|
|
15
|
+
if (!allowedLocales || allowedLocales.length === 0) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const pathSegments = routePath.split("/").filter(Boolean);
|
|
19
|
+
const firstSegment = pathSegments[0];
|
|
20
|
+
const allLocales = i18nConfig.locales?.map((l) => l.code) || [];
|
|
21
|
+
if (firstSegment && allLocales.includes(firstSegment) && !allowedLocales.includes(firstSegment)) {
|
|
22
|
+
console.log("Locale not allowed, throwing 404");
|
|
23
|
+
throw createError({
|
|
24
|
+
statusCode: 404,
|
|
25
|
+
statusMessage: "Page Not Found"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
};
|
|
8
29
|
const handleRedirect = async (to) => {
|
|
9
30
|
const currentLocale = nuxtApp.$getLocale().toString();
|
|
10
31
|
const name = to.name?.toString();
|
|
@@ -26,10 +47,16 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
26
47
|
});
|
|
27
48
|
}
|
|
28
49
|
};
|
|
29
|
-
if (import.meta.server
|
|
30
|
-
|
|
50
|
+
if (import.meta.server) {
|
|
51
|
+
checkRouteLocales(route);
|
|
52
|
+
if (isPrefixStrategy(i18nConfig.strategy) || isNoPrefixStrategy(i18nConfig.strategy)) {
|
|
53
|
+
await handleRedirect(route);
|
|
54
|
+
}
|
|
31
55
|
}
|
|
32
56
|
router.beforeEach(async (to, from, next) => {
|
|
57
|
+
if (from.path !== to.path) {
|
|
58
|
+
checkRouteLocales(to);
|
|
59
|
+
}
|
|
33
60
|
if (isPrefixStrategy(i18nConfig.strategy) || isNoPrefixStrategy(i18nConfig.strategy)) {
|
|
34
61
|
await handleRedirect(to);
|
|
35
62
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a path should be excluded from i18n routing
|
|
3
|
+
* @param path - The path to check
|
|
4
|
+
* @param excludePatterns - Optional custom exclusion patterns
|
|
5
|
+
* @returns true if the path should be excluded
|
|
6
|
+
*/
|
|
7
|
+
export declare const isInternalPath: (path: string, excludePatterns?: (string | RegExp | object)[]) => boolean;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const DEFAULT_STATIC_PATTERNS = [
|
|
2
|
+
/^\/sitemap.*\.xml$/,
|
|
3
|
+
/^\/sitemap\.xml$/,
|
|
4
|
+
/^\/robots\.txt$/,
|
|
5
|
+
/^\/favicon\.ico$/,
|
|
6
|
+
/^\/apple-touch-icon.*\.png$/,
|
|
7
|
+
/^\/manifest\.json$/,
|
|
8
|
+
/^\/sw\.js$/,
|
|
9
|
+
/^\/workbox-.*\.js$/,
|
|
10
|
+
/\.(xml|txt|ico|json|js|css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/
|
|
11
|
+
];
|
|
12
|
+
export const isInternalPath = (path, excludePatterns) => {
|
|
13
|
+
if (/(?:^|\/)__[^/]+/.test(path)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
for (const pattern of DEFAULT_STATIC_PATTERNS) {
|
|
17
|
+
if (pattern.test(path)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (excludePatterns) {
|
|
22
|
+
for (const pattern of excludePatterns) {
|
|
23
|
+
if (typeof pattern === "string") {
|
|
24
|
+
if (pattern.includes("*") || pattern.includes("?")) {
|
|
25
|
+
const regex = new RegExp(pattern.replace(/\*/g, ".*").replace(/\?/g, "."));
|
|
26
|
+
if (regex.test(path)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
} else if (path === pattern || path.startsWith(pattern)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
} else if (pattern instanceof RegExp) {
|
|
33
|
+
if (pattern.test(path)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-i18n-micro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.98.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-
|
|
66
|
-
"nuxt-i18n-micro-
|
|
65
|
+
"nuxt-i18n-micro-types": "1.0.8",
|
|
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":"6376a03b-f005-4f0f-b051-3b017577666c","timestamp":1758813945370,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|