多语言项目可通过自定义打包插件剔除未使用的语言包以减小体积。插件在构建阶段静态分析 locale 路径,仅保留白名单语言,支持 Vite(resolveId 钩子)和 Webpack(NormalModuleReplacementPlugin),并可进阶结合 AST 扫描自动推断语言标识。
在多语言项目中,构建产物常因引入完整语言包(如 moment、ant-design、element-plus 的 locale 文件)而体积膨胀。自定义打包插件可在构建阶段静态分析并剔除未使用的语言包代码,无需手动维护,效果直接且可复用。
多数 UI 库或工具库将语言包组织为独立模块,路径有明显规律,例如:
antd/es/locale/zh_CN.js、antd/es/locale/en_US.js
element-plus/lib/locale/lang/zh-cn.js、en.js
dayjs/locale/zh-cn.js、ja.js
插件需监听这些路径的导入语句,在依赖图生成阶段捕获实际被引用的语言包,其余视为冗余。
Vite 插件可通过 transform 或 resolveId 钩子介入模块解析。推荐使用 resolveId 拦截 locale 路径,仅保留白名单语言:
export default function localePrunePlugin(options = {}) { const { include = ['zh-CN', 'zh-cn', 'en-US', 'en-us'] } = options; const validLocales = new Set(include.map(s => s.toLowerCase())); return { name: 'locale-prune', resolveId(id) { // 匹配常见 locale 路径模式 const localeMatch = id.match(//locale/(?:lang/)?([a-z_-]+).js$/i); if (localeMatch) { const lang = localeMatch[1].toLowerCase(); if (!validLocales.has(lang)) { return { id: ' virtual:empty-locale', external: true }; } } }, load(id) { if (id === ' virtual:empty-locale') return 'export default {};'; } };}
在 vite.config.ts 中启用:
import localePrune from './plugins/locale-prune';export default defineConfig({ plugins: [localePrune({ include: ['zh-cn', 'en-us'] })],});
Webpack 更适合用 NormalModuleReplacementPlugin 在模块解析时重定向:
const webpack = require('webpack');module.exports = { plugins: [ new webpack.NormalModuleReplacementPlugin( //locale/(?:lang/)?([a-z_-]+).js$/i, (resource) => { const lang = resource.request.match(//locale/(?:lang/)?([a-z_-]+).js$/i)?.[1]?.toLowerCase(); if (lang && !['zh-cn', 'en-us'].includes(lang)) { resource.request = path.resolve(__dirname, 'src/stubs/empty-locale.js'); } } ) ]};
确保 src/stubs/empty-locale.js 存在且导出空对象或兼容接口,避免运行时报错。
若项目通过 import locale from 'xxx/locale/zh-CN' 显式引入,可进一步在 transform 阶段扫描 AST,提取所有字面量语言标识,动态构建白名单。例如用 @babel/parser 解析源码,查找 CallExpression 或 ImportDeclaration 中匹配 /locale/[a-z_-]+/i 的字符串,汇总后用于后续剔除逻辑。这种方式无需硬编码 include,更适合大型多语言切换项目。