使用您最喜欢的AI助手总结文档,并引用此页面和AI提供商
版本历史
- "`purge` 和 `minify` 现在可通过 `@intlayer/swc` 在 Next.js 上工作 — 无需 `babel.config.js`"v9.2.12026/8/9
- "在参考表中按所需的流水线顺序列出 Babel 插件(extract → purge → minify → optimize)"v8.12.02026/6/24
- "为 Babel/Webpack 引入了 `intlayerPurgeBabelPlugin` 和 `intlayerMinifyBabelPlugin`,明确了插件管线"v8.12.02026/6/7
- "向构建配置中添加了 `minify` 和 `purge` 选项"v8.7.02026/4/8
此页面的内容已使用 AI 翻译。
查看英文原文的最新版本如果您有改善此文档的想法,请随时通过在GitHub上提交拉取请求来贡献。
文档的 GitHub 链接复制文档 Markdown 到剪贴板
优化 i18n 打包体积与性能
依赖 JSON 文件的传统 i18n 解决方案中最常见的挑战之一是管理内容体积。如果开发者没有手动将内容拆分到各个命名空间(namespaces),用户通常会为了查看一个页面而下载所有页面、甚至是所有语言的翻译。
例如,一个应用有 10 个页面并被翻译成了 10 种语言,可能导致用户为了这 10 个页面下载所有的内容,尽管他们只想要一个页面的内容(当前语言版本的当前页面)。这不仅会造成带宽浪费,也会导致更慢的加载时间。
Intlayer 通过在构建时(build-time)进行优化来解决这一问题。 它可以分析你的代码以检测每个组件实际使用了哪些字典,并只将必要的内容注入到你的打包结果(bundle)中。
目录
分析你的包大小
分析你的打包结果是找出“臃肿”的 JSON 文件和考虑进行代码分割(code-splitting)的首要步骤。这些工具可以生成你应用程序编译代码的树状可视视图(treemap),让你能够清楚地看到究竟是哪些库占据了最多的空间。
Vite / Rollup
Vite 在底层使用了 Rollup。插件 rollup-plugin-visualizer 能够生成一个交互式 HTML 文件,展示依赖图(graph)中每个模块的体积。
复制代码到剪贴板
复制代码到剪贴板
Next.js (Turbopack)
对于使用了 App Router 和 Turbopack 的项目,Next.js 提供了一个内置的实验性分析器,它不需要额外的依赖。
复制代码到剪贴板
Next.js (Webpack)
如果你在 Next.js 中使用的是默认的 Webpack 打包器,请使用官方的 bundle analyzer。你可以通过在构建期间设置一个环境变量来触发它。
复制代码到剪贴板
复制代码到剪贴板
用法:
复制代码到剪贴板
纯 Webpack
针对 Create React App (ejected)、Angular 或是自定义 Webpack 的设置,使用业界标准的 webpack-bundle-analyzer。
复制代码到剪贴板
复制代码到剪贴板
它是如何运作的
Intlayer 使用一种基于组件的方法(per-component approach)。与全局 JSON 文件不同,你的内容会在组件旁边或是组件内部进行定义。在构建流程中,Intlayer 将会:
- 分析你的代码以寻找
useIntlayer的调用。 - 构建对应的字典内容。
- 替换
useIntlayer调用为依据你的配置进行优化后的代码。
这样能够确保:
- 如果一个组件未被导入,它的内容将不会包含在打包产物中(Dead Code Elimination / 死代码消除)。
- 如果一个组件被延迟加载,其内容同样会被延迟加载。
插件参考
Intlayer 的构建优化被划分为若干个职责单一的插件。了解它们各自的用途可以防止在配置它们时产生困惑。
Babel 插件 (@intlayer/babel)
这些被直接运用在基于 Webpack 设置的 babel.config.js 当中(比如使用了 Babel 的 Next.js、CRA,或是自定义的 Webpack 等)。
下表按所需的流水线顺序列出它们(与它们必须在 babel.config.js 中出现的顺序相同):
在弹窗中打开表格以清晰地查看所有数据
| 插件 | 功能说明 |
|---|---|
intlayerExtractBabelPlugin | 扫描 .content.ts 文件并把编译好的字典写入 .intlayer/ |
intlayerPurgeBabelPlugin | 扫描所有源代码文件,从已编译的 .intlayer/**/*.json 字典文件中删除未被使用的内容字段 |
intlayerMinifyBabelPlugin | 重命名内容字段的键(keys) 为简短的字母别名(例如 title 变成 a),作用范围包括 JSON 与源代码 |
intlayerOptimizeBabelPlugin | 将 useIntlayer('key') 重写为 useDictionary(hash) 并注入匹配对应字典的 import 语句 |
插件的执行顺序很重要。 在你的babel.config.js里,purge 和 minify 的插件必须放置在 optimize 插件之前。优化步骤(optimize)会把useIntlayer('key')替换为模糊的useDictionary(hash),此举抹除了能够让 purge 和 minify 识别哪些字段被使用过的字典 key 信息。
每一个 Babel 插件都有对应的选项助手(options helper),该助手会在配置加载时读取一遍 intlayer.config.ts,并返回预解析的值:
在弹窗中打开表格以清晰地查看所有数据
| 选项助手 | 配套插件 |
|---|---|
getExtractPluginOptions() | intlayerExtractBabelPlugin |
getPurgePluginOptions() | intlayerPurgeBabelPlugin |
getMinifyPluginOptions() | intlayerMinifyBabelPlugin |
getOptimizePluginOptions() | intlayerOptimizeBabelPlugin |
Vite 插件 (vite-intlayer)
Vite 用户不需要直接对它们进行配置。当你在 vite.config.ts 里调用 withIntlayer() 时,它们会自动生效。只需要在 intlayer.config.ts 中设定 build.purge 和 build.minify,即可开启相应的功能,且不需要额外的插件注册过程。
在弹窗中打开表格以清晰地查看所有数据
| 内部 Vite 插件 | 等效行为 |
|---|---|
| Usage analyzer | 等同于 intlayerPurgeBabelPlugin 的分析步骤 |
| Dictionary prune | 等同于 intlayerPurgeBabelPlugin 的 JSON 写入步骤 |
| Dictionary minify | 等同于 intlayerMinifyBabelPlugin 的 JSON 写入步骤 |
| Babel transform | 等同于 intlayerMinifyBabelPlugin 的代码重命名步骤 + intlayerOptimizeBabelPlugin |
SWC 插件(@intlayer/swc)
Next.js 用户同样从不直接配置这些。自 v9.2.1 起,next.config.ts 中的 withIntlayer() 仅凭 build.purge 和 build.minify 两个标志就会运行完整流水线 —— 清除、压缩和导入重写。
工作被分成两部分,因为 SWC Wasm 插件一次只转换一个文件,且无法访问文件系统:
在弹窗中打开表格以清晰地查看所有数据
| 阶段 | 运行位置 | 作用 |
|---|---|---|
| 使用分析 + JSON 清除/压缩 | Node,位于 withIntlayer() 内 | 读取每个组件源文件,重写 .intlayer/**/*.json,生成重命名表 |
源码重写(content.title → .a) | @intlayer/swc(Wasm) | 将重命名表应用到你代码中对应的属性访问 |
导入重写(useIntlayer → dict) | @intlayer/swc(Wasm) | 与 intlayerOptimizeBabelPlugin 相同 |
判断哪些字段未被使用以及每个字段获得什么别名,需要跨文件状态和文件 I/O,因此这一半在 Node 中运行;SWC 插件只接收生成的表。
各平台配置指南
Next.js
Next.js 需要 @intlayer/swc 插件,因为 Next.js 使用 SWC 进行构建。自 v9.2.1 起,这一个包即可覆盖整条流水线 —— 优化(导入重写)、清除和压缩。
该插件并未默认安装,因为 SWC 插件在 Next.js 当中目前仍处于实验阶段。未来这部分有可能会发生改变。
Next.js 16.1.0 是最低版本。 它是首个基于 SWC 向前兼容 Wasm 插件 ABI 构建的版本;更早的版本会拒绝该插件。withIntlayer 会读取你项目的 Next.js 版本,低于 16.1.0 时干脆不注册该插件 —— 这些构建仍会成功,只是在没有打包优化的情况下运行。
复制代码到剪贴板
复制代码到剪贴板
安装完毕后,Intlayer 将会自动侦测并使用该插件。
清除和压缩阶段(字段移除与字段重命名)不需要额外的包,也不需要 babel.config.js。用 withIntlayer 包裹你的配置,并在 intlayer.config.ts 中开启相应标志:
复制代码到剪贴板
复制代码到剪贴板
在 next build 期间,withIntlayer 会分析你的源码、重写已编译的字典,并将生成的字段重命名表传给 @intlayer/swc,由它更新你代码中对应的属性访问。
请使用异步的withIntlayer,而不是withIntlayerSync。同步版本不会运行分析流水线,因此清除和压缩对它没有效果。
清除和压缩仅在next build时运行 —— 优化流水线在next dev期间是关闭的。
当配置了兼容适配器调用方时它们也会被禁用(swcExtraCallers,由@intlayer/next-intl、@intlayer/react-i18next等兼容包设置):这些调用点对使用分析器不可见,因此清除会移除代码仍在读取的字段。导入重写仍保持启用。
更早的版本(9.2.1 之前) 需要 @intlayer/babel 以及一个声明 intlayerPurgeBabelPlugin 和 intlayerMinifyBabelPlugin 的 babel.config.js。该文件不再需要,可以删除。
Vite
Vite 使用了包含在 vite-intlayer 依赖当中的 @intlayer/babel 插件。整个优化管线 —— 包括导入重写、清除和压缩 —— 是默认开启的,且无需任何额外的插件注册。
在 intlayer.config.ts 里设定相对应的标志来开启 purge 以及 minify:
复制代码到剪贴板
Webpack (以及配置了 Babel 的 Next.js)
安装 @intlayer/babel:
复制代码到剪贴板
在 babel.config.js 里按照正确的顺序添加所有四个插件:
复制代码到剪贴板
配置选项
你可通过在你的 intlayer.config.ts 里的 build 属性来控制 Intlayer 怎样去优化你的代码包。
复制代码到剪贴板
在大多数情况之下,推荐为optimize保留它的默认值(undefined)。
请参阅配置参考资料以了解所有的选项:配置说明
构建选项
在弹窗中打开表格以清晰地查看所有数据
| 属性 | 类型 | 默认值 | 详细说明 |
|---|---|---|---|
optimize | boolean / undefined | undefined | 用于开启 import 语句的重写。undefined = 仅在打包成生产环境模式时生效。当设定为 false 的时候,同样会导致 purge 以及 minify 均一并被关闭。 |
minify | boolean | false | 用于对已编译好的 JSON 档案里的内容键名改写成短的单字母名称。同理也会一并将源代码里相关访问属性同样改名。仅当 optimize 为 false 时无效。 |
purge | boolean | false | 用于移除无论如何也不会被调用的且静态的源文件的内容字段,从 JSON 输出中去除掉。仅当 optimize 为 false 时无效。 |
压缩 / Minification (重命名字段键值)
build.minify 并非压缩你的 JavaScript —— 那是你的打包器应该处理的工作。它的工作,是把编译后的字典对应的 JSON 文件的每一个自定义内容的字段,全用短位的字母标识来替代,借此将其体积进行压缩:
复制代码到剪贴板
该重新命名的方法同样也会应用到那些处于代码里的属性名访问阶段,所以在最终编译好的结果里,content.title 便会演变为 content.a。它们在运行时的实际表现完全一致。
复制代码到剪贴板
当optimize为false时,压缩会被跳过。当editor.enabled为true时,压缩仍会运行,但不执行字段重命名步骤——可视化编辑器通过keyPath解析编辑内容,因此原始字段名称必须保留。
在 Next.js 上,当 @intlayer/swc 未安装或无法加载时(Next.js 低于 16.1.0),压缩同样会被跳过。重写源码访问的正是这个插件,因此在没有它的情况下重命名字典,会让你的代码读取已不存在的字段名。
同理,如果是利用了 importMode: 'fetch' 来载入字段时此过程同样不适用。因为它们的内容会以原始命名由后端 API 所提供,对客户端内容随意重命名会破坏客户端与服务端的匹配契约。
字段清除 / Purging (去掉未被引用的字段内容)
build.purge 能自动分析究竟哪些字段真真切切地被你的源代码所引用,进而只把实际引用过的数据予以保留,把其他的垃圾数据从编译生成的 JSON 里排除掉。
复制代码到剪贴板
示例说明: 我们有一个包含五个不同字段数据的字典,但是代码实际就用到了里边的俩:
复制代码到剪贴板
当optimize为false时,清除(Purge)会被跳过。当editor.enabled为true时,它仍保持启用——被清除的字段不会被任何组件读取,因此编辑器永远不会渲染它。在 Next.js 上,当@intlayer/swc不可用以及配置了兼容适配器调用方时,还会被额外跳过。
当检测到某份代码因为异常无法顺利解析、又或者当把由 useIntlayer 输出的值以静态解析器难以预测分析的模式在不同组件中来回丢(比如被打包成对象传入等而未被进行解构)的时候,它同样会跳过,以此保守地保留整部字典的全部信息,避免意外发生。
导入模式(Import Mode)
对于包含多个页面和地区规模比较大的应用程序来说,你的 JSON 可能会占去绝大一部分包(Bundle)的内容。所以,你可以凭借着 importMode 这个参数让 Intlayer 来调整对字典内容本身的实际拉取行为。
全局定义
该参数可以经由你本身的 intlayer.config.ts 这个文件在全局进行指定。
复制代码到剪贴板
为字典独立配置
我们也可以在这其中部分独立词典原本配置内的 .content.{{ts|tsx|js|jsx|mjs|cjs|json|jsonc|json5|md|mdx|yaml|yml}} 把该参数改写成另外想要的规则。
复制代码到剪贴板
在弹窗中打开表格以清晰地查看所有数据
| 属性 | 取值类型 | 默认设置 | 描述说明 |
|---|---|---|---|
importMode | 'static', 'dynamic', 'fetch' | 'static' | 已被弃用: 建议调整为 dictionary.importMode。这决定了应当怎样加载各项字典。(细节参考下文)。 |
这处 importMode 设置用于指挥组件拉取各项内容的真实手段。要么被指定在了全局 intlayer.config.ts 中的 dictionary 里,要不便以 content.ts 形式来分别定制。
1. 静态模式(Static Mode - default)
在这个默认设定下,Intlayer 自动把全部 useIntlayer 变成了 useDictionary 以做到将所需的翻译数据无缝拼接入 JavaScript 包内。
- 优势(Pros): 即时就能被加载(属于同步型),由于没有水合(hydration)从而意味着额外的零请求。
- 缺点(Cons): 但这个包同样也就塞满了给该组件用的各种不同可用语言的对应版本,这非常占空间。
- 最佳应用场合: 单页应用(SPA)。
代码在转换之后的图示(举例):
复制代码到剪贴板
2. 动态模式(Dynamic Mode)
在这一设定之下,Intlayer 自动把全部 useIntlayer 全给变成了 useDictionaryAsync。这个操作能将拉取行文变成动态 import()(这和 Suspense 的表现接近),并有针对性地对当地所在的特殊语言去单独异步请求其实际数据。
- 优势(Pros): 支持基于地区语言所实施的代码树抖动分离(Tree shaking)。 说白了就是,正在浏览英语界面的使用者仅仅会只被发送那部分英文对应的词典内容,法文或是其他国家的数据都不会加载。
- 缺点(Cons): 这个阶段下,将会让组件因数据要求而针对各项水合需求(hydration)各自产生不同种的调用申请(拉取各部件内容)。
- 最佳应用场合: 内容巨大且充斥了长文博客的复杂平台、亦或是本身涵盖了大量的翻译种类因而在包(bundle)体积极为严格的情况下使用。
代码在转换之后的图示(举例):
复制代码到剪贴板
使用了importMode: 'dynamic'这个方案之时,如果某一页刚好凑齐了 100 个包含了useIntlayer内容的部件时,浏览器就有极大的概率朝着服务器丢过去多达 100 种不重样的 fetch 请求。如果意在免于这样的连环「瀑布效应」,尝试尽可能去少写单个的独立.content(尝试着每几块凑一起合并起来比如每一个大的分块区共享其内容字典,而非是切得那么碎,比如连每一个单小按钮都设单独请求。)如果把多个带有单独名字.content内容给附加上相同的 Key 名称,程序依然能够很轻易将这些零散碎落的数据融合成单独庞大且统一的一本完整的字典对象里去。
3. Fetch 模式(Fetch Mode)
行为跟 Dynamic 有所重叠,不过最优先则是朝 Intlayer Live Sync API 请求其词典内容。假如获取数据时遭到拦截或者内容不属于实时的数据内容体系的话,接下来便将其作为兜底顺延递回之前的动切请求。
代码在转换之后的图示(举例):
复制代码到剪贴板
如果还需要获得对于 CMS 获取方面的认知的话:可以去查看 CMS 说明
此模式同样会一如既往地因为 JSON 内容会直接借由后端直接输送而不遭受 purge 跟 minify 等等这几类数据剔除方案的干扰。
概要: 静态 和 动态
在弹窗中打开表格以清晰地查看所有数据
| 模式特征 | 静态模式(Static Mode) | 动态模式(Dynamic Mode) |
|---|---|---|
| JS 产生的 Bundle 体积 | 庞大(包含了供各个不同组件使用时调用的其他全部外语语系信息) | 小巧(内容直接为空、只有代码框架) |
| 最初加载所需时间 | 极速(毕竟那些所需信息一开始都已经存在于包(Bundle)里面了) | 略需等待(拉取 JSON 所需消耗时间) |
| 附带发生的网络请求 | 无,无需任何等待直接 0 次 | 取决字典请求次数(一次 Key 取出就是一回) |
| 树抖动(Tree Shaking) | 按单一组件的级别而做分割 | 依组件级别外加依据地区与语种去实施拆分 |
| 最佳应用与方案环境 | 普通交互式元件内容或单一界面的轻型程序等 | 极其充满内容的纯文区块或极其繁多的语系 |
