replexica new-compiler 翻译工具链完全指南:伪本地化与磁盘缓存的工程化实践
【免费下载链接】replexicaOpen-source localization engineering tools. Connects to Lingo.dev localization engineering platform for consistent, quality translations.项目地址: https://gitcode.com/GitHub_Trending/re/replexica
replexica(连接 Lingo.dev 本地化工程平台的翻译工具集)在其新一代编译器(packages/new-compiler)中提供了一套面向 React Server Components 的翻译工具模块(@lingo.dev/compiler-beta/translate)。本文以 translators/README.md 为主线,系统讲解该模块的两大核心能力——伪本地化(Pseudolocalization)与翻译缓存(Caching):从「一行配置自动开启」到「手动接管翻译流程」,再到缓存落盘的目录结构,并结合仓库源码(PseudoTranslator、LocalTranslationCache、TranslationService等)剖析其底层实现原理。读完本文,你将掌握在 Next.js/Turbopack 项目中快速验证 i18n 布局、为任何翻译函数叠加磁盘缓存、以及深入定制翻译管线的完整方法。
模块概览:@lingo.dev/compiler-beta/translate提供了什么
翻译工具模块是编译器的「翻译层」,统一了「翻译器(Translator)」与「缓存(Cache)」两类抽象。从 index.ts 的导出可以看到其核心 API 组成:
- 翻译器实现:
PseudoTranslator(伪本地化,用于开发测试)、LingoTranslator(真实 AI 翻译,接入 Lingo.dev 或自定义 LLM Provider); - 编排器:
TranslationService,负责在 metadata、cache、translator 之间协调完整翻译工作流; - 缓存抽象:
TranslationCache接口与createCache工厂,当前支持本地磁盘缓存(local类型); - 类型定义:
Translator、TranslatableEntry、DictionarySchema、LocalCacheConfig等。
所有翻译器统一实现Translator<Config>接口(见 api.ts):
export interface Translator<Config> { config: Config; translate: ( locale: LocaleCode, entriesMap: Record<string, TranslatableEntry>, ) => Promise<Record<string, string>>; }其中TranslatableEntry = { text: string; context: Record<string, any> }。该接口支持批量翻译——一次调用可以传入一个或多个条目,翻译结果以hash -> translated text的扁平映射返回。这是理解后续所有用法的基础:无论是伪翻译、AI 翻译还是自定义翻译器,都遵循同一套契约,因此可以方便地叠加统一的缓存包装器。
伪本地化:不花一分钱 API 也能测试国际化布局
伪本地化(Pseudolocalization)是把源语言文本替换成「看似外语、实则保留原词形」的变体字符(如Hello World→Ĥéĺĺó Ŵóŕĺḍ),并人为拉长文本长度。它的价值在于在真实翻译接入之前,提前暴露 UI 布局问题:文本变长导致的换行、溢出、截断,以及硬编码字符串等 i18n 隐患,都能在开发阶段暴露出来。
仓库中的PseudoTranslator(pseudotranslator/index.ts)正是为此设计——它的注释明确写道:「Pseudotranslator for testing without actual translation APIs」。它内部维护了一张PSEUDO_MAP字符映射表(a→á、l→ĺ、o→ó……A→Á、Z→Ẑ),把可见字符逐一替换为带重音/变音符的等价字符。
推荐方式:通过 Loader 配置一键开启
README 推荐的最简方式是在 Next.js 的 Turbopack 配置中通过 loader 配置启用:
// next.config.ts export default { turbopack: { rules: { "*.{tsx,jsx}": { loaders: [ { loader: "@lingo.dev/compiler-beta/loader", options: { sourceRoot: "./app", lingoDir: ".lingo", sourceLocale: "en", translator: "pseudo", // 🎯 Enable automatic pseudolocalization }, }, ], as: "*.tsx", }, }, }, };各选项含义如下:
| 选项 | 示例值 | 作用 |
|---|---|---|
loader | @lingo.dev/compiler-beta/loader | 编译期 loader 入口,负责对 TSX/JSX 做静态转换 |
sourceRoot | ./app | 源代码根目录,也是 metadata 与缓存文件的相对基准 |
lingoDir | .lingo | 存放 metadata 与缓存的目录名 |
sourceLocale | en | 源语言 locale 代码 |
translator | "pseudo" | 翻译器选择;设为"pseudo"即启用自动伪本地化 |
当translator: "pseudo"被设置后,编译器会自动完成三件事:
- 导入并初始化带缓存的伪翻译器(cached pseudotranslator);
- 将其注入到所有 Server Components的翻译调用链中;
- 把翻译结果缓存到
.lingo/cache/目录,避免重复计算。
示例输出(README 给出的转换效果):
"Hello World"→"[Ĥéĺĺó Ŵóŕĺḍ ]""Welcome"→"[Ŵéĺçóṁé ]"
(说明:文档示例中统一以方括号包裹输出以便辨识;当前源码实现中pseudolocalize的返回值为「伪本地化字符 + 约 30% 长度的空格填充」,详见下文源码剖析。)
手动伪本地化(高级用法)
如果你不使用 loader 配置,也可以手动把pseudoTranslate当作翻译函数直接传入:
import { pseudoTranslate } from "@lingo.dev/compiler-beta/translate"; // Use as translation function const t = await getServerTranslations({ metadata: __lingoMetadata, sourceLocale: "en", translate: pseudoTranslate, });这里getServerTranslations来自 react/server-only/index.ts,它返回一个t(hash, sourceText, params?)函数;当translations[hash]缺失时回退到sourceText,并支持富文本参数渲染。手动方式适合需要完全掌控翻译来源、或把伪翻译与真实翻译器动态切换的场景。
为翻译函数叠加磁盘缓存:createCachedTranslator
无论使用哪种翻译器,都可以用createCachedTranslator包一层,让「已翻译过的 hash 直接命中缓存,不再重复调用翻译器」:
import { createCachedTranslator, pseudoTranslate, } from "@lingo.dev/compiler-beta/translate"; // Create cached version const cachedTranslate = createCachedTranslator(pseudoTranslate, { cacheDir: ".lingo", sourceRoot: "./app", }); // Use in server components const t = await getServerTranslations({ metadata: __lingoMetadata, sourceLocale: "en", translate: cachedTranslate, // Will use cache/.json files });createCachedTranslator返回的仍是Translator接口,因此可以无缝替换任何位置的翻译函数。其缓存实现由 cache-factory.ts 的createCache(config)工厂创建:目前仅支持cacheType: "local",对应LocalTranslationCache;若传入其他类型会抛出Unknown cache type错误——这是 TypeScript 类型与运行时双重保障的设计。
服务端缓存的直接管理:ServerTranslationCache
如果需要在 Server Component 中手动读写缓存(例如预取某语言、主动清理缓存),可以直接使用ServerTranslationCache:
import { ServerTranslationCache } from "@lingo.dev/compiler-beta/translate"; const cache = new ServerTranslationCache({ cacheDir: ".lingo", sourceRoot: "./app", }); // Check if cached const hasFrench = await cache.has("fr"); // Get translations const translations = await cache.getTranslations("fr"); // Set translations await cache.set("fr", dictionarySchema); // Clear cache await cache.clear("fr"); await cache.clearAll();这套 API 对应 cache.ts 中定义的TranslationCache接口的完整能力:get(支持按 hash 列表部分获取)、update(合并式更新,不覆盖已有条目)、set(整体替换某 locale 的缓存)、has、clear(清单个 locale)与clearAll(清空全部)。
自动转换示例:一行配置,编译器替你生成全部样板代码
README 用一个「前后对比」直观展示了配置驱动模式下的魔法。原始代码:
// app/page.tsx - Your original code export default function Home() { return <h1>Hello World</h1>; }编译器将其自动转换为:
// Transformed (automatic, no manual changes needed) import { getServerTranslations } from '@lingo.dev/compiler-beta/react/server'; import { createCachedTranslator, pseudoTranslate } from '@lingo.dev/compiler-beta/translate'; import __lingoMetadata from './.lingo/metadata.json'; const __lingoTranslate = createCachedTranslator(pseudoTranslate, { cacheDir: '.lingo', sourceRoot: './app', }); export default async function Home() { const t = await getServerTranslations({ metadata: __lingoMetadata, sourceLocale: 'en', translate: __lingoTranslate, }); return <h1>{t("63b8a9ec9544")}</h1>; // "[Ĥéĺĺó Ŵóŕĺḍ ]" }可以看到转换的完整链路:硬编码文案Hello World被提取为 hash 键63b8a9ec9544(hash 来源于源文本),并注入getServerTranslations、createCachedTranslator(pseudoTranslate, …)与.lingo/metadata.json导入。这条「提取 → 注入翻译函数 → 按 hash 取词」的路径,正是编译期 loader 的核心工作。与之对应的转换逻辑在编译插件的 transform 管线中实现(参见 transform/transform.test.ts 中大量关于「注入 getServerTranslations 与 hash 数组」的断言)。
手动设置示例:布局级别的翻译注入
在需要手动控制(例如把翻译注入根布局)时,README 给出如下模板:
// app/layout.tsx import { getServerTranslations } from '@lingo.dev/compiler-beta/react/server'; import { createCachedTranslator, pseudoTranslate } from '@lingo.dev/compiler-beta/translate'; import __lingoMetadata from './.lingo/metadata.json'; // Create cached translator const translate = createCachedTranslator(pseudoTranslate, { cacheDir: '.lingo', sourceRoot: './app', }); export default async function RootLayout({ children }) { const t = await getServerTranslations({ metadata: __lingoMetadata, sourceLocale: 'en', translate, // Pseudolocalize with caching }); return ( <html> <body>{children}</body> </html> ); }要点:getServerTranslations是 async API(React Server Component 中可直接await);createCachedTranslator接收缓存配置并返回翻译函数;metadata 从.lingo/metadata.json导入。整个流程不需要任何运行时翻译服务即可工作。
缓存结构:.lingo/cache/<locale>.json
所有翻译结果缓存在<sourceRoot>/<cacheDir>/cache/<locale>.json,README 给出的目录示意:
app/ .lingo/ metadata.json # Source strings cache/ en.json # English (source) fr.json # French translations pseudo.json # Pseudolocalizedmetadata.json保存源字符串(含 context 等元信息),是 hash 与源文本的对应表;cache/en.json等按 locale 命名,是该语言的hash -> 译文映射。
磁盘缓存的落盘实现在 local-cache.ts:getDictionary读取<cacheDir>/<locale>.json并JSON.parse;setDictionary先fs.mkdir(cacheDir, { recursive: true })确保目录存在,再以JSON.stringify(dictionary, null, 2)写入。所有文件 I/O 都被 utils/timeout.ts 的withTimeout包裹(默认DEFAULT_TIMEOUTS.FILE_IO,即 10 秒),防止缓存读写导致构建/请求无限挂起。缓存文件结构遵循DictionarySchema(见 api.ts):{ version, locale, entries },其中entries即 hash 到译文的扁平映射。
值得注意的合并语义:update()采用「读旧值 → 合并新值 → 整体写回」的策略(local-cache.ts的update方法),即新增翻译不会清空已有缓存;而set()则整体替换。clearAll()只删除目录下以.json结尾的文件,忽略其他内容。
客户端组件的不同处理方式
伪本地化与磁盘缓存主要面向 Server Components(Node 运行时允许 fs 操作)。对于客户端组件,README 明确指出翻译机制不同:
- 使用
useTranslation()hook(由编译器自动注入); - 翻译通过 API 或打包资源加载;
- 浏览器端缓存可考虑 IndexedDB(尚未实现,属计划能力,勿当作既有功能)。
这一点也从测试中可以得到印证:transform 测试中客户端组件断言「使用统一 hook 导入,而不是 getServerTranslations / await getServerTranslations」(见 transform.test.ts)。
源码纵深:三个关键实现的原理剖析
1.pseudolocalize:保留占位符的字符级替换
pseudolocalize(pseudotranslator/index.ts)是伪本地化的核心函数,实现要点:
- 跳过纯空白与纯变量文本:
!text.trim()或整体匹配^{.*}$时原样返回; - 保留正则:
/(\{\w+}|<\/?\w+\/?>)/g匹配{name}变量占位符与<a0>/</a0>组件标签,将这些片段标记为preserve: true不参与字符替换——这一点对 React 富文本至关重要,因为标签一旦被破坏会导致渲染错误; - 只对可翻译片段做字符映射:逐字符查
PSEUDO_MAP,未收录字符(如数字、标点)原样保留; - 按源文本长度追加约 30% 空格:
" ".repeat(Math.ceil(text.length * 0.3)),用于模拟真实翻译后的文本膨胀。
配套测试 pseudotranslator/index.test.ts 覆盖了纯文本、单/多变量占位符、单/多组件标签、混合场景与纯空白等用例,例如断言pseudolocalize("Hello {name}")同时包含{name}与Ĥéĺĺó。
2.LocalTranslationCache与MemoryTranslationCache:两种缓存实现
- local-cache.ts 是磁盘实现:每次
get都读文件并解析 JSON,读取失败(文件不存在)返回{}而不是抛错;has用fs.access判断文件存在性;clear对不存在的文件静默忽略错误。 - memory-cache.ts 是内存实现:内部用
Map<LocaleCode, Map<string, string>>,适合一次性进程内会话(如伪翻译的开发回退场景)。
3.TranslationService:缓存优先的编排器
translation-service.ts 的translate(locale, metadata, requestedHashes?)完整展示了 README「Cache is checked before calling translate function」的实现:
- 确定本次需要处理的 hash 集合(未传时取 metadata 全部键);
- 先查缓存,得到
cachedTranslations; - 过滤出未命中的 hash(
uncachedHashes),若全部命中则直接返回(cached: N, translated: 0); - 对未命中条目处理复数化(若启用
pluralization服务); - 检查每个条目的
overrides[locale],有覆盖值则直接用覆盖值,不调用翻译器; - 对仍需翻译的条目调用
translator.translate(locale, entriesToTranslate); - 源 locale 直接返回(可能经过复数化的)
sourceText,不调用翻译器; - 处理
PartialTranslationError的部分失败结果(已付费的翻译不丢弃,见 api.ts); - 成功翻译写入缓存(
cache.update合并),缓存写失败不阻断请求; - 汇总
stats: { total, cached, translated, failed }与errors返回。
此外,构造器里有一套清晰的降级策略:开发环境下若dev.usePseudotranslator为 true,或创建LingoTranslator失败(如缺少 API Key),都会自动回退到PseudoTranslator({ delayMedian: 100 })+MemoryTranslationCache;生产环境则直接抛错,避免静默降级。
4. 真实翻译:LingoTranslator与自定义翻译器
伪本地化之外,同一套Translator接口也服务于真实 AI 翻译(LingoTranslator,见 lingo/README.md):支持models: "lingo.dev"(官方引擎)或自定义模型映射(如"en:es": "google:gemini-2.0-flash"、"*:*": "openrouter:..."),并通过环境变量提供密钥(LINGODOTDEV_API_KEY,或GOOGLE_API_KEY、GROQ_API_KEY、OPENROUTER_API_KEY、MISTRAL_API_KEY等)。你也可以实现自定义Translator并用createCachedTranslator包装,完整示例见同目录的 USAGE.md(含旧TranslateFunctionAPI 的迁移对照)。
注意事项与最佳实践
README 末尾的 Notes 是实践中最重要的约束,逐条整理如下:
- Server Components:可使用磁盘缓存(允许 fs 操作),这是
createCachedTranslator/ServerTranslationCache的适用场景; - Client Components:需要浏览器侧缓存方案(IndexedDB、API endpoints),当前未实现,请勿依赖;
- 缓存优先:翻译前先查缓存,命中则跳过翻译函数——这是避免重复翻译、节省 API 费用的关键设计;
- 文本膨胀:伪本地化会把文本长度拉长约 30%,这是有意为之的布局压力测试手段,不是缺陷——正是靠它提前发现 UI 溢出问题;
- 生产环境不要依赖伪翻译:
translator: "pseudo"仅用于开发验证,正式发布请切换到LingoTranslator或自定义翻译器,并正确配置 API Key。
综合来看,@lingo.dev/compiler-beta/translate模块提供了「配置驱动优先、手动控制兜底」的两级使用模式:日常开发用translator: "pseudo"一条配置完成伪本地化 + 缓存闭环;进阶场景用createCachedTranslator/ServerTranslationCache精细管理翻译与缓存生命周期。配合Translator统一接口,无论是伪翻译、Lingo.dev 还是自定义 LLM,都能以一致的姿势接入,这正是该模块在工程可维护性上的核心价值。
【免费下载链接】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),仅供参考