开发工具前端【免费下载链接】js-lingui A readable, automated, and optimized (2 kb) internationalization for JavaScript项目地址https://gitcode.com/gh_mirrors/js/js-lingui点击查看免费下载导读本文基于 js-lingui 仓库website/docs/guides/optimizing-bundle-size.md展开深入讲解 Lingui 如何通过「将消息替换为紧凑 ID」与「构建期预编译、生产环境移除消息编译器」两大策略压缩 i18n 产物体积。你将了解 dev 与 prod 行为差异的底层原理、生产环境出现原始 ID 的排查方法、动态加载翻译时的运行时编译回退方案以及descriptorFields与setMessagesCompiler的完整配置矩阵。为什么 i18n 会让包体积失控构建现代应用时国际化i18n很容易让产物变得臃肿。语言和消息越多体积增长越快。以一个最简单的消息为例TransHello world/Trans如果这条消息原样保留在代码和翻译数据中你可能会得到源代码中的消息字符串Hello world翻译目录catalog中作为 key 的同一字符串默认语言目录中作为 value 的又一份相同字符串。同一个东西出现了三份而且每条消息都是如此。再乘以数百条消息和数种语言体积膨胀可想而知。Lingui 的思路是在保证可读、可维护的前提下尽可能把重复与冗余消灭在构建阶段——这正是其「可读、自动化、优化」设计目标在包体积上的体现。Lingui 如何压缩产物体积1. 用紧凑 ID 替换消息本体构建应用时Lingui 的宏macro会把TransHello world/Trans转换成类似Trans idzfhb1 /消息本体不再进入产物取而代之的是一个短 ID。这带来两个直接收益节省空间ID 远比完整字符串短消除重复只需要存在翻译本身原始文本不再需要打包进产物。从源码看这一替换发生在宏展开阶段packages/babel-plugin-lingui-macro/src/messageDescriptorUtils.ts中的createIdProperty会调用generateMessageId(message, context)生成 ID然后仅当descriptorFields模式要求时才保留message等字段详见下文「descriptorFields 字段保留规则」。2. 从生产环境移除消息编译器像下面这样的消息{count, plural, one {# item} other {# items}}使用的是 ICU MessageFormat 语法必须被编译成 Lingui 运行时可以执行的形态即编译后的 token 数组才能渲染。Lingui 自带消息编译器来完成这件事但编译器本身并不小。与其把它发给浏览器Lingui 选择在构建期编译 catalog 时提前完成编译。这样生产环境就完全不需要携带编译器。这正是无论 catalog 是什么格式哪怕已是 JSON 而非.po都必须执行lingui compile的原因编译不是文件格式转换而是把消息变换成运行时可以执行的形态。编译产物是一个经过JSON.parse包裹的 JS 对象。以compileNamespace: es为例参见 conf.md输出类似/* eslint-disable */export const messages JSON.parse(...)compileNamespace支持cjs默认、es、ts、json以及window.xxx/global.xxx等命名空间可根据目标环境选择产物形态。:::note ✅ 提示如果你使用lingui/vite-plugin、lingui/loader或lingui/metro-transformer就不需要手动执行lingui compile——这些插件会在你 import catalog 时自动完成编译。例如packages/vite-plugin/src/index.ts中的vite-plugin-lingui-load-catalogtransform 会在加载 catalog 时调用createCompiledCatalog产出编译后的模块代码。 :::为什么开发环境一切照常这是 Lingui 设计的精妙之处dev 与 prod 行为刻意不同。开发环境下Lingui在产物中保留原始消息如Hello world保留消息编译器让新消息可以立即工作。这样迭代非常快你可以随时新增一个Trans即使尚未执行 extract 或 compile也能立刻在浏览器里看到原文。从源码看i18n.ts 的I18n构造函数在NODE_ENV ! production时会自动调用this.setMessagesCompiler(compileMessage)为未编译的消息兜底。生产环境下Lingui剥离全部原始消息完全移除消息编译器。这意味着你必须提前 extract 并 compile 所有消息——否则 Lingui 无法知道如何渲染它们。I18n._渲染入口的实现印证了这一约束i18n.ts当拿到的翻译仍是字符串即未编译时若存在_messageCompiler则现场编译若不存在生产环境默认没有编译器则会打印Uncompiled message detected!警告——ICU 插值、复数等特性将无法正常工作。对应的测试用例见 i18n.test.ts。常见问题生产环境出现奇怪的消息 ID假设你新增了一条消息TransThis is a new message/Trans本地一切正常但部署后用户在界面上看到类似z3fd2这通常只有一个原因构建前没有执行消息提取。当 Lingui 编译 catalog 时它会尝试把每个消息 ID 与源代码中的消息对应起来。如果该消息不在 catalog 里就没有任何可回退的内容——于是原始 ID 直接出现在 UI 上。✅ 解决方案构建前务必先 extract确保构建脚本先提取最新消息build: lingui extract-template vite build这样能保证 catalog 与源代码保持同步。除了在脚本里串联命令你也可以在 CI 中借助lingui check sync校验 catalog 是否与源码同步lingui check missing校验是否有缺失翻译做到「失同步即失败」把问题挡在构建之前。CLI 的完整用法见 cli.md。动态加载翻译时怎么办Lingui 的设计面向构建期静态分析对大多数应用很合适但在以下场景会变得棘手从 CMS 加载翻译支持目录的 OTAover-the-air推送在运行时注入新消息。在这些场景下不能只依赖预编译 catalog你需要重新引入运行时消息编译器import { compileMessage } from lingui/message-utils/compileMessage; i18n.setMessagesCompiler(compileMessage);setMessagesCompiler定义在 i18n.ts它把编译器注册到_messageCompilerI18n._在遇到未编译字符串时会调用它现场编译。compileMessage实现在 compileMessage.ts内部用messageformat/parser解析消息再经processTokens递归转换为 token 数组字符串片段、[arg]、[arg, plural, {one: ..., other: ...}]等形式若解析失败会打印错误并原样返回消息保证不崩溃。⚠️ 请记住这样做会让包体积再次增大因为编译器及其依赖如 ICU 解析器会进入产物。对应的行为在 i18n.test.ts 中有测试覆盖。按需配置 LinguidescriptorFields选项控制宏转换后保留哪些描述符字段决定最终进入产物的内容。它的完整取值定义见 babel-plugin-lingui-macro/src/index.ts如下取值保留字段典型用途auto默认生产环境只保留id其他环境等同all默认推荐allid、message、context、commentLingui CLI 提取消息时使用id-only仅id生产环境最小化messageid、message、context不含comment运行时需要访问 message 与 context字段保留的判定逻辑位于 messageDescriptorUtils.tsid-only时message与context均被丢弃all时才保留comment。auto的解析则依据process.env.NODE_ENV production决定index.ts传入非法值会直接抛出Invalid descriptorFields value错误测试见 descriptor-fields-validation.test.ts。从 v6 起旧的extract与stripMessageField选项已被移除并提示改用descriptorFields。场景一希望生产环境表现得像开发环境保留原始消息、上下文并在生产环境也使用运行时编译——例如用于调试或动态 catalog。// Macro 配置 descriptorFields: message; // 运行时配置 i18n.setMessagesCompiler(compileMessage);输出中会保留id、message和context。context之所以保留是因为正确的消息 ID 生成依赖它generateMessageId(message, context)把 context 作为 ID 计算的输入见 messageDescriptorUtils.ts。场景二追求 dev 与 prod 完全一致希望两个环境都剥离所有字段便于尽早发现问题。// Macro 配置 descriptorFields: id-only; // 运行时配置 i18n.setMessagesCompiler(null);注意setMessagesCompiler(null)的类型签名为MessageCompiler传null用于显式关闭编译器——此时若出现未编译消息I18n._将直接打印警告见上文。就类型而言以实际发布的.d.ts为准建议结合 TS 严格模式验证。场景三使用 Lingui 默认行为什么都不改。Lingui 自动在生产环境剥离消息descriptorFields: auto→ 解析为id-only在开发环境保留全部字段。只需确保构建前始终执行extract-template。宏配置的补充说明Lingui 宏的接入方式因构建工具而异作为独立的Babel 插件作为SWC 插件通过babel-plugin-macros使用。每种方式的配置入口略有不同。以 Vite 为例lingui/vite-plugin在vite-plugin-lingui-macro-transform中会依据this.environment.config.isProduction自动把descriptorFields设为id-only或allvite-plugin/src/index.ts与auto的语义保持一致。如果你使用 SWC 插件或 babel-plugin-macros请以对应插件的文档为准。小结Lingui 的包体积优化可以概括为两条主线ID 化宏把消息替换为紧凑 ID消除「源码字符串 catalog key catalog value」的三重冗余预编译构建期完成消息编译生产环境完全不带编译器descriptorFields负责精确控制哪些字段进入产物。配套纪律是构建前先extract-template保证 catalog 与源码同步避免生产环境泄露原始 ID。若需要 CMS 或 OTA 这类运行时注入场景再通过i18n.setMessagesCompiler(compileMessage)显式带回编译器同时接受体积的相应增长。理解了 dev/prod 的差异与这套配置矩阵你就能在包体积、可调试性和动态能力之间找到适合自己项目的平衡点。赞分享开发工具前端【免费下载链接】js-lingui A readable, automated, and optimized (2 kb) internationalization for JavaScript项目地址https://gitcode.com/gh_mirrors/js/js-lingui点击查看免费下载相关推荐Notepad Markdown语法高亮终极指南10主题提升你的写作效率Notepad Markdown语法高亮终极指南10主题提升你的写作效率 还在为Notepad中单调的Markdown编辑体验而烦恼吗markdo开发工具CLI一条命令归档全部 QQ 空间历史说说GetQzonehistory 使用说明一条命令归档全部 QQ 空间历史说说GetQzonehistory 使用说明 GetQzonehistory 是一个用 Python 写的说说导出、说说备份工网页爬虫数据分析TradingAgents-CN多智能体交易系统零基础构建AI投资分析平台的终极指南TradingAgents CN多智能体交易系统零基础构建AI投资分析平台的终极指南 想要快速搭建一个专业的AI投资分析系统吗TradingAgents C人工智能大模型AI Agent多智能体金融科技后端前端上一篇Reactide项目搜索功能快速定位文件与代码片段下一篇2025 必学工具 Modin一行代码让 Pandas 提速 4 倍的秘密创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?