nuxt-i18n-micro 3.18.3 → 3.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/bbe443af-4371-4ec3-b41c-7a5e9deb9b9b.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 +57 -149
- package/dist/runtime/composables/useI18nLocale.js +3 -3
- package/dist/runtime/composables/useLocaleHead.js +1 -1
- package/dist/runtime/plugins/01.plugin.js +6 -3
- package/dist/runtime/plugins/02.meta.js +2 -2
- package/dist/runtime/plugins/05.hooks.js +1 -1
- package/dist/runtime/plugins/06.redirect.js +4 -4
- package/dist/runtime/server/middleware/i18n.global.js +4 -4
- 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.js +11 -1
- package/dist/runtime/server/utils/locale-server-middleware.js +2 -2
- package/dist/runtime/server/utils/server-loader.d.ts +3 -3
- package/dist/runtime/server/utils/server-loader.js +32 -8
- package/dist/runtime/server/utils/translation-server-middleware.js +2 -2
- package/dist/runtime/utils/storage.d.ts +4 -6
- package/dist/runtime/utils/storage.js +20 -11
- package/dist/types.d.mts +2 -0
- package/package.json +11 -8
- 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,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Server-side translation loader.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* In `premerged` mode, layers/fallback/root+page are merged at build time and this loader
|
|
4
|
+
* reads a single Nitro asset. In `source` mode, compact source files are merged at runtime.
|
|
5
5
|
*/
|
|
6
6
|
import type { Translations } from '@i18n-micro/types';
|
|
7
7
|
/**
|
|
8
8
|
* Load translations for a given locale and page.
|
|
9
|
-
* Returns a
|
|
9
|
+
* Returns merged translations and a pre-serialized JSON string for the API route.
|
|
10
10
|
*/
|
|
11
11
|
export declare function loadTranslationsFromServer(locale: string, routeName: string): Promise<{
|
|
12
12
|
data: Translations;
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
+
import { SERVER_CC_KEY } from "@i18n-micro/hmr/cache-keys";
|
|
2
|
+
import { isEnabledLocale } from "@i18n-micro/utils/active-locales";
|
|
3
|
+
import { CacheControl } from "@i18n-micro/utils/cache-control";
|
|
4
|
+
import { normalizeConfiguredLocales } from "@i18n-micro/utils/merge-source";
|
|
5
|
+
import { fetchTranslationPayloadFromHost } from "@i18n-micro/utils/payload-fetch";
|
|
6
|
+
import { resolveTranslationPayloadPage } from "@i18n-micro/utils/payload-url";
|
|
7
|
+
import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
|
|
8
|
+
import { loadSourceTranslationsFromStorage } from "@i18n-micro/utils/source-loader";
|
|
1
9
|
import { useStorage } from "nitropack/runtime";
|
|
2
10
|
import { getI18nConfig } from "#i18n-internal/strategy";
|
|
3
|
-
import { isEnabledLocale } from "../../utils/active-locales.js";
|
|
4
|
-
import { CacheControl } from "../../utils/cache-control.js";
|
|
5
|
-
import { resolveI18nConfigWithRuntimeOverrides } from "../../utils/runtime-i18n-config.js";
|
|
6
|
-
const CC_KEY = Symbol.for("__NUXT_I18N_SERVER_CACHE_CC__");
|
|
7
11
|
function getServerCacheControl() {
|
|
8
12
|
const g = globalThis;
|
|
9
|
-
if (!g[
|
|
13
|
+
if (!g[SERVER_CC_KEY]) {
|
|
10
14
|
const cfg = resolveI18nConfigWithRuntimeOverrides(getI18nConfig());
|
|
11
|
-
g[
|
|
15
|
+
g[SERVER_CC_KEY] = new CacheControl({ maxSize: cfg.cacheMaxSize ?? 0, ttl: cfg.cacheTtl ?? 0 });
|
|
12
16
|
}
|
|
13
|
-
return g[
|
|
17
|
+
return g[SERVER_CC_KEY];
|
|
14
18
|
}
|
|
15
19
|
const ASSETS_PREFIX = "assets:i18n";
|
|
16
20
|
function toTranslations(data) {
|
|
@@ -35,8 +39,28 @@ export async function loadTranslationsFromServer(locale, routeName) {
|
|
|
35
39
|
}
|
|
36
40
|
const storage = useStorage();
|
|
37
41
|
const routesLocaleLinks = config.routesLocaleLinks || {};
|
|
38
|
-
const resolvedPage =
|
|
42
|
+
const resolvedPage = resolveTranslationPayloadPage(routeName, routesLocaleLinks);
|
|
39
43
|
const normalizedPage = resolvedPage.replace(/\//g, ":");
|
|
44
|
+
if (config.apiBaseServerHost) {
|
|
45
|
+
const data2 = await fetchTranslationPayloadFromHost(config, locale, resolvedPage, $fetch);
|
|
46
|
+
const json2 = JSON.stringify(data2).replace(/</g, "\\u003c");
|
|
47
|
+
const entry2 = { data: data2, json: json2 };
|
|
48
|
+
cc.set(cacheKey, entry2);
|
|
49
|
+
return entry2;
|
|
50
|
+
}
|
|
51
|
+
if (config.translationPayloadMode === "source") {
|
|
52
|
+
const data2 = await loadSourceTranslationsFromStorage(storage, {
|
|
53
|
+
locale,
|
|
54
|
+
pageName: resolvedPage,
|
|
55
|
+
locales: normalizeConfiguredLocales(config.locales),
|
|
56
|
+
globalFallbackLocale: config.fallbackLocale,
|
|
57
|
+
disablePageLocales: config.disablePageLocales
|
|
58
|
+
});
|
|
59
|
+
const json2 = JSON.stringify(data2).replace(/</g, "\\u003c");
|
|
60
|
+
const entry2 = { data: data2, json: json2 };
|
|
61
|
+
cc.set(cacheKey, entry2);
|
|
62
|
+
return entry2;
|
|
63
|
+
}
|
|
40
64
|
const key = `${ASSETS_PREFIX}:pages:${normalizedPage}:${locale}.json`;
|
|
41
65
|
const loaded = await storage.getItem(key);
|
|
42
66
|
const data = toTranslations(loaded);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { interpolate } from "@i18n-micro/core";
|
|
2
|
+
import { getEnabledLocales } from "@i18n-micro/utils/active-locales";
|
|
3
|
+
import { resolveI18nConfigWithRuntimeOverrides } from "@i18n-micro/utils/runtime-config";
|
|
2
4
|
import { getI18nConfig } from "#i18n-internal/strategy";
|
|
3
5
|
import { useRuntimeConfig } from "#imports";
|
|
4
|
-
import { getEnabledLocales } from "../../utils/active-locales.js";
|
|
5
|
-
import { resolveI18nConfigWithRuntimeOverrides } from "../../utils/runtime-i18n-config.js";
|
|
6
6
|
import { detectCurrentLocale } from "./locale-detector.js";
|
|
7
7
|
import { loadTranslationsFromServer } from "./server-loader.js";
|
|
8
8
|
const I18N_CONTEXT_KEY = "__i18n_translations__";
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Translation Storage
|
|
3
|
-
* Unified translation storage for client and server.
|
|
4
|
-
* Cache control (TTL, maxSize) delegated to CacheControl.
|
|
5
|
-
*/
|
|
6
|
-
import { type CacheControlOptions } from './cache-control.js';
|
|
1
|
+
import { type CacheControlOptions } from '@i18n-micro/utils/cache-control';
|
|
7
2
|
declare global {
|
|
8
3
|
interface Window {
|
|
9
4
|
__I18N__?: Record<string, unknown>;
|
|
@@ -12,7 +7,10 @@ declare global {
|
|
|
12
7
|
export interface LoadOptions {
|
|
13
8
|
apiBaseUrl: string;
|
|
14
9
|
baseURL: string;
|
|
10
|
+
apiBaseClientHost?: string;
|
|
11
|
+
apiBaseServerHost?: string;
|
|
15
12
|
dateBuild?: string | number;
|
|
13
|
+
routesLocaleLinks?: Record<string, string>;
|
|
16
14
|
}
|
|
17
15
|
export interface LoadResult {
|
|
18
16
|
data: Record<string, unknown>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { STORAGE_CC_KEY } from "@i18n-micro/hmr/cache-keys";
|
|
2
|
+
import { CacheControl } from "@i18n-micro/utils/cache-control";
|
|
3
|
+
import { buildTranslationPayloadFetchRequest } from "@i18n-micro/utils/payload-url";
|
|
3
4
|
function getStorageCacheControl() {
|
|
4
5
|
const g = globalThis;
|
|
5
|
-
if (!g[
|
|
6
|
-
g[
|
|
6
|
+
if (!g[STORAGE_CC_KEY]) {
|
|
7
|
+
g[STORAGE_CC_KEY] = new CacheControl();
|
|
7
8
|
}
|
|
8
|
-
return g[
|
|
9
|
+
return g[STORAGE_CC_KEY];
|
|
9
10
|
}
|
|
10
11
|
class TranslationStorage {
|
|
11
12
|
cc;
|
|
@@ -28,12 +29,20 @@ class TranslationStorage {
|
|
|
28
29
|
// FETCH LOADER
|
|
29
30
|
// ==========================================================================
|
|
30
31
|
async fetchTranslations(locale, routeName, options) {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
const request = buildTranslationPayloadFetchRequest({
|
|
33
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
34
|
+
routeName,
|
|
35
|
+
locale,
|
|
36
|
+
isServer: import.meta.server,
|
|
37
|
+
baseURL: options.baseURL,
|
|
38
|
+
apiBaseClientHost: options.apiBaseClientHost,
|
|
39
|
+
apiBaseServerHost: options.apiBaseServerHost,
|
|
40
|
+
dateBuild: options.dateBuild,
|
|
41
|
+
routesLocaleLinks: options.routesLocaleLinks
|
|
42
|
+
});
|
|
43
|
+
return await $fetch(request.path, {
|
|
44
|
+
baseURL: request.baseURL,
|
|
45
|
+
params: request.params
|
|
37
46
|
});
|
|
38
47
|
}
|
|
39
48
|
// ==========================================================================
|
package/dist/types.d.mts
CHANGED
|
@@ -8,6 +8,8 @@ export { type Getter, type GlobalLocaleRoutes, type Locale, type LocaleCode, typ
|
|
|
8
8
|
|
|
9
9
|
export { type PluginsInjections } from '../dist/runtime/plugins/01.plugin.js'
|
|
10
10
|
|
|
11
|
+
export { type TranslationPayloadMode, type resolveTranslationPayloadMode, type resolveTranslationPayloadOptions, type resolveTranslationPayloadPublicDir } from '@i18n-micro/utils/payload-config'
|
|
12
|
+
|
|
11
13
|
export { default } from './module.mjs'
|
|
12
14
|
|
|
13
15
|
export { type ModuleHooks } from './module.mjs'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-i18n-micro",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.0",
|
|
4
4
|
"description": "Nuxt I18n Micro is a lightweight, high-performance internationalization module for Nuxt, designed to handle multi-language support with minimal overhead, fast build times, and efficient runtime performance.",
|
|
5
5
|
"repository": "s00d/nuxt-i18n-micro",
|
|
6
6
|
"license": "MIT",
|
|
@@ -75,10 +75,12 @@
|
|
|
75
75
|
"globby": "^14.1.0",
|
|
76
76
|
"ufo": "^1.5.4",
|
|
77
77
|
"@i18n-micro/core": "1.3.1",
|
|
78
|
-
"@i18n-micro/route-strategy": "1.1.7",
|
|
79
|
-
"@i18n-micro/test-utils": "1.2.1",
|
|
80
78
|
"@i18n-micro/path-strategy": "1.3.2",
|
|
81
|
-
"@i18n-micro/
|
|
79
|
+
"@i18n-micro/hmr": "1.0.0",
|
|
80
|
+
"@i18n-micro/route-strategy": "1.1.7",
|
|
81
|
+
"@i18n-micro/types": "1.2.3",
|
|
82
|
+
"@i18n-micro/utils": "1.0.0",
|
|
83
|
+
"@i18n-micro/test-utils": "1.2.1"
|
|
82
84
|
},
|
|
83
85
|
"devDependencies": {
|
|
84
86
|
"@biomejs/biome": "^2.3.14",
|
|
@@ -122,9 +124,10 @@
|
|
|
122
124
|
"dev:generate": "nuxi generate playground",
|
|
123
125
|
"release": "pnpm run release:check && pnpm run release:patch",
|
|
124
126
|
"release:check": "pnpm run format && pnpm run lint && pnpm run typecheck && pnpm run test:types && pnpm run test && pnpm run test:vitest && pnpm run test:workspaces",
|
|
125
|
-
"release:
|
|
126
|
-
"release:
|
|
127
|
-
"release:
|
|
127
|
+
"release:auth": "node scripts/ensure-npm-auth.mjs",
|
|
128
|
+
"release:patch": "pnpm run release:auth && pnpm run prepack && node scripts/run-changelogen-release.mjs patch && pnpm publish -r && git push --follow-tags",
|
|
129
|
+
"release:minor": "pnpm run release:auth && pnpm run prepack && node scripts/run-changelogen-release.mjs minor && pnpm publish -r && git push --follow-tags",
|
|
130
|
+
"release:major": "pnpm run release:auth && pnpm run prepack && node scripts/run-changelogen-release.mjs major && pnpm publish -r && git push --follow-tags",
|
|
128
131
|
"lint": "biome check .",
|
|
129
132
|
"lint:fix": "biome check --write .",
|
|
130
133
|
"format": "biome format --write .",
|
|
@@ -144,7 +147,7 @@
|
|
|
144
147
|
"verify:packages": "node scripts/verify-packages.mjs",
|
|
145
148
|
"verify:packages:publint": "node scripts/verify-packages.mjs --publint",
|
|
146
149
|
"compare:published": "node scripts/compare-published-dist.mjs",
|
|
147
|
-
"test:dist:packages": "pnpm --filter \"@i18n-micro/core\" --filter \"@i18n-micro/vue\" --filter \"@i18n-micro/path-strategy\" --filter \"@i18n-micro/route-strategy\" --filter \"@i18n-micro/devtools-ui\" --filter \"@i18n-micro/astro\" run test:dist",
|
|
150
|
+
"test:dist:packages": "pnpm --filter \"@i18n-micro/core\" --filter \"@i18n-micro/utils\" --filter \"@i18n-micro/hmr\" --filter \"@i18n-micro/vue\" --filter \"@i18n-micro/path-strategy\" --filter \"@i18n-micro/route-strategy\" --filter \"@i18n-micro/devtools-ui\" --filter \"@i18n-micro/astro\" run test:dist",
|
|
148
151
|
"typecheck": "tsc --noEmit",
|
|
149
152
|
"typecheck:nuxt": "nuxt typecheck --no-emit",
|
|
150
153
|
"docs:dev": "vitepress dev docs",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"id":"c5d2004e-f981-4363-a9ec-4d72cf7f4a8f","timestamp":1781272693310,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export declare function parseAcceptLanguage(header: string | undefined): string[];
|
|
2
|
-
export declare function detectLocaleFromAcceptLanguage(header: string | undefined, validLocales: string[]): string | null;
|
|
3
|
-
export declare function applyAutoDetectLanguage(locale: string, hasExplicitPreference: boolean, autoDetectLanguage: boolean | undefined, acceptLanguageHeader: string | undefined, validLocales: string[]): string;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export function parseAcceptLanguage(header) {
|
|
2
|
-
if (!header) return [];
|
|
3
|
-
return header.split(",").map((entry) => {
|
|
4
|
-
const [lang] = entry.split(";");
|
|
5
|
-
return (lang ?? "").trim();
|
|
6
|
-
}).filter((s) => s.length > 0);
|
|
7
|
-
}
|
|
8
|
-
export function detectLocaleFromAcceptLanguage(header, validLocales) {
|
|
9
|
-
const langs = parseAcceptLanguage(header);
|
|
10
|
-
for (const lang of langs) {
|
|
11
|
-
const lowerCaseLanguage = lang.toLowerCase();
|
|
12
|
-
const primaryLanguage = lowerCaseLanguage.split("-")[0];
|
|
13
|
-
const found = validLocales.find((l) => l.toLowerCase() === lowerCaseLanguage || l.toLowerCase() === primaryLanguage);
|
|
14
|
-
if (found) return found;
|
|
15
|
-
}
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
export function applyAutoDetectLanguage(locale, hasExplicitPreference, autoDetectLanguage, acceptLanguageHeader, validLocales) {
|
|
19
|
-
if (!autoDetectLanguage || hasExplicitPreference) return locale;
|
|
20
|
-
return detectLocaleFromAcceptLanguage(acceptLanguageHeader, validLocales) ?? locale;
|
|
21
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { Locale } from '@i18n-micro/types';
|
|
2
|
-
export declare function getEnabledLocales(locales?: Locale[] | null): Locale[];
|
|
3
|
-
export declare function getEnabledLocaleCodes(locales?: Locale[] | null): string[];
|
|
4
|
-
export declare function isEnabledLocale(locales: Locale[] | null | undefined, code: string): boolean;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export function getEnabledLocales(locales) {
|
|
2
|
-
return (locales ?? []).filter((locale) => !locale.disabled);
|
|
3
|
-
}
|
|
4
|
-
export function getEnabledLocaleCodes(locales) {
|
|
5
|
-
return getEnabledLocales(locales).map((locale) => locale.code);
|
|
6
|
-
}
|
|
7
|
-
export function isEnabledLocale(locales, code) {
|
|
8
|
-
return getEnabledLocales(locales).some((locale) => locale.code === code);
|
|
9
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CacheControl — O(1) LRU cache with optional sliding TTL.
|
|
3
|
-
*
|
|
4
|
-
* Map is created internally — no external references, no desync risk.
|
|
5
|
-
*
|
|
6
|
-
* Performance:
|
|
7
|
-
* get — O(1) (lazy TTL check + LRU reorder via Map delete/set)
|
|
8
|
-
* set — O(1) (LRU eviction via Map.keys().next())
|
|
9
|
-
* has — O(1) (lazy TTL eviction)
|
|
10
|
-
*
|
|
11
|
-
* Eviction: LRU (Least Recently Used) based on native Map insertion order.
|
|
12
|
-
*/
|
|
13
|
-
export interface CacheControlOptions {
|
|
14
|
-
maxSize?: number;
|
|
15
|
-
/** TTL in seconds (0 = no expiration) */
|
|
16
|
-
ttl?: number;
|
|
17
|
-
}
|
|
18
|
-
export declare class CacheControl<T> {
|
|
19
|
-
private readonly cache;
|
|
20
|
-
private readonly expiry;
|
|
21
|
-
private maxSize;
|
|
22
|
-
private ttlMs;
|
|
23
|
-
constructor(options?: CacheControlOptions);
|
|
24
|
-
/** Update limits at runtime. Last call wins. */
|
|
25
|
-
configure(options: CacheControlOptions): void;
|
|
26
|
-
/**
|
|
27
|
-
* Get entry. Returns undefined if missing or expired.
|
|
28
|
-
* Refreshes TTL on hit (sliding expiration).
|
|
29
|
-
* Promotes key to most-recently-used position.
|
|
30
|
-
*/
|
|
31
|
-
get(key: string): T | undefined;
|
|
32
|
-
/**
|
|
33
|
-
* Check existence. Lazily evicts expired entries (side-effect by design).
|
|
34
|
-
*/
|
|
35
|
-
has(key: string): boolean;
|
|
36
|
-
/**
|
|
37
|
-
* Store entry with O(1) LRU eviction.
|
|
38
|
-
* Existing key — refreshes LRU position.
|
|
39
|
-
* New key at capacity — evicts least-recently-used (first Map key).
|
|
40
|
-
*/
|
|
41
|
-
set(key: string, value: T): void;
|
|
42
|
-
/** Delete entry and its expiry metadata. */
|
|
43
|
-
delete(key: string): boolean;
|
|
44
|
-
/** Clear all entries and metadata. */
|
|
45
|
-
clear(): void;
|
|
46
|
-
/** Iterate over all cache keys. */
|
|
47
|
-
keys(): IterableIterator<string>;
|
|
48
|
-
/** Current number of entries. */
|
|
49
|
-
get size(): number;
|
|
50
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
export class CacheControl {
|
|
2
|
-
cache = /* @__PURE__ */ new Map();
|
|
3
|
-
expiry = /* @__PURE__ */ new Map();
|
|
4
|
-
maxSize = 0;
|
|
5
|
-
ttlMs = 0;
|
|
6
|
-
constructor(options) {
|
|
7
|
-
if (options) this.configure(options);
|
|
8
|
-
}
|
|
9
|
-
/** Update limits at runtime. Last call wins. */
|
|
10
|
-
configure(options) {
|
|
11
|
-
this.maxSize = options.maxSize ?? 0;
|
|
12
|
-
this.ttlMs = (options.ttl ?? 0) * 1e3;
|
|
13
|
-
if (this.ttlMs === 0) {
|
|
14
|
-
this.expiry.clear();
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Get entry. Returns undefined if missing or expired.
|
|
19
|
-
* Refreshes TTL on hit (sliding expiration).
|
|
20
|
-
* Promotes key to most-recently-used position.
|
|
21
|
-
*/
|
|
22
|
-
get(key) {
|
|
23
|
-
const entry = this.cache.get(key);
|
|
24
|
-
if (entry === void 0) return void 0;
|
|
25
|
-
if (this.ttlMs > 0) {
|
|
26
|
-
const exp = this.expiry.get(key);
|
|
27
|
-
if (exp && Date.now() > exp) {
|
|
28
|
-
this.delete(key);
|
|
29
|
-
return void 0;
|
|
30
|
-
}
|
|
31
|
-
this.expiry.set(key, Date.now() + this.ttlMs);
|
|
32
|
-
}
|
|
33
|
-
this.cache.delete(key);
|
|
34
|
-
this.cache.set(key, entry);
|
|
35
|
-
return entry;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Check existence. Lazily evicts expired entries (side-effect by design).
|
|
39
|
-
*/
|
|
40
|
-
has(key) {
|
|
41
|
-
if (!this.cache.has(key)) return false;
|
|
42
|
-
if (this.ttlMs > 0) {
|
|
43
|
-
const exp = this.expiry.get(key);
|
|
44
|
-
if (exp && Date.now() > exp) {
|
|
45
|
-
this.delete(key);
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Store entry with O(1) LRU eviction.
|
|
53
|
-
* Existing key — refreshes LRU position.
|
|
54
|
-
* New key at capacity — evicts least-recently-used (first Map key).
|
|
55
|
-
*/
|
|
56
|
-
set(key, value) {
|
|
57
|
-
if (this.cache.has(key)) {
|
|
58
|
-
this.cache.delete(key);
|
|
59
|
-
} else if (this.maxSize > 0 && this.cache.size >= this.maxSize) {
|
|
60
|
-
const oldestKey = this.cache.keys().next().value;
|
|
61
|
-
if (oldestKey !== void 0) {
|
|
62
|
-
this.delete(oldestKey);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
this.cache.set(key, value);
|
|
66
|
-
if (this.ttlMs > 0) {
|
|
67
|
-
this.expiry.set(key, Date.now() + this.ttlMs);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/** Delete entry and its expiry metadata. */
|
|
71
|
-
delete(key) {
|
|
72
|
-
this.expiry.delete(key);
|
|
73
|
-
return this.cache.delete(key);
|
|
74
|
-
}
|
|
75
|
-
/** Clear all entries and metadata. */
|
|
76
|
-
clear() {
|
|
77
|
-
this.cache.clear();
|
|
78
|
-
this.expiry.clear();
|
|
79
|
-
}
|
|
80
|
-
/** Iterate over all cache keys. */
|
|
81
|
-
keys() {
|
|
82
|
-
return this.cache.keys();
|
|
83
|
-
}
|
|
84
|
-
/** Current number of entries. */
|
|
85
|
-
get size() {
|
|
86
|
-
return this.cache.size;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { ModuleOptionsExtend } from '@i18n-micro/types';
|
|
2
|
-
export declare const DEFAULT_COOKIE_NAME = "user-locale";
|
|
3
|
-
export declare const DEFAULT_HASH_COOKIE_NAME = "hash-locale";
|
|
4
|
-
export declare const DEFAULT_COOKIE_MAX_AGE: number;
|
|
5
|
-
/**
|
|
6
|
-
* Returns cookie name from config.
|
|
7
|
-
* If localeCookie === null, returns null (cookies disabled).
|
|
8
|
-
*/
|
|
9
|
-
export declare function getLocaleCookieName(config: ModuleOptionsExtend): string | null;
|
|
10
|
-
/**
|
|
11
|
-
* Returns hash cookie name when hashMode is enabled.
|
|
12
|
-
* Returns null when hashMode is false.
|
|
13
|
-
*/
|
|
14
|
-
export declare function getHashCookieName(config: ModuleOptionsExtend): string | null;
|
|
15
|
-
/**
|
|
16
|
-
* Returns standard options for locale/hash cookies.
|
|
17
|
-
*/
|
|
18
|
-
export declare function getLocaleCookieOptions(): {
|
|
19
|
-
expires: Date;
|
|
20
|
-
maxAge: number;
|
|
21
|
-
path: string;
|
|
22
|
-
watch: boolean;
|
|
23
|
-
sameSite: "lax";
|
|
24
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_COOKIE_NAME = "user-locale";
|
|
2
|
-
export const DEFAULT_HASH_COOKIE_NAME = "hash-locale";
|
|
3
|
-
export const DEFAULT_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
4
|
-
export function getLocaleCookieName(config) {
|
|
5
|
-
if (config.localeCookie === null) return null;
|
|
6
|
-
return config.localeCookie || DEFAULT_COOKIE_NAME;
|
|
7
|
-
}
|
|
8
|
-
export function getHashCookieName(config) {
|
|
9
|
-
if (!config.hashMode) return null;
|
|
10
|
-
return DEFAULT_HASH_COOKIE_NAME;
|
|
11
|
-
}
|
|
12
|
-
export function getLocaleCookieOptions() {
|
|
13
|
-
const date = /* @__PURE__ */ new Date();
|
|
14
|
-
date.setTime(date.getTime() + DEFAULT_COOKIE_MAX_AGE * 1e3);
|
|
15
|
-
return {
|
|
16
|
-
expires: date,
|
|
17
|
-
maxAge: DEFAULT_COOKIE_MAX_AGE,
|
|
18
|
-
path: "/",
|
|
19
|
-
watch: true,
|
|
20
|
-
sameSite: "lax"
|
|
21
|
-
};
|
|
22
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Optimized deep merge for translation objects (2-level depth).
|
|
3
|
-
*
|
|
4
|
-
* Regular shallow spread `{ ...old, ...new }` overwrites nested objects entirely:
|
|
5
|
-
* { common: { fromA: "A" } } + { common: { fromB: "B" } } => { common: { fromB: "B" } }
|
|
6
|
-
*
|
|
7
|
-
* This function preserves sibling keys inside nested objects:
|
|
8
|
-
* { common: { fromA: "A" } } + { common: { fromB: "B" } } => { common: { fromA: "A", fromB: "B" } }
|
|
9
|
-
*
|
|
10
|
-
* Performance: only iterates top-level keys of `source`; inner merge is a single spread.
|
|
11
|
-
*
|
|
12
|
-
* Limitation: only merges 2 levels deep. At level 3+, source overwrites target.
|
|
13
|
-
* For 99% of i18n files (Section → Key → Value), this is sufficient.
|
|
14
|
-
*/
|
|
15
|
-
export declare function deepMergeTranslations(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export function deepMergeTranslations(target, source) {
|
|
2
|
-
const result = { ...target };
|
|
3
|
-
for (const key in source) {
|
|
4
|
-
if (key === "__proto__" || key === "constructor") continue;
|
|
5
|
-
const src = source[key];
|
|
6
|
-
const dst = result[key];
|
|
7
|
-
if (src !== null && typeof src === "object" && !Array.isArray(src) && dst !== null && typeof dst === "object" && !Array.isArray(dst)) {
|
|
8
|
-
result[key] = { ...dst, ...src };
|
|
9
|
-
} else {
|
|
10
|
-
result[key] = src;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return result;
|
|
14
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export interface ResolveServerLocaleInput {
|
|
2
|
-
defaultLocale: string;
|
|
3
|
-
validLocales: string[];
|
|
4
|
-
autoDetectLanguage?: boolean;
|
|
5
|
-
hasLocaleInUrl: boolean;
|
|
6
|
-
urlLocale?: string | null;
|
|
7
|
-
queryLocale?: string | null;
|
|
8
|
-
cookieLocale?: string | null;
|
|
9
|
-
acceptLanguageHeader?: string | null;
|
|
10
|
-
}
|
|
11
|
-
export declare function resolveServerLocale(input: ResolveServerLocaleInput): string;
|
|
12
|
-
export interface ResolvePreferredLocaleInput {
|
|
13
|
-
defaultLocale: string;
|
|
14
|
-
validLocales: string[];
|
|
15
|
-
autoDetectLanguage?: boolean;
|
|
16
|
-
stateLocale?: string | null;
|
|
17
|
-
cookieLocale?: string | null;
|
|
18
|
-
acceptLanguageHeader?: string | null;
|
|
19
|
-
ignoreStateLocale?: boolean;
|
|
20
|
-
}
|
|
21
|
-
export declare function resolvePreferredLocale(input: ResolvePreferredLocaleInput): string;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { applyAutoDetectLanguage } from "./accept-language.js";
|
|
2
|
-
export function resolveServerLocale(input) {
|
|
3
|
-
const { defaultLocale, validLocales, autoDetectLanguage, hasLocaleInUrl, urlLocale, queryLocale, cookieLocale, acceptLanguageHeader } = input;
|
|
4
|
-
let locale = defaultLocale;
|
|
5
|
-
let hasExplicitPreference = false;
|
|
6
|
-
if (hasLocaleInUrl && urlLocale && validLocales.includes(urlLocale)) {
|
|
7
|
-
locale = urlLocale;
|
|
8
|
-
hasExplicitPreference = true;
|
|
9
|
-
} else if (queryLocale && validLocales.includes(queryLocale)) {
|
|
10
|
-
locale = queryLocale;
|
|
11
|
-
hasExplicitPreference = true;
|
|
12
|
-
} else if (cookieLocale && validLocales.includes(cookieLocale)) {
|
|
13
|
-
locale = cookieLocale;
|
|
14
|
-
hasExplicitPreference = true;
|
|
15
|
-
}
|
|
16
|
-
return applyAutoDetectLanguage(locale, hasExplicitPreference, autoDetectLanguage, acceptLanguageHeader ?? void 0, validLocales);
|
|
17
|
-
}
|
|
18
|
-
export function resolvePreferredLocale(input) {
|
|
19
|
-
const { defaultLocale, validLocales, autoDetectLanguage, stateLocale, cookieLocale, acceptLanguageHeader, ignoreStateLocale = false } = input;
|
|
20
|
-
let preferredLocale = defaultLocale;
|
|
21
|
-
let hasExplicitPreference = false;
|
|
22
|
-
if (!ignoreStateLocale && stateLocale && validLocales.includes(stateLocale)) {
|
|
23
|
-
preferredLocale = stateLocale;
|
|
24
|
-
hasExplicitPreference = true;
|
|
25
|
-
} else if (cookieLocale && validLocales.includes(cookieLocale)) {
|
|
26
|
-
preferredLocale = cookieLocale;
|
|
27
|
-
hasExplicitPreference = true;
|
|
28
|
-
}
|
|
29
|
-
return applyAutoDetectLanguage(preferredLocale, hasExplicitPreference, autoDetectLanguage, acceptLanguageHeader ?? void 0, validLocales);
|
|
30
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { RouteLocationNormalizedLoaded } from 'vue-router';
|
|
2
|
-
/**
|
|
3
|
-
* Extracts the base route pattern from a matched route path
|
|
4
|
-
* Converts Vue Router dynamic route patterns to file-based route patterns
|
|
5
|
-
*
|
|
6
|
-
* @param matchedPath - The matched route path from route.matched[0].path
|
|
7
|
-
* @returns The base route pattern (e.g., "/test/[param]")
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* extractBaseRoutePattern('/:locale(es)/test/:param()') // Returns '/test/[param]'
|
|
11
|
-
* extractBaseRoutePattern('/:locale(en)/static') // Returns '/static'
|
|
12
|
-
* extractBaseRoutePattern('/:locale(fr)/about/:id') // Returns '/about/[id]'
|
|
13
|
-
*/
|
|
14
|
-
export declare function extractBaseRoutePattern(matchedPath: string): string;
|
|
15
|
-
/**
|
|
16
|
-
* Finds allowed locales for a route using various matching strategies
|
|
17
|
-
*
|
|
18
|
-
* @param route - The route object
|
|
19
|
-
* @param routeLocales - The routeLocales configuration object
|
|
20
|
-
* @param localizedRouteNamePrefix - Prefix for localized route names
|
|
21
|
-
* @param localeCodes - Optional list of locale codes; when provided, path like /es/test is also looked up as routeLocales['test']/['/test']
|
|
22
|
-
* @returns Array of allowed locale codes or null if no restrictions
|
|
23
|
-
*/
|
|
24
|
-
export declare function findAllowedLocalesForRoute(route: RouteLocationNormalizedLoaded, routeLocales: Record<string, string[]> | undefined, localizedRouteNamePrefix?: string, localeCodes?: string[]): string[] | null;
|
|
25
|
-
/**
|
|
26
|
-
* Checks if meta tags should be disabled for a route
|
|
27
|
-
*
|
|
28
|
-
* @param route - The route object
|
|
29
|
-
* @param routeDisableMeta - The routeDisableMeta configuration object
|
|
30
|
-
* @param currentLocale - The current locale code
|
|
31
|
-
* @returns True if meta tags should be disabled, false otherwise
|
|
32
|
-
*/
|
|
33
|
-
export declare function isMetaDisabledForRoute(route: RouteLocationNormalizedLoaded, routeDisableMeta: Record<string, boolean | string[]> | undefined, currentLocale?: string, localizedRouteNamePrefix?: string): boolean;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
export function extractBaseRoutePattern(matchedPath) {
|
|
2
|
-
return matchedPath.replace(/\/:locale\([^)]+\)/g, "").replace(/\/:([^()]+)\(\)/g, "/[$1]").replace(/\/:([^()]+)/g, "/[$1]");
|
|
3
|
-
}
|
|
4
|
-
export function findAllowedLocalesForRoute(route, routeLocales, localizedRouteNamePrefix = "localized-", localeCodes) {
|
|
5
|
-
const routePath = route.path;
|
|
6
|
-
const routeName = route.name?.toString();
|
|
7
|
-
const normalizedRouteName = routeName?.replace(localizedRouteNamePrefix, "");
|
|
8
|
-
const normalizedRoutePath = normalizedRouteName ? `/${normalizedRouteName}` : void 0;
|
|
9
|
-
let allowedLocales = routeName && routeLocales?.[routeName] || normalizedRouteName && routeLocales?.[normalizedRouteName] || normalizedRoutePath && routeLocales?.[normalizedRoutePath] || normalizedRoutePath && routeLocales?.[normalizedRoutePath.replace(/^\//, "")] || routeLocales?.[routePath] || routePath && routeLocales?.[routePath.replace(/^\//, "")];
|
|
10
|
-
if (!allowedLocales && routeLocales && localeCodes?.length) {
|
|
11
|
-
const segments = routePath.split("/").filter(Boolean);
|
|
12
|
-
const first = segments[0];
|
|
13
|
-
if (first && localeCodes.includes(first) && segments.length > 1) {
|
|
14
|
-
const pathWithoutLocale = `/${segments.slice(1).join("/")}`;
|
|
15
|
-
const pathKey = pathWithoutLocale === "/" ? "/" : pathWithoutLocale.replace(/^\//, "");
|
|
16
|
-
allowedLocales = routeLocales[pathWithoutLocale] ?? routeLocales[pathKey] ?? void 0;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
if (!allowedLocales && route.matched && route.matched.length > 0) {
|
|
20
|
-
const matchedRoute = route.matched[0];
|
|
21
|
-
if (!matchedRoute) return null;
|
|
22
|
-
const matchedPath = matchedRoute.path;
|
|
23
|
-
const baseRoutePattern = extractBaseRoutePattern(matchedPath);
|
|
24
|
-
if (routeLocales?.[baseRoutePattern]) {
|
|
25
|
-
allowedLocales = routeLocales[baseRoutePattern];
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return allowedLocales || null;
|
|
29
|
-
}
|
|
30
|
-
export function isMetaDisabledForRoute(route, routeDisableMeta, currentLocale, localizedRouteNamePrefix = "localized-") {
|
|
31
|
-
if (!routeDisableMeta) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
const routePath = route.path;
|
|
35
|
-
const routeName = route.name?.toString();
|
|
36
|
-
const normalizedRouteName = routeName?.replace(localizedRouteNamePrefix, "");
|
|
37
|
-
const normalizedRoutePath = normalizedRouteName ? `/${normalizedRouteName}` : void 0;
|
|
38
|
-
const checkDisableMeta = (disableMetaValue) => {
|
|
39
|
-
if (disableMetaValue === void 0) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
if (typeof disableMetaValue === "boolean") {
|
|
43
|
-
return disableMetaValue;
|
|
44
|
-
}
|
|
45
|
-
if (Array.isArray(disableMetaValue)) {
|
|
46
|
-
return currentLocale ? disableMetaValue.includes(currentLocale) : false;
|
|
47
|
-
}
|
|
48
|
-
return false;
|
|
49
|
-
};
|
|
50
|
-
if (checkDisableMeta(routeDisableMeta[routePath]) || routeName && checkDisableMeta(routeDisableMeta[routeName]) || normalizedRouteName && checkDisableMeta(routeDisableMeta[normalizedRouteName]) || normalizedRoutePath && checkDisableMeta(routeDisableMeta[normalizedRoutePath])) {
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
if (route.matched && route.matched.length > 0) {
|
|
54
|
-
const matchedRoute = route.matched[0];
|
|
55
|
-
if (!matchedRoute) return false;
|
|
56
|
-
const matchedPath = matchedRoute.path;
|
|
57
|
-
const baseRoutePattern = extractBaseRoutePattern(matchedPath);
|
|
58
|
-
if (checkDisableMeta(routeDisableMeta[baseRoutePattern])) {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { ModuleOptionsExtend } from '@i18n-micro/types';
|
|
2
|
-
export interface RuntimeI18nOverrides {
|
|
3
|
-
defaultLocale?: string;
|
|
4
|
-
fallbackLocale?: string;
|
|
5
|
-
disabledLocales?: string[];
|
|
6
|
-
strategy?: string;
|
|
7
|
-
}
|
|
8
|
-
export declare function resolveI18nConfigWithRuntimeOverrides(baseConfig: ModuleOptionsExtend, runtimePublic?: Record<string, unknown>, warn?: (message: string) => void): ModuleOptionsExtend;
|