nuxt-i18n-micro 3.18.3 → 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.d.mts +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +197 -231
- 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 +5 -6
- package/dist/runtime/composables/useLocaleHead.js +2 -2
- 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 +69 -313
- package/dist/runtime/plugins/02.meta.js +2 -2
- package/dist/runtime/plugins/05.hooks.js +32 -32
- package/dist/runtime/plugins/06.redirect.d.ts +1 -0
- package/dist/runtime/plugins/06.redirect.js +15 -86
- package/dist/runtime/server/middleware/i18n.global.js +17 -5
- package/dist/runtime/server/plugins/watcher.dev.js +36 -66
- package/dist/runtime/server/routes/i18n.js +2 -2
- package/dist/runtime/server/utils/locale-detector.d.ts +9 -12
- package/dist/runtime/server/utils/locale-detector.js +25 -18
- package/dist/runtime/server/utils/locale-server-middleware.js +10 -8
- package/dist/runtime/server/utils/server-loader.d.ts +3 -3
- package/dist/runtime/server/utils/server-loader.js +40 -15
- package/dist/runtime/server/utils/translation-server-middleware.js +10 -8
- 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 +11 -12
- package/dist/runtime/utils/storage.js +37 -21
- package/dist/types.d.mts +2 -0
- package/internals.d.mts +1 -0
- package/package.json +21 -15
- package/dist/client/_nuxt/B-cq5x_h.js +0 -14
- package/dist/client/_nuxt/builds/meta/c5d2004e-f981-4363-a9ec-4d72cf7f4a8f.json +0 -1
- package/dist/runtime/utils/accept-language.d.ts +0 -3
- package/dist/runtime/utils/accept-language.js +0 -21
- package/dist/runtime/utils/active-locales.d.ts +0 -4
- package/dist/runtime/utils/active-locales.js +0 -9
- package/dist/runtime/utils/cache-control.d.ts +0 -50
- package/dist/runtime/utils/cache-control.js +0 -88
- package/dist/runtime/utils/cookie.d.ts +0 -24
- package/dist/runtime/utils/cookie.js +0 -22
- package/dist/runtime/utils/deep-merge.d.ts +0 -15
- package/dist/runtime/utils/deep-merge.js +0 -14
- package/dist/runtime/utils/resolve-server-locale.d.ts +0 -21
- package/dist/runtime/utils/resolve-server-locale.js +0 -30
- package/dist/runtime/utils/route-utils.d.ts +0 -33
- package/dist/runtime/utils/route-utils.js +0 -63
- package/dist/runtime/utils/runtime-i18n-config.d.ts +0 -8
- package/dist/runtime/utils/runtime-i18n-config.js +0 -90
|
@@ -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.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { HookResult } from '@nuxt/schema';
|
|
|
3
3
|
import { ModuleOptions } from '@i18n-micro/types';
|
|
4
4
|
export { Getter, GlobalLocaleRoutes, Locale, LocaleCode, ModuleOptions, PluralFunc, Strategies } from '@i18n-micro/types';
|
|
5
5
|
export { PluginsInjections } from '../dist/runtime/plugins/01.plugin.js';
|
|
6
|
+
export { TranslationPayloadMode, resolveTranslationPayloadMode, resolveTranslationPayloadOptions, resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config';
|
|
6
7
|
|
|
7
8
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
8
9
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
|
-
import fs__default, { readFileSync,
|
|
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';
|
|
6
6
|
import { defaultPlural, isNoPrefixStrategy, withPrefixStrategy } from '@i18n-micro/core';
|
|
7
|
+
import { generateHmrPlugin } from '@i18n-micro/hmr/generate-plugin';
|
|
7
8
|
import { RouteGenerator, isLocaleAllowedForUnlocalizedRoute, normalizePath, isInternalPath } from '@i18n-micro/route-strategy';
|
|
8
|
-
import {
|
|
9
|
+
import { buildTranslationSourceLayers, preMergeLocales } from '@i18n-micro/utils/build';
|
|
10
|
+
import { resolveTranslationPayloadOptions, getTranslationPayloadMisconfigurationWarnings, getTranslationPayloadSizeWarning, resolveTranslationPayloadWarningThresholds, resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config';
|
|
11
|
+
export { resolveTranslationPayloadMode, resolveTranslationPayloadOptions, resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config';
|
|
12
|
+
import { scanTranslationPayloadDirectory } from '@i18n-micro/utils/payload-stats';
|
|
13
|
+
import { useNuxt, defineNuxtModule, useLogger, createResolver, addTemplate, addImportsDir, addPlugin, addRouteMiddleware, addServerImportsDir, addServerHandler, addComponentsDir, addTypeTemplate, addVitePlugin, addPrerenderRoutes } from '@nuxt/kit';
|
|
9
14
|
import { globby } from 'globby';
|
|
10
15
|
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
16
|
+
import { createUnplugin } from 'unplugin';
|
|
17
|
+
import { parse } from '@vue/compiler-sfc';
|
|
11
18
|
|
|
12
19
|
const DEVTOOLS_UI_PORT = 3030;
|
|
13
20
|
const DEVTOOLS_UI_ROUTE = "/__nuxt-i18n-micro";
|
|
@@ -118,46 +125,6 @@ function setupDevToolsUI(options, resolve2, rootDirs) {
|
|
|
118
125
|
});
|
|
119
126
|
}
|
|
120
127
|
|
|
121
|
-
function generateHmrPlugin(files) {
|
|
122
|
-
const accepts = files.map((file) => {
|
|
123
|
-
const isPage = /\/pages\//.test(file);
|
|
124
|
-
let pageName = "";
|
|
125
|
-
let locale = "";
|
|
126
|
-
if (isPage) {
|
|
127
|
-
const m = /\/pages\/(.+)\/([^/]+)\.json$/.exec(file);
|
|
128
|
-
pageName = m?.[1] || "";
|
|
129
|
-
locale = m?.[2] || "";
|
|
130
|
-
} else {
|
|
131
|
-
const m = /\/([^/]+)\.json$/.exec(file);
|
|
132
|
-
locale = m?.[1] || "";
|
|
133
|
-
}
|
|
134
|
-
return `
|
|
135
|
-
if (import.meta.hot) {
|
|
136
|
-
import.meta.hot.accept('${file}', async (mod) => {
|
|
137
|
-
const nuxtApp = useNuxtApp()
|
|
138
|
-
const data = (mod && typeof mod === 'object' && Object.prototype.hasOwnProperty.call(mod, 'default'))
|
|
139
|
-
? mod.default
|
|
140
|
-
: mod
|
|
141
|
-
try {
|
|
142
|
-
${isPage ? `await nuxtApp.$loadPageTranslations('${locale}', '${pageName}', data)` : `await nuxtApp.$loadTranslations('${locale}', data)`}
|
|
143
|
-
console.log('[i18n HMR] Translations reloaded:', '${isPage ? "page" : "global"}', '${locale}'${isPage ? `, '${pageName}'` : ""})
|
|
144
|
-
}
|
|
145
|
-
catch (e) {
|
|
146
|
-
console.warn('[i18n HMR] Failed to reload translations for', '${file}', e)
|
|
147
|
-
}
|
|
148
|
-
})
|
|
149
|
-
}
|
|
150
|
-
`.trim();
|
|
151
|
-
}).join("\n");
|
|
152
|
-
return `
|
|
153
|
-
import { defineNuxtPlugin, useNuxtApp } from '#imports'
|
|
154
|
-
|
|
155
|
-
export default defineNuxtPlugin(() => {
|
|
156
|
-
${accepts}
|
|
157
|
-
})
|
|
158
|
-
`.trim();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
128
|
function shouldLocalizeRouteRulePath(originalPath) {
|
|
162
129
|
const routeRulePath = originalPath.trim();
|
|
163
130
|
if (!routeRulePath) return true;
|
|
@@ -168,8 +135,11 @@ function shouldLocalizeRouteRulePath(originalPath) {
|
|
|
168
135
|
}
|
|
169
136
|
|
|
170
137
|
function extractScriptContent(content) {
|
|
171
|
-
const
|
|
172
|
-
|
|
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;
|
|
173
143
|
}
|
|
174
144
|
function removeTypeScriptTypes(scriptContent) {
|
|
175
145
|
return scriptContent.replace(/\((\w+):[^)]*\)/g, "($1)");
|
|
@@ -214,17 +184,10 @@ function findDefineI18nRouteConfig(scriptContent) {
|
|
|
214
184
|
const scriptWithoutImports = scriptContent.split("\n").filter((line) => !line.trim().startsWith("import ")).join("\n");
|
|
215
185
|
const cleanScript = removeTypeScriptTypes(scriptWithoutImports);
|
|
216
186
|
const safeScript = `
|
|
217
|
-
// Mock $defineI18nRoute to prevent errors
|
|
218
187
|
const $defineI18nRoute = () => {}
|
|
219
188
|
const defineI18nRoute = () => {}
|
|
220
|
-
|
|
221
|
-
// Mock process.env for conditional logic
|
|
222
189
|
const process = { env: { NODE_ENV: 'development' } }
|
|
223
|
-
|
|
224
|
-
// Execute the script content without imports and TypeScript types
|
|
225
190
|
${cleanScript}
|
|
226
|
-
|
|
227
|
-
// Return the config object
|
|
228
191
|
return (${cleanConfigStr})
|
|
229
192
|
`;
|
|
230
193
|
const configObject = Function(`"use strict";${safeScript}`)();
|
|
@@ -242,6 +205,37 @@ function findDefineI18nRouteConfig(scriptContent) {
|
|
|
242
205
|
return null;
|
|
243
206
|
}
|
|
244
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
|
+
}
|
|
245
239
|
function extractDefineI18nRouteData(content, _filePath) {
|
|
246
240
|
try {
|
|
247
241
|
const scriptContent = extractScriptContent(content);
|
|
@@ -252,131 +246,79 @@ function extractDefineI18nRouteData(content, _filePath) {
|
|
|
252
246
|
if (!configObject) {
|
|
253
247
|
return null;
|
|
254
248
|
}
|
|
255
|
-
|
|
256
|
-
const localesObj = configObject.locales;
|
|
257
|
-
const normalizedLocales = [];
|
|
258
|
-
const normalizedLocaleRoutes = {};
|
|
259
|
-
for (const [locale, value] of Object.entries(localesObj)) {
|
|
260
|
-
normalizedLocales.push(locale);
|
|
261
|
-
if (value && typeof value === "object" && "path" in value && typeof value.path === "string") {
|
|
262
|
-
normalizedLocaleRoutes[locale] = value.path;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return {
|
|
266
|
-
...configObject,
|
|
267
|
-
locales: normalizedLocales,
|
|
268
|
-
localeRoutes: configObject.localeRoutes || Object.keys(normalizedLocaleRoutes).length > 0 ? { ...configObject.localeRoutes, ...normalizedLocaleRoutes } : void 0
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
if (Array.isArray(configObject.locales) && configObject.locales.length > 0 && typeof configObject.locales[0] === "object") {
|
|
272
|
-
const normalizedLocales = configObject.locales.map((item) => {
|
|
273
|
-
if (item && typeof item === "object" && "code" in item) {
|
|
274
|
-
return item.code;
|
|
275
|
-
}
|
|
276
|
-
return String(item);
|
|
277
|
-
});
|
|
278
|
-
return {
|
|
279
|
-
...configObject,
|
|
280
|
-
locales: normalizedLocales
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
return configObject;
|
|
249
|
+
return normalizeDefineConfig(configObject);
|
|
284
250
|
} catch {
|
|
285
251
|
return null;
|
|
286
252
|
}
|
|
287
253
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (key === "__proto__" || key === "constructor") continue;
|
|
294
|
-
const src = source[key];
|
|
295
|
-
const dst = output[key];
|
|
296
|
-
if (src && typeof src === "object" && !Array.isArray(src) && dst && typeof dst === "object" && !Array.isArray(dst)) {
|
|
297
|
-
output[key] = deepMergeTranslations(dst, src);
|
|
298
|
-
} else {
|
|
299
|
-
output[key] = src;
|
|
300
|
-
}
|
|
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 "/";
|
|
301
259
|
}
|
|
302
|
-
|
|
260
|
+
const raw = withoutPagesPrefix.replace(/[/\\]index\.vue$/, "").replace(/\.vue$/, "").replace(/[/\\]$/, "").replace(/\\/g, "/");
|
|
261
|
+
return raw === "" ? "/" : raw;
|
|
303
262
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
let content = {};
|
|
318
|
-
for (const lp of layerPaths) {
|
|
319
|
-
const fp = join(lp, file);
|
|
320
|
-
if (existsSync(fp)) {
|
|
321
|
-
try {
|
|
322
|
-
content = deepMergeTranslations(content, JSON.parse(readFileSync(fp, "utf-8")));
|
|
323
|
-
} catch {
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
merged.set(file, content);
|
|
328
|
-
}
|
|
329
|
-
const rootMap = /* @__PURE__ */ new Map();
|
|
330
|
-
const pageMap = /* @__PURE__ */ new Map();
|
|
331
|
-
for (const [file, content] of merged) {
|
|
332
|
-
const dir = dirname(file);
|
|
333
|
-
const locale = file.slice(file.lastIndexOf("/") + 1).replace(".json", "");
|
|
334
|
-
if (dir === ".") {
|
|
335
|
-
rootMap.set(locale, content);
|
|
336
|
-
} else {
|
|
337
|
-
if (!pageMap.has(dir)) pageMap.set(dir, /* @__PURE__ */ new Map());
|
|
338
|
-
pageMap.get(dir).set(locale, content);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
const knownCodes = new Set(locales.map((l) => l.code));
|
|
342
|
-
const applyFallback = (map) => {
|
|
343
|
-
for (const locale of locales) {
|
|
344
|
-
const chain = [globalFallbackLocale, locale.fallbackLocale, locale.code].filter((l) => !!l && knownCodes.has(l)).filter((v, i, arr) => arr.indexOf(v) === i);
|
|
345
|
-
if (chain.length <= 1) continue;
|
|
346
|
-
let result = {};
|
|
347
|
-
for (const code of chain) {
|
|
348
|
-
const data = map.get(code);
|
|
349
|
-
if (data) result = deepMergeTranslations(result, data);
|
|
350
|
-
}
|
|
351
|
-
map.set(locale.code, result);
|
|
352
|
-
}
|
|
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));
|
|
353
276
|
};
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
+
});
|
|
369
294
|
}
|
|
295
|
+
writeMetaFile();
|
|
296
|
+
return null;
|
|
297
|
+
},
|
|
298
|
+
buildEnd() {
|
|
299
|
+
writeMetaFile();
|
|
370
300
|
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
|
314
|
+
});
|
|
315
|
+
} catch {
|
|
377
316
|
}
|
|
378
317
|
}
|
|
318
|
+
return entries;
|
|
379
319
|
}
|
|
320
|
+
|
|
321
|
+
const DEFAULT_CANONICAL_QUERY_WHITELIST = ["page", "sort", "filter", "search", "q", "query", "tag"];
|
|
380
322
|
function generateI18nTypes() {
|
|
381
323
|
return `
|
|
382
324
|
import type {PluginsInjections} from "nuxt-i18n-micro";
|
|
@@ -425,6 +367,13 @@ const module = defineNuxtModule({
|
|
|
425
367
|
apiBaseUrl: "_locales",
|
|
426
368
|
apiBaseClientHost: void 0,
|
|
427
369
|
apiBaseServerHost: void 0,
|
|
370
|
+
translationPayloads: {
|
|
371
|
+
mode: "premerged",
|
|
372
|
+
serverAssets: true,
|
|
373
|
+
serverHandler: true,
|
|
374
|
+
publicAssets: true,
|
|
375
|
+
prerenderRoutes: true
|
|
376
|
+
},
|
|
428
377
|
routesLocaleLinks: {},
|
|
429
378
|
globalLocaleRoutes: {},
|
|
430
379
|
canonicalQueryWhitelist: void 0,
|
|
@@ -438,7 +387,7 @@ const module = defineNuxtModule({
|
|
|
438
387
|
},
|
|
439
388
|
async setup(options, nuxt) {
|
|
440
389
|
const defaultLocale = process.env.DEFAULT_LOCALE ?? options.defaultLocale ?? "en";
|
|
441
|
-
const isSSG = nuxt.options.nitro
|
|
390
|
+
const isSSG = Boolean(nuxt.options.nitro?.static);
|
|
442
391
|
const logger = useLogger("nuxt-i18n-micro");
|
|
443
392
|
if (options.strategy === "no_prefix" && !options.localeCookie) {
|
|
444
393
|
options.localeCookie = "user-locale";
|
|
@@ -452,27 +401,42 @@ const module = defineNuxtModule({
|
|
|
452
401
|
const resolver = createResolver(import.meta.url);
|
|
453
402
|
const rootDirs = nuxt.options._layers.map((layer) => layer.config.rootDir).reverse();
|
|
454
403
|
const mergedLocalesDir = resolve(nuxt.options.buildDir, "i18n-merged");
|
|
404
|
+
const sourceLocalesDir = resolve(nuxt.options.buildDir, "i18n-source");
|
|
455
405
|
const translationDirName = options.translationDir || "locales";
|
|
406
|
+
const translationPayloads = resolveTranslationPayloadOptions(options);
|
|
407
|
+
const translationAssetsDir = translationPayloads.mode === "source" ? sourceLocalesDir : mergedLocalesDir;
|
|
408
|
+
for (const warning of getTranslationPayloadMisconfigurationWarnings({
|
|
409
|
+
translationPayloads,
|
|
410
|
+
apiBaseClientHost: options.apiBaseClientHost,
|
|
411
|
+
apiBaseServerHost: options.apiBaseServerHost
|
|
412
|
+
})) {
|
|
413
|
+
logger.warn(warning.replace("[nuxt-i18n-micro] ", ""));
|
|
414
|
+
}
|
|
456
415
|
const localeInfos = (options.locales ?? []).map(
|
|
457
416
|
(l) => typeof l === "string" ? { code: l } : { code: l.code, fallbackLocale: l.fallbackLocale }
|
|
458
417
|
);
|
|
459
418
|
nuxt.hook("build:before", async () => {
|
|
460
|
-
|
|
461
|
-
|
|
419
|
+
if (translationPayloads.mode === "source") {
|
|
420
|
+
await buildTranslationSourceLayers(rootDirs, translationDirName, sourceLocalesDir);
|
|
421
|
+
logger.info(`Built compact translation source from ${rootDirs.length} layer(s) into ${sourceLocalesDir}`);
|
|
422
|
+
} else {
|
|
423
|
+
await preMergeLocales(rootDirs, translationDirName, mergedLocalesDir, localeInfos, options.fallbackLocale, options.disablePageLocales);
|
|
424
|
+
logger.info(`Pre-merged translations from ${rootDirs.length} layer(s) into ${mergedLocalesDir}`);
|
|
425
|
+
}
|
|
426
|
+
const payloadStats = scanTranslationPayloadDirectory(translationAssetsDir);
|
|
427
|
+
const sizeWarning = getTranslationPayloadSizeWarning(payloadStats, resolveTranslationPayloadWarningThresholds(options.translationPayloads));
|
|
428
|
+
if (sizeWarning) {
|
|
429
|
+
logger.warn(sizeWarning.replace("[nuxt-i18n-micro] ", ""));
|
|
430
|
+
}
|
|
462
431
|
});
|
|
463
432
|
const routeLocales = { ...options.routeLocales ?? {} };
|
|
464
433
|
const globalLocaleRoutes = {};
|
|
465
434
|
const routeDisableMeta = {};
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
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)) {
|
|
469
438
|
try {
|
|
470
|
-
const fileContent = readFileSync(fullPath, "utf-8");
|
|
471
|
-
const config = extractDefineI18nRouteData(fileContent, fullPath);
|
|
472
|
-
if (!config) continue;
|
|
473
439
|
const { locales: extractedLocales, localeRoutes, disableMeta } = config;
|
|
474
|
-
const raw = pageFile.replace(/^(app\/)?pages\//, "").replace(/\/index\.vue$/, "").replace(/\.vue$/, "").replace(/\/$/, "");
|
|
475
|
-
const routePath = raw === "" || raw === "index" ? "/" : raw;
|
|
476
440
|
if (extractedLocales) {
|
|
477
441
|
if (Array.isArray(extractedLocales)) {
|
|
478
442
|
routeLocales[routePath] = extractedLocales;
|
|
@@ -552,7 +516,6 @@ const module = defineNuxtModule({
|
|
|
552
516
|
hashMode: nuxt.options?.router?.options?.hashMode ?? false,
|
|
553
517
|
apiBaseUrl,
|
|
554
518
|
apiBaseClientHost,
|
|
555
|
-
apiBaseServerHost,
|
|
556
519
|
isSSG,
|
|
557
520
|
disablePageLocales: options.disablePageLocales ?? false,
|
|
558
521
|
canonicalQueryWhitelist: options.canonicalQueryWhitelist ?? DEFAULT_CANONICAL_QUERY_WHITELIST,
|
|
@@ -562,6 +525,7 @@ const module = defineNuxtModule({
|
|
|
562
525
|
globalLocaleRoutes: mergedGlobalLocaleRoutes,
|
|
563
526
|
missingWarn: options.missingWarn ?? true,
|
|
564
527
|
redirects: options.redirects !== false,
|
|
528
|
+
hooks: options.hooks !== false,
|
|
565
529
|
hmr: options.hmr ?? true,
|
|
566
530
|
localizedRouteNamePrefix: options.localizedRouteNamePrefix ?? "localized-",
|
|
567
531
|
routesLocaleLinks: options.routesLocaleLinks ?? {},
|
|
@@ -569,7 +533,8 @@ const module = defineNuxtModule({
|
|
|
569
533
|
debug: options.debug ?? false,
|
|
570
534
|
customRegexMatcher: options.customRegexMatcher instanceof RegExp ? options.customRegexMatcher.source : options.customRegexMatcher,
|
|
571
535
|
cacheMaxSize: options.cacheMaxSize ?? 0,
|
|
572
|
-
cacheTtl: options.cacheTtl ?? 0
|
|
536
|
+
cacheTtl: options.cacheTtl ?? 0,
|
|
537
|
+
translationPayloadMode: translationPayloads.mode
|
|
573
538
|
};
|
|
574
539
|
const fullConfigJson = JSON.stringify(fullConfig);
|
|
575
540
|
const strategyTemplate = addTemplate({
|
|
@@ -633,7 +598,8 @@ export function createI18nStrategy(router) {
|
|
|
633
598
|
routesLocaleLinks: options.routesLocaleLinks ?? {},
|
|
634
599
|
apiBaseUrl,
|
|
635
600
|
apiBaseClientHost,
|
|
636
|
-
apiBaseServerHost
|
|
601
|
+
apiBaseServerHost,
|
|
602
|
+
serverTranslationPreload: options.serverTranslationPreload ?? false
|
|
637
603
|
};
|
|
638
604
|
const privateConfigJson = JSON.stringify(privateConfig);
|
|
639
605
|
const configTemplate = addTemplate({
|
|
@@ -675,14 +641,24 @@ export function getI18nPrivateConfig() { return __privateConfig }
|
|
|
675
641
|
}
|
|
676
642
|
addPlugin({
|
|
677
643
|
src: resolver.resolve("./runtime/plugins/06.redirect"),
|
|
678
|
-
mode: "
|
|
644
|
+
mode: "server",
|
|
679
645
|
name: "i18n-plugin-redirect",
|
|
680
646
|
order: 10
|
|
681
647
|
});
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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"));
|
|
656
|
+
if (translationPayloads.serverHandler) {
|
|
657
|
+
addServerHandler({
|
|
658
|
+
route: `/${apiBaseUrl}/:page/:locale/data.json`,
|
|
659
|
+
handler: resolver.resolve("./runtime/server/routes/i18n")
|
|
660
|
+
});
|
|
661
|
+
}
|
|
686
662
|
if (options.components !== false) {
|
|
687
663
|
addComponentsDir({
|
|
688
664
|
path: resolver.resolve("./runtime/components"),
|
|
@@ -758,6 +734,7 @@ declare module '#i18n-internal/plural' {
|
|
|
758
734
|
}
|
|
759
735
|
});
|
|
760
736
|
const addDataRoutes = (pages = []) => {
|
|
737
|
+
if (!translationPayloads.prerenderRoutes) return;
|
|
761
738
|
const pagesForDataRoutes = pages.filter((p) => p.name !== void 0 && (!options.routesLocaleLinks || !options.routesLocaleLinks[p.name]));
|
|
762
739
|
const dataRoutes = routeGenerator.generateDataRoutes(pagesForDataRoutes, apiBaseUrl, !!options.disablePageLocales);
|
|
763
740
|
addPrerenderRoutes(dataRoutes);
|
|
@@ -773,47 +750,38 @@ declare module '#i18n-internal/plural' {
|
|
|
773
750
|
if (options.disablePageLocales) {
|
|
774
751
|
nuxt.hook("build:before", () => addDataRoutes([]));
|
|
775
752
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
{ find: "#i18n-internal/plural", replacement: pluralTemplate.dst },
|
|
783
|
-
{ find: "#i18n-internal/strategy", replacement: strategyTemplate.dst },
|
|
784
|
-
{ find: "#i18n-internal/config", replacement: configTemplate.dst }
|
|
785
|
-
] : {
|
|
786
|
-
...alias,
|
|
787
|
-
"#i18n-internal/plural": pluralTemplate.dst,
|
|
788
|
-
"#i18n-internal/strategy": strategyTemplate.dst,
|
|
789
|
-
"#i18n-internal/config": configTemplate.dst
|
|
790
|
-
};
|
|
791
|
-
});
|
|
753
|
+
addVitePlugin(
|
|
754
|
+
createDefineI18nRoutePlugin({
|
|
755
|
+
buildDir: nuxt.options.buildDir,
|
|
756
|
+
rootDirs
|
|
757
|
+
}).vite()
|
|
758
|
+
);
|
|
792
759
|
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
793
760
|
nitroConfig.alias = nitroConfig.alias || {};
|
|
794
761
|
nitroConfig.alias["#i18n-internal/plural"] = pluralTemplate.dst;
|
|
795
762
|
nitroConfig.alias["#i18n-internal/strategy"] = strategyTemplate.dst;
|
|
796
763
|
nitroConfig.alias["#i18n-internal/config"] = configTemplate.dst;
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
if (nitroConfig.imports) {
|
|
803
|
-
nitroConfig.imports.presets = nitroConfig.imports.presets || [];
|
|
804
|
-
nitroConfig.imports.presets.push({
|
|
805
|
-
from: resolver.resolve("./runtime/server/utils/translation-server-middleware"),
|
|
806
|
-
imports: ["useTranslationServerMiddleware"]
|
|
807
|
-
});
|
|
808
|
-
nitroConfig.imports.presets.push({
|
|
809
|
-
from: resolver.resolve("./runtime/server/utils/locale-server-middleware"),
|
|
810
|
-
imports: ["useLocaleServerMiddleware"]
|
|
764
|
+
if (translationPayloads.serverAssets) {
|
|
765
|
+
nitroConfig.serverAssets = nitroConfig.serverAssets || [];
|
|
766
|
+
nitroConfig.serverAssets.push({
|
|
767
|
+
baseName: "i18n",
|
|
768
|
+
dir: translationAssetsDir
|
|
811
769
|
});
|
|
812
770
|
}
|
|
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
|
+
};
|
|
813
782
|
const routeRules = nuxt.options.routeRules || {};
|
|
814
783
|
const strategy = options.strategy;
|
|
815
784
|
if (routeRules && Object.keys(routeRules).length && !isNoPrefixStrategy(strategy)) {
|
|
816
|
-
nitroConfig.routeRules = nitroConfig.routeRules || {};
|
|
817
785
|
for (const [originalPath, ruleValue] of Object.entries(routeRules)) {
|
|
818
786
|
if (!shouldLocalizeRouteRulePath(originalPath)) continue;
|
|
819
787
|
routeGenerator.locales.forEach((localeObj) => {
|
|
@@ -835,34 +803,32 @@ declare module '#i18n-internal/plural' {
|
|
|
835
803
|
});
|
|
836
804
|
}
|
|
837
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
|
+
});
|
|
838
815
|
});
|
|
839
816
|
nuxt.hook("nitro:build:public-assets", (nitro) => {
|
|
840
817
|
const isProd = nuxt.options.dev === false;
|
|
841
|
-
if (isProd) {
|
|
842
|
-
const publicDir =
|
|
818
|
+
if (isProd && translationPayloads.publicAssets) {
|
|
819
|
+
const publicDir = resolveTranslationPayloadPublicDir(nitro.options.output.publicDir, options);
|
|
843
820
|
try {
|
|
844
|
-
if (existsSync(
|
|
845
|
-
fs__default.cpSync(
|
|
846
|
-
logger.log(`
|
|
821
|
+
if (existsSync(translationAssetsDir)) {
|
|
822
|
+
fs__default.cpSync(translationAssetsDir, publicDir, { recursive: true });
|
|
823
|
+
logger.log(`Translation payloads copied to public directory`);
|
|
847
824
|
} else {
|
|
848
|
-
logger.warn(`
|
|
825
|
+
logger.warn(`Translation assets directory not found: ${translationAssetsDir}`);
|
|
849
826
|
}
|
|
850
827
|
} catch (err) {
|
|
851
828
|
logger.error("Error copying translations:", err);
|
|
852
829
|
}
|
|
853
830
|
}
|
|
854
831
|
});
|
|
855
|
-
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
856
|
-
nitroConfig.plugins = nitroConfig.plugins || [];
|
|
857
|
-
if (nuxt.options.dev && (options.hmr ?? true)) {
|
|
858
|
-
nitroConfig.plugins.push(resolver.resolve("./runtime/server/plugins/watcher.dev"));
|
|
859
|
-
}
|
|
860
|
-
nitroConfig.handlers = nitroConfig.handlers || [];
|
|
861
|
-
nitroConfig.handlers.unshift({
|
|
862
|
-
middleware: true,
|
|
863
|
-
handler: resolver.resolve("./runtime/server/middleware/i18n.global")
|
|
864
|
-
});
|
|
865
|
-
});
|
|
866
832
|
nuxt.hook("prerender:routes", async (prerenderRoutes) => {
|
|
867
833
|
if (isNoPrefixStrategy(options.strategy)) {
|
|
868
834
|
return;
|