@astroscope/i18n 0.6.0 → 0.6.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.
@@ -1,4 +1,4 @@
1
- import { r as formatMessageToParts, t as compileMessage } from "../compiler-DjYjJc0V.js";
1
+ import { r as formatMessageToParts, t as compileMessage } from "../compiler-CkOVGWU5.js";
2
2
  import { n as normalizeMeta, t as partsToNodes } from "../rich-CHisXE-A.js";
3
3
  import { t as getI18nState } from "../state-DeudhdeT.js";
4
4
  //#region src/client/translate.ts
@@ -1,7 +1,10 @@
1
1
  import { MessageFormat } from "messageformat";
2
2
  import { DraftFunctions } from "messageformat/functions";
3
3
  //#region src/shared/compiler.ts
4
- const mfOptions = { functions: DraftFunctions };
4
+ const mfOptions = {
5
+ functions: DraftFunctions,
6
+ bidiIsolation: "none"
7
+ };
5
8
  const cache = /* @__PURE__ */ new Map();
6
9
  function getOrCreateMessageFormat(locale, message) {
7
10
  const cacheKey = `${locale}:${message}`;
@@ -42,4 +45,4 @@ function compileTranslations(locale, raw) {
42
45
  //#endregion
43
46
  export { compileTranslations as n, formatMessageToParts as r, compileMessage as t };
44
47
 
45
- //# sourceMappingURL=compiler-DjYjJc0V.js.map
48
+ //# sourceMappingURL=compiler-CkOVGWU5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler-CkOVGWU5.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\n// bidi isolation is disabled: the default wraps string placeholders in invisible\n// U+2068/U+2069 characters that end up in the DOM and copy-paste\nconst mfOptions = { functions: DraftFunctions, bidiIsolation: 'none' as const };\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":";;;AAQA,MAAM,YAAY;CAAE,WAAW;CAAgB,eAAe;AAAgB;AAE9E,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"}
@@ -1,5 +1,5 @@
1
1
  import { t as getGlobalState } from "./manifest-D883_BIS.js";
2
- import { n as compileTranslations } from "./compiler-DjYjJc0V.js";
2
+ import { n as compileTranslations } from "./compiler-CkOVGWU5.js";
3
3
  import { createHash } from "node:crypto";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
5
5
  //#region src/extraction/hash.ts
@@ -244,4 +244,4 @@ function getContext() {
244
244
  //#endregion
245
245
  export { runWithContext as n, i18n as r, getContext as t };
246
246
 
247
- //# sourceMappingURL=context-CNd4O0Pf.js.map
247
+ //# sourceMappingURL=context-DT91WgEO.js.map
@@ -1 +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
+ {"version":3,"file":"context-DT91WgEO.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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"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as i18nVitePlugin } from "./vite-plugin-DVUlcvEH.js";
2
- import { n as runWithContext, r as i18n } from "./context-CNd4O0Pf.js";
2
+ import { n as runWithContext, r as i18n } from "./context-DT91WgEO.js";
3
3
  import { RECOMMENDED_EXCLUDES, shouldExclude } from "@astroscope/excludes";
4
4
  //#region src/integration/integration.ts
5
5
  const CLIENT_DIRECTIVES = [
package/dist/internal.js CHANGED
@@ -1,2 +1,2 @@
1
- import { r as i18n, t as getContext } from "./context-CNd4O0Pf.js";
1
+ import { r as i18n, t as getContext } from "./context-DT91WgEO.js";
2
2
  export { getContext, i18n };
@@ -1,5 +1,5 @@
1
- import { r as i18n, t as getContext } from "../context-CNd4O0Pf.js";
2
- import { r as formatMessageToParts, t as compileMessage } from "../compiler-DjYjJc0V.js";
1
+ import { r as i18n, t as getContext } from "../context-DT91WgEO.js";
2
+ import { r as formatMessageToParts, t as compileMessage } from "../compiler-CkOVGWU5.js";
3
3
  import { n as normalizeMeta, t as partsToNodes } from "../rich-CHisXE-A.js";
4
4
  //#region src/server/translate.ts
5
5
  const fallbackCache = /* @__PURE__ */ new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astroscope/i18n",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "i18n for Astro + React islands — MessageFormat 2, automatic tree-shaking, parallel loading, any translation source",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1 +0,0 @@
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"}