ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Replexica(Lingo.dev)翻译架构深度解析:元数据、无状态 Translator 与缓存分离的编排设计

Replexica(Lingo.dev)翻译架构深度解析:元数据、无状态 Translator 与缓存分离的编排设计 ReplexicaLingo.dev翻译架构深度解析元数据、无状态 Translator 与缓存分离的编排设计【免费下载链接】replexicaOpen-source localization engineering tools. Connects to Lingo.dev localization engineering platform for consistent, quality translations.项目地址: https://gitcode.com/GitHub_Trending/re/replexica导读本文基于 packages/new-compiler/docs/TRANSLATION_ARCHITECTURE.md 展开系统讲解 Replexica 新一代编译器中元数据管理、翻译执行、缓存策略三者清晰分离的重构后翻译架构。你将理解 AST 转换器如何产出翻译条目、LMDB 元数据层如何读写、TranslationService如何编排一次翻译请求以及如何通过无状态Translator接口与可替换的TranslationCache实现实现低成本接入与优雅的部分失败处理。读完本文你可以直接基于仓库源码复现这套架构的设计决策并动手扩展一个新的 Translator 或缓存实现。架构设计原则四个核心约束重构后的翻译架构围绕四条原则展开它们共同决定了整个new-compiler模块的代码组织方式元数据存储只有两个知情者元数据函数负责读写 LMDB 数据库与 Translation Service负责协调一切的编排器。除此之外任何模块都不直接触碰元数据文件结构从而避免元数据格式变化引发连锁改动。Translator 是无状态的它只面向抽象的TranslatableEntry类型工作不了解元数据文件结构、不内置任何缓存因此可以被隔离起来独立测试测试成本极低。缓存放到了服务层TranslationService拥有缓存策略的决策权缓存实现本地磁盘、远程、内存等可以随时替换而不影响翻译逻辑。部分失败被优雅处理服务同时返回成功翻译与错误信息即使新翻译失败也依然可以基于缓存中的旧翻译继续出结果错误信息携带足够的上下文便于调试。这四条原则在仓库中的落点非常清晰元数据逻辑集中在 packages/new-compiler/src/metadata/manager.ts缓存抽象与实现位于 packages/new-compiler/src/translators/cache.ts 及其同目录实现而编排逻辑全部收敛在 packages/new-compiler/src/translators/translation-service.ts。组件架构总览原文档给出了组件关系图结合当前仓库源码数据流如下┌─────────────────────────────────────────────────┐ │ AST Transformer │ │ - Extracts text from JSX │ │ - Knows ComponentType, context │ └────────────────┬────────────────────────────────┘ │ writes ↓ ┌─────────────────────────────────────────────────┐ │ Metadata Functions (saveMetadata/loadMetadata) │ │ - Pure functions for LMDB database access │ │ - Per-operation connections (see manager.ts) │ │ - Returns TranslationEntry[] │ └────────────────┬────────────────────────────────┘ │ reads from ↓ ┌─────────────────────────────────────────────────┐ │ TranslationService (orchestrator) │ │ - Coordinates translation workflow │ │ - Handles cache strategy │ │ - Determines what needs translation │ │ - Manages partial failures │ │ - Returns TranslationResult with errors │ └───┬─────────────────────┬──────────────────────┘ │ │ │ uses │ uses ↓ ↓ ┌──────────────┐ ┌────────────────────┐ │ Cache │ │ Translator │ │ - get() │ │ - translate() │ │ - set() │ │ - batchTranslate()│ │ - update() │ │ - Pseudo, LCP, etc│ │ - Local/ │ │ - Stateless │ │ Remote │ │ - No file I/O │ └──────────────┘ └────────────────────┘流程起点是 AST Transformer它从 JSX 与 Next.js metadata 对象中提取可翻译文本生成带type、sourceText、context、location、hash的TranslationEntry列表见 packages/new-compiler/src/types.ts随后写入元数据层翻译阶段由TranslationService读取元数据、查询缓存、调用 Translator最后把结果合并回缓存与字典。元数据层基于 LMDB 的纯函数存取元数据函数的设计目标是纯粹只做 LMDB 数据库的读写不掺杂任何业务逻辑。实现在 packages/new-compiler/src/metadata/manager.ts 中loadMetadata(dbPath, noSync?)打开连接、通过db.getRange()全量读出所有key - TranslationEntry返回MetadataSchema即以 hash 为 key 的映射saveMetadata(dbPath, entries, noSync?)在单个原子事务中批量写入所有条目db.transactionSyncdb.putSync(entry.hash, entry)避免半写状态cleanupExistingMetadata(metadataDbPath)清空旧元数据数据库用于构建时重建getMetadataPath(config)根据environment决定目录名——开发环境用metadata-dev生产构建用metadata-build。值得注意的实现细节每次操作都走runWithDbConnection即按操作开连接、用完即关。注释说明这是因为lmdb-js在 C 层面对同一路径的open()做了引用计数去重开销很低同时每次 open 还能清理已终止 worker 的陈旧读事务。另外lmdb通过动态import()加载防止 bundler 或 require hook 转换其 CJS 产物时崩溃packages/new-compiler/src/metadata/manager.ts。元数据的产出方在编译期以 Next.js metadata 为例packages/new-compiler/src/plugin/transform/metadata.ts 会递归遍历metadata对象或generateMetadata函数仅对白名单字段title、description、openGraph.*、twitter.*、appleWebApp.title等支持openGraph.images[*].alt这类数组通配模式生成翻译条目并把字符串字面量替换为t(hash, fallback)调用同时把静态export const metadata改造成 async 的generateMetadata以便服务端取翻译。TranslationService一次翻译请求的八步编排TranslationService是整个架构的中枢构造与执行逻辑都在 packages/new-compiler/src/translators/translation-service.ts。它的构造器负责组装三件套缓存实例、真实 Translator、可选的 PluralizationService并且内置了开发环境的降级策略显式开启dev.usePseudotranslator时直接使用PseudoTranslatorMemoryTranslationCache否则尝试创建LingoTranslator与createCache(config)若创建失败例如缺少 API Key开发环境自动降级到伪翻译器并打印警告生产环境则直接抛错packages/new-compiler/src/translators/translation-service.ts。一次translate(locale, metadata, requestedHashes?)调用按以下步骤执行确定工作集取requestedHashes或元数据全部 hash先查缓存cache.get(locale)一次性读出该语言全部已缓存翻译源语言也走同一路径计算未命中集合过滤出缓存中不存在的 hash若全部命中则直接返回结果含stats.cached过滤元数据只保留未命中条目交给后续阶段复数化处理若配置了pluralization调用PluralizationService.process把源文本自动转换为 ICU MessageFormat 形态分离 override检查每条未命中条目的overrides[locale]有手工覆盖的条目直接采用覆盖文本不进 AI 翻译翻译源语言直接回写sourceText目标语言调用translator.translate(locale, entriesToTranslate)再与覆盖结果合并回写缓存并合并返回用cache.update(locale, newTranslations)增量合并缓存最后按工作集裁剪出translations连同errors与statstotal/cached/translated/failed一起返回。返回结构定义在同文件中export interface TranslationResult { translations: Recordstring, string; // hash - 译文 errors: TranslationError[]; // { hash, sourceText, error } stats: { total: number; cached: number; translated: number; failed: number }; }翻译请求的配置项TranslationServiceConfig支持sourceLocale、pluralization、modelslingo.dev或 locale 对映射如{ en:es: google:gemini-2.0-flash, *:*: groq:llama3-8b-8192 }、自定义prompt、aiTimeout默认 120000ms与environment完整字段可对照 packages/new-compiler/src/types.ts 中的LingoConfig。无状态 Translator 接口与部分失败契约Translator 被刻意设计成哑组件其契约定义在 packages/new-compiler/src/translators/api.tsexport type TranslatableEntry { text: string; context: Recordstring, any }; export interface TranslatorConfig { config: Config; translate: ( locale: LocaleCode, entriesMap: Recordstring, TranslatableEntry, ) PromiseRecordstring, string; }Translator 只接收locale与text context映射返回 hash 到译文的映射完全不感知元数据文件与缓存。仓库中已有两种实现LingoTranslatorpackages/new-compiler/src/translators/lingo/translator.ts对接 Lingo.dev 平台或直接 LLM 提供商的真实翻译器构造时会校验并拉取 API KeyPseudoTranslatorpackages/new-compiler/src/translators/pseudotranslator/index.ts本地伪翻译用于开发阶段验证 i18n 接入、节省 AI 额度。关键的部分失败契约是PartialTranslationError当一次翻译在中途失败如 AI 超时时抛出的错误会携带已经付费产出、但尚未落盘的 partialTranslations服务层将其Object.assign进结果并写入缓存——因为已经付过钱的译文如果被丢弃下一次构建就会重新请求并再次付费。这正是测试 packages/new-compiler/src/translators/translation-service.test.ts 中 should cache the entries the translator finished before it failed 与 should not re-request what the failed run already cached 所验证的行为。缓存层接口、两种实现与工厂缓存被抽象为纯粹的hash - 译文映射优化层不需要知道元数据结构或上下文。接口定义在 packages/new-compiler/src/translators/cache.tsexport interface TranslationCache { get(locale: LocaleCode): PromiseRecordstring, string; get(locale: LocaleCode, hashes: string[]): PromiseRecordstring, string; update(locale: LocaleCode, translations: Recordstring, string): Promisevoid; // 合并 set(locale: LocaleCode, translations: Recordstring, string): Promisevoid; // 整体替换 has(locale: LocaleCode): Promiseboolean; clear(locale: LocaleCode): Promisevoid; clearAll(): Promisevoid; }仓库现有两种实现LocalTranslationCachepackages/new-compiler/src/translators/local-cache.ts把每个 locale 的译文写成.lingo/cache/locale.json文件内容为DictionarySchema{ version, locale, entries }update先读后并再写所有文件 I/O 都套了 10 秒超时防止挂死MemoryTranslationCachepackages/new-compiler/src/translators/memory-cache.tsMapLocaleCode, Mapstring, string的内存实现主要服务伪翻译器开发场景。具体选型由工厂函数createCachepackages/new-compiler/src/translators/cache-factory.ts按cacheType决定——当前类型系统里只有local未知类型会直接抛错兜底。缓存目录由 packages/new-compiler/src/utils/path-helpers.ts 中的getCacheDir基于lingoDir推导与元数据目录保持在同一.lingo根下。未来增强远程缓存实现原文档给出了远程缓存的方向性示例接口契约完全兼容class RemoteTranslationCache implements TranslationCache { constructor(private apiUrl: string) {} async get(locale: string) { const response await fetch(${this.apiUrl}/cache/${locale}); return response.json(); } // ... other methods }只要实现上述TranslationCache接口并在createCache中增加对应分支或将cacheType扩展为remote即可在不动 Translator 与编排逻辑的前提下接入分布式缓存。这也印证了缓存是基础设施关注点而非翻译关注点的设计初衷。常见问题解答Q: 为什么不在 Translator 内部做缓存A: 缓存是基础设施关注点不是翻译关注点。不同部署需要不同缓存策略本地开发 vs 生产、单机 vs 分布式把缓存上移到服务层才能让 Translator 保持无状态、可独立测试。Q: 如果缓存里需要上下文怎么办A: 服务层本身已经能访问元数据上下文。如果确实需要把上下文带进缓存扩展缓存接口让get/set存储TranslationEntry而不是纯字符串即可。Q: 如何新增一个 TranslatorA: 实现Translator接口然后在TranslationService构造器中按配置接入当前仓库通过models配置选择LingoTranslator开发模式可选择PseudoTranslator无需关心缓存——缓存完全由服务层负责。Q: 翻译失败会发生什么A: 服务返回带错误的部分结果已缓存译文照常返回errors数组携带每条失败条目的 hash、源文本与错误描述且PartialTranslationError中的部分译文会被保留进缓存避免重复付费。总结Replexicanew-compiler的翻译架构本质是一次关注点分离的重构LMDB 元数据层manager.ts只负责存取Translatorapi.ts只负责翻译TranslationCachecache.ts只负责缓存而TranslationServicetranslation-service.ts承担全部编排、降级、覆盖与部分失败决策。这套设计让每一层都能独立演进与测试——新增翻译源、替换缓存介质、改变元数据格式都不会波及彼此。若想进一步深入可继续阅读 packages/new-compiler/docs/TRANSLATION_ARCHITECTURE.md 原始文档、packages/new-compiler/src/translators/translation-service.test.ts 的失败路径测试以及 packages/new-compiler/src/plugin/transform/TRANSFORMATION_PIPELINE.md 了解 AST 转换侧如何生成元数据。【免费下载链接】replexicaOpen-source localization engineering tools. Connects to Lingo.dev localization engineering platform for consistent, quality translations.项目地址: https://gitcode.com/GitHub_Trending/re/replexica创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表