静态页面国际化无感切换的关键是不重载、不闪动、不丢状态,需全面覆盖data-i18n标记(含placeholder/title/alt/option/SVG等),同步更新所有lang属性,缓存并恢复滚动位置,严格使用BCP 47语言码,fetch失败时提供多级fallback,并手动翻译动态插入内容。

静态页面实现国际化无感切换,关键不是“换语言”,而是“不重载、不闪动、不丢状态”。只要避开 reload()、innerHTML 全量替换、只改根 lang 这三类典型错误,就能做到真正无感。
data-i18n 标记必须覆盖所有可翻译节点
只给 <h1> 和 <p> 加 data-i18n 是不够的。placeholder、title、alt、<option> 文字、SVG <text> 都要显式标记,否则切换后这些地方会留空或保持旧语言。
-
<input data-i18n-placeholder="search_hint">才能更新 placeholder;data-i18n本身不会管这个属性 -
<select data-i18n="country_list"><option>中国</option></select>不生效——得遍历option,查语言包里"country_list.options"数组 -
<img alt="logo" data-i18n-alt="logo_desc">才能同步 alt 文本 -
<svg><text data-i18n="download">下载</text></svg>需单独识别SVGTextElement类型并调用textContent - 别在
<script>、<style>、<pre>里加data-i18n——它们不参与文本渲染,JS 替换无效
切换时必须同步更新所有 lang 属性
只设 document.documentElement.lang = 'en',对已存在的 <p lang="zh"> 或 <blockquote lang="ja"> 完全没影响。屏幕阅读器、字体回退、标点间距都按元素自身 lang 值决定,不是继承来的。
- 切换前先缓存当前滚动位置:
const scrollY = window.scrollY - 更新完所有
data-i18n节点后,再遍历所有带lang属性的元素(除<html>外),把它们的lang值也设为新语言码 - 最后立刻执行
window.scrollTo(0, scrollY),否则页面跳回顶部 - BCP 47 必须严格:用
zh-Hans,别用zh_CN或chinese,否则Intl和部分浏览器会 fallback 失败
fetch 语言包失败必须有硬编码 fallback
fetch('./locales/en.json') 报 TypeError: Failed to fetch 或返回 404,是常态。网络抖动、路径写错、CORS、甚至本地双击打开 file:// 协议都会触发。没兜底=整页文案变键名,用户看到 home.title 这种裸字符串。
立即学习“前端免费学习笔记(深入)”;
- 用
try/catch包住fetch(),捕获网络层错误 - 再用
response.ok判断 HTTP 状态是否为 2xx - fallback 顺序:先试完整语言码(如
zh-HK),再截主语言(zh),最后落到内置默认对象,例如{ "header_title": "Welcome", "submit": "Submit" } - 别依赖
localStorage存“上次选的语言”来决定首次加载——用户可能清过缓存,或在新设备打开,此时navigator.language更可靠
动态插入内容必须手动触发翻译
弹窗、AJAX 表格行、Toast 提示、document.createElement 新增的节点,都不会自动响应 data-i18n。等 DOM 插入完成,必须立即调用翻译函数,否则文字永远是英文或空。
- 封装一个
t(key, options?)函数,内部查当前激活语言包,支持插值(如"search.placeholder": "Search {type}") - 动态创建按钮时:
btn.textContent = t('common.cancel') - 用
innerHTML插入含 HTML 的文案(如"terms_link": "请阅读<a href="https://www.php.cn/link/07fd2295ead5c4d45892fe3ab22a846a">使用条款</a>")时,确保语言包值是可信纯 HTML 片段,否则有 XSS 风险 - 若用了
Intl.DateTimeFormat或Intl.NumberFormat,切换语言后必须重建实例,旧对象不会自动更新格式
最容易被忽略的是:局部多语言内容(比如英文术语、代码块、引文)必须显式写 lang 属性,且不能跟着主语言一起切——否则一段 <pre lang="bash"> 被改成 lang="en",语法高亮和屏幕阅读器就全乱了。



















