nuxt-i18n-micro 3.6.0 → 3.8.1
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 +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/d7d6673c-1eb1-4a32-9ad2-3189a0806b98.json +1 -0
- package/dist/client/index.html +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +119 -22
- package/dist/runtime/composables/useI18n.js +0 -1
- package/dist/runtime/plugins/01.plugin.d.ts +4 -7
- package/dist/runtime/plugins/01.plugin.js +25 -46
- package/dist/runtime/plugins/02.meta.js +5 -2
- package/dist/runtime/plugins/03.define.js +2 -2
- package/dist/runtime/server/routes/i18n.js +1 -1
- package/dist/runtime/server/utils/server-loader.d.ts +6 -6
- package/dist/runtime/server/utils/server-loader.js +11 -59
- package/dist/runtime/server/utils/translation-server-middleware.js +28 -18
- package/dist/runtime/utils/storage.js +3 -2
- package/internals.d.mts +0 -1
- package/package.json +5 -5
- package/dist/client/_nuxt/builds/meta/628ed9e2-a5ab-4c20-b953-a0de4eafc594.json +0 -1
package/dist/client/200.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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CXlkzKTu.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"
|
|
1
|
+
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CXlkzKTu.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"d7d6673c-1eb1-4a32-9ad2-3189a0806b98",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1770976916930,false]</script></body></html>
|
package/dist/client/404.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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CXlkzKTu.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"
|
|
1
|
+
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CXlkzKTu.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"d7d6673c-1eb1-4a32-9ad2-3189a0806b98",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1770976916930,false]</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"
|
|
1
|
+
{"id":"d7d6673c-1eb1-4a32-9ad2-3189a0806b98","timestamp":1770976913871}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id":"d7d6673c-1eb1-4a32-9ad2-3189a0806b98","timestamp":1770976913871,"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/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CXlkzKTu.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"
|
|
1
|
+
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/entry.Kj_DYE7z.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js"><script type="module" src="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/D_aXYej0.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-404.CyBDSRXN.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Brscz0ku.js"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/Dt5GiM1K.js"><link rel="prefetch" as="style" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/error-500.DJtCwW7w.css"><link rel="prefetch" as="script" crossorigin href="/__NUXT_DEVTOOLS_I18N_BASE__/_nuxt/CXlkzKTu.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__NUXT_DEVTOOLS_I18N_BASE__/",buildId:"d7d6673c-1eb1-4a32-9ad2-3189a0806b98",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1770976916930,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 } from 'node:fs';
|
|
2
|
+
import fs__default, { readFileSync, existsSync, mkdirSync, writeFileSync } 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';
|
|
@@ -277,6 +277,96 @@ function extractDefineI18nRouteData(content, _filePath) {
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
function deepMergeTranslations(target, source) {
|
|
281
|
+
if (!target || Object.keys(target).length === 0) return { ...source };
|
|
282
|
+
const output = { ...target };
|
|
283
|
+
for (const key in source) {
|
|
284
|
+
if (key === "__proto__" || key === "constructor") continue;
|
|
285
|
+
const src = source[key];
|
|
286
|
+
const dst = output[key];
|
|
287
|
+
if (src && typeof src === "object" && !Array.isArray(src) && dst && typeof dst === "object" && !Array.isArray(dst)) {
|
|
288
|
+
output[key] = deepMergeTranslations(dst, src);
|
|
289
|
+
} else {
|
|
290
|
+
output[key] = src;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return output;
|
|
294
|
+
}
|
|
295
|
+
async function preMergeLocales(rootDirs, translationDirName, outputDir, locales, globalFallbackLocale, disablePageLocales) {
|
|
296
|
+
if (existsSync(outputDir)) fs__default.rmSync(outputDir, { recursive: true, force: true });
|
|
297
|
+
mkdirSync(outputDir, { recursive: true });
|
|
298
|
+
const layerPaths = rootDirs.map((dir) => join(dir, translationDirName));
|
|
299
|
+
const allFiles = /* @__PURE__ */ new Set();
|
|
300
|
+
for (const lp of layerPaths) {
|
|
301
|
+
if (!existsSync(lp)) continue;
|
|
302
|
+
const files = await globby("**/*.json", { cwd: lp });
|
|
303
|
+
files.forEach((f) => allFiles.add(f));
|
|
304
|
+
}
|
|
305
|
+
const merged = /* @__PURE__ */ new Map();
|
|
306
|
+
for (const file of allFiles) {
|
|
307
|
+
let content = {};
|
|
308
|
+
for (const lp of layerPaths) {
|
|
309
|
+
const fp = join(lp, file);
|
|
310
|
+
if (existsSync(fp)) {
|
|
311
|
+
try {
|
|
312
|
+
content = deepMergeTranslations(content, JSON.parse(readFileSync(fp, "utf-8")));
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
merged.set(file, content);
|
|
318
|
+
}
|
|
319
|
+
const rootMap = /* @__PURE__ */ new Map();
|
|
320
|
+
const pageMap = /* @__PURE__ */ new Map();
|
|
321
|
+
for (const [file, content] of merged) {
|
|
322
|
+
const dir = dirname(file);
|
|
323
|
+
const locale = file.slice(file.lastIndexOf("/") + 1).replace(".json", "");
|
|
324
|
+
if (dir === ".") {
|
|
325
|
+
rootMap.set(locale, content);
|
|
326
|
+
} else {
|
|
327
|
+
if (!pageMap.has(dir)) pageMap.set(dir, /* @__PURE__ */ new Map());
|
|
328
|
+
pageMap.get(dir).set(locale, content);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const knownCodes = new Set(locales.map((l) => l.code));
|
|
332
|
+
const applyFallback = (map) => {
|
|
333
|
+
for (const locale of locales) {
|
|
334
|
+
const chain = [globalFallbackLocale, locale.fallbackLocale, locale.code].filter((l) => !!l && knownCodes.has(l)).filter((v, i, arr) => arr.indexOf(v) === i);
|
|
335
|
+
if (chain.length <= 1) continue;
|
|
336
|
+
let result = {};
|
|
337
|
+
for (const code of chain) {
|
|
338
|
+
const data = map.get(code);
|
|
339
|
+
if (data) result = deepMergeTranslations(result, data);
|
|
340
|
+
}
|
|
341
|
+
map.set(locale.code, result);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
applyFallback(rootMap);
|
|
345
|
+
for (const localeMap of pageMap.values()) {
|
|
346
|
+
applyFallback(localeMap);
|
|
347
|
+
}
|
|
348
|
+
if (disablePageLocales || pageMap.size === 0) {
|
|
349
|
+
const indexMap = /* @__PURE__ */ new Map();
|
|
350
|
+
for (const [locale, data] of rootMap) {
|
|
351
|
+
indexMap.set(locale, { ...data });
|
|
352
|
+
}
|
|
353
|
+
pageMap.set("pages/index", indexMap);
|
|
354
|
+
} else {
|
|
355
|
+
for (const [, localeMap] of pageMap) {
|
|
356
|
+
for (const [locale, rootData] of rootMap) {
|
|
357
|
+
const pageData = localeMap.get(locale);
|
|
358
|
+
localeMap.set(locale, pageData ? deepMergeTranslations(rootData, pageData) : { ...rootData });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
for (const [context, localeMap] of pageMap) {
|
|
363
|
+
for (const [locale, data] of localeMap) {
|
|
364
|
+
const targetPath = join(outputDir, context, `${locale}.json`);
|
|
365
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
366
|
+
writeFileSync(targetPath, JSON.stringify(data));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
280
370
|
function generateI18nTypes() {
|
|
281
371
|
return `
|
|
282
372
|
import type {PluginsInjections} from "nuxt-i18n-micro";
|
|
@@ -318,7 +408,6 @@ const module = defineNuxtModule({
|
|
|
318
408
|
autoDetectLanguage: true,
|
|
319
409
|
disablePageLocales: false,
|
|
320
410
|
disableWatcher: false,
|
|
321
|
-
// previousPageFallback and hmr are now main options (not experimental)
|
|
322
411
|
noPrefixRedirect: false,
|
|
323
412
|
fallbackLocale: void 0,
|
|
324
413
|
localeCookie: null,
|
|
@@ -332,7 +421,9 @@ const module = defineNuxtModule({
|
|
|
332
421
|
customRegexMatcher: void 0,
|
|
333
422
|
excludePatterns: void 0,
|
|
334
423
|
localizedRouteNamePrefix: "localized-",
|
|
335
|
-
missingWarn: true
|
|
424
|
+
missingWarn: true,
|
|
425
|
+
metaTrustForwardedHost: true,
|
|
426
|
+
metaTrustForwardedProto: true
|
|
336
427
|
},
|
|
337
428
|
async setup(options, nuxt) {
|
|
338
429
|
const defaultLocale = process.env.DEFAULT_LOCALE ?? options.defaultLocale ?? "en";
|
|
@@ -349,6 +440,15 @@ const module = defineNuxtModule({
|
|
|
349
440
|
}
|
|
350
441
|
const resolver = createResolver(import.meta.url);
|
|
351
442
|
const rootDirs = nuxt.options._layers.map((layer) => layer.config.rootDir).reverse();
|
|
443
|
+
const mergedLocalesDir = resolve(nuxt.options.buildDir, "i18n-merged");
|
|
444
|
+
const translationDirName = options.translationDir || "locales";
|
|
445
|
+
const localeInfos = (options.locales ?? []).map(
|
|
446
|
+
(l) => typeof l === "string" ? { code: l } : { code: l.code, fallbackLocale: l.fallbackLocale }
|
|
447
|
+
);
|
|
448
|
+
nuxt.hook("build:before", async () => {
|
|
449
|
+
await preMergeLocales(rootDirs, translationDirName, mergedLocalesDir, localeInfos, options.fallbackLocale, options.disablePageLocales);
|
|
450
|
+
logger.info(`Pre-merged translations from ${rootDirs.length} layer(s) into ${mergedLocalesDir}`);
|
|
451
|
+
});
|
|
352
452
|
const routeLocales = {};
|
|
353
453
|
const globalLocaleRoutes = {};
|
|
354
454
|
const routeDisableMeta = {};
|
|
@@ -427,7 +527,9 @@ const module = defineNuxtModule({
|
|
|
427
527
|
const apiBaseUrl = rawUrl.replace(/^\/+|\/+$|\/{2,}/, "");
|
|
428
528
|
const fullConfig = {
|
|
429
529
|
locales: routeGenerator.locales ?? [],
|
|
430
|
-
metaBaseUrl: options.metaBaseUrl
|
|
530
|
+
metaBaseUrl: options.metaBaseUrl || void 0,
|
|
531
|
+
metaTrustForwardedHost: options.metaTrustForwardedHost ?? true,
|
|
532
|
+
metaTrustForwardedProto: options.metaTrustForwardedProto ?? true,
|
|
431
533
|
defaultLocale,
|
|
432
534
|
fallbackLocale: options.fallbackLocale ?? void 0,
|
|
433
535
|
localeCookie: options.localeCookie ?? null,
|
|
@@ -448,7 +550,6 @@ const module = defineNuxtModule({
|
|
|
448
550
|
globalLocaleRoutes: mergedGlobalLocaleRoutes,
|
|
449
551
|
missingWarn: options.missingWarn ?? true,
|
|
450
552
|
redirects: options.redirects !== false,
|
|
451
|
-
previousPageFallback: options.previousPageFallback ?? false,
|
|
452
553
|
hmr: options.hmr ?? true,
|
|
453
554
|
localizedRouteNamePrefix: options.localizedRouteNamePrefix ?? "localized-",
|
|
454
555
|
routesLocaleLinks: options.routesLocaleLinks ?? {},
|
|
@@ -514,7 +615,6 @@ export function createI18nStrategy(router) {
|
|
|
514
615
|
}
|
|
515
616
|
const privateConfig = {
|
|
516
617
|
rootDir: nuxt.options.rootDir,
|
|
517
|
-
rootDirs,
|
|
518
618
|
debug: options.debug ?? false,
|
|
519
619
|
fallbackLocale: options.fallbackLocale ?? void 0,
|
|
520
620
|
translationDir: options.translationDir ?? "locales",
|
|
@@ -661,10 +761,10 @@ declare module '#i18n-internal/plural' {
|
|
|
661
761
|
nuxt.hook("build:before", () => addDataRoutes([]));
|
|
662
762
|
}
|
|
663
763
|
nuxt.hook("vite:extendConfig", (viteConfig) => {
|
|
664
|
-
const
|
|
665
|
-
viteConfig.resolve =
|
|
666
|
-
const alias =
|
|
667
|
-
|
|
764
|
+
const resolve2 = viteConfig.resolve ?? {};
|
|
765
|
+
viteConfig.resolve = resolve2;
|
|
766
|
+
const alias = resolve2.alias || {};
|
|
767
|
+
resolve2.alias = Array.isArray(alias) ? [
|
|
668
768
|
...alias,
|
|
669
769
|
{ find: "#i18n-internal/plural", replacement: pluralTemplate.dst },
|
|
670
770
|
{ find: "#i18n-internal/strategy", replacement: strategyTemplate.dst },
|
|
@@ -682,15 +782,9 @@ declare module '#i18n-internal/plural' {
|
|
|
682
782
|
nitroConfig.alias["#i18n-internal/strategy"] = strategyTemplate.dst;
|
|
683
783
|
nitroConfig.alias["#i18n-internal/config"] = configTemplate.dst;
|
|
684
784
|
nitroConfig.serverAssets = nitroConfig.serverAssets || [];
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
nitroConfig.serverAssets.push({
|
|
689
|
-
// Give each layer a unique name so we can find them at runtime
|
|
690
|
-
baseName: `i18n_layer_${index}`,
|
|
691
|
-
dir: dirPath
|
|
692
|
-
});
|
|
693
|
-
}
|
|
785
|
+
nitroConfig.serverAssets.push({
|
|
786
|
+
baseName: "i18n",
|
|
787
|
+
dir: mergedLocalesDir
|
|
694
788
|
});
|
|
695
789
|
if (nitroConfig.imports) {
|
|
696
790
|
nitroConfig.imports.presets = nitroConfig.imports.presets || [];
|
|
@@ -730,10 +824,13 @@ declare module '#i18n-internal/plural' {
|
|
|
730
824
|
const isProd = nuxt.options.dev === false;
|
|
731
825
|
if (isProd) {
|
|
732
826
|
const publicDir = path.join(nitro.options.output.publicDir ?? "./dist", options.translationDir ?? "locales");
|
|
733
|
-
const translationDir = path.join(nuxt.options.rootDir, options.translationDir ?? "locales");
|
|
734
827
|
try {
|
|
735
|
-
|
|
736
|
-
|
|
828
|
+
if (existsSync(mergedLocalesDir)) {
|
|
829
|
+
fs__default.cpSync(mergedLocalesDir, publicDir, { recursive: true });
|
|
830
|
+
logger.log(`Pre-merged translations copied to public directory`);
|
|
831
|
+
} else {
|
|
832
|
+
logger.warn(`Pre-merged translations directory not found: ${mergedLocalesDir}`);
|
|
833
|
+
}
|
|
737
834
|
} catch (err) {
|
|
738
835
|
logger.error("Error copying translations:", err);
|
|
739
836
|
}
|
|
@@ -19,7 +19,6 @@ export function useI18n() {
|
|
|
19
19
|
$has: nuxtApp.$has,
|
|
20
20
|
$tc: nuxtApp.$tc,
|
|
21
21
|
$mergeTranslations: nuxtApp.$mergeTranslations,
|
|
22
|
-
$mergeGlobalTranslations: nuxtApp.$mergeGlobalTranslations,
|
|
23
22
|
$setI18nRouteParams: nuxtApp.$setI18nRouteParams,
|
|
24
23
|
$switchLocaleRoute: nuxtApp.$switchLocaleRoute,
|
|
25
24
|
$switchLocalePath: nuxtApp.$switchLocalePath,
|
|
@@ -4,6 +4,8 @@ import type { RouteLocationNamedRaw, RouteLocationNormalizedLoaded, RouteLocatio
|
|
|
4
4
|
declare const _default: import("nuxt/app").Plugin<{
|
|
5
5
|
getI18nConfig: () => {
|
|
6
6
|
locales: never[];
|
|
7
|
+
metaTrustForwardedHost: boolean;
|
|
8
|
+
metaTrustForwardedProto: boolean;
|
|
7
9
|
defaultLocale: string;
|
|
8
10
|
localeCookie: null;
|
|
9
11
|
autoDetectLanguage: boolean;
|
|
@@ -21,7 +23,6 @@ declare const _default: import("nuxt/app").Plugin<{
|
|
|
21
23
|
globalLocaleRoutes: {};
|
|
22
24
|
missingWarn: boolean;
|
|
23
25
|
redirects: boolean;
|
|
24
|
-
previousPageFallback: boolean;
|
|
25
26
|
hmr: boolean;
|
|
26
27
|
localizedRouteNamePrefix: string;
|
|
27
28
|
routesLocaleLinks: {};
|
|
@@ -34,7 +35,6 @@ declare const _default: import("nuxt/app").Plugin<{
|
|
|
34
35
|
__micro: boolean;
|
|
35
36
|
helper: {
|
|
36
37
|
mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force?: boolean): Promise<void>;
|
|
37
|
-
mergeGlobalTranslation(locale: string, newTranslations: Translations, _force?: boolean): Promise<void>;
|
|
38
38
|
};
|
|
39
39
|
i18nStrategy: import("~/packages/path-strategy/dist/prefix-except-default-strategy.mjs").PrefixExceptDefaultPathStrategy;
|
|
40
40
|
getLocale: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => string;
|
|
@@ -52,7 +52,6 @@ declare const _default: import("nuxt/app").Plugin<{
|
|
|
52
52
|
tdr: (value: Date | number | string, options?: Intl.RelativeTimeFormatOptions) => string;
|
|
53
53
|
has: (key: string) => boolean;
|
|
54
54
|
mergeTranslations: (newTranslations: Translations) => void;
|
|
55
|
-
mergeGlobalTranslations: (newTranslations: Translations) => void;
|
|
56
55
|
switchLocaleRoute: (toLocale: string) => RouteLocationRaw;
|
|
57
56
|
clearCache: () => void;
|
|
58
57
|
switchLocalePath: (toLocale: string) => string;
|
|
@@ -66,6 +65,8 @@ declare const _default: import("nuxt/app").Plugin<{
|
|
|
66
65
|
}> & import("nuxt/app").ObjectPlugin<{
|
|
67
66
|
getI18nConfig: () => {
|
|
68
67
|
locales: never[];
|
|
68
|
+
metaTrustForwardedHost: boolean;
|
|
69
|
+
metaTrustForwardedProto: boolean;
|
|
69
70
|
defaultLocale: string;
|
|
70
71
|
localeCookie: null;
|
|
71
72
|
autoDetectLanguage: boolean;
|
|
@@ -83,7 +84,6 @@ declare const _default: import("nuxt/app").Plugin<{
|
|
|
83
84
|
globalLocaleRoutes: {};
|
|
84
85
|
missingWarn: boolean;
|
|
85
86
|
redirects: boolean;
|
|
86
|
-
previousPageFallback: boolean;
|
|
87
87
|
hmr: boolean;
|
|
88
88
|
localizedRouteNamePrefix: string;
|
|
89
89
|
routesLocaleLinks: {};
|
|
@@ -96,7 +96,6 @@ declare const _default: import("nuxt/app").Plugin<{
|
|
|
96
96
|
__micro: boolean;
|
|
97
97
|
helper: {
|
|
98
98
|
mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force?: boolean): Promise<void>;
|
|
99
|
-
mergeGlobalTranslation(locale: string, newTranslations: Translations, _force?: boolean): Promise<void>;
|
|
100
99
|
};
|
|
101
100
|
i18nStrategy: import("~/packages/path-strategy/dist/prefix-except-default-strategy.mjs").PrefixExceptDefaultPathStrategy;
|
|
102
101
|
getLocale: (route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric) => string;
|
|
@@ -114,7 +113,6 @@ declare const _default: import("nuxt/app").Plugin<{
|
|
|
114
113
|
tdr: (value: Date | number | string, options?: Intl.RelativeTimeFormatOptions) => string;
|
|
115
114
|
has: (key: string) => boolean;
|
|
116
115
|
mergeTranslations: (newTranslations: Translations) => void;
|
|
117
|
-
mergeGlobalTranslations: (newTranslations: Translations) => void;
|
|
118
116
|
switchLocaleRoute: (toLocale: string) => RouteLocationRaw;
|
|
119
117
|
clearCache: () => void;
|
|
120
118
|
switchLocalePath: (toLocale: string) => string;
|
|
@@ -145,7 +143,6 @@ export interface PluginsInjections {
|
|
|
145
143
|
$tdr: (value: Date | number | string, options?: Intl.DateTimeFormatOptions) => string;
|
|
146
144
|
$has: (key: string) => boolean;
|
|
147
145
|
$mergeTranslations: (newTranslations: Translations) => void;
|
|
148
|
-
$mergeGlobalTranslations: (newTranslations: Translations) => void;
|
|
149
146
|
$switchLocaleRoute: (locale: string) => RouteLocationRaw;
|
|
150
147
|
$switchLocalePath: (locale: string) => string;
|
|
151
148
|
$switchLocale: (locale: string) => void;
|
|
@@ -31,6 +31,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
31
31
|
let currentLocale = "";
|
|
32
32
|
let currentRouteName = "";
|
|
33
33
|
let cachedTranslations = {};
|
|
34
|
+
let pendingCleanState = null;
|
|
34
35
|
const contextSignal = shallowRef(0);
|
|
35
36
|
const { locale: localeState, setLocale, getLocale, getEffectiveLocale, resolveInitialLocale, isValidLocale } = useI18nLocale();
|
|
36
37
|
const getDefaultLocaleGetter = () => getLocale() ?? null;
|
|
@@ -64,15 +65,14 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
64
65
|
}
|
|
65
66
|
return router.push(switchedRoute);
|
|
66
67
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
nuxtApp.hook("page:finish", () => {
|
|
72
|
-
if (import.meta.client) {
|
|
73
|
-
previousPageInfo.value = null;
|
|
68
|
+
nuxtApp.hook("page:transition:finish", () => {
|
|
69
|
+
if (pendingCleanState) {
|
|
70
|
+
cachedTranslations = pendingCleanState;
|
|
71
|
+
pendingCleanState = null;
|
|
74
72
|
}
|
|
75
73
|
});
|
|
74
|
+
const i18nRouteParams = useState("i18n-route-params", () => ({}));
|
|
75
|
+
const customMissingHandler = useState("i18n-missing-handler", () => null);
|
|
76
76
|
const missingWarn = i18nConfig.missingWarn ?? true;
|
|
77
77
|
const loadOptions = {
|
|
78
78
|
apiBaseUrl: i18nConfig.apiBaseUrl ?? "_locales",
|
|
@@ -80,7 +80,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
80
80
|
dateBuild: i18nConfig.dateBuild
|
|
81
81
|
};
|
|
82
82
|
const getCacheKey = (locale, routeName) => {
|
|
83
|
-
return
|
|
83
|
+
return `${locale}:${routeName || "index"}`;
|
|
84
84
|
};
|
|
85
85
|
const loadFromCacheSync = (locale, routeName) => {
|
|
86
86
|
const cacheKey = getCacheKey(locale, routeName);
|
|
@@ -124,9 +124,15 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
124
124
|
if (data === null) {
|
|
125
125
|
data = await loadAsync(locale, routeName);
|
|
126
126
|
}
|
|
127
|
+
if (currentLocale === locale) {
|
|
128
|
+
cachedTranslations = { ...cachedTranslations, ...data };
|
|
129
|
+
pendingCleanState = data;
|
|
130
|
+
} else {
|
|
131
|
+
cachedTranslations = data;
|
|
132
|
+
pendingCleanState = null;
|
|
133
|
+
}
|
|
127
134
|
currentLocale = locale;
|
|
128
135
|
currentRouteName = routeName || "";
|
|
129
|
-
cachedTranslations = data;
|
|
130
136
|
triggerRef(contextSignal);
|
|
131
137
|
};
|
|
132
138
|
const serverLocale = import.meta.server ? nuxtApp.ssrContext?.event?.context?.i18n?.locale : void 0;
|
|
@@ -166,11 +172,6 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
166
172
|
const targetLocale = getEffectiveLocale(to, (r) => getCurrentLocale(r));
|
|
167
173
|
const targetRouteName = getPluginRouteName(to, targetLocale);
|
|
168
174
|
if (targetLocale !== currentLocale || targetRouteName !== currentRouteName) {
|
|
169
|
-
if (import.meta.client && enablePreviousPageFallback && from.path !== to.path) {
|
|
170
|
-
const fromLocale = getCurrentLocale(from);
|
|
171
|
-
const fromRouteName = getPluginRouteName(from, fromLocale);
|
|
172
|
-
previousPageInfo.value = { locale: fromLocale, routeName: fromRouteName };
|
|
173
|
-
}
|
|
174
175
|
await switchContext(targetLocale, targetRouteName);
|
|
175
176
|
}
|
|
176
177
|
if (targetLocale && isValidLocale(targetLocale) && localeState.value !== targetLocale) {
|
|
@@ -194,14 +195,6 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
194
195
|
if (val === void 0 && key.includes(".")) {
|
|
195
196
|
val = getByPath(translations, key);
|
|
196
197
|
}
|
|
197
|
-
if (val === void 0 && enablePreviousPageFallback && previousPageInfo.value) {
|
|
198
|
-
const prev = previousPageInfo.value;
|
|
199
|
-
const prevTranslations = loadedChunks.get(getCacheKey(prev.locale, prev.routeName)) || {};
|
|
200
|
-
val = prevTranslations[key];
|
|
201
|
-
if (val === void 0 && key.includes(".")) {
|
|
202
|
-
val = getByPath(prevTranslations, key);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
198
|
if (val === void 0) {
|
|
206
199
|
if (customMissingHandler.value) {
|
|
207
200
|
customMissingHandler.value(currentLocale, key, currentRouteName);
|
|
@@ -225,12 +218,6 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
225
218
|
contextSignal.value;
|
|
226
219
|
if (cachedTranslations[key] !== void 0) return true;
|
|
227
220
|
if (key.includes(".") && getByPath(cachedTranslations, key) !== void 0) return true;
|
|
228
|
-
if (enablePreviousPageFallback && previousPageInfo.value) {
|
|
229
|
-
const prev = previousPageInfo.value;
|
|
230
|
-
const prevTranslations = loadedChunks.get(getCacheKey(prev.locale, prev.routeName)) || {};
|
|
231
|
-
if (prevTranslations[key] !== void 0) return true;
|
|
232
|
-
if (key.includes(".") && getByPath(prevTranslations, key) !== void 0) return true;
|
|
233
|
-
}
|
|
234
221
|
return false;
|
|
235
222
|
};
|
|
236
223
|
const mergeTranslations = (newTranslations) => {
|
|
@@ -238,7 +225,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
238
225
|
const current = loadedChunks.get(cacheKey) || {};
|
|
239
226
|
const merged = { ...current, ...newTranslations };
|
|
240
227
|
loadedChunks.set(cacheKey, merged);
|
|
241
|
-
cachedTranslations = merged;
|
|
228
|
+
cachedTranslations = { ...cachedTranslations, ...merged };
|
|
229
|
+
if (pendingCleanState) pendingCleanState = merged;
|
|
242
230
|
triggerRef(contextSignal);
|
|
243
231
|
};
|
|
244
232
|
const helper = {
|
|
@@ -248,21 +236,11 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
248
236
|
await loadAsync(locale, routeName || void 0);
|
|
249
237
|
}
|
|
250
238
|
const existing = loadedChunks.get(cacheKey) || {};
|
|
251
|
-
|
|
239
|
+
const mergedChunk = { ...existing, ...newTranslations };
|
|
240
|
+
loadedChunks.set(cacheKey, mergedChunk);
|
|
252
241
|
if (locale === currentLocale && routeName === currentRouteName) {
|
|
253
|
-
cachedTranslations =
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
async mergeGlobalTranslation(locale, newTranslations, _force = false) {
|
|
258
|
-
const cacheKey = getCacheKey(locale, currentRouteName);
|
|
259
|
-
if (!loadedChunks.has(cacheKey)) {
|
|
260
|
-
await loadAsync(locale, currentRouteName || void 0);
|
|
261
|
-
}
|
|
262
|
-
const existing = loadedChunks.get(cacheKey) || {};
|
|
263
|
-
loadedChunks.set(cacheKey, { ...existing, ...newTranslations });
|
|
264
|
-
if (locale === currentLocale) {
|
|
265
|
-
cachedTranslations = loadedChunks.get(cacheKey);
|
|
242
|
+
cachedTranslations = { ...cachedTranslations, ...mergedChunk };
|
|
243
|
+
if (pendingCleanState) pendingCleanState = mergedChunk;
|
|
266
244
|
triggerRef(contextSignal);
|
|
267
245
|
}
|
|
268
246
|
}
|
|
@@ -313,7 +291,6 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
313
291
|
},
|
|
314
292
|
has: hasTranslation,
|
|
315
293
|
mergeTranslations,
|
|
316
|
-
mergeGlobalTranslations: mergeTranslations,
|
|
317
294
|
switchLocaleRoute: (toLocale) => {
|
|
318
295
|
const route = router.currentRoute.value;
|
|
319
296
|
const fromLocale = getCurrentLocale(route);
|
|
@@ -379,9 +356,11 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
379
356
|
loadPageTranslations: async (locale, routeName, translations) => {
|
|
380
357
|
const cacheKey = getCacheKey(locale, routeName);
|
|
381
358
|
const current = loadedChunks.get(cacheKey) || {};
|
|
382
|
-
|
|
359
|
+
const mergedChunk = { ...current, ...translations };
|
|
360
|
+
loadedChunks.set(cacheKey, mergedChunk);
|
|
383
361
|
if (locale === currentLocale && routeName === currentRouteName) {
|
|
384
|
-
cachedTranslations =
|
|
362
|
+
cachedTranslations = { ...cachedTranslations, ...mergedChunk };
|
|
363
|
+
if (pendingCleanState) pendingCleanState = mergedChunk;
|
|
385
364
|
triggerRef(contextSignal);
|
|
386
365
|
}
|
|
387
366
|
},
|
|
@@ -10,8 +10,11 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
10
10
|
if (isMetaDisabledForRoute(route, i18nConfig.routeDisableMeta, currentLocale)) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
const url = useRequestURL(
|
|
14
|
-
|
|
13
|
+
const url = useRequestURL({
|
|
14
|
+
xForwardedHost: i18nConfig.metaTrustForwardedHost !== false,
|
|
15
|
+
xForwardedProto: i18nConfig.metaTrustForwardedProto !== false
|
|
16
|
+
});
|
|
17
|
+
const baseUrl = i18nConfig.metaBaseUrl || url.origin;
|
|
15
18
|
const { metaObject, updateMeta } = useLocaleHead({
|
|
16
19
|
addDirAttribute: true,
|
|
17
20
|
identifierAttribute: "id",
|
|
@@ -13,13 +13,13 @@ const normalizeLocales = (locales) => {
|
|
|
13
13
|
};
|
|
14
14
|
export default defineNuxtPlugin(() => {
|
|
15
15
|
const defineI18nRoute = async (routeDefinition) => {
|
|
16
|
-
const { $getLocale, $
|
|
16
|
+
const { $getLocale, $mergeTranslations } = useNuxtApp();
|
|
17
17
|
let currentLocale = computed(() => $getLocale());
|
|
18
18
|
const normalizedLocales = normalizeLocales(routeDefinition.locales);
|
|
19
19
|
const updateTranslations = () => {
|
|
20
20
|
const localeValue = unref(currentLocale);
|
|
21
21
|
if (localeValue && normalizedLocales[localeValue]) {
|
|
22
|
-
$
|
|
22
|
+
$mergeTranslations?.(normalizedLocales[localeValue]);
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
updateTranslations();
|
|
@@ -11,7 +11,7 @@ export default defineEventHandler(async (event) => {
|
|
|
11
11
|
if (!config.locales?.find((l) => l.code === locale)) {
|
|
12
12
|
throw createError({ statusCode: 404, statusMessage: "Locale not found" });
|
|
13
13
|
}
|
|
14
|
-
const { json } = await loadTranslationsFromServer(locale, page
|
|
14
|
+
const { json } = await loadTranslationsFromServer(locale, page);
|
|
15
15
|
setResponseHeader(event, "Content-Type", "application/json; charset=utf-8");
|
|
16
16
|
return send(event, json);
|
|
17
17
|
});
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Server-side translation loader
|
|
3
|
-
*
|
|
2
|
+
* Server-side translation loader.
|
|
3
|
+
* All merging (layers, fallback locales, global + page) is done at build time.
|
|
4
|
+
* This loader simply reads a single pre-built file from Nitro storage and returns it.
|
|
4
5
|
*/
|
|
5
6
|
import type { Translations } from '@i18n-micro/types';
|
|
6
7
|
/**
|
|
7
|
-
* Load translations
|
|
8
|
-
*
|
|
9
|
-
* Results are cached at the process level with TTL and maxSize support.
|
|
8
|
+
* Load translations for a given locale and page.
|
|
9
|
+
* Returns a single pre-built file (global + page + fallback already baked in at build time).
|
|
10
10
|
*/
|
|
11
|
-
export declare function loadTranslationsFromServer(locale: string, routeName
|
|
11
|
+
export declare function loadTranslationsFromServer(locale: string, routeName: string): Promise<{
|
|
12
12
|
data: Translations;
|
|
13
13
|
json: string;
|
|
14
14
|
}>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useStorage } from "nitropack/runtime";
|
|
2
|
-
import { getI18nPrivateConfig } from "#i18n-internal/config";
|
|
3
2
|
import { getI18nConfig } from "#i18n-internal/strategy";
|
|
4
3
|
import { CacheControl } from "../../utils/cache-control.js";
|
|
5
4
|
const CC_KEY = Symbol.for("__NUXT_I18N_SERVER_CACHE_CC__");
|
|
@@ -11,23 +10,7 @@ function getServerCacheControl() {
|
|
|
11
10
|
}
|
|
12
11
|
return g[CC_KEY];
|
|
13
12
|
}
|
|
14
|
-
|
|
15
|
-
if (!target || Object.keys(target).length === 0) {
|
|
16
|
-
return { ...source };
|
|
17
|
-
}
|
|
18
|
-
const output = { ...target };
|
|
19
|
-
for (const key in source) {
|
|
20
|
-
if (key === "__proto__" || key === "constructor") continue;
|
|
21
|
-
const src = source[key];
|
|
22
|
-
const dst = output[key];
|
|
23
|
-
if (src && typeof src === "object" && !Array.isArray(src) && dst && typeof dst === "object" && !Array.isArray(dst)) {
|
|
24
|
-
output[key] = deepMerge(dst, src);
|
|
25
|
-
} else {
|
|
26
|
-
output[key] = src;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return output;
|
|
30
|
-
}
|
|
13
|
+
const ASSETS_PREFIX = "assets:i18n";
|
|
31
14
|
function toTranslations(data) {
|
|
32
15
|
if (!data) return {};
|
|
33
16
|
if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
@@ -35,58 +18,27 @@ function toTranslations(data) {
|
|
|
35
18
|
}
|
|
36
19
|
return {};
|
|
37
20
|
}
|
|
38
|
-
async function loadFromNitroStorage(storage, locale, pageName) {
|
|
39
|
-
const privateConfig = getI18nPrivateConfig();
|
|
40
|
-
const rootDirs = privateConfig.rootDirs || [];
|
|
41
|
-
const routesLocaleLinks = privateConfig.routesLocaleLinks || {};
|
|
42
|
-
let merged = {};
|
|
43
|
-
const fileLookupPage = pageName ? routesLocaleLinks[pageName] || pageName : void 0;
|
|
44
|
-
const normalizedPage = fileLookupPage?.replace(/\//g, ":");
|
|
45
|
-
for (let i = 0; i < rootDirs.length; i++) {
|
|
46
|
-
const prefix = `assets:i18n_layer_${i}`;
|
|
47
|
-
const key = normalizedPage ? `${prefix}:pages:${normalizedPage}:${locale}.json` : `${prefix}:${locale}.json`;
|
|
48
|
-
if (await storage.hasItem(key)) {
|
|
49
|
-
const raw = await storage.getItem(key);
|
|
50
|
-
if (raw) {
|
|
51
|
-
merged = deepMerge(merged, toTranslations(raw));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return merged;
|
|
56
|
-
}
|
|
57
|
-
async function loadMergedFromServer(locale, page) {
|
|
58
|
-
const storage = useStorage();
|
|
59
|
-
const general = await loadFromNitroStorage(storage, locale);
|
|
60
|
-
if (!page || page === "general") {
|
|
61
|
-
return general;
|
|
62
|
-
}
|
|
63
|
-
const pageSpecific = await loadFromNitroStorage(storage, locale, page);
|
|
64
|
-
return deepMerge(general, pageSpecific);
|
|
65
|
-
}
|
|
66
21
|
export async function loadTranslationsFromServer(locale, routeName) {
|
|
67
22
|
const cc = getServerCacheControl();
|
|
68
|
-
const cacheKey = `${locale}:${routeName
|
|
23
|
+
const cacheKey = `${locale}:${routeName}`;
|
|
69
24
|
const cached = cc.get(cacheKey);
|
|
70
25
|
if (cached) {
|
|
71
26
|
return cached;
|
|
72
27
|
}
|
|
73
28
|
const config = getI18nConfig();
|
|
74
|
-
|
|
75
|
-
const localeConfig = locales?.find((l) => l.code === locale);
|
|
76
|
-
if (!localeConfig) {
|
|
29
|
+
if (!config.locales?.find((l) => l.code === locale)) {
|
|
77
30
|
const empty = { data: {}, json: "{}" };
|
|
78
31
|
cc.set(cacheKey, empty);
|
|
79
32
|
return empty;
|
|
80
33
|
}
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
const entry = { data: result, json };
|
|
34
|
+
const storage = useStorage();
|
|
35
|
+
const routesLocaleLinks = config.routesLocaleLinks || {};
|
|
36
|
+
const resolvedPage = routesLocaleLinks[routeName] || routeName;
|
|
37
|
+
const normalizedPage = resolvedPage.replace(/\//g, ":");
|
|
38
|
+
const key = `${ASSETS_PREFIX}:pages:${normalizedPage}:${locale}.json`;
|
|
39
|
+
const data = await storage.hasItem(key) ? toTranslations(await storage.getItem(key)) : {};
|
|
40
|
+
const json = JSON.stringify(data).replace(/</g, "\\u003c");
|
|
41
|
+
const entry = { data, json };
|
|
90
42
|
cc.set(cacheKey, entry);
|
|
91
43
|
return entry;
|
|
92
44
|
}
|
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
import { interpolate
|
|
1
|
+
import { interpolate } from "@i18n-micro/core";
|
|
2
2
|
import { getI18nConfig } from "#i18n-internal/strategy";
|
|
3
3
|
import { detectCurrentLocale } from "./locale-detector.js";
|
|
4
4
|
import { loadTranslationsFromServer } from "./server-loader.js";
|
|
5
|
-
const I18N_CONTEXT_KEY = "
|
|
5
|
+
const I18N_CONTEXT_KEY = "__i18n_translations__";
|
|
6
6
|
export const useTranslationServerMiddleware = async (event, defaultLocale, currentLocale) => {
|
|
7
|
-
if (!event.context[I18N_CONTEXT_KEY]) {
|
|
8
|
-
event.context[I18N_CONTEXT_KEY] = {
|
|
9
|
-
translations: /* @__PURE__ */ new Map()
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
const storage = event.context[I18N_CONTEXT_KEY];
|
|
13
|
-
const { getTranslation, loadTranslations, hasGeneralTranslation } = useTranslationHelper(storage);
|
|
14
7
|
const { locales, fallbackLocale, defaultLocale: configDefaultLocale } = getI18nConfig();
|
|
15
8
|
const locale = currentLocale || event.context.i18n?.locale || detectCurrentLocale(
|
|
16
9
|
event,
|
|
@@ -21,20 +14,37 @@ export const useTranslationServerMiddleware = async (event, defaultLocale, curre
|
|
|
21
14
|
},
|
|
22
15
|
defaultLocale
|
|
23
16
|
);
|
|
24
|
-
if (!
|
|
25
|
-
let
|
|
17
|
+
if (!event.context[I18N_CONTEXT_KEY]) {
|
|
18
|
+
let translations2;
|
|
26
19
|
if (event.context.i18n?.translations && Object.keys(event.context.i18n.translations).length > 0) {
|
|
27
|
-
|
|
20
|
+
translations2 = event.context.i18n.translations;
|
|
28
21
|
} else {
|
|
29
|
-
const { data } = await loadTranslationsFromServer(locale);
|
|
30
|
-
|
|
22
|
+
const { data } = await loadTranslationsFromServer(locale, "index");
|
|
23
|
+
translations2 = data;
|
|
31
24
|
}
|
|
32
|
-
|
|
25
|
+
event.context[I18N_CONTEXT_KEY] = translations2;
|
|
33
26
|
}
|
|
27
|
+
const translations = event.context[I18N_CONTEXT_KEY];
|
|
34
28
|
function t(key, params, defaultValue) {
|
|
35
|
-
let
|
|
36
|
-
if (
|
|
37
|
-
|
|
29
|
+
let value = translations[key];
|
|
30
|
+
if (value === void 0 && key.includes(".")) {
|
|
31
|
+
const parts = key.split(".");
|
|
32
|
+
let current = translations;
|
|
33
|
+
for (const part of parts) {
|
|
34
|
+
if (current && typeof current === "object" && part in current) {
|
|
35
|
+
current = current[part];
|
|
36
|
+
} else {
|
|
37
|
+
current = void 0;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
value = current;
|
|
42
|
+
}
|
|
43
|
+
if (value === void 0 || value === null) {
|
|
44
|
+
return defaultValue || key;
|
|
45
|
+
}
|
|
46
|
+
const str = typeof value === "string" ? value : String(value);
|
|
47
|
+
return params ? interpolate(str, params) : str;
|
|
38
48
|
}
|
|
39
49
|
return t;
|
|
40
50
|
};
|
|
@@ -22,14 +22,15 @@ class TranslationStorage {
|
|
|
22
22
|
// HELPERS
|
|
23
23
|
// ==========================================================================
|
|
24
24
|
getCacheKey(locale, routeName) {
|
|
25
|
-
return
|
|
25
|
+
return `${locale}:${routeName || "index"}`;
|
|
26
26
|
}
|
|
27
27
|
// ==========================================================================
|
|
28
28
|
// FETCH LOADER
|
|
29
29
|
// ==========================================================================
|
|
30
30
|
async fetchTranslations(locale, routeName, options) {
|
|
31
31
|
const { apiBaseUrl, baseURL, dateBuild } = options;
|
|
32
|
-
const
|
|
32
|
+
const page = routeName || "index";
|
|
33
|
+
const path = `/${apiBaseUrl}/${page}/${locale}/data.json`;
|
|
33
34
|
return await $fetch(path.replace(/\/{2,}/g, "/"), {
|
|
34
35
|
baseURL,
|
|
35
36
|
params: dateBuild ? { v: dateBuild } : void 0
|
package/internals.d.mts
CHANGED
|
@@ -5,7 +5,6 @@ declare module '#build/i18n.plural.mjs' {
|
|
|
5
5
|
/** Приватный конфиг i18n (серверный): rootDir, rootDirs, debug, translationDir и т.д. Копия полей ModulePrivateOptionsExtend без импорта. */
|
|
6
6
|
interface I18nPrivateConfig {
|
|
7
7
|
rootDir: string
|
|
8
|
-
rootDirs: string[]
|
|
9
8
|
debug: boolean
|
|
10
9
|
translationDir: string
|
|
11
10
|
fallbackLocale?: string
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-i18n-micro",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
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",
|
|
@@ -74,11 +74,11 @@
|
|
|
74
74
|
"chokidar": "^3.6.0",
|
|
75
75
|
"globby": "^14.1.0",
|
|
76
76
|
"ufo": "^1.5.4",
|
|
77
|
-
"@i18n-micro/core": "1.
|
|
78
|
-
"@i18n-micro/path-strategy": "1.
|
|
79
|
-
"@i18n-micro/
|
|
77
|
+
"@i18n-micro/core": "1.2.0",
|
|
78
|
+
"@i18n-micro/path-strategy": "1.2.0",
|
|
79
|
+
"@i18n-micro/test-utils": "1.2.0",
|
|
80
80
|
"@i18n-micro/route-strategy": "1.1.3",
|
|
81
|
-
"@i18n-micro/
|
|
81
|
+
"@i18n-micro/types": "1.1.5"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@biomejs/biome": "^2.3.14",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"id":"628ed9e2-a5ab-4c20-b953-a0de4eafc594","timestamp":1770721345795,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|