HTML本身不支持国际化分包,所谓“分包”本质是语言资源的按需加载策略,必须由JS模块系统驱动;应按语言(en.json、zh.json)或功能模块(common.json、form.json)拆分JSON文件,禁用key级拆分与动态路径拼接,避免全量下载与lookup失败。

HTML 本身不支持国际化分包,所谓“分包”本质是语言资源的按需加载策略,必须由 JS 模块系统驱动,且要避开常见路径陷阱。
language.json 怎么拆成多个 chunk
别直接把所有语言塞进一个 locales.json——它会随任意语言切换全量下载。真正可拆的只有语言包文件本身,但拆法取决于加载时机:
- 按语言拆:每个语言一个 JSON 文件(
en.json、zh.json),配合import()动态加载,Webpack/Vite 能自动为每个生成独立 chunk - 按模块拆:把
common.json、form.json、error.json分开,再用Promise.all([import('./locales/common.json'), import('./locales/form.json')])并行拉取 - 禁止按 key 拆:比如把
"home.title"和"home.subtitle"放不同文件——key 是逻辑单元,不是加载单元,会导致 lookup 失败或重复请求
注意:import('./locales/' + lang + '.json') 这种拼接写法会让构建工具无法静态分析,最终 fallback 到全量打包;必须写成 import('./locales/en.json') 或用 require.context 配合白名单。
data-i18n 元素怎么避免重渲染闪动
语言切换时遍历所有 data-i18n 并更新 textContent 是安全的,但若页面含大量动态组件(如 React 子树、自定义元素),直接改 DOM 会破坏状态。关键在局部刷新范围控制:
立即学习“前端免费学习笔记(深入)”;
- 只更新带
data-i18n的元素,跳过已挂载的第三方组件容器(如div#chart-container) - 对含 HTML 结构的翻译项(如带
<strong>的提示语),用innerHTML前必须过滤:通过DOMPurify.sanitize()或白名单标签校验,否则易引入 XSS - 切换后同步调用
Intl.DateTimeFormat和Intl.NumberFormat重建实例,旧实例不会自动更新格式 - 滚动位置和表单输入值不丢失,前提是不触发
window.location.reload()或整页innerHTML替换
服务端怎么和前端共享 locale 检测逻辑
纯前端靠 navigator.language 不可靠,SSR 场景下更会首屏错乱。稳定方案是服务端注入 + 前端 fallback:
- Node.js 后端用
accepts库解析Accept-Language请求头,得出最匹配语言(如zh-CN→zh),写入 HTML 的<html lang="zh">和全局变量window.__LOCALE__ = 'zh' - 前端初始化时优先读
document.documentElement.lang,没值再查window.__LOCALE__,最后 fallback 到navigator.language - 用户手动切换语言时,发 POST 到
/api/locale同步服务端偏好,避免下次访问又回退到浏览器默认值 - localStorage 只作临时缓存,不用于覆盖服务端判断——新设备、隐私模式下该值为空或过期
真正难的是语言包与组件生命周期的耦合:Custom Element 的 connectedCallback 里如果依赖翻译结果,就必须等语言加载完成再渲染;而 import('./locales/zh.json') 是异步的,得用 customElements.define('my-card', class extends HTMLElement { ... }) 配合内部 Promise 状态管理,这点容易被忽略。



















