nuxt-i18n-micro 3.19.0 → 3.20.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/README.md +16 -16
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/{D6byAye8.js → BA2bGsrZ.js} +1 -1
- package/dist/client/_nuxt/{BNXusRXY.js → C_HK2snF.js} +1 -1
- package/dist/client/_nuxt/{BCTbvRLO.js → Dx6dmpFi.js} +1 -1
- package/dist/client/_nuxt/SDQMalAT.js +14 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/5a55317c-1ad8-41ca-a7f6-2fe58e16b2f6.json +1 -0
- package/dist/client/index.html +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +147 -89
- package/dist/runtime/components/i18n-group.vue +1 -4
- package/dist/runtime/components/i18n-link.vue +2 -12
- package/dist/runtime/components/i18n-switcher.vue +7 -30
- package/dist/runtime/composables/useI18n.js +1 -1
- package/dist/runtime/composables/useI18nLocale.js +2 -3
- package/dist/runtime/composables/useLocaleHead.js +1 -1
- package/dist/runtime/middleware/i18n-redirect.global.d.ts +6 -0
- package/dist/runtime/middleware/i18n-redirect.global.js +43 -0
- package/dist/runtime/plugins/01.plugin.d.ts +21 -21
- package/dist/runtime/plugins/01.plugin.js +65 -312
- package/dist/runtime/plugins/05.hooks.js +31 -31
- package/dist/runtime/plugins/06.redirect.d.ts +1 -0
- package/dist/runtime/plugins/06.redirect.js +12 -83
- package/dist/runtime/server/middleware/i18n.global.js +13 -1
- package/dist/runtime/server/utils/locale-detector.d.ts +9 -12
- package/dist/runtime/server/utils/locale-detector.js +25 -28
- package/dist/runtime/server/utils/locale-server-middleware.js +8 -6
- package/dist/runtime/server/utils/server-loader.js +10 -9
- package/dist/runtime/server/utils/translation-server-middleware.js +8 -6
- package/dist/runtime/utils/nuxt-i18n.d.ts +122 -0
- package/dist/runtime/utils/nuxt-i18n.js +335 -0
- package/dist/runtime/utils/storage.d.ts +7 -6
- package/dist/runtime/utils/storage.js +18 -11
- package/internals.d.mts +1 -0
- package/package.json +16 -13
- package/dist/client/_nuxt/B-cq5x_h.js +0 -14
- package/dist/client/_nuxt/builds/meta/bbe443af-4371-4ec3-b41c-7a5e9deb9b9b.json +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"
|
|
1
|
+
{"id":"5a55317c-1ad8-41ca-a7f6-2fe58e16b2f6","timestamp":1781512935536}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id":"5a55317c-1ad8-41ca-a7f6-2fe58e16b2f6","timestamp":1781512935536,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|
package/dist/client/index.html
CHANGED
|
@@ -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/
|
|
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/SDQMalAT.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/SDQMalAT.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/Dx6dmpFi.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/BA2bGsrZ.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/C_HK2snF.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:"5a55317c-1ad8-41ca-a7f6-2fe58e16b2f6",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1781512938774,false]</script></body></html>
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
|
-
import fs__default, { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import fs__default, { readFileSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import path, { resolve, join, dirname } from 'node:path';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -10,9 +10,11 @@ import { buildTranslationSourceLayers, preMergeLocales } from '@i18n-micro/utils
|
|
|
10
10
|
import { resolveTranslationPayloadOptions, getTranslationPayloadMisconfigurationWarnings, getTranslationPayloadSizeWarning, resolveTranslationPayloadWarningThresholds, resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config';
|
|
11
11
|
export { resolveTranslationPayloadMode, resolveTranslationPayloadOptions, resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config';
|
|
12
12
|
import { scanTranslationPayloadDirectory } from '@i18n-micro/utils/payload-stats';
|
|
13
|
-
import { useNuxt, defineNuxtModule, useLogger, createResolver, addTemplate, addImportsDir, addPlugin, addServerHandler, addComponentsDir, addTypeTemplate, addPrerenderRoutes } from '@nuxt/kit';
|
|
13
|
+
import { useNuxt, defineNuxtModule, useLogger, createResolver, addTemplate, addImportsDir, addPlugin, addRouteMiddleware, addServerImportsDir, addServerHandler, addComponentsDir, addTypeTemplate, addVitePlugin, addPrerenderRoutes } from '@nuxt/kit';
|
|
14
14
|
import { globby } from 'globby';
|
|
15
15
|
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
16
|
+
import { createUnplugin } from 'unplugin';
|
|
17
|
+
import { parse } from '@vue/compiler-sfc';
|
|
16
18
|
|
|
17
19
|
const DEVTOOLS_UI_PORT = 3030;
|
|
18
20
|
const DEVTOOLS_UI_ROUTE = "/__nuxt-i18n-micro";
|
|
@@ -133,8 +135,11 @@ function shouldLocalizeRouteRulePath(originalPath) {
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
function extractScriptContent(content) {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
+
const { descriptor } = parse(content, { pad: false });
|
|
139
|
+
const parts = [];
|
|
140
|
+
if (descriptor.script?.content) parts.push(descriptor.script.content);
|
|
141
|
+
if (descriptor.scriptSetup?.content) parts.push(descriptor.scriptSetup.content);
|
|
142
|
+
return parts.length > 0 ? parts.join("\n") : null;
|
|
138
143
|
}
|
|
139
144
|
function removeTypeScriptTypes(scriptContent) {
|
|
140
145
|
return scriptContent.replace(/\((\w+):[^)]*\)/g, "($1)");
|
|
@@ -179,17 +184,10 @@ function findDefineI18nRouteConfig(scriptContent) {
|
|
|
179
184
|
const scriptWithoutImports = scriptContent.split("\n").filter((line) => !line.trim().startsWith("import ")).join("\n");
|
|
180
185
|
const cleanScript = removeTypeScriptTypes(scriptWithoutImports);
|
|
181
186
|
const safeScript = `
|
|
182
|
-
// Mock $defineI18nRoute to prevent errors
|
|
183
187
|
const $defineI18nRoute = () => {}
|
|
184
188
|
const defineI18nRoute = () => {}
|
|
185
|
-
|
|
186
|
-
// Mock process.env for conditional logic
|
|
187
189
|
const process = { env: { NODE_ENV: 'development' } }
|
|
188
|
-
|
|
189
|
-
// Execute the script content without imports and TypeScript types
|
|
190
190
|
${cleanScript}
|
|
191
|
-
|
|
192
|
-
// Return the config object
|
|
193
191
|
return (${cleanConfigStr})
|
|
194
192
|
`;
|
|
195
193
|
const configObject = Function(`"use strict";${safeScript}`)();
|
|
@@ -207,6 +205,37 @@ function findDefineI18nRouteConfig(scriptContent) {
|
|
|
207
205
|
return null;
|
|
208
206
|
}
|
|
209
207
|
}
|
|
208
|
+
function normalizeDefineConfig(configObject) {
|
|
209
|
+
if (configObject.locales && typeof configObject.locales === "object" && !Array.isArray(configObject.locales)) {
|
|
210
|
+
const localesObj = configObject.locales;
|
|
211
|
+
const normalizedLocales = [];
|
|
212
|
+
const normalizedLocaleRoutes = {};
|
|
213
|
+
for (const [locale, value] of Object.entries(localesObj)) {
|
|
214
|
+
normalizedLocales.push(locale);
|
|
215
|
+
if (value && typeof value === "object" && "path" in value && typeof value.path === "string") {
|
|
216
|
+
normalizedLocaleRoutes[locale] = value.path;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
...configObject,
|
|
221
|
+
locales: normalizedLocales,
|
|
222
|
+
localeRoutes: configObject.localeRoutes || Object.keys(normalizedLocaleRoutes).length > 0 ? { ...configObject.localeRoutes, ...normalizedLocaleRoutes } : void 0
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (Array.isArray(configObject.locales) && configObject.locales.length > 0 && typeof configObject.locales[0] === "object") {
|
|
226
|
+
const normalizedLocales = configObject.locales.map((item) => {
|
|
227
|
+
if (item && typeof item === "object" && "code" in item) {
|
|
228
|
+
return item.code;
|
|
229
|
+
}
|
|
230
|
+
return String(item);
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
...configObject,
|
|
234
|
+
locales: normalizedLocales
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return configObject;
|
|
238
|
+
}
|
|
210
239
|
function extractDefineI18nRouteData(content, _filePath) {
|
|
211
240
|
try {
|
|
212
241
|
const scriptContent = extractScriptContent(content);
|
|
@@ -217,38 +246,76 @@ function extractDefineI18nRouteData(content, _filePath) {
|
|
|
217
246
|
if (!configObject) {
|
|
218
247
|
return null;
|
|
219
248
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
249
|
+
return normalizeDefineConfig(configObject);
|
|
250
|
+
} catch {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function pageFilePathToRoutePath(pageFile, rootDir) {
|
|
255
|
+
const relative = pageFile.replace(rootDir, "").replace(/^[/\\]+/, "");
|
|
256
|
+
const withoutPagesPrefix = relative.replace(/^(app[/\\])?pages[/\\]/, "");
|
|
257
|
+
if (withoutPagesPrefix === "index.vue") {
|
|
258
|
+
return "/";
|
|
259
|
+
}
|
|
260
|
+
const raw = withoutPagesPrefix.replace(/[/\\]index\.vue$/, "").replace(/\.vue$/, "").replace(/[/\\]$/, "").replace(/\\/g, "/");
|
|
261
|
+
return raw === "" ? "/" : raw;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function resolveRootDir(filePath, rootDirs) {
|
|
265
|
+
const sorted = [...rootDirs].sort((a, b) => b.length - a.length);
|
|
266
|
+
return sorted.find((root) => filePath.startsWith(root));
|
|
267
|
+
}
|
|
268
|
+
function createDefineI18nRoutePlugin(options) {
|
|
269
|
+
const metaByFile = /* @__PURE__ */ new Map();
|
|
270
|
+
const writeMetaFile = () => {
|
|
271
|
+
const entries = [...metaByFile.values()].filter((entry) => entry !== null);
|
|
272
|
+
options.onMetaUpdate?.(entries);
|
|
273
|
+
const metaPath = join(options.buildDir, "i18n-route-meta.json");
|
|
274
|
+
mkdirSync(dirname(metaPath), { recursive: true });
|
|
275
|
+
writeFileSync(metaPath, JSON.stringify(entries, null, 2));
|
|
276
|
+
};
|
|
277
|
+
return createUnplugin(() => ({
|
|
278
|
+
name: "nuxt-i18n-micro-define-route",
|
|
279
|
+
enforce: "pre",
|
|
280
|
+
transformInclude(id) {
|
|
281
|
+
return id.endsWith(".vue") && !id.includes("node_modules");
|
|
282
|
+
},
|
|
283
|
+
transform(code, id) {
|
|
284
|
+
const rootDir = resolveRootDir(id, options.rootDirs);
|
|
285
|
+
if (!rootDir) return null;
|
|
286
|
+
const config = extractDefineI18nRouteData(code);
|
|
287
|
+
if (!config) {
|
|
288
|
+
metaByFile.delete(id);
|
|
289
|
+
} else {
|
|
290
|
+
metaByFile.set(id, {
|
|
291
|
+
routePath: pageFilePathToRoutePath(id, rootDir),
|
|
292
|
+
config
|
|
293
|
+
});
|
|
229
294
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
295
|
+
writeMetaFile();
|
|
296
|
+
return null;
|
|
297
|
+
},
|
|
298
|
+
buildEnd() {
|
|
299
|
+
writeMetaFile();
|
|
235
300
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
303
|
+
function collectDefineI18nRouteMetaFromFiles(pageFiles, rootDirs) {
|
|
304
|
+
const entries = [];
|
|
305
|
+
for (const pageFile of pageFiles) {
|
|
306
|
+
const rootDir = resolveRootDir(pageFile, rootDirs);
|
|
307
|
+
if (!rootDir) continue;
|
|
308
|
+
try {
|
|
309
|
+
const config = extractDefineI18nRouteData(readFileSync(pageFile, "utf-8"), pageFile);
|
|
310
|
+
if (!config) continue;
|
|
311
|
+
entries.push({
|
|
312
|
+
routePath: pageFilePathToRoutePath(pageFile, rootDir),
|
|
313
|
+
config
|
|
242
314
|
});
|
|
243
|
-
|
|
244
|
-
...configObject,
|
|
245
|
-
locales: normalizedLocales
|
|
246
|
-
};
|
|
315
|
+
} catch {
|
|
247
316
|
}
|
|
248
|
-
return configObject;
|
|
249
|
-
} catch {
|
|
250
|
-
return null;
|
|
251
317
|
}
|
|
318
|
+
return entries;
|
|
252
319
|
}
|
|
253
320
|
|
|
254
321
|
const DEFAULT_CANONICAL_QUERY_WHITELIST = ["page", "sort", "filter", "search", "q", "query", "tag"];
|
|
@@ -320,7 +387,7 @@ const module = defineNuxtModule({
|
|
|
320
387
|
},
|
|
321
388
|
async setup(options, nuxt) {
|
|
322
389
|
const defaultLocale = process.env.DEFAULT_LOCALE ?? options.defaultLocale ?? "en";
|
|
323
|
-
const isSSG = nuxt.options.nitro
|
|
390
|
+
const isSSG = Boolean(nuxt.options.nitro?.static);
|
|
324
391
|
const logger = useLogger("nuxt-i18n-micro");
|
|
325
392
|
if (options.strategy === "no_prefix" && !options.localeCookie) {
|
|
326
393
|
options.localeCookie = "user-locale";
|
|
@@ -365,16 +432,11 @@ const module = defineNuxtModule({
|
|
|
365
432
|
const routeLocales = { ...options.routeLocales ?? {} };
|
|
366
433
|
const globalLocaleRoutes = {};
|
|
367
434
|
const routeDisableMeta = {};
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
435
|
+
const pageGlobs = rootDirs.flatMap((root) => [join(root, "pages/**/*.vue"), join(root, "app/pages/**/*.vue")]);
|
|
436
|
+
const pageFiles = await globby(pageGlobs, { absolute: true });
|
|
437
|
+
for (const { routePath, config } of collectDefineI18nRouteMetaFromFiles(pageFiles, rootDirs)) {
|
|
371
438
|
try {
|
|
372
|
-
const fileContent = readFileSync(fullPath, "utf-8");
|
|
373
|
-
const config = extractDefineI18nRouteData(fileContent, fullPath);
|
|
374
|
-
if (!config) continue;
|
|
375
439
|
const { locales: extractedLocales, localeRoutes, disableMeta } = config;
|
|
376
|
-
const raw = pageFile.replace(/^(app\/)?pages\//, "").replace(/\/index\.vue$/, "").replace(/\.vue$/, "").replace(/\/$/, "");
|
|
377
|
-
const routePath = raw === "" || raw === "index" ? "/" : raw;
|
|
378
440
|
if (extractedLocales) {
|
|
379
441
|
if (Array.isArray(extractedLocales)) {
|
|
380
442
|
routeLocales[routePath] = extractedLocales;
|
|
@@ -454,7 +516,6 @@ const module = defineNuxtModule({
|
|
|
454
516
|
hashMode: nuxt.options?.router?.options?.hashMode ?? false,
|
|
455
517
|
apiBaseUrl,
|
|
456
518
|
apiBaseClientHost,
|
|
457
|
-
apiBaseServerHost,
|
|
458
519
|
isSSG,
|
|
459
520
|
disablePageLocales: options.disablePageLocales ?? false,
|
|
460
521
|
canonicalQueryWhitelist: options.canonicalQueryWhitelist ?? DEFAULT_CANONICAL_QUERY_WHITELIST,
|
|
@@ -464,6 +525,7 @@ const module = defineNuxtModule({
|
|
|
464
525
|
globalLocaleRoutes: mergedGlobalLocaleRoutes,
|
|
465
526
|
missingWarn: options.missingWarn ?? true,
|
|
466
527
|
redirects: options.redirects !== false,
|
|
528
|
+
hooks: options.hooks !== false,
|
|
467
529
|
hmr: options.hmr ?? true,
|
|
468
530
|
localizedRouteNamePrefix: options.localizedRouteNamePrefix ?? "localized-",
|
|
469
531
|
routesLocaleLinks: options.routesLocaleLinks ?? {},
|
|
@@ -536,7 +598,8 @@ export function createI18nStrategy(router) {
|
|
|
536
598
|
routesLocaleLinks: options.routesLocaleLinks ?? {},
|
|
537
599
|
apiBaseUrl,
|
|
538
600
|
apiBaseClientHost,
|
|
539
|
-
apiBaseServerHost
|
|
601
|
+
apiBaseServerHost,
|
|
602
|
+
serverTranslationPreload: options.serverTranslationPreload ?? false
|
|
540
603
|
};
|
|
541
604
|
const privateConfigJson = JSON.stringify(privateConfig);
|
|
542
605
|
const configTemplate = addTemplate({
|
|
@@ -578,10 +641,18 @@ export function getI18nPrivateConfig() { return __privateConfig }
|
|
|
578
641
|
}
|
|
579
642
|
addPlugin({
|
|
580
643
|
src: resolver.resolve("./runtime/plugins/06.redirect"),
|
|
581
|
-
mode: "
|
|
644
|
+
mode: "server",
|
|
582
645
|
name: "i18n-plugin-redirect",
|
|
583
646
|
order: 10
|
|
584
647
|
});
|
|
648
|
+
if (options.redirects !== false && options.plugin !== false) {
|
|
649
|
+
addRouteMiddleware({
|
|
650
|
+
name: "i18n-redirect",
|
|
651
|
+
path: resolver.resolve("./runtime/middleware/i18n-redirect.global"),
|
|
652
|
+
global: true
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
addServerImportsDir(resolver.resolve("./runtime/server/utils"));
|
|
585
656
|
if (translationPayloads.serverHandler) {
|
|
586
657
|
addServerHandler({
|
|
587
658
|
route: `/${apiBaseUrl}/:page/:locale/data.json`,
|
|
@@ -679,22 +750,12 @@ declare module '#i18n-internal/plural' {
|
|
|
679
750
|
if (options.disablePageLocales) {
|
|
680
751
|
nuxt.hook("build:before", () => addDataRoutes([]));
|
|
681
752
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
{ find: "#i18n-internal/plural", replacement: pluralTemplate.dst },
|
|
689
|
-
{ find: "#i18n-internal/strategy", replacement: strategyTemplate.dst },
|
|
690
|
-
{ find: "#i18n-internal/config", replacement: configTemplate.dst }
|
|
691
|
-
] : {
|
|
692
|
-
...alias,
|
|
693
|
-
"#i18n-internal/plural": pluralTemplate.dst,
|
|
694
|
-
"#i18n-internal/strategy": strategyTemplate.dst,
|
|
695
|
-
"#i18n-internal/config": configTemplate.dst
|
|
696
|
-
};
|
|
697
|
-
});
|
|
753
|
+
addVitePlugin(
|
|
754
|
+
createDefineI18nRoutePlugin({
|
|
755
|
+
buildDir: nuxt.options.buildDir,
|
|
756
|
+
rootDirs
|
|
757
|
+
}).vite()
|
|
758
|
+
);
|
|
698
759
|
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
699
760
|
nitroConfig.alias = nitroConfig.alias || {};
|
|
700
761
|
nitroConfig.alias["#i18n-internal/plural"] = pluralTemplate.dst;
|
|
@@ -707,21 +768,20 @@ declare module '#i18n-internal/plural' {
|
|
|
707
768
|
dir: translationAssetsDir
|
|
708
769
|
});
|
|
709
770
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
nitroConfig.
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
}
|
|
771
|
+
nitroConfig.routeRules = nitroConfig.routeRules || {};
|
|
772
|
+
nitroConfig.routeRules[`/${apiBaseUrl}/**`] = {
|
|
773
|
+
...nitroConfig.routeRules[`/${apiBaseUrl}/**`] || {},
|
|
774
|
+
cors: true,
|
|
775
|
+
...nuxt.options.dev ? {} : {
|
|
776
|
+
cache: {
|
|
777
|
+
maxAge: 60,
|
|
778
|
+
swr: true
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
};
|
|
721
782
|
const routeRules = nuxt.options.routeRules || {};
|
|
722
783
|
const strategy = options.strategy;
|
|
723
784
|
if (routeRules && Object.keys(routeRules).length && !isNoPrefixStrategy(strategy)) {
|
|
724
|
-
nitroConfig.routeRules = nitroConfig.routeRules || {};
|
|
725
785
|
for (const [originalPath, ruleValue] of Object.entries(routeRules)) {
|
|
726
786
|
if (!shouldLocalizeRouteRulePath(originalPath)) continue;
|
|
727
787
|
routeGenerator.locales.forEach((localeObj) => {
|
|
@@ -743,6 +803,15 @@ declare module '#i18n-internal/plural' {
|
|
|
743
803
|
});
|
|
744
804
|
}
|
|
745
805
|
}
|
|
806
|
+
nitroConfig.plugins = nitroConfig.plugins || [];
|
|
807
|
+
if (nuxt.options.dev && (options.hmr ?? true)) {
|
|
808
|
+
nitroConfig.plugins.push(resolver.resolve("./runtime/server/plugins/watcher.dev"));
|
|
809
|
+
}
|
|
810
|
+
nitroConfig.handlers = nitroConfig.handlers || [];
|
|
811
|
+
nitroConfig.handlers.unshift({
|
|
812
|
+
middleware: true,
|
|
813
|
+
handler: resolver.resolve("./runtime/server/middleware/i18n.global")
|
|
814
|
+
});
|
|
746
815
|
});
|
|
747
816
|
nuxt.hook("nitro:build:public-assets", (nitro) => {
|
|
748
817
|
const isProd = nuxt.options.dev === false;
|
|
@@ -760,17 +829,6 @@ declare module '#i18n-internal/plural' {
|
|
|
760
829
|
}
|
|
761
830
|
}
|
|
762
831
|
});
|
|
763
|
-
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
764
|
-
nitroConfig.plugins = nitroConfig.plugins || [];
|
|
765
|
-
if (nuxt.options.dev && (options.hmr ?? true)) {
|
|
766
|
-
nitroConfig.plugins.push(resolver.resolve("./runtime/server/plugins/watcher.dev"));
|
|
767
|
-
}
|
|
768
|
-
nitroConfig.handlers = nitroConfig.handlers || [];
|
|
769
|
-
nitroConfig.handlers.unshift({
|
|
770
|
-
middleware: true,
|
|
771
|
-
handler: resolver.resolve("./runtime/server/middleware/i18n.global")
|
|
772
|
-
});
|
|
773
|
-
});
|
|
774
832
|
nuxt.hook("prerender:routes", async (prerenderRoutes) => {
|
|
775
833
|
if (isNoPrefixStrategy(options.strategy)) {
|
|
776
834
|
return;
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<a
|
|
3
|
-
v-if="isExternalLink"
|
|
4
|
-
:href="externalHref"
|
|
5
|
-
:style="computedStyle"
|
|
6
|
-
target="_blank"
|
|
7
|
-
rel="noopener noreferrer"
|
|
8
|
-
>
|
|
2
|
+
<a v-if="isExternalLink" :href="externalHref" :style="computedStyle" target="_blank" rel="noopener noreferrer">
|
|
9
3
|
<slot />
|
|
10
4
|
</a>
|
|
11
5
|
|
|
12
|
-
<NuxtLink
|
|
13
|
-
v-else
|
|
14
|
-
:to="$localePath(to)"
|
|
15
|
-
:style="computedStyle"
|
|
16
|
-
>
|
|
6
|
+
<NuxtLink v-else :to="$localePath(to)" :style="computedStyle">
|
|
17
7
|
<slot />
|
|
18
8
|
</NuxtLink>
|
|
19
9
|
</template>
|
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
<div :style="[wrapperStyle, customWrapperStyle]">
|
|
3
3
|
<slot name="before-button" />
|
|
4
4
|
|
|
5
|
-
<button
|
|
6
|
-
class="language-switcher"
|
|
7
|
-
:style="[buttonStyle, customButtonStyle]"
|
|
8
|
-
@click="toggleDropdown"
|
|
9
|
-
>
|
|
5
|
+
<button class="language-switcher" :style="[buttonStyle, customButtonStyle]" @click="toggleDropdown">
|
|
10
6
|
<slot name="before-selected-locale" />
|
|
11
7
|
{{ currentLocaleLabel }}
|
|
12
8
|
<slot name="after-selected-locale" />
|
|
@@ -15,21 +11,11 @@
|
|
|
15
11
|
|
|
16
12
|
<slot name="before-dropdown" />
|
|
17
13
|
|
|
18
|
-
<ul
|
|
19
|
-
v-show="dropdownOpen"
|
|
20
|
-
:style="[dropdownStyle, customDropdownStyle]"
|
|
21
|
-
>
|
|
14
|
+
<ul v-show="dropdownOpen" :style="[dropdownStyle, customDropdownStyle]">
|
|
22
15
|
<slot name="before-dropdown-items" />
|
|
23
16
|
|
|
24
|
-
<li
|
|
25
|
-
|
|
26
|
-
:key="locale.code"
|
|
27
|
-
:style="[itemStyle, customItemStyle]"
|
|
28
|
-
>
|
|
29
|
-
<slot
|
|
30
|
-
name="before-item"
|
|
31
|
-
:locale="locale"
|
|
32
|
-
/>
|
|
17
|
+
<li v-for="locale in locales" :key="locale.code" :style="[itemStyle, customItemStyle]">
|
|
18
|
+
<slot name="before-item" :locale="locale" />
|
|
33
19
|
|
|
34
20
|
<NuxtLink
|
|
35
21
|
:class="`switcher-locale-${locale.code}`"
|
|
@@ -43,21 +29,12 @@
|
|
|
43
29
|
:hreflang="locale.iso || locale.code"
|
|
44
30
|
@click="switchLocale(locale.code)"
|
|
45
31
|
>
|
|
46
|
-
<slot
|
|
47
|
-
name="before-link-content"
|
|
48
|
-
:locale="locale"
|
|
49
|
-
/>
|
|
32
|
+
<slot name="before-link-content" :locale="locale" />
|
|
50
33
|
{{ localeLabel(locale) }}
|
|
51
|
-
<slot
|
|
52
|
-
name="after-link-content"
|
|
53
|
-
:locale="locale"
|
|
54
|
-
/>
|
|
34
|
+
<slot name="after-link-content" :locale="locale" />
|
|
55
35
|
</NuxtLink>
|
|
56
36
|
|
|
57
|
-
<slot
|
|
58
|
-
name="after-item"
|
|
59
|
-
:locale="locale"
|
|
60
|
-
/>
|
|
37
|
+
<slot name="after-item" :locale="locale" />
|
|
61
38
|
</li>
|
|
62
39
|
|
|
63
40
|
<slot name="after-dropdown-items" />
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { getEnabledLocaleCodes } from "@i18n-micro/utils/active-locales";
|
|
2
2
|
import { getHashCookieName, getLocaleCookieName, getLocaleCookieOptions } from "@i18n-micro/utils/cookie";
|
|
3
3
|
import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
|
|
4
|
-
import { useNuxtApp, useState } from "#app";
|
|
4
|
+
import { useCookie, useNuxtApp, useState } from "#app";
|
|
5
5
|
import { getI18nConfig } from "#build/i18n.strategy.mjs";
|
|
6
|
-
import { useCookie } from "#imports";
|
|
7
6
|
export function useI18nLocale() {
|
|
8
7
|
const nuxtApp = useNuxtApp();
|
|
9
8
|
const i18nConfig = resolveI18nConfigWithRuntimeOverrides(nuxtApp.$getI18nConfig?.() ?? getI18nConfig());
|
|
@@ -35,7 +34,7 @@ export function useI18nLocale() {
|
|
|
35
34
|
return localeState.value ?? localeCookie.value ?? serverLocale ?? null;
|
|
36
35
|
};
|
|
37
36
|
const getEffectiveLocale = (route, getLocaleFromRoute) => {
|
|
38
|
-
if (i18nConfig.hashMode && localeState.value
|
|
37
|
+
if (i18nConfig.hashMode && localeState.value !== null && localeState.value !== void 0) return localeState.value;
|
|
39
38
|
return getLocaleFromRoute(route);
|
|
40
39
|
};
|
|
41
40
|
const resolveInitialLocale = (options) => {
|
|
@@ -2,7 +2,7 @@ import { isNoPrefixStrategy } from "@i18n-micro/core";
|
|
|
2
2
|
import { findAllowedLocalesForRoute } from "@i18n-micro/utils/route";
|
|
3
3
|
import { joinURL, parseURL, withQuery } from "ufo";
|
|
4
4
|
import { ref, unref, watch } from "vue";
|
|
5
|
-
import { useNuxtApp, useRoute } from "#
|
|
5
|
+
import { useNuxtApp, useRoute } from "#app";
|
|
6
6
|
export const useLocaleHead = ({
|
|
7
7
|
addDirAttribute = true,
|
|
8
8
|
identifierAttribute = "id",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getEnabledLocaleCodes } from "@i18n-micro/utils/active-locales";
|
|
2
|
+
import { defineNuxtRouteMiddleware, navigateTo, useNuxtApp } from "#imports";
|
|
3
|
+
import { useI18nLocale } from "../composables/useI18nLocale.js";
|
|
4
|
+
function redirectTo(path, to) {
|
|
5
|
+
return navigateTo({ path, query: to.query, hash: to.hash }, { replace: true, redirectCode: 302 });
|
|
6
|
+
}
|
|
7
|
+
export default defineNuxtRouteMiddleware((to, from) => {
|
|
8
|
+
if (import.meta.server) return;
|
|
9
|
+
const nuxtApp = useNuxtApp();
|
|
10
|
+
const getRuntimeConfig = nuxtApp.$getI18nConfig;
|
|
11
|
+
const i18nStrategy = nuxtApp.$i18nStrategy;
|
|
12
|
+
if (typeof getRuntimeConfig !== "function" || !i18nStrategy) return;
|
|
13
|
+
const i18nConfig = getRuntimeConfig();
|
|
14
|
+
if (i18nConfig.redirects === false) return;
|
|
15
|
+
if (to.path === from.path) return;
|
|
16
|
+
const validLocales = getEnabledLocaleCodes(i18nConfig.locales);
|
|
17
|
+
const defaultLocale = i18nConfig.defaultLocale || "en";
|
|
18
|
+
const autoDetectPath = i18nConfig.autoDetectPath || "/";
|
|
19
|
+
const { getPreferredLocale } = useI18nLocale();
|
|
20
|
+
const routeLocale = i18nStrategy.getCurrentLocale(to, getPreferredLocale());
|
|
21
|
+
let preferredLocale = routeLocale || getPreferredLocale() || defaultLocale;
|
|
22
|
+
const path = to.path || "/";
|
|
23
|
+
const pathSegments = path.replace(/^\//, "").split("/").filter(Boolean);
|
|
24
|
+
const firstSegment = pathSegments[0];
|
|
25
|
+
const hasLocalePrefix = Boolean(firstSegment && validLocales.includes(firstSegment));
|
|
26
|
+
if (autoDetectPath === "*" && !hasLocalePrefix) {
|
|
27
|
+
preferredLocale = defaultLocale;
|
|
28
|
+
}
|
|
29
|
+
if (autoDetectPath === "*" && hasLocalePrefix && firstSegment !== preferredLocale) {
|
|
30
|
+
const rest = pathSegments.slice(1).join("/");
|
|
31
|
+
let targetPath;
|
|
32
|
+
if (preferredLocale === defaultLocale && i18nConfig.strategy === "prefix_except_default") {
|
|
33
|
+
targetPath = rest ? `/${rest}` : "/";
|
|
34
|
+
} else {
|
|
35
|
+
targetPath = rest ? `/${preferredLocale}/${rest}` : `/${preferredLocale}`;
|
|
36
|
+
}
|
|
37
|
+
return redirectTo(targetPath, to);
|
|
38
|
+
}
|
|
39
|
+
const redirectPath = i18nStrategy.getClientRedirect(path, preferredLocale);
|
|
40
|
+
if (redirectPath) {
|
|
41
|
+
return redirectTo(redirectPath, to);
|
|
42
|
+
}
|
|
43
|
+
});
|