@astroscope/i18n 0.4.6 → 0.6.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 +11 -11
- package/dist/babel-plugin-C8O9spUS.js +120 -0
- package/dist/babel-plugin-C8O9spUS.js.map +1 -0
- package/dist/client/directives/idle.d.ts +4 -9
- package/dist/client/directives/idle.d.ts.map +1 -0
- package/dist/client/directives/idle.js +13 -21
- package/dist/client/directives/idle.js.map +1 -0
- package/dist/client/directives/load.d.ts +4 -9
- package/dist/client/directives/load.d.ts.map +1 -0
- package/dist/client/directives/load.js +9 -14
- package/dist/client/directives/load.js.map +1 -0
- package/dist/client/directives/media.d.ts +4 -9
- package/dist/client/directives/media.d.ts.map +1 -0
- package/dist/client/directives/media.js +16 -24
- package/dist/client/directives/media.js.map +1 -0
- package/dist/client/directives/only.d.ts +4 -11
- package/dist/client/directives/only.d.ts.map +1 -0
- package/dist/client/directives/only.js +9 -14
- package/dist/client/directives/only.js.map +1 -0
- package/dist/client/directives/utils.d.ts +3 -4
- package/dist/client/directives/utils.d.ts.map +1 -0
- package/dist/client/directives/utils.js +32 -7
- package/dist/client/directives/utils.js.map +1 -0
- package/dist/client/directives/visible.d.ts +4 -9
- package/dist/client/directives/visible.d.ts.map +1 -0
- package/dist/client/directives/visible.js +22 -29
- package/dist/client/directives/visible.js.map +1 -0
- package/dist/client/translate.d.ts +5 -4
- package/dist/client/translate.d.ts.map +1 -0
- package/dist/client/translate.js +68 -41
- package/dist/client/translate.js.map +1 -0
- package/dist/compiler-DjYjJc0V.js +45 -0
- package/dist/compiler-DjYjJc0V.js.map +1 -0
- package/dist/context-CNd4O0Pf.js +247 -0
- package/dist/context-CNd4O0Pf.js.map +1 -0
- package/dist/extraction/index.d.ts +12 -17
- package/dist/extraction/index.d.ts.map +1 -0
- package/dist/extraction/index.js +3 -11
- package/dist/i18n-DzmH9JmE.d.ts +79 -0
- package/dist/i18n-DzmH9JmE.d.ts.map +1 -0
- package/dist/index.d.ts +37 -33
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +163 -148
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +6 -6
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +2 -10
- package/dist/manifest-D883_BIS.js +43 -0
- package/dist/manifest-D883_BIS.js.map +1 -0
- package/dist/rich-CHisXE-A.js +87 -0
- package/dist/rich-CHisXE-A.js.map +1 -0
- package/dist/{rich-CQUhdwBc.d.ts → rich-CqNMP3tk.d.ts} +4 -2
- package/dist/rich-CqNMP3tk.d.ts.map +1 -0
- package/dist/server/translate.d.ts +5 -3
- package/dist/server/translate.d.ts.map +1 -0
- package/dist/server/translate.js +75 -64
- package/dist/server/translate.js.map +1 -0
- package/dist/state-DeudhdeT.js +20 -0
- package/dist/state-DeudhdeT.js.map +1 -0
- package/dist/{types-moTgocy7.d.ts → types-BfyoJsoB.d.ts} +11 -10
- package/dist/types-BfyoJsoB.d.ts.map +1 -0
- package/dist/types-Bo4jMK8g.d.ts +20 -0
- package/dist/types-Bo4jMK8g.d.ts.map +1 -0
- package/dist/{types-DNRFj1XA.d.ts → types-C98CcjtN.d.ts} +13 -11
- package/dist/types-C98CcjtN.d.ts.map +1 -0
- package/dist/url-B8334XSD.js +36 -0
- package/dist/url-B8334XSD.js.map +1 -0
- package/dist/vite-plugin-DVUlcvEH.js +421 -0
- package/dist/vite-plugin-DVUlcvEH.js.map +1 -0
- package/package.json +14 -12
- package/dist/babel-plugin-GOB6FMZ2.js +0 -119
- package/dist/chunk-33JVAFGQ.js +0 -44
- package/dist/chunk-4A5BKHFN.js +0 -16
- package/dist/chunk-7MSGHEST.js +0 -31
- package/dist/chunk-FYKC3DAZ.js +0 -36
- package/dist/chunk-LXW5WYBS.js +0 -298
- package/dist/chunk-PACQPCSW.js +0 -479
- package/dist/chunk-VGPVG5JP.js +0 -73
- package/dist/i18n-BmK1MjH7.d.ts +0 -76
- package/dist/types-B_8vAmvv.d.ts +0 -18
- package/dist/types-BeDFfFIL.d.ts +0 -7
package/dist/client/translate.js
CHANGED
|
@@ -1,46 +1,73 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
compileMessage,
|
|
7
|
-
formatMessageToParts
|
|
8
|
-
} from "../chunk-33JVAFGQ.js";
|
|
9
|
-
|
|
10
|
-
// src/client/translate.ts
|
|
11
|
-
var cache = /* @__PURE__ */ new Map();
|
|
1
|
+
import { r as formatMessageToParts, t as compileMessage } from "../compiler-DjYjJc0V.js";
|
|
2
|
+
import { n as normalizeMeta, t as partsToNodes } from "../rich-CHisXE-A.js";
|
|
3
|
+
import { t as getI18nState } from "../state-DeudhdeT.js";
|
|
4
|
+
//#region src/client/translate.ts
|
|
5
|
+
const cache = /* @__PURE__ */ new Map();
|
|
12
6
|
function getLocale() {
|
|
13
|
-
|
|
7
|
+
return getI18nState()?.locale ?? "";
|
|
14
8
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Client-side translate function
|
|
11
|
+
*
|
|
12
|
+
* Reads from window.__i18n__.translations (injected per-chunk).
|
|
13
|
+
* In production, translations are always loaded before component code runs
|
|
14
|
+
* via the chunk loader injected by the Vite plugin.
|
|
15
|
+
*
|
|
16
|
+
* Call patterns:
|
|
17
|
+
* - Development: t('key', 'fallback') or t('key', 'fallback', values)
|
|
18
|
+
* - Production: t('key') or t('key', undefined, values)
|
|
19
|
+
*
|
|
20
|
+
* Falls back to meta.fallback (dev) or key (prod) when translation is missing.
|
|
21
|
+
*/
|
|
22
|
+
const t = ((key, meta, values) => {
|
|
23
|
+
const normalizedMeta = meta ? normalizeMeta(meta) : { fallback: "" };
|
|
24
|
+
const state = getI18nState();
|
|
25
|
+
const locale = state?.locale ?? "";
|
|
26
|
+
const raw = state?.translations[key];
|
|
27
|
+
if (!raw) {
|
|
28
|
+
if (normalizedMeta.fallback) {
|
|
29
|
+
let compiled = cache.get(`__fallback__${key}`);
|
|
30
|
+
if (!compiled) {
|
|
31
|
+
compiled = compileMessage(locale, normalizedMeta.fallback);
|
|
32
|
+
cache.set(`__fallback__${key}`, compiled);
|
|
33
|
+
}
|
|
34
|
+
return compiled(values);
|
|
35
|
+
}
|
|
36
|
+
return key;
|
|
37
|
+
}
|
|
38
|
+
let compiled = cache.get(key);
|
|
39
|
+
if (!compiled) {
|
|
40
|
+
compiled = compileMessage(locale, raw);
|
|
41
|
+
cache.set(key, compiled);
|
|
42
|
+
}
|
|
43
|
+
return compiled(values);
|
|
35
44
|
});
|
|
45
|
+
/**
|
|
46
|
+
* Client-side rich text translation function
|
|
47
|
+
*
|
|
48
|
+
* Parses MF2 markup syntax and applies component callbacks to wrap content.
|
|
49
|
+
* Works with any JSX runtime (React, Preact, etc.).
|
|
50
|
+
*
|
|
51
|
+
* @param key - Translation key
|
|
52
|
+
* @param meta - Fallback string or meta object with fallback (used as message template)
|
|
53
|
+
* @param components - Map of tag names to component callbacks
|
|
54
|
+
* @param values - Runtime interpolation values (ICU MessageFormat)
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* rich('tos', 'Read our {#link}Terms{/link}', {
|
|
59
|
+
* link: (children) => <a href="/tos">{children}</a>
|
|
60
|
+
* })
|
|
61
|
+
* // Returns: ['Read our ', <a href="/tos">Terms</a>]
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
36
64
|
function rich(key, meta, components, values) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
65
|
+
const normalizedMeta = meta ? normalizeMeta(meta) : { fallback: "" };
|
|
66
|
+
const state = getI18nState();
|
|
67
|
+
const raw = state?.translations[key] ?? normalizedMeta.fallback ?? key;
|
|
68
|
+
return partsToNodes(formatMessageToParts(state?.locale ?? "", raw, values), components ?? {});
|
|
41
69
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
70
|
+
//#endregion
|
|
71
|
+
export { getLocale, rich, t };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=translate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.js","names":[],"sources":["../../src/client/translate.ts"],"sourcesContent":["import { compileMessage, formatMessageToParts } from '../shared/compiler.js';\nimport { normalizeMeta } from '../shared/meta.js';\nimport { type RichComponents, partsToNodes } from '../shared/rich.js';\nimport type { CompiledTranslation, TranslateFunction, TranslationMeta } from '../shared/types.js';\nimport { getI18nState } from './state.js';\n\nconst cache = new Map<string, CompiledTranslation>();\n\nexport function getLocale(): string {\n return getI18nState()?.locale ?? '';\n}\n\n/**\n * Client-side translate function\n *\n * Reads from window.__i18n__.translations (injected per-chunk).\n * In production, translations are always loaded before component code runs\n * via the chunk loader injected by the Vite plugin.\n *\n * Call patterns:\n * - Development: t('key', 'fallback') or t('key', 'fallback', values)\n * - Production: t('key') or t('key', undefined, values)\n *\n * Falls back to meta.fallback (dev) or key (prod) when translation is missing.\n */\nexport const t: TranslateFunction = ((\n key: string,\n meta?: TranslationMeta | string,\n values?: Record<string, unknown>,\n): string => {\n const normalizedMeta: TranslationMeta = meta ? normalizeMeta(meta) : { fallback: '' };\n\n const state = getI18nState();\n const locale = state?.locale ?? '';\n\n // look up raw translation\n const raw = state?.translations[key];\n\n if (!raw) {\n // translation missing: use fallback (dev) or key (prod)\n if (normalizedMeta.fallback) {\n let compiled = cache.get(`__fallback__${key}`);\n\n if (!compiled) {\n compiled = compileMessage(locale, normalizedMeta.fallback);\n cache.set(`__fallback__${key}`, compiled);\n }\n\n return compiled(values);\n }\n\n // production: return key as-is\n return key;\n }\n\n // check cache\n let compiled = cache.get(key);\n\n if (!compiled) {\n compiled = compileMessage(locale, raw);\n cache.set(key, compiled);\n }\n\n return compiled(values);\n}) as TranslateFunction;\n\n/**\n * Client-side rich text translation function\n *\n * Parses MF2 markup syntax and applies component callbacks to wrap content.\n * Works with any JSX runtime (React, Preact, etc.).\n *\n * @param key - Translation key\n * @param meta - Fallback string or meta object with fallback (used as message template)\n * @param components - Map of tag names to component callbacks\n * @param values - Runtime interpolation values (ICU MessageFormat)\n *\n * @example\n * ```tsx\n * rich('tos', 'Read our {#link}Terms{/link}', {\n * link: (children) => <a href=\"/tos\">{children}</a>\n * })\n * // Returns: ['Read our ', <a href=\"/tos\">Terms</a>]\n * ```\n */\nexport function rich<T = unknown>(\n key: string,\n meta?: TranslationMeta | string | undefined,\n components?: RichComponents<T> | undefined,\n values?: Record<string, unknown> | undefined,\n): (string | T)[] {\n const normalizedMeta: TranslationMeta = meta ? normalizeMeta(meta) : { fallback: '' };\n\n const state = getI18nState();\n\n // look up raw translation, fall back to meta.fallback or key\n const raw = state?.translations[key] ?? normalizedMeta.fallback ?? key;\n\n const parts = formatMessageToParts(state?.locale ?? '', raw, values);\n\n return partsToNodes(parts, components ?? {});\n}\n"],"mappings":";;;;AAMA,MAAM,wBAAQ,IAAI,IAAiC;AAEnD,SAAgB,YAAoB;CAClC,OAAO,aAAa,CAAC,EAAE,UAAU;AACnC;;;;;;;;;;;;;;AAeA,MAAa,MACX,KACA,MACA,WACW;CACX,MAAM,iBAAkC,OAAO,cAAc,IAAI,IAAI,EAAE,UAAU,GAAG;CAEpF,MAAM,QAAQ,aAAa;CAC3B,MAAM,SAAS,OAAO,UAAU;CAGhC,MAAM,MAAM,OAAO,aAAa;CAEhC,IAAI,CAAC,KAAK;EAER,IAAI,eAAe,UAAU;GAC3B,IAAI,WAAW,MAAM,IAAI,eAAe,KAAK;GAE7C,IAAI,CAAC,UAAU;IACb,WAAW,eAAe,QAAQ,eAAe,QAAQ;IACzD,MAAM,IAAI,eAAe,OAAO,QAAQ;GAC1C;GAEA,OAAO,SAAS,MAAM;EACxB;EAGA,OAAO;CACT;CAGA,IAAI,WAAW,MAAM,IAAI,GAAG;CAE5B,IAAI,CAAC,UAAU;EACb,WAAW,eAAe,QAAQ,GAAG;EACrC,MAAM,IAAI,KAAK,QAAQ;CACzB;CAEA,OAAO,SAAS,MAAM;AACxB;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,KACd,KACA,MACA,YACA,QACgB;CAChB,MAAM,iBAAkC,OAAO,cAAc,IAAI,IAAI,EAAE,UAAU,GAAG;CAEpF,MAAM,QAAQ,aAAa;CAG3B,MAAM,MAAM,OAAO,aAAa,QAAQ,eAAe,YAAY;CAInE,OAAO,aAFO,qBAAqB,OAAO,UAAU,IAAI,KAAK,MAErC,GAAG,cAAc,CAAC,CAAC;AAC7C"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MessageFormat } from "messageformat";
|
|
2
|
+
import { DraftFunctions } from "messageformat/functions";
|
|
3
|
+
//#region src/shared/compiler.ts
|
|
4
|
+
const mfOptions = { functions: DraftFunctions };
|
|
5
|
+
const cache = /* @__PURE__ */ new Map();
|
|
6
|
+
function getOrCreateMessageFormat(locale, message) {
|
|
7
|
+
const cacheKey = `${locale}:${message}`;
|
|
8
|
+
const cached = cache.get(cacheKey);
|
|
9
|
+
if (cached) return cached;
|
|
10
|
+
const mf = new MessageFormat(locale, message, mfOptions);
|
|
11
|
+
cache.set(cacheKey, mf);
|
|
12
|
+
return mf;
|
|
13
|
+
}
|
|
14
|
+
function compileMessage(locale, message) {
|
|
15
|
+
try {
|
|
16
|
+
const mf = getOrCreateMessageFormat(locale, message);
|
|
17
|
+
return (values) => mf.format(values);
|
|
18
|
+
} catch {
|
|
19
|
+
return () => message;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Format a message to parts for rich text rendering.
|
|
24
|
+
* Returns an array of MessagePart objects that can be processed
|
|
25
|
+
* to build component trees with markup elements.
|
|
26
|
+
*/
|
|
27
|
+
function formatMessageToParts(locale, message, values) {
|
|
28
|
+
try {
|
|
29
|
+
return getOrCreateMessageFormat(locale, message).formatToParts(values);
|
|
30
|
+
} catch {
|
|
31
|
+
return [{
|
|
32
|
+
type: "literal",
|
|
33
|
+
value: message
|
|
34
|
+
}];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function compileTranslations(locale, raw) {
|
|
38
|
+
const compiled = {};
|
|
39
|
+
for (const [key, message] of Object.entries(raw)) compiled[key] = compileMessage(locale, message);
|
|
40
|
+
return compiled;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { compileTranslations as n, formatMessageToParts as r, compileMessage as t };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=compiler-DjYjJc0V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler-DjYjJc0V.js","names":[],"sources":["../src/shared/compiler.ts"],"sourcesContent":["import { MessageFormat, type MessagePart } from 'messageformat';\nimport { DraftFunctions } from 'messageformat/functions';\n\nimport type { CompiledTranslation, CompiledTranslations, RawTranslations } from './types.js';\n\n// enable draft functions for :currency, :date, :datetime, :time, :percent, :unit\nconst mfOptions = { functions: DraftFunctions };\n\nconst cache = new Map<string, MessageFormat<string, string>>();\n\nfunction getOrCreateMessageFormat(locale: string, message: string): MessageFormat<string, string> {\n const cacheKey = `${locale}:${message}`;\n const cached = cache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const mf = new MessageFormat(locale, message, mfOptions);\n\n cache.set(cacheKey, mf);\n\n return mf;\n}\n\nexport function compileMessage(locale: string, message: string): CompiledTranslation {\n try {\n const mf = getOrCreateMessageFormat(locale, message);\n\n return (values) => mf.format(values);\n } catch {\n // if MF2 parsing fails, return raw message so the problem is visible to the user\n return () => message;\n }\n}\n\n/**\n * Format a message to parts for rich text rendering.\n * Returns an array of MessagePart objects that can be processed\n * to build component trees with markup elements.\n */\nexport function formatMessageToParts(\n locale: string,\n message: string,\n values?: Record<string, unknown> | undefined,\n): MessagePart<string>[] {\n try {\n const mf = getOrCreateMessageFormat(locale, message);\n\n return mf.formatToParts(values);\n } catch {\n // if MF2 parsing fails, return a single literal part with the raw message\n return [{ type: 'literal', value: message }];\n }\n}\n\nexport function compileTranslations(locale: string, raw: RawTranslations): CompiledTranslations {\n const compiled: CompiledTranslations = {};\n\n for (const [key, message] of Object.entries(raw)) {\n compiled[key] = compileMessage(locale, message);\n }\n\n return compiled;\n}\n"],"mappings":";;;AAMA,MAAM,YAAY,EAAE,WAAW,eAAe;AAE9C,MAAM,wBAAQ,IAAI,IAA2C;AAE7D,SAAS,yBAAyB,QAAgB,SAAgD;CAChG,MAAM,WAAW,GAAG,OAAO,GAAG;CAC9B,MAAM,SAAS,MAAM,IAAI,QAAQ;CAEjC,IAAI,QACF,OAAO;CAGT,MAAM,KAAK,IAAI,cAAc,QAAQ,SAAS,SAAS;CAEvD,MAAM,IAAI,UAAU,EAAE;CAEtB,OAAO;AACT;AAEA,SAAgB,eAAe,QAAgB,SAAsC;CACnF,IAAI;EACF,MAAM,KAAK,yBAAyB,QAAQ,OAAO;EAEnD,QAAQ,WAAW,GAAG,OAAO,MAAM;CACrC,QAAQ;EAEN,aAAa;CACf;AACF;;;;;;AAOA,SAAgB,qBACd,QACA,SACA,QACuB;CACvB,IAAI;EAGF,OAFW,yBAAyB,QAAQ,OAEpC,CAAC,CAAC,cAAc,MAAM;CAChC,QAAQ;EAEN,OAAO,CAAC;GAAE,MAAM;GAAW,OAAO;EAAQ,CAAC;CAC7C;AACF;AAEA,SAAgB,oBAAoB,QAAgB,KAA4C;CAC9F,MAAM,WAAiC,CAAC;CAExC,KAAK,MAAM,CAAC,KAAK,YAAY,OAAO,QAAQ,GAAG,GAC7C,SAAS,OAAO,eAAe,QAAQ,OAAO;CAGhD,OAAO;AACT"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { t as getGlobalState } from "./manifest-D883_BIS.js";
|
|
2
|
+
import { n as compileTranslations } from "./compiler-DjYjJc0V.js";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
+
//#region src/extraction/hash.ts
|
|
6
|
+
/**
|
|
7
|
+
* Hash a string to a short hash (for cache busting)
|
|
8
|
+
*/
|
|
9
|
+
function hashString(str) {
|
|
10
|
+
return createHash("sha256").update(str).digest("hex").substring(0, 8);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Compute hash for chunk translations
|
|
14
|
+
*/
|
|
15
|
+
function computeChunkHash(translations, keys) {
|
|
16
|
+
const relevantTranslations = {};
|
|
17
|
+
for (const key of keys.sort()) if (translations[key]) relevantTranslations[key] = translations[key];
|
|
18
|
+
return hashString(JSON.stringify(relevantTranslations));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Compute hashes for all chunks in manifest
|
|
22
|
+
*/
|
|
23
|
+
function computeAllChunkHashes(translations, manifest) {
|
|
24
|
+
const hashes = {};
|
|
25
|
+
for (const [chunkId, keys] of Object.entries(manifest)) hashes[chunkId] = computeChunkHash(translations, keys);
|
|
26
|
+
return hashes;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/server/utils.ts
|
|
30
|
+
const LOWERCASE_A = 97;
|
|
31
|
+
/**
|
|
32
|
+
* Convert index to lowercase letter: 0→'a', 1→'b', ..., 25→'z'
|
|
33
|
+
*/
|
|
34
|
+
function indexToLetter(index) {
|
|
35
|
+
return String.fromCharCode(LOWERCASE_A + index);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Generate short variable name using bijective base-26: 0→'a', 25→'z', 26→'aa', 27→'ab', ...
|
|
39
|
+
*/
|
|
40
|
+
function generateBB26(index) {
|
|
41
|
+
let name = "";
|
|
42
|
+
let remaining = index;
|
|
43
|
+
do {
|
|
44
|
+
name = indexToLetter(remaining % 26) + name;
|
|
45
|
+
remaining = Math.floor(remaining / 26) - 1;
|
|
46
|
+
} while (remaining >= 0);
|
|
47
|
+
return name;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/server/i18n.ts
|
|
51
|
+
var I18nSingleton = class {
|
|
52
|
+
config = null;
|
|
53
|
+
rawCache = /* @__PURE__ */ new Map();
|
|
54
|
+
mergedCache = /* @__PURE__ */ new Map();
|
|
55
|
+
compiledCache = /* @__PURE__ */ new Map();
|
|
56
|
+
hashCache = /* @__PURE__ */ new Map();
|
|
57
|
+
scriptCache = /* @__PURE__ */ new Map();
|
|
58
|
+
chunkCache = /* @__PURE__ */ new Map();
|
|
59
|
+
manifestGetter = null;
|
|
60
|
+
manifestVersion = 0;
|
|
61
|
+
async configure(config) {
|
|
62
|
+
if (!config.locales?.length) throw new Error("i18n.configure(): locales array is required and must not be empty");
|
|
63
|
+
for (const locale of config.locales) {
|
|
64
|
+
if (!locale) throw new Error("i18n.configure(): locale cannot be empty");
|
|
65
|
+
if (locale !== locale.trim()) throw new Error(`i18n.configure(): locale "${locale}" has leading or trailing whitespace`);
|
|
66
|
+
}
|
|
67
|
+
if (new Set(config.locales).size !== config.locales.length) throw new Error("i18n.configure(): locales array contains duplicates");
|
|
68
|
+
if (config.defaultLocale !== void 0) {
|
|
69
|
+
if (!config.defaultLocale) throw new Error("i18n.configure(): defaultLocale cannot be empty");
|
|
70
|
+
if (config.defaultLocale !== config.defaultLocale.trim()) throw new Error(`i18n.configure(): defaultLocale "${config.defaultLocale}" has leading or trailing whitespace`);
|
|
71
|
+
if (!config.locales.includes(config.defaultLocale)) throw new Error(`i18n.configure(): defaultLocale "${config.defaultLocale}" is not in locales array`);
|
|
72
|
+
}
|
|
73
|
+
this.config = {
|
|
74
|
+
locales: config.locales,
|
|
75
|
+
defaultLocale: config.defaultLocale ?? config.locales[0],
|
|
76
|
+
fallback: config.fallback ?? "fallback"
|
|
77
|
+
};
|
|
78
|
+
if (!this.manifestGetter) {
|
|
79
|
+
const m = await import("virtual:@astroscope/i18n/manifest");
|
|
80
|
+
this.manifestGetter = m.getManifest;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
isConfigured() {
|
|
84
|
+
return this.config !== null;
|
|
85
|
+
}
|
|
86
|
+
getConfig() {
|
|
87
|
+
if (!this.config) throw new Error("i18n not configured. Call i18n.configure() first.");
|
|
88
|
+
return this.config;
|
|
89
|
+
}
|
|
90
|
+
setTranslations(locale, raw) {
|
|
91
|
+
this.rawCache.set(locale, raw);
|
|
92
|
+
this.mergedCache.delete(locale);
|
|
93
|
+
this.compiledCache.delete(locale);
|
|
94
|
+
this.hashCache.delete(locale);
|
|
95
|
+
this.scriptCache.delete(locale);
|
|
96
|
+
for (const key of this.chunkCache.keys()) if (key.startsWith(`${locale}:`)) this.chunkCache.delete(key);
|
|
97
|
+
const { chunks } = this.getManifest();
|
|
98
|
+
if (Object.keys(chunks).length > 0) this.hashCache.set(locale, computeAllChunkHashes(raw, chunks));
|
|
99
|
+
}
|
|
100
|
+
getTranslations(locale) {
|
|
101
|
+
this.invalidateIfManifestChanged();
|
|
102
|
+
const cached = this.mergedCache.get(locale);
|
|
103
|
+
if (cached) return cached;
|
|
104
|
+
const raw = this.rawCache.get(locale) ?? {};
|
|
105
|
+
const manifest = this.manifestGetter?.() ?? {
|
|
106
|
+
keys: [],
|
|
107
|
+
chunks: {},
|
|
108
|
+
imports: {}
|
|
109
|
+
};
|
|
110
|
+
if (manifest.keys.length === 0) return raw;
|
|
111
|
+
const withFallbacks = { ...raw };
|
|
112
|
+
for (const { key, meta } of manifest.keys) if (!(key in withFallbacks) && meta.fallback) withFallbacks[key] = meta.fallback;
|
|
113
|
+
this.mergedCache.set(locale, withFallbacks);
|
|
114
|
+
return withFallbacks;
|
|
115
|
+
}
|
|
116
|
+
getCompiledTranslations(locale) {
|
|
117
|
+
const cached = this.compiledCache.get(locale);
|
|
118
|
+
if (cached) return cached;
|
|
119
|
+
const compiled = compileTranslations(locale, this.getTranslations(locale));
|
|
120
|
+
this.compiledCache.set(locale, compiled);
|
|
121
|
+
return compiled;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get the extraction manifest (keys + chunks).
|
|
125
|
+
* - keys: translation keys extracted from codebase with metadata
|
|
126
|
+
* - chunks: mapping of chunk names to their translation keys
|
|
127
|
+
*/
|
|
128
|
+
getManifest() {
|
|
129
|
+
if (!this.manifestGetter) throw new Error("i18n manifest not initialized. Call i18n.configure() first.");
|
|
130
|
+
return this.manifestGetter();
|
|
131
|
+
}
|
|
132
|
+
getHashes(locale) {
|
|
133
|
+
return this.hashCache.get(locale) ?? {};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get the encoded chunk response body for a locale/chunk combination.
|
|
137
|
+
* Cached to avoid repeated JSON.stringify and encoding on each request.
|
|
138
|
+
* Returns undefined if chunk not found in manifest.
|
|
139
|
+
*/
|
|
140
|
+
getChunkBody(locale, chunkName) {
|
|
141
|
+
const keys = this.getManifest().chunks[chunkName];
|
|
142
|
+
if (!keys) return;
|
|
143
|
+
const cacheKey = `${locale}:${chunkName}`;
|
|
144
|
+
const cached = this.chunkCache.get(cacheKey);
|
|
145
|
+
if (cached) return cached;
|
|
146
|
+
const translations = this.getTranslations(locale);
|
|
147
|
+
const chunkTranslations = {};
|
|
148
|
+
for (const key of keys) if (translations[key]) chunkTranslations[key] = translations[key];
|
|
149
|
+
const js = `/* @astroscope/i18n chunk: ${chunkName} */
|
|
150
|
+
(function() {
|
|
151
|
+
var i = window.__i18n__;
|
|
152
|
+
if (i) Object.assign(i.translations, ${JSON.stringify(chunkTranslations)});
|
|
153
|
+
})();
|
|
154
|
+
`;
|
|
155
|
+
const body = new TextEncoder().encode(js);
|
|
156
|
+
this.chunkCache.set(cacheKey, body);
|
|
157
|
+
return body;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get the inline script for I18nScript component.
|
|
161
|
+
* Cached per locale, invalidated when translations change.
|
|
162
|
+
*/
|
|
163
|
+
getClientScript(locale) {
|
|
164
|
+
const cached = this.scriptCache.get(locale);
|
|
165
|
+
if (cached) return cached;
|
|
166
|
+
const script = this.createClientScript(locale);
|
|
167
|
+
this.scriptCache.set(locale, script);
|
|
168
|
+
return script;
|
|
169
|
+
}
|
|
170
|
+
createClientScript(locale) {
|
|
171
|
+
const { chunks, imports } = this.getManifest();
|
|
172
|
+
if (!(Object.keys(chunks).length > 0)) {
|
|
173
|
+
const raw = this.getTranslations(locale);
|
|
174
|
+
return `window.__i18n__=${JSON.stringify({
|
|
175
|
+
locale,
|
|
176
|
+
hashes: {},
|
|
177
|
+
imports: {},
|
|
178
|
+
translations: raw
|
|
179
|
+
})};`;
|
|
180
|
+
}
|
|
181
|
+
const hashes = this.getHashes(locale);
|
|
182
|
+
const allChunks = new Set(Object.keys(hashes));
|
|
183
|
+
for (const deps of Object.values(imports)) deps.forEach((d) => allChunks.add(d));
|
|
184
|
+
const chunkToVar = /* @__PURE__ */ new Map();
|
|
185
|
+
let varIndex = 0;
|
|
186
|
+
for (const chunk of allChunks) chunkToVar.set(chunk, generateBB26(varIndex++));
|
|
187
|
+
const varDecls = [...chunkToVar.entries()].map(([chunk, v]) => `${v}=${JSON.stringify(chunk)}`).join(",");
|
|
188
|
+
const hashesObj = Object.entries(hashes).map(([chunk, hash]) => `[${chunkToVar.get(chunk)}]:${JSON.stringify(hash)}`).join(",");
|
|
189
|
+
const importsObj = Object.entries(imports).filter(([chunk]) => chunkToVar.has(chunk)).map(([chunk, deps]) => {
|
|
190
|
+
const validDeps = deps.filter((d) => chunkToVar.has(d));
|
|
191
|
+
return validDeps.length > 0 ? `[${chunkToVar.get(chunk)}]:[${validDeps.map((d) => chunkToVar.get(d)).join(",")}]` : null;
|
|
192
|
+
}).filter(Boolean).join(",");
|
|
193
|
+
return `(()=>{var ${varDecls};window.__i18n__={locale:${JSON.stringify(locale)},hashes:{${hashesObj}},imports:{${importsObj}},translations:{}};})();`;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Clear translations for specified locale(s).
|
|
197
|
+
* @param locales - Locale(s) to clear, or all if not specified
|
|
198
|
+
*/
|
|
199
|
+
clear(locales) {
|
|
200
|
+
const targets = this.resolveLocales(locales);
|
|
201
|
+
for (const locale of targets) {
|
|
202
|
+
this.rawCache.delete(locale);
|
|
203
|
+
this.mergedCache.delete(locale);
|
|
204
|
+
this.compiledCache.delete(locale);
|
|
205
|
+
this.hashCache.delete(locale);
|
|
206
|
+
this.scriptCache.delete(locale);
|
|
207
|
+
for (const key of this.chunkCache.keys()) if (key.startsWith(`${locale}:`)) this.chunkCache.delete(key);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* invalidate derived caches when the extraction manifest has changed (dev mode HMR)
|
|
212
|
+
*/
|
|
213
|
+
invalidateIfManifestChanged() {
|
|
214
|
+
const { version } = getGlobalState();
|
|
215
|
+
if (version === this.manifestVersion) return;
|
|
216
|
+
this.manifestVersion = version;
|
|
217
|
+
this.mergedCache.clear();
|
|
218
|
+
this.compiledCache.clear();
|
|
219
|
+
this.scriptCache.clear();
|
|
220
|
+
this.chunkCache.clear();
|
|
221
|
+
}
|
|
222
|
+
resolveLocales(locales) {
|
|
223
|
+
if (!locales) return this.config?.locales ?? [];
|
|
224
|
+
if (typeof locales === "string") return [locales];
|
|
225
|
+
return locales;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const i18n = new I18nSingleton();
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region src/server/context.ts
|
|
231
|
+
const als = new AsyncLocalStorage();
|
|
232
|
+
/**
|
|
233
|
+
* Run a function with i18n context (called by middleware)
|
|
234
|
+
*/
|
|
235
|
+
function runWithContext(context, fn) {
|
|
236
|
+
return als.run(context, fn);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get the current i18n context
|
|
240
|
+
*/
|
|
241
|
+
function getContext() {
|
|
242
|
+
return als.getStore() ?? null;
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
export { runWithContext as n, i18n as r, getContext as t };
|
|
246
|
+
|
|
247
|
+
//# sourceMappingURL=context-CNd4O0Pf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-CNd4O0Pf.js","names":[],"sources":["../src/extraction/hash.ts","../src/server/utils.ts","../src/server/i18n.ts","../src/server/context.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { RawTranslations } from '../shared/types.js';\nimport type { ChunkManifest } from './types.js';\n\n/**\n * Hash a string to a short hash (for cache busting)\n */\nfunction hashString(str: string): string {\n return createHash('sha256').update(str).digest('hex').substring(0, 8);\n}\n\n/**\n * Compute hash for chunk translations\n */\nexport function computeChunkHash(translations: RawTranslations, keys: string[]): string {\n const relevantTranslations: RawTranslations = {};\n\n for (const key of keys.sort()) {\n if (translations[key]) {\n relevantTranslations[key] = translations[key];\n }\n }\n\n return hashString(JSON.stringify(relevantTranslations));\n}\n\n/**\n * Compute hashes for all chunks in manifest\n */\nexport function computeAllChunkHashes(translations: RawTranslations, manifest: ChunkManifest): Record<string, string> {\n const hashes: Record<string, string> = {};\n\n for (const [chunkId, keys] of Object.entries(manifest)) {\n hashes[chunkId] = computeChunkHash(translations, keys);\n }\n\n return hashes;\n}\n","const LOWERCASE_A = 97;\n\n/**\n * Convert index to lowercase letter: 0→'a', 1→'b', ..., 25→'z'\n */\nfunction indexToLetter(index: number): string {\n return String.fromCharCode(LOWERCASE_A + index);\n}\n\n/**\n * Generate short variable name using bijective base-26: 0→'a', 25→'z', 26→'aa', 27→'ab', ...\n */\nexport function generateBB26(index: number): string {\n let name = '';\n let remaining = index;\n\n do {\n name = indexToLetter(remaining % 26) + name;\n remaining = Math.floor(remaining / 26) - 1;\n } while (remaining >= 0);\n\n return name;\n}\n","import { computeAllChunkHashes } from '../extraction/hash.js';\nimport { getGlobalState } from '../extraction/manifest.js';\nimport type { ExtractionManifest } from '../extraction/types.js';\nimport { compileTranslations } from '../shared/compiler.js';\nimport type { CompiledTranslations, RawTranslations } from '../shared/types.js';\nimport type { FallbackBehavior } from './types.js';\nimport { generateBB26 } from './utils.js';\n\nexport type I18nConfig = {\n locales: string[];\n defaultLocale?: string | undefined;\n fallback?: FallbackBehavior | undefined;\n};\n\ntype NormalizedConfig = {\n locales: string[];\n defaultLocale: string;\n fallback: FallbackBehavior;\n};\n\nclass I18nSingleton {\n // normalized user config with defaults applied\n private config: NormalizedConfig | null = null;\n\n // locale -> raw translation strings (before ICU compilation)\n private rawCache = new Map<string, RawTranslations>();\n\n // locale -> raw translations merged with manifest fallbacks\n private mergedCache = new Map<string, RawTranslations>();\n\n // locale -> compiled ICU MessageFormat functions\n private compiledCache = new Map<string, CompiledTranslations>();\n\n // locale -> chunk name -> content hash (for cache busting)\n private hashCache = new Map<string, Record<string, string>>();\n\n // locale -> inline script for I18nScript component\n private scriptCache = new Map<string, string>();\n\n // \"locale:chunkName\" -> encoded chunk response body\n private chunkCache = new Map<string, Uint8Array>();\n\n // manifest getter (set by init module, provides live data in dev mode)\n private manifestGetter: (() => ExtractionManifest) | null = null;\n\n // tracks the manifest version to detect when derived caches need invalidation\n private manifestVersion = 0;\n\n async configure(config: I18nConfig): Promise<void> {\n if (!config.locales?.length) {\n throw new Error('i18n.configure(): locales array is required and must not be empty');\n }\n\n for (const locale of config.locales) {\n if (!locale) {\n throw new Error('i18n.configure(): locale cannot be empty');\n }\n\n if (locale !== locale.trim()) {\n throw new Error(`i18n.configure(): locale \"${locale}\" has leading or trailing whitespace`);\n }\n }\n\n if (new Set(config.locales).size !== config.locales.length) {\n throw new Error('i18n.configure(): locales array contains duplicates');\n }\n\n if (config.defaultLocale !== undefined) {\n if (!config.defaultLocale) {\n throw new Error('i18n.configure(): defaultLocale cannot be empty');\n }\n\n if (config.defaultLocale !== config.defaultLocale.trim()) {\n throw new Error(`i18n.configure(): defaultLocale \"${config.defaultLocale}\" has leading or trailing whitespace`);\n }\n\n if (!config.locales.includes(config.defaultLocale)) {\n throw new Error(`i18n.configure(): defaultLocale \"${config.defaultLocale}\" is not in locales array`);\n }\n }\n\n this.config = {\n locales: config.locales,\n defaultLocale: config.defaultLocale ?? config.locales[0]!,\n fallback: config.fallback ?? 'fallback',\n };\n\n // dynamically import manifest getter from virtual module\n // this removes the need for a separate `import '@astroscope/i18n/init'`\n if (!this.manifestGetter) {\n const m = await import('virtual:@astroscope/i18n/manifest');\n this.manifestGetter = m.getManifest;\n }\n }\n\n isConfigured(): boolean {\n return this.config !== null;\n }\n\n getConfig(): NormalizedConfig {\n if (!this.config) {\n throw new Error('i18n not configured. Call i18n.configure() first.');\n }\n\n return this.config;\n }\n\n setTranslations(locale: string, raw: RawTranslations): void {\n this.rawCache.set(locale, raw);\n this.mergedCache.delete(locale); // invalidate merged cache\n this.compiledCache.delete(locale); // invalidate compiled cache\n this.hashCache.delete(locale); // invalidate hash cache\n this.scriptCache.delete(locale); // invalidate script cache\n\n // invalidate chunk cache entries for this locale\n for (const key of this.chunkCache.keys()) {\n if (key.startsWith(`${locale}:`)) {\n this.chunkCache.delete(key);\n }\n }\n\n // recompute hashes if chunk manifest exists (production only)\n const { chunks } = this.getManifest();\n\n if (Object.keys(chunks).length > 0) {\n this.hashCache.set(locale, computeAllChunkHashes(raw, chunks));\n }\n }\n\n getTranslations(locale: string): RawTranslations {\n this.invalidateIfManifestChanged();\n\n const cached = this.mergedCache.get(locale);\n\n if (cached) {\n return cached;\n }\n\n const raw = this.rawCache.get(locale) ?? {};\n const manifest = this.manifestGetter?.() ?? { keys: [], chunks: {}, imports: {} };\n\n if (manifest.keys.length === 0) {\n return raw;\n }\n\n // merge fallbacks for keys that don't have translations\n const withFallbacks: RawTranslations = { ...raw };\n\n for (const { key, meta } of manifest.keys) {\n if (!(key in withFallbacks) && meta.fallback) {\n withFallbacks[key] = meta.fallback;\n }\n }\n\n this.mergedCache.set(locale, withFallbacks);\n\n return withFallbacks;\n }\n\n getCompiledTranslations(locale: string): CompiledTranslations {\n const cached = this.compiledCache.get(locale);\n\n if (cached) {\n return cached;\n }\n\n const compiled = compileTranslations(locale, this.getTranslations(locale));\n\n this.compiledCache.set(locale, compiled);\n\n return compiled;\n }\n\n /**\n * Get the extraction manifest (keys + chunks).\n * - keys: translation keys extracted from codebase with metadata\n * - chunks: mapping of chunk names to their translation keys\n */\n getManifest(): ExtractionManifest {\n if (!this.manifestGetter) {\n throw new Error('i18n manifest not initialized. Call i18n.configure() first.');\n }\n\n return this.manifestGetter();\n }\n\n getHashes(locale: string): Record<string, string> {\n return this.hashCache.get(locale) ?? {};\n }\n\n /**\n * Get the encoded chunk response body for a locale/chunk combination.\n * Cached to avoid repeated JSON.stringify and encoding on each request.\n * Returns undefined if chunk not found in manifest.\n */\n getChunkBody(locale: string, chunkName: string): Uint8Array | undefined {\n const keys = this.getManifest().chunks[chunkName];\n\n if (!keys) {\n return undefined;\n }\n\n const cacheKey = `${locale}:${chunkName}`;\n const cached = this.chunkCache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const translations = this.getTranslations(locale);\n const chunkTranslations: RawTranslations = {};\n\n for (const key of keys) {\n if (translations[key]) {\n chunkTranslations[key] = translations[key];\n }\n }\n\n const js = `/* @astroscope/i18n chunk: ${chunkName} */\n(function() {\n var i = window.__i18n__;\n if (i) Object.assign(i.translations, ${JSON.stringify(chunkTranslations)});\n})();\n`;\n\n const body = new TextEncoder().encode(js);\n\n this.chunkCache.set(cacheKey, body);\n\n return body;\n }\n\n /**\n * Get the inline script for I18nScript component.\n * Cached per locale, invalidated when translations change.\n */\n getClientScript(locale: string): string {\n const cached = this.scriptCache.get(locale);\n\n if (cached) {\n return cached;\n }\n\n const script = this.createClientScript(locale);\n\n this.scriptCache.set(locale, script);\n\n return script;\n }\n\n private createClientScript(locale: string): string {\n const { chunks, imports } = this.getManifest();\n const hasChunks = Object.keys(chunks).length > 0;\n\n if (!hasChunks) {\n const raw = this.getTranslations(locale);\n\n return `window.__i18n__=${JSON.stringify({ locale, hashes: {}, imports: {}, translations: raw })};`;\n }\n\n const hashes = this.getHashes(locale);\n\n // collect all unique chunk names used in hashes and imports\n const allChunks = new Set<string>(Object.keys(hashes));\n\n for (const deps of Object.values(imports)) {\n deps.forEach((d) => allChunks.add(d));\n }\n\n // generate short variable names: a, b, c, ..., z, aa, ab, ...\n const chunkToVar = new Map<string, string>();\n let varIndex = 0;\n\n for (const chunk of allChunks) {\n chunkToVar.set(chunk, generateBB26(varIndex++));\n }\n\n // build IIFE with aliases for compact output\n const varDecls = [...chunkToVar.entries()].map(([chunk, v]) => `${v}=${JSON.stringify(chunk)}`).join(',');\n\n const hashesObj = Object.entries(hashes)\n .map(([chunk, hash]) => `[${chunkToVar.get(chunk)}]:${JSON.stringify(hash)}`)\n .join(',');\n\n // only include imports that are in our chunk set (have translations for this locale)\n const importsObj = Object.entries(imports)\n .filter(([chunk]) => chunkToVar.has(chunk))\n .map(([chunk, deps]) => {\n const validDeps = deps.filter((d) => chunkToVar.has(d));\n\n return validDeps.length > 0\n ? `[${chunkToVar.get(chunk)}]:[${validDeps.map((d) => chunkToVar.get(d)).join(',')}]`\n : null;\n })\n .filter(Boolean)\n .join(',');\n\n return (\n `(()=>{var ${varDecls};` +\n `window.__i18n__={locale:${JSON.stringify(locale)},hashes:{${hashesObj}},imports:{${importsObj}},translations:{}};` +\n `})();`\n );\n }\n\n /**\n * Clear translations for specified locale(s).\n * @param locales - Locale(s) to clear, or all if not specified\n */\n clear(locales?: string | string[] | undefined): void {\n const targets = this.resolveLocales(locales);\n\n for (const locale of targets) {\n this.rawCache.delete(locale);\n this.mergedCache.delete(locale);\n this.compiledCache.delete(locale);\n this.hashCache.delete(locale);\n this.scriptCache.delete(locale);\n\n // clear chunk cache entries for this locale\n for (const key of this.chunkCache.keys()) {\n if (key.startsWith(`${locale}:`)) {\n this.chunkCache.delete(key);\n }\n }\n }\n }\n\n /**\n * invalidate derived caches when the extraction manifest has changed (dev mode HMR)\n */\n private invalidateIfManifestChanged(): void {\n const { version } = getGlobalState();\n\n if (version === this.manifestVersion) {\n return;\n }\n\n this.manifestVersion = version;\n this.mergedCache.clear();\n this.compiledCache.clear();\n this.scriptCache.clear();\n this.chunkCache.clear();\n }\n\n private resolveLocales(locales?: string | string[] | undefined): string[] {\n if (!locales) {\n return this.config?.locales ?? [];\n }\n\n if (typeof locales === 'string') {\n return [locales];\n }\n\n return locales;\n }\n}\n\nexport const i18n = new I18nSingleton();\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { I18nContext } from './types.js';\n\nconst als = new AsyncLocalStorage<I18nContext>();\n\n/**\n * Run a function with i18n context (called by middleware)\n */\nexport function runWithContext<T>(context: I18nContext, fn: () => T): T {\n return als.run(context, fn);\n}\n\n/**\n * Get the current i18n context\n */\nexport function getContext(): I18nContext | null {\n return als.getStore() ?? null;\n}\n"],"mappings":"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"}
|
|
@@ -1,25 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { AstroIntegrationLogger } from
|
|
4
|
-
import { Plugin } from
|
|
5
|
-
import { C as ConsistencyCheckLevel } from '../types-B_8vAmvv.js';
|
|
6
|
-
import '../types-DNRFj1XA.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Manifest state management for i18n extraction
|
|
10
|
-
*
|
|
11
|
-
* Separated from vite-plugin to avoid bundling Babel at runtime
|
|
12
|
-
*/
|
|
1
|
+
import { n as ExtractedKey, r as ExtractionManifest, t as ChunkManifest } from "../types-BfyoJsoB.js";
|
|
2
|
+
import { t as ConsistencyCheckLevel } from "../types-Bo4jMK8g.js";
|
|
3
|
+
import { AstroIntegrationLogger } from "astro";
|
|
4
|
+
import { Plugin } from "vite";
|
|
13
5
|
|
|
6
|
+
//#region src/extraction/manifest.d.ts
|
|
14
7
|
/**
|
|
15
8
|
* Get the current extraction manifest (for dev mode live access).
|
|
16
9
|
* This bypasses Vite's module caching to return fresh data.
|
|
17
10
|
*/
|
|
18
11
|
declare function getManifest(): ExtractionManifest;
|
|
19
|
-
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/extraction/vite-plugin.d.ts
|
|
20
14
|
type I18nVitePluginOptions = {
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
logger: AstroIntegrationLogger;
|
|
16
|
+
consistency: ConsistencyCheckLevel;
|
|
23
17
|
};
|
|
24
18
|
/**
|
|
25
19
|
* Vite plugin for i18n extraction, chunk mapping, and loader injection
|
|
@@ -32,5 +26,6 @@ type I18nVitePluginOptions = {
|
|
|
32
26
|
* 5. Injects translation loader into chunks that use t()
|
|
33
27
|
*/
|
|
34
28
|
declare function i18nVitePlugin(options: I18nVitePluginOptions): Plugin;
|
|
35
|
-
|
|
36
|
-
export { ExtractionManifest, type I18nVitePluginOptions, getManifest, i18nVitePlugin };
|
|
29
|
+
//#endregion
|
|
30
|
+
export { type ChunkManifest, type ExtractedKey, type ExtractionManifest, type I18nVitePluginOptions, getManifest, i18nVitePlugin };
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/extraction/manifest.ts","../../src/extraction/vite-plugin.ts"],"mappings":";;;;;;ACsBA;;;;AAAA,iBD0BgB,WAAA,IAAe,kBAAkB;;;KC1BrC,qBAAA;EACV,MAAA,EAAQ,sBAAA;EACR,WAAA,EAAa,qBAAqB;AAAA;;;;ADwBa;;;;AC1BjD;;;iBAegB,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,MAAM"}
|
package/dist/extraction/index.js
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
getManifest
|
|
6
|
-
} from "../chunk-FYKC3DAZ.js";
|
|
7
|
-
import "../chunk-4A5BKHFN.js";
|
|
8
|
-
export {
|
|
9
|
-
getManifest,
|
|
10
|
-
i18nVitePlugin
|
|
11
|
-
};
|
|
1
|
+
import { t as i18nVitePlugin } from "../vite-plugin-DVUlcvEH.js";
|
|
2
|
+
import { n as getManifest } from "../manifest-D883_BIS.js";
|
|
3
|
+
export { getManifest, i18nVitePlugin };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { a as TranslationMeta, r as RawTranslations, t as CompiledTranslations } from "./types-C98CcjtN.js";
|
|
2
|
+
import { r as ExtractionManifest } from "./types-BfyoJsoB.js";
|
|
3
|
+
|
|
4
|
+
//#region src/server/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Fallback behavior when translation is missing
|
|
7
|
+
*/
|
|
8
|
+
type FallbackBehavior = 'key' | 'fallback' | 'throw' | ((key: string, meta?: TranslationMeta | undefined) => string);
|
|
9
|
+
/**
|
|
10
|
+
* Request-scoped i18n context
|
|
11
|
+
*/
|
|
12
|
+
type I18nContext = {
|
|
13
|
+
locale: string;
|
|
14
|
+
translations: CompiledTranslations;
|
|
15
|
+
rawTranslations: RawTranslations;
|
|
16
|
+
fallback: FallbackBehavior;
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/server/i18n.d.ts
|
|
20
|
+
type I18nConfig = {
|
|
21
|
+
locales: string[];
|
|
22
|
+
defaultLocale?: string | undefined;
|
|
23
|
+
fallback?: FallbackBehavior | undefined;
|
|
24
|
+
};
|
|
25
|
+
type NormalizedConfig = {
|
|
26
|
+
locales: string[];
|
|
27
|
+
defaultLocale: string;
|
|
28
|
+
fallback: FallbackBehavior;
|
|
29
|
+
};
|
|
30
|
+
declare class I18nSingleton {
|
|
31
|
+
private config;
|
|
32
|
+
private rawCache;
|
|
33
|
+
private mergedCache;
|
|
34
|
+
private compiledCache;
|
|
35
|
+
private hashCache;
|
|
36
|
+
private scriptCache;
|
|
37
|
+
private chunkCache;
|
|
38
|
+
private manifestGetter;
|
|
39
|
+
private manifestVersion;
|
|
40
|
+
configure(config: I18nConfig): Promise<void>;
|
|
41
|
+
isConfigured(): boolean;
|
|
42
|
+
getConfig(): NormalizedConfig;
|
|
43
|
+
setTranslations(locale: string, raw: RawTranslations): void;
|
|
44
|
+
getTranslations(locale: string): RawTranslations;
|
|
45
|
+
getCompiledTranslations(locale: string): CompiledTranslations;
|
|
46
|
+
/**
|
|
47
|
+
* Get the extraction manifest (keys + chunks).
|
|
48
|
+
* - keys: translation keys extracted from codebase with metadata
|
|
49
|
+
* - chunks: mapping of chunk names to their translation keys
|
|
50
|
+
*/
|
|
51
|
+
getManifest(): ExtractionManifest;
|
|
52
|
+
getHashes(locale: string): Record<string, string>;
|
|
53
|
+
/**
|
|
54
|
+
* Get the encoded chunk response body for a locale/chunk combination.
|
|
55
|
+
* Cached to avoid repeated JSON.stringify and encoding on each request.
|
|
56
|
+
* Returns undefined if chunk not found in manifest.
|
|
57
|
+
*/
|
|
58
|
+
getChunkBody(locale: string, chunkName: string): Uint8Array | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Get the inline script for I18nScript component.
|
|
61
|
+
* Cached per locale, invalidated when translations change.
|
|
62
|
+
*/
|
|
63
|
+
getClientScript(locale: string): string;
|
|
64
|
+
private createClientScript;
|
|
65
|
+
/**
|
|
66
|
+
* Clear translations for specified locale(s).
|
|
67
|
+
* @param locales - Locale(s) to clear, or all if not specified
|
|
68
|
+
*/
|
|
69
|
+
clear(locales?: string | string[] | undefined): void;
|
|
70
|
+
/**
|
|
71
|
+
* invalidate derived caches when the extraction manifest has changed (dev mode HMR)
|
|
72
|
+
*/
|
|
73
|
+
private invalidateIfManifestChanged;
|
|
74
|
+
private resolveLocales;
|
|
75
|
+
}
|
|
76
|
+
declare const i18n: I18nSingleton;
|
|
77
|
+
//#endregion
|
|
78
|
+
export { I18nContext as i, i18n as n, FallbackBehavior as r, I18nConfig as t };
|
|
79
|
+
//# sourceMappingURL=i18n-DzmH9JmE.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n-DzmH9JmE.d.ts","names":[],"sources":["../src/server/types.ts","../src/server/i18n.ts"],"mappings":";;;;;;;KAKY,gBAAA,oCACuB,GAAA,UAAa,IAAA,GAAO,eAAe;;;;KAK1D,WAAA;EACV,MAAA;EAEA,YAAA,EAAc,oBAAA;EAEd,eAAA,EAAiB,eAAA;EACjB,QAAA,EAAU,gBAAA;AAAA;;;KCTA,UAAA;EACV,OAAA;EACA,aAAA;EACA,QAAA,GAAW,gBAAgB;AAAA;AAAA,KAGxB,gBAAA;EACH,OAAA;EACA,aAAA;EACA,QAAA,EAAU,gBAAgB;AAAA;AAAA,cAGtB,aAAA;EAAA,QAEI,MAAA;EAAA,QAGA,QAAA;EAAA,QAGA,WAAA;EAAA,QAGA,aAAA;EAAA,QAGA,SAAA;EAAA,QAGA,WAAA;EAAA,QAGA,UAAA;EAAA,QAGA,cAAA;EAAA,QAGA,eAAA;EAEF,SAAA,CAAU,MAAA,EAAQ,UAAA,GAAa,OAAA;EA+CrC,YAAA;EAIA,SAAA,IAAa,gBAAA;EAQb,eAAA,CAAgB,MAAA,UAAgB,GAAA,EAAK,eAAA;EAsBrC,eAAA,CAAgB,MAAA,WAAiB,eAAA;EA8BjC,uBAAA,CAAwB,MAAA,WAAiB,oBAAA;ED9If;;;;ACT5B;EA0KE,WAAA,IAAe,kBAAA;EAQf,SAAA,CAAU,MAAA,WAAiB,MAAA;EA/KA;;;;;EAwL3B,YAAA,CAAa,MAAA,UAAgB,SAAA,WAAoB,UAAA;EAxLtB;AAC3B;;;EAgOA,eAAA,CAAgB,MAAA;EAAA,QAcR,kBAAA;EA1OR;;;;EAoSA,KAAA,CAAM,OAAA;EAhSF;;;EAAA,QAsTI,2BAAA;EAAA,QAcA,cAAA;AAAA;AAAA,cAaG,IAAA,EAAI,aAAsB"}
|