@universal-i18n/core 1.0.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/index.d.mts +347 -0
- package/dist/index.d.ts +347 -0
- package/dist/index.js +804 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +752 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/cache/memory-cache.ts","../src/cache/fs-cache.ts","../src/cache/redis-cache.ts","../src/cache/index.ts","../src/utils.ts","../src/engine.ts"],"sourcesContent":["// @universal-i18n/core — Public API\r\n\r\nexport {\r\n // Error classes\r\n TranslationError,\r\n CacheError,\r\n ConfigError,\r\n RateLimitError,\r\n // Types\r\n type EngineConfig,\r\n type TranslateOpts,\r\n type BatchItem,\r\n type BatchResult,\r\n type CacheConfig,\r\n type CacheStats,\r\n type CacheEntry,\r\n type ICache,\r\n type UniversalI18nConfig,\r\n type GlossaryEntry,\r\n type BucketConfig,\r\n type LogLevel,\r\n type Logger,\r\n type ProviderType,\r\n type I18nMessages,\r\n type I18nContextValue,\r\n} from './types.js';\r\n\r\nexport { UniversalI18nEngine } from './engine.js';\r\n\r\nexport { createCache, MemoryCache, FileSystemCache, RedisCache } from './cache/index.js';\r\n\r\nexport {\r\n generateCacheKey,\r\n sha256,\r\n createLogger,\r\n flattenObject,\r\n unflattenObject,\r\n} from './utils.js';\r\n\r\n// ─── Factory & Config Helpers ────────────────────────────────────────────────\r\n\r\nimport type { EngineConfig, UniversalI18nConfig } from './types.js';\r\nimport { UniversalI18nEngine } from './engine.js';\r\n\r\n/**\r\n * Create a new translation engine instance.\r\n *\r\n * @example\r\n * ```ts\r\n * import { createEngine } from '@universal-i18n/core'\r\n *\r\n * const engine = createEngine({\r\n * apiKey: process.env.LINGODOTDEV_API_KEY,\r\n * sourceLocale: 'en',\r\n * targetLocales: ['es', 'fr'],\r\n * cache: { backend: 'fs' }\r\n * })\r\n *\r\n * const translated = await engine.translate('Hello', {\r\n * sourceLocale: 'en',\r\n * targetLocale: 'es'\r\n * })\r\n * ```\r\n */\r\nexport function createEngine(config: EngineConfig): UniversalI18nEngine {\r\n return new UniversalI18nEngine(config);\r\n}\r\n\r\n/**\r\n * Define a universal-i18n configuration object with type safety.\r\n *\r\n * @example\r\n * ```ts\r\n * // i18n.config.ts\r\n * import { defineConfig } from '@universal-i18n/core'\r\n *\r\n * export default defineConfig({\r\n * sourceLocale: 'en',\r\n * targetLocales: ['es', 'fr', 'de'],\r\n * cache: { backend: 'fs', ttl: 604800 }\r\n * })\r\n * ```\r\n */\r\nexport function defineConfig(config: UniversalI18nConfig): UniversalI18nConfig {\r\n return config;\r\n}\r\n","// ─── Error Classes ───────────────────────────────────────────────────────────\r\n\r\nexport class TranslationError extends Error {\r\n constructor(\r\n message: string,\r\n public readonly sourceText?: string,\r\n public readonly targetLocale?: string,\r\n public readonly cause?: Error,\r\n ) {\r\n super(message);\r\n this.name = 'TranslationError';\r\n }\r\n}\r\n\r\nexport class CacheError extends Error {\r\n constructor(\r\n message: string,\r\n public readonly backend?: string,\r\n public readonly cause?: Error,\r\n ) {\r\n super(message);\r\n this.name = 'CacheError';\r\n }\r\n}\r\n\r\nexport class ConfigError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = 'ConfigError';\r\n }\r\n}\r\n\r\nexport class RateLimitError extends Error {\r\n constructor(\r\n message: string,\r\n public readonly retryAfterMs?: number,\r\n ) {\r\n super(message);\r\n this.name = 'RateLimitError';\r\n }\r\n}\r\n\r\n// ─── Cache Interfaces ────────────────────────────────────────────────────────\r\n\r\nexport interface CacheEntry {\r\n value: string;\r\n translatedAt: string;\r\n ttl?: number;\r\n}\r\n\r\nexport interface CacheStats {\r\n totalEntries: number;\r\n hitRate: number;\r\n missRate: number;\r\n hits: number;\r\n misses: number;\r\n sizeBytes?: number;\r\n oldestEntry?: string;\r\n newestEntry?: string;\r\n}\r\n\r\nexport interface ICache {\r\n get(key: string): Promise<string | null>;\r\n set(key: string, value: string, ttl?: number): Promise<void>;\r\n delete(key: string): Promise<void>;\r\n clear(pattern?: string): Promise<void>;\r\n stats(): Promise<CacheStats>;\r\n}\r\n\r\nexport interface CacheConfig {\r\n /** Cache backend type. Default: 'fs' */\r\n backend: 'fs' | 'redis' | 'memory';\r\n /** TTL in seconds. Default: 604800 (7 days) */\r\n ttl?: number;\r\n /** Filesystem cache directory. Default: '.cache/universal-i18n' */\r\n fsDir?: string;\r\n /** Redis connection URL */\r\n redisUrl?: string;\r\n /** Max entries for in-memory LRU cache. Default: 1000 */\r\n memoryMaxEntries?: number;\r\n /** Compress cached values with gzip. Default: false */\r\n compression?: boolean;\r\n}\r\n\r\n// ─── Engine Interfaces ───────────────────────────────────────────────────────\r\n\r\nexport type ProviderType = 'lingo.dev' | 'openai' | 'anthropic' | 'groq';\r\n\r\nexport interface EngineConfig {\r\n /** API key. Falls back to LINGODOTDEV_API_KEY env var */\r\n apiKey?: string;\r\n /** Source locale. Default: 'en' */\r\n sourceLocale?: string;\r\n /** Target locales to support */\r\n targetLocales: string[];\r\n /** Cache config */\r\n cache?: CacheConfig;\r\n /** Batch delay in ms before sending batched requests. Default: 50 */\r\n batchDelayMs?: number;\r\n /** Max retries on failure. Default: 3 */\r\n maxRetries?: number;\r\n /** Return original text on translation failure. Default: true */\r\n fallbackOnError?: boolean;\r\n /** Translation provider. Default: 'lingo.dev' */\r\n provider?: ProviderType;\r\n /** API key for non-lingo.dev providers */\r\n providerApiKey?: string;\r\n /** Model identifier for the chosen provider */\r\n model?: string;\r\n}\r\n\r\nexport interface TranslateOpts {\r\n /** Source locale */\r\n sourceLocale: string;\r\n /** Target locale */\r\n targetLocale: string;\r\n /** Additional context hint for the LLM */\r\n context?: string;\r\n /** Namespace used as cache key prefix */\r\n namespace?: string;\r\n}\r\n\r\nexport interface BatchItem {\r\n text: string;\r\n id?: string;\r\n}\r\n\r\nexport interface BatchResult {\r\n id?: string;\r\n original: string;\r\n translated: string;\r\n cached: boolean;\r\n}\r\n\r\n// ─── Config Interfaces ───────────────────────────────────────────────────────\r\n\r\nexport interface GlossaryEntry {\r\n [locale: string]: string;\r\n}\r\n\r\nexport interface BucketConfig {\r\n include: string[];\r\n}\r\n\r\nexport interface UniversalI18nConfig {\r\n /** Source locale. Default: 'en' */\r\n sourceLocale: string;\r\n /** Target locales */\r\n targetLocales: string[];\r\n /** Directory for locale JSON files. Default: './locales' */\r\n localesDir?: string;\r\n /** Cache configuration */\r\n cache?: CacheConfig;\r\n /** Translation provider config */\r\n provider?: {\r\n id: ProviderType;\r\n apiKey?: string;\r\n model?: string;\r\n };\r\n /** Brand glossary: terms that should never or always be translated a specific way */\r\n glossary?: Record<string, GlossaryEntry>;\r\n /** Namespace buckets mapping to lingo.dev buckets config */\r\n buckets?: Record<string, BucketConfig>;\r\n /** Lifecycle hooks */\r\n hooks?: {\r\n beforeTranslate?: (text: string, locale: string) => Promise<string>;\r\n afterTranslate?: (text: string, locale: string) => Promise<string>;\r\n };\r\n}\r\n\r\n// ─── Logger ──────────────────────────────────────────────────────────────────\r\n\r\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\r\n\r\nexport interface Logger {\r\n debug(message: string, ...args: unknown[]): void;\r\n info(message: string, ...args: unknown[]): void;\r\n warn(message: string, ...args: unknown[]): void;\r\n error(message: string, ...args: unknown[]): void;\r\n}\r\n\r\n// ─── React Provider Types ────────────────────────────────────────────────────\r\n\r\nexport interface I18nMessages {\r\n [locale: string]: Record<string, string | Record<string, unknown>>;\r\n}\r\n\r\nexport interface I18nContextValue {\r\n locale: string;\r\n setLocale: (locale: string) => void;\r\n messages: I18nMessages;\r\n t: (key: string, vars?: Record<string, string>) => string;\r\n availableLocales: string[];\r\n isLoading: boolean;\r\n fallbackLocale?: string;\r\n onMissingKey?: (key: string, locale: string) => void;\r\n}\r\n","import type { ICache, CacheStats } from '../types.js';\r\n\r\ninterface LRUEntry {\r\n value: string;\r\n createdAt: number;\r\n ttl?: number;\r\n}\r\n\r\n/**\r\n * In-memory LRU cache with optional TTL support.\r\n * Ideal for edge/serverless environments where filesystem isn't persistent.\r\n */\r\nexport class MemoryCache implements ICache {\r\n private cache: Map<string, LRUEntry>;\r\n private maxEntries: number;\r\n private hits = 0;\r\n private misses = 0;\r\n\r\n constructor(maxEntries = 1000) {\r\n this.cache = new Map();\r\n this.maxEntries = maxEntries;\r\n }\r\n\r\n async get(key: string): Promise<string | null> {\r\n const entry = this.cache.get(key);\r\n\r\n if (!entry) {\r\n this.misses++;\r\n return null;\r\n }\r\n\r\n // Check TTL expiration\r\n if (entry.ttl !== undefined && Date.now() - entry.createdAt > entry.ttl * 1000) {\r\n this.cache.delete(key);\r\n this.misses++;\r\n return null;\r\n }\r\n\r\n // Move to end (most recently used) by re-inserting\r\n this.cache.delete(key);\r\n this.cache.set(key, entry);\r\n\r\n this.hits++;\r\n return entry.value;\r\n }\r\n\r\n async set(key: string, value: string, ttl?: number): Promise<void> {\r\n // If key exists, delete first (to move to end)\r\n if (this.cache.has(key)) {\r\n this.cache.delete(key);\r\n }\r\n\r\n // Evict least recently used entry if at capacity\r\n if (this.cache.size >= this.maxEntries) {\r\n const firstKey = this.cache.keys().next().value;\r\n if (firstKey !== undefined) {\r\n this.cache.delete(firstKey);\r\n }\r\n }\r\n\r\n this.cache.set(key, {\r\n value,\r\n createdAt: Date.now(),\r\n ttl,\r\n });\r\n }\r\n\r\n async delete(key: string): Promise<void> {\r\n this.cache.delete(key);\r\n }\r\n\r\n async clear(pattern?: string): Promise<void> {\r\n if (!pattern) {\r\n this.cache.clear();\r\n this.hits = 0;\r\n this.misses = 0;\r\n return;\r\n }\r\n\r\n // Simple glob pattern: support trailing * wildcard\r\n const prefix = pattern.replace(/\\*$/, '');\r\n for (const key of this.cache.keys()) {\r\n if (key.startsWith(prefix)) {\r\n this.cache.delete(key);\r\n }\r\n }\r\n }\r\n\r\n async stats(): Promise<CacheStats> {\r\n const total = this.hits + this.misses;\r\n const entries = Array.from(this.cache.values());\r\n\r\n let oldestEntry: string | undefined;\r\n let newestEntry: string | undefined;\r\n\r\n if (entries.length > 0) {\r\n const sorted = entries.sort((a, b) => a.createdAt - b.createdAt);\r\n oldestEntry = new Date(sorted[0].createdAt).toISOString();\r\n newestEntry = new Date(sorted[sorted.length - 1].createdAt).toISOString();\r\n }\r\n\r\n return {\r\n totalEntries: this.cache.size,\r\n hits: this.hits,\r\n misses: this.misses,\r\n hitRate: total > 0 ? this.hits / total : 0,\r\n missRate: total > 0 ? this.misses / total : 0,\r\n oldestEntry,\r\n newestEntry,\r\n };\r\n }\r\n}\r\n","import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, readdirSync, statSync, unlinkSync } from 'node:fs';\r\nimport { join, dirname } from 'node:path';\r\nimport type { ICache, CacheEntry, CacheStats } from '../types.js';\r\n\r\ninterface CacheFile {\r\n version: string;\r\n entries: Record<string, CacheEntry>;\r\n}\r\n\r\n/**\r\n * Filesystem-based cache that stores translations as JSON files.\r\n * Uses atomic writes (write to .tmp then rename) for safety.\r\n * Cache structure: {fsDir}/{namespace}.json\r\n */\r\nexport class FileSystemCache implements ICache {\r\n private dir: string;\r\n private hits = 0;\r\n private misses = 0;\r\n private defaultTtl: number;\r\n\r\n constructor(dir = '.cache/universal-i18n', defaultTtl = 604800) {\r\n this.dir = dir;\r\n this.defaultTtl = defaultTtl;\r\n this.ensureDir(this.dir);\r\n }\r\n\r\n private ensureDir(dirPath: string): void {\r\n if (!existsSync(dirPath)) {\r\n mkdirSync(dirPath, { recursive: true });\r\n }\r\n }\r\n\r\n private getFilePath(key: string): { filePath: string; entryKey: string } {\r\n // Key format: ui18n:{srcLocale}:{tgtLocale}:{namespace}:{hash}\r\n const parts = key.split(':');\r\n const namespace = parts[3] || 'default';\r\n const locale = parts[2] || 'unknown';\r\n const entryKey = parts[4] || key;\r\n\r\n const localeDir = join(this.dir, locale);\r\n this.ensureDir(localeDir);\r\n\r\n return {\r\n filePath: join(localeDir, `${namespace}.json`),\r\n entryKey,\r\n };\r\n }\r\n\r\n private readCacheFile(filePath: string): CacheFile {\r\n if (!existsSync(filePath)) {\r\n return { version: '1.0', entries: {} };\r\n }\r\n try {\r\n const raw = readFileSync(filePath, 'utf-8');\r\n return JSON.parse(raw) as CacheFile;\r\n } catch {\r\n return { version: '1.0', entries: {} };\r\n }\r\n }\r\n\r\n private writeCacheFile(filePath: string, data: CacheFile): void {\r\n const tmpPath = `${filePath}.tmp`;\r\n this.ensureDir(dirname(filePath));\r\n writeFileSync(tmpPath, JSON.stringify(data, null, 2), 'utf-8');\r\n renameSync(tmpPath, filePath);\r\n }\r\n\r\n async get(key: string): Promise<string | null> {\r\n const { filePath, entryKey } = this.getFilePath(key);\r\n const cacheFile = this.readCacheFile(filePath);\r\n const entry = cacheFile.entries[entryKey];\r\n\r\n if (!entry) {\r\n this.misses++;\r\n return null;\r\n }\r\n\r\n // Check TTL\r\n const ttl = entry.ttl ?? this.defaultTtl;\r\n const age = (Date.now() - new Date(entry.translatedAt).getTime()) / 1000;\r\n if (age > ttl) {\r\n delete cacheFile.entries[entryKey];\r\n this.writeCacheFile(filePath, cacheFile);\r\n this.misses++;\r\n return null;\r\n }\r\n\r\n this.hits++;\r\n return entry.value;\r\n }\r\n\r\n async set(key: string, value: string, ttl?: number): Promise<void> {\r\n const { filePath, entryKey } = this.getFilePath(key);\r\n const cacheFile = this.readCacheFile(filePath);\r\n\r\n cacheFile.entries[entryKey] = {\r\n value,\r\n translatedAt: new Date().toISOString(),\r\n ttl: ttl ?? this.defaultTtl,\r\n };\r\n\r\n this.writeCacheFile(filePath, cacheFile);\r\n }\r\n\r\n async delete(key: string): Promise<void> {\r\n const { filePath, entryKey } = this.getFilePath(key);\r\n const cacheFile = this.readCacheFile(filePath);\r\n\r\n if (entryKey in cacheFile.entries) {\r\n delete cacheFile.entries[entryKey];\r\n this.writeCacheFile(filePath, cacheFile);\r\n }\r\n }\r\n\r\n async clear(pattern?: string): Promise<void> {\r\n if (!pattern) {\r\n // Clear entire cache directory\r\n this.clearDirectory(this.dir);\r\n this.hits = 0;\r\n this.misses = 0;\r\n return;\r\n }\r\n\r\n // Pattern-based clear: e.g. 'ui18n:en:es:*' clears all es locale entries\r\n const parts = pattern.split(':');\r\n const locale = parts[2];\r\n\r\n if (locale) {\r\n const localeDir = join(this.dir, locale);\r\n if (existsSync(localeDir)) {\r\n this.clearDirectory(localeDir);\r\n }\r\n }\r\n }\r\n\r\n private clearDirectory(dirPath: string): void {\r\n if (!existsSync(dirPath)) return;\r\n\r\n const entries = readdirSync(dirPath, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const fullPath = join(dirPath, entry.name);\r\n if (entry.isDirectory()) {\r\n this.clearDirectory(fullPath);\r\n } else {\r\n unlinkSync(fullPath);\r\n }\r\n }\r\n }\r\n\r\n async stats(): Promise<CacheStats> {\r\n const total = this.hits + this.misses;\r\n let totalEntries = 0;\r\n let sizeBytes = 0;\r\n let oldest: Date | undefined;\r\n let newest: Date | undefined;\r\n\r\n this.scanDirectory(this.dir, (filePath) => {\r\n try {\r\n const stat = statSync(filePath);\r\n sizeBytes += stat.size;\r\n\r\n const cacheFile = this.readCacheFile(filePath);\r\n for (const entry of Object.values(cacheFile.entries)) {\r\n totalEntries++;\r\n const date = new Date(entry.translatedAt);\r\n if (!oldest || date < oldest) oldest = date;\r\n if (!newest || date > newest) newest = date;\r\n }\r\n } catch {\r\n // Skip files that can't be read\r\n }\r\n });\r\n\r\n return {\r\n totalEntries,\r\n hits: this.hits,\r\n misses: this.misses,\r\n hitRate: total > 0 ? this.hits / total : 0,\r\n missRate: total > 0 ? this.misses / total : 0,\r\n sizeBytes,\r\n oldestEntry: oldest?.toISOString(),\r\n newestEntry: newest?.toISOString(),\r\n };\r\n }\r\n\r\n private scanDirectory(dirPath: string, callback: (filePath: string) => void): void {\r\n if (!existsSync(dirPath)) return;\r\n const entries = readdirSync(dirPath, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = join(dirPath, entry.name);\r\n if (entry.isDirectory()) {\r\n this.scanDirectory(fullPath, callback);\r\n } else if (entry.name.endsWith('.json')) {\r\n callback(fullPath);\r\n }\r\n }\r\n }\r\n}\r\n","import type { ICache, CacheStats } from '../types.js';\r\n\r\n/**\r\n * Redis-based cache using ioredis.\r\n * ioredis is a peer dependency — this module gracefully handles missing imports.\r\n *\r\n * Uses HSET/HGET with key pattern: ui18n:{srcLocale}:{tgtLocale}:{namespace}:{hash}\r\n * TTL via EXPIRE on individual keys.\r\n */\r\nexport class RedisCache implements ICache {\r\n private client: any;\r\n private defaultTtl: number;\r\n private hits = 0;\r\n private misses = 0;\r\n private connected = false;\r\n\r\n constructor(redisUrl?: string, defaultTtl = 604800) {\r\n this.defaultTtl = defaultTtl;\r\n this.initClient(redisUrl);\r\n }\r\n\r\n private async initClient(redisUrl?: string): Promise<void> {\r\n try {\r\n const Redis = await this.loadRedis();\r\n const url = redisUrl || process.env.REDIS_URL || 'redis://localhost:6379';\r\n this.client = new Redis(url, {\r\n maxRetriesPerRequest: 3,\r\n retryStrategy(times: number) {\r\n if (times > 3) return null;\r\n return Math.min(times * 200, 2000);\r\n },\r\n lazyConnect: true,\r\n });\r\n await this.client.connect();\r\n this.connected = true;\r\n } catch (error) {\r\n console.warn('[ui18n:warn] Redis connection failed. Cache operations will be no-ops.', error);\r\n this.connected = false;\r\n }\r\n }\r\n\r\n private async loadRedis(): Promise<any> {\r\n try {\r\n const mod = await import('ioredis');\r\n return mod.default || mod;\r\n } catch {\r\n throw new Error(\r\n 'ioredis is required for Redis cache backend. Install it with: npm install ioredis',\r\n );\r\n }\r\n }\r\n\r\n async get(key: string): Promise<string | null> {\r\n if (!this.connected || !this.client) {\r\n this.misses++;\r\n return null;\r\n }\r\n\r\n try {\r\n const value = await this.client.get(key);\r\n if (value === null) {\r\n this.misses++;\r\n return null;\r\n }\r\n this.hits++;\r\n return value;\r\n } catch {\r\n this.misses++;\r\n return null;\r\n }\r\n }\r\n\r\n async set(key: string, value: string, ttl?: number): Promise<void> {\r\n if (!this.connected || !this.client) return;\r\n\r\n try {\r\n const effectiveTtl = ttl ?? this.defaultTtl;\r\n await this.client.set(key, value, 'EX', effectiveTtl);\r\n } catch {\r\n // Degrade gracefully — log was already handled by initClient\r\n }\r\n }\r\n\r\n async delete(key: string): Promise<void> {\r\n if (!this.connected || !this.client) return;\r\n\r\n try {\r\n await this.client.del(key);\r\n } catch {\r\n // Degrade gracefully\r\n }\r\n }\r\n\r\n async clear(pattern?: string): Promise<void> {\r\n if (!this.connected || !this.client) return;\r\n\r\n try {\r\n if (!pattern) {\r\n const keys = await this.client.keys('ui18n:*');\r\n if (keys.length > 0) {\r\n await this.client.del(...keys);\r\n }\r\n } else {\r\n const keys = await this.client.keys(pattern);\r\n if (keys.length > 0) {\r\n await this.client.del(...keys);\r\n }\r\n }\r\n this.hits = 0;\r\n this.misses = 0;\r\n } catch {\r\n // Degrade gracefully\r\n }\r\n }\r\n\r\n async stats(): Promise<CacheStats> {\r\n const total = this.hits + this.misses;\r\n let totalEntries = 0;\r\n\r\n if (this.connected && this.client) {\r\n try {\r\n const keys = await this.client.keys('ui18n:*');\r\n totalEntries = keys.length;\r\n } catch {\r\n // Ignore\r\n }\r\n }\r\n\r\n return {\r\n totalEntries,\r\n hits: this.hits,\r\n misses: this.misses,\r\n hitRate: total > 0 ? this.hits / total : 0,\r\n missRate: total > 0 ? this.misses / total : 0,\r\n };\r\n }\r\n\r\n async disconnect(): Promise<void> {\r\n if (this.connected && this.client) {\r\n await this.client.quit();\r\n this.connected = false;\r\n }\r\n }\r\n}\r\n","import type { CacheConfig, ICache } from '../types.js';\r\nimport { MemoryCache } from './memory-cache.js';\r\nimport { FileSystemCache } from './fs-cache.js';\r\nimport { RedisCache } from './redis-cache.js';\r\n\r\nexport { MemoryCache } from './memory-cache.js';\r\nexport { FileSystemCache } from './fs-cache.js';\r\nexport { RedisCache } from './redis-cache.js';\r\n\r\n/**\r\n * Factory function to create a cache instance based on configuration.\r\n */\r\nexport function createCache(config?: CacheConfig): ICache {\r\n const backend = config?.backend ?? 'fs';\r\n const ttl = config?.ttl ?? 604800; // 7 days default\r\n\r\n switch (backend) {\r\n case 'memory':\r\n return new MemoryCache(config?.memoryMaxEntries ?? 1000);\r\n\r\n case 'redis':\r\n return new RedisCache(config?.redisUrl, ttl);\r\n\r\n case 'fs':\r\n default: {\r\n const dir = config?.fsDir ?? process.env.UI18N_CACHE_DIR ?? '.cache/universal-i18n';\r\n return new FileSystemCache(dir, ttl);\r\n }\r\n }\r\n}\r\n","import { createHash } from 'node:crypto';\r\nimport type { Logger, LogLevel } from './types.js';\r\n\r\n/**\r\n * Generate a cache key from translation parameters.\r\n * Format: ui18n:{sourceLocale}:{targetLocale}:{namespace}:{sha256(sourceText)}\r\n */\r\nexport function generateCacheKey(\r\n sourceLocale: string,\r\n targetLocale: string,\r\n namespace: string,\r\n sourceText: string,\r\n): string {\r\n const hash = createHash('sha256').update(sourceText).digest('hex').slice(0, 16);\r\n return `ui18n:${sourceLocale}:${targetLocale}:${namespace}:${hash}`;\r\n}\r\n\r\n/**\r\n * Compute a SHA-256 hash of a string, returning a hex digest.\r\n */\r\nexport function sha256(text: string): string {\r\n return createHash('sha256').update(text).digest('hex');\r\n}\r\n\r\n/**\r\n * Sleep for a specified number of milliseconds.\r\n */\r\nexport function sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/**\r\n * Compute exponential backoff delay with jitter.\r\n * Base delay: 1000ms, multiplied by 2^attempt with ±25% jitter.\r\n */\r\nexport function exponentialBackoff(attempt: number): number {\r\n const baseDelay = 1000;\r\n const delay = baseDelay * Math.pow(2, attempt);\r\n const jitter = delay * 0.25 * (Math.random() * 2 - 1);\r\n return Math.round(delay + jitter);\r\n}\r\n\r\nconst LOG_LEVELS: Record<LogLevel, number> = {\r\n debug: 0,\r\n info: 1,\r\n warn: 2,\r\n error: 3,\r\n};\r\n\r\n/**\r\n * Create a pluggable logger respecting UI18N_LOG_LEVEL env var.\r\n */\r\nexport function createLogger(level?: LogLevel): Logger {\r\n const envLevel = (typeof process !== 'undefined' && process.env?.UI18N_LOG_LEVEL) as LogLevel | undefined;\r\n const currentLevel = LOG_LEVELS[level ?? envLevel ?? 'info'];\r\n\r\n const shouldLog = (msgLevel: LogLevel): boolean => LOG_LEVELS[msgLevel] >= currentLevel;\r\n\r\n return {\r\n debug(message: string, ...args: unknown[]) {\r\n if (shouldLog('debug')) console.debug(`[ui18n:debug] ${message}`, ...args);\r\n },\r\n info(message: string, ...args: unknown[]) {\r\n if (shouldLog('info')) console.info(`[ui18n:info] ${message}`, ...args);\r\n },\r\n warn(message: string, ...args: unknown[]) {\r\n if (shouldLog('warn')) console.warn(`[ui18n:warn] ${message}`, ...args);\r\n },\r\n error(message: string, ...args: unknown[]) {\r\n if (shouldLog('error')) console.error(`[ui18n:error] ${message}`, ...args);\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Deep-clone an object (JSON serializable).\r\n */\r\nexport function deepClone<T>(obj: T): T {\r\n return JSON.parse(JSON.stringify(obj));\r\n}\r\n\r\n/**\r\n * Flatten a nested object into dot-notation keys.\r\n * { a: { b: 'c' } } => { 'a.b': 'c' }\r\n */\r\nexport function flattenObject(\r\n obj: Record<string, unknown>,\r\n prefix = '',\r\n): Record<string, string> {\r\n const result: Record<string, string> = {};\r\n\r\n for (const [key, value] of Object.entries(obj)) {\r\n const fullKey = prefix ? `${prefix}.${key}` : key;\r\n\r\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\r\n Object.assign(result, flattenObject(value as Record<string, unknown>, fullKey));\r\n } else {\r\n result[fullKey] = String(value);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Unflatten dot-notation keys back into a nested object.\r\n * { 'a.b': 'c' } => { a: { b: 'c' } }\r\n */\r\nexport function unflattenObject(obj: Record<string, string>): Record<string, unknown> {\r\n const result: Record<string, unknown> = {};\r\n\r\n for (const [key, value] of Object.entries(obj)) {\r\n const parts = key.split('.');\r\n let current: Record<string, unknown> = result;\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (!(part in current) || typeof current[part] !== 'object') {\r\n current[part] = {};\r\n }\r\n current = current[part] as Record<string, unknown>;\r\n }\r\n\r\n current[parts[parts.length - 1]] = value;\r\n }\r\n\r\n return result;\r\n}\r\n","import type {\r\n EngineConfig,\r\n TranslateOpts,\r\n BatchItem,\r\n BatchResult,\r\n CacheStats,\r\n ICache,\r\n Logger,\r\n} from './types.js';\r\nimport { TranslationError, CacheError, RateLimitError } from './types.js';\r\nimport { createCache } from './cache/index.js';\r\nimport { generateCacheKey, createLogger, sleep, exponentialBackoff, flattenObject, unflattenObject } from './utils.js';\r\n\r\ninterface PendingTranslation {\r\n text: string;\r\n opts: TranslateOpts;\r\n resolve: (value: string) => void;\r\n reject: (error: Error) => void;\r\n}\r\n\r\n/**\r\n * Core translation engine wrapping LingoDotDevEngine with:\r\n * - Cache-first strategy\r\n * - Request batching (configurable delay)\r\n * - Automatic retry with exponential backoff\r\n * - Graceful fallback on errors\r\n */\r\nexport class UniversalI18nEngine {\r\n private cache: ICache;\r\n private logger: Logger;\r\n private apiKey: string;\r\n private sourceLocale: string;\r\n private targetLocales: string[];\r\n private batchDelayMs: number;\r\n private maxRetries: number;\r\n private fallbackOnError: boolean;\r\n private provider: string;\r\n private providerApiKey?: string;\r\n private model?: string;\r\n\r\n // Batching state\r\n private pendingBatch: PendingTranslation[] = [];\r\n private batchTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n // SDK engine instance (lazy-loaded)\r\n private sdkEngine: any = null;\r\n\r\n constructor(config: EngineConfig) {\r\n this.apiKey = config.apiKey || process.env.LINGODOTDEV_API_KEY || '';\r\n this.sourceLocale = config.sourceLocale ?? 'en';\r\n this.targetLocales = config.targetLocales;\r\n this.batchDelayMs = config.batchDelayMs ?? 50;\r\n this.maxRetries = config.maxRetries ?? 3;\r\n this.fallbackOnError = config.fallbackOnError ?? true;\r\n this.provider = config.provider ?? 'lingo.dev';\r\n this.providerApiKey = config.providerApiKey;\r\n this.model = config.model;\r\n\r\n this.cache = createCache(config.cache);\r\n this.logger = createLogger();\r\n }\r\n\r\n /**\r\n * Initialize the lingo.dev SDK engine lazily.\r\n */\r\n private async getSDKEngine(): Promise<any> {\r\n if (this.sdkEngine) return this.sdkEngine;\r\n\r\n try {\r\n const sdk = await import('lingo.dev/sdk');\r\n const LingoDotDevEngine = (sdk as any).LingoDotDevEngine || (sdk as any).default?.LingoDotDevEngine;\r\n\r\n if (!LingoDotDevEngine) {\r\n throw new Error('LingoDotDevEngine not found in lingo.dev package');\r\n }\r\n\r\n this.sdkEngine = new LingoDotDevEngine({\r\n apiKey: this.apiKey,\r\n });\r\n\r\n return this.sdkEngine;\r\n } catch (error) {\r\n this.logger.error('Failed to initialize lingo.dev SDK', error);\r\n throw new TranslationError(\r\n 'Failed to initialize translation engine. Ensure lingo.dev is installed.',\r\n undefined,\r\n undefined,\r\n error instanceof Error ? error : undefined,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Translate a single string with cache-first strategy.\r\n */\r\n async translate(text: string, opts: TranslateOpts): Promise<string> {\r\n const namespace = opts.namespace ?? 'default';\r\n const cacheKey = generateCacheKey(opts.sourceLocale, opts.targetLocale, namespace, text);\r\n\r\n // 1. Check cache\r\n try {\r\n const cached = await this.cache.get(cacheKey);\r\n if (cached !== null) {\r\n this.logger.debug(`Cache hit for key: ${cacheKey}`);\r\n return cached;\r\n }\r\n } catch (error) {\r\n this.logger.warn('Cache read failed, proceeding with API call', error);\r\n }\r\n\r\n // 2. Translate via SDK with retry\r\n const translated = await this.translateWithRetry(text, opts);\r\n\r\n // 3. Cache write-back\r\n try {\r\n await this.cache.set(cacheKey, translated);\r\n } catch (error) {\r\n this.logger.warn('Cache write failed', error);\r\n }\r\n\r\n return translated;\r\n }\r\n\r\n /**\r\n * Translate a nested object (all string values) with cache.\r\n */\r\n async translateObject<T extends Record<string, unknown>>(obj: T, opts: TranslateOpts): Promise<T> {\r\n const flat = flattenObject(obj);\r\n const translatedFlat: Record<string, string> = {};\r\n\r\n // Translate each value\r\n const entries = Object.entries(flat);\r\n const translations = await Promise.all(\r\n entries.map(([, value]) => this.translate(value, opts)),\r\n );\r\n\r\n entries.forEach(([key], idx) => {\r\n translatedFlat[key] = translations[idx];\r\n });\r\n\r\n return unflattenObject(translatedFlat) as T;\r\n }\r\n\r\n /**\r\n * Translate an HTML string.\r\n */\r\n async translateHTML(html: string, opts: TranslateOpts): Promise<string> {\r\n const namespace = opts.namespace ?? 'html';\r\n const cacheKey = generateCacheKey(opts.sourceLocale, opts.targetLocale, namespace, html);\r\n\r\n // Check cache\r\n try {\r\n const cached = await this.cache.get(cacheKey);\r\n if (cached !== null) return cached;\r\n } catch {\r\n // Proceed to API\r\n }\r\n\r\n // Translate\r\n try {\r\n const engine = await this.getSDKEngine();\r\n const translated = await this.callWithRetry(async () => {\r\n return await engine.translateHtml(html, {\r\n sourceLocale: opts.sourceLocale,\r\n targetLocale: opts.targetLocale,\r\n });\r\n });\r\n\r\n await this.cache.set(cacheKey, translated).catch(() => { });\r\n return translated;\r\n } catch (error) {\r\n if (this.fallbackOnError) {\r\n this.logger.warn('HTML translation failed, returning original', error);\r\n return html;\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Translate multiple items, leveraging batching and caching.\r\n */\r\n async translateBatch(items: BatchItem[], opts: TranslateOpts): Promise<BatchResult[]> {\r\n const results: BatchResult[] = [];\r\n\r\n for (const item of items) {\r\n const namespace = opts.namespace ?? 'default';\r\n const cacheKey = generateCacheKey(opts.sourceLocale, opts.targetLocale, namespace, item.text);\r\n\r\n let cached = false;\r\n let translated = item.text;\r\n\r\n try {\r\n const cachedValue = await this.cache.get(cacheKey);\r\n if (cachedValue !== null) {\r\n translated = cachedValue;\r\n cached = true;\r\n }\r\n } catch {\r\n // Proceed without cache\r\n }\r\n\r\n if (!cached) {\r\n try {\r\n translated = await this.translateWithRetry(item.text, opts);\r\n await this.cache.set(cacheKey, translated).catch(() => { });\r\n } catch {\r\n if (!this.fallbackOnError) {\r\n throw new TranslationError(\r\n `Batch translation failed for item: ${item.id ?? item.text.slice(0, 50)}`,\r\n );\r\n }\r\n }\r\n }\r\n\r\n results.push({\r\n id: item.id,\r\n original: item.text,\r\n translated,\r\n cached,\r\n });\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * Get current cache statistics.\r\n */\r\n getCacheStats(): Promise<CacheStats> {\r\n return this.cache.stats();\r\n }\r\n\r\n /**\r\n * Clear the cache, optionally for a specific locale.\r\n */\r\n async clearCache(locale?: string): Promise<void> {\r\n if (locale) {\r\n await this.cache.clear(`ui18n:*:${locale}:*`);\r\n } else {\r\n await this.cache.clear();\r\n }\r\n this.logger.info(`Cache cleared${locale ? ` for locale: ${locale}` : ''}`);\r\n }\r\n\r\n /**\r\n * Pre-populate cache by translating all provided keys for specified locales.\r\n */\r\n async warmCache(keys: string[], locales: string[]): Promise<void> {\r\n this.logger.info(`Warming cache: ${keys.length} keys × ${locales.length} locales`);\r\n\r\n for (const locale of locales) {\r\n for (const key of keys) {\r\n await this.translate(key, {\r\n sourceLocale: this.sourceLocale,\r\n targetLocale: locale,\r\n namespace: 'warm',\r\n });\r\n }\r\n }\r\n\r\n this.logger.info('Cache warming complete');\r\n }\r\n\r\n /**\r\n * Translate with automatic retry and exponential backoff.\r\n */\r\n private async translateWithRetry(text: string, opts: TranslateOpts): Promise<string> {\r\n return this.callWithRetry(async () => {\r\n const engine = await this.getSDKEngine();\r\n const result = await engine.translateText(text, {\r\n sourceLocale: opts.sourceLocale,\r\n targetLocale: opts.targetLocale,\r\n });\r\n return result;\r\n }, text, opts.targetLocale);\r\n }\r\n\r\n /**\r\n * Generic retry wrapper with exponential backoff.\r\n */\r\n private async callWithRetry(\r\n fn: () => Promise<string>,\r\n sourceText?: string,\r\n targetLocale?: string,\r\n ): Promise<string> {\r\n let lastError: Error | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n lastError = error instanceof Error ? error : new Error(String(error));\r\n this.logger.warn(\r\n `Translation attempt ${attempt + 1}/${this.maxRetries + 1} failed`,\r\n lastError.message,\r\n );\r\n\r\n // Check for rate limiting\r\n if (lastError.message.includes('429') || lastError.message.toLowerCase().includes('rate limit')) {\r\n const retryDelay = exponentialBackoff(attempt + 1);\r\n this.logger.info(`Rate limited. Waiting ${retryDelay}ms before retry...`);\r\n await sleep(retryDelay);\r\n continue;\r\n }\r\n\r\n if (attempt < this.maxRetries) {\r\n const delay = exponentialBackoff(attempt);\r\n await sleep(delay);\r\n }\r\n }\r\n }\r\n\r\n if (this.fallbackOnError) {\r\n this.logger.error('All translation attempts failed, returning original text');\r\n return sourceText ?? '';\r\n }\r\n\r\n throw new TranslationError(\r\n `Translation failed after ${this.maxRetries + 1} attempts`,\r\n sourceText,\r\n targetLocale,\r\n lastError,\r\n );\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|