lang属性必须显式设置在每个含文本的语义化容器上,data-i18n需覆盖所有可翻译属性,语言包须用fetch+try/catch按BCP 47规范加载并校验,含HTML文案须用innerHTML安全替换。

lang属性必须显式设置在每个语义化容器上,不是只改就够了
只给document.documentElement.lang赋值,其他元素不设lang,会导致屏幕阅读器读错语言、标点间距异常(比如中文顿号被当英文逗号处理)、字体回退失效(如<pre lang="bash">没设lang就可能用中文字体渲染代码)。
- 所有含文本的语义化标签——
<h1>、<p>、<section>、<article>、<li>等——都应带lang属性,值与当前激活语言包一致(如zh-HK、en-US) - 已有
lang的内联元素(如<pre lang="bash">)切换语言时不能覆盖,这是多语言混排的合法场景 -
<script>和<style>里加lang无效,别写
data-i18n标记必须覆盖所有可翻译属性,不只是textContent
只给<button>提交</button>加data-i18n="btn_submit",却漏掉placeholder、title、aria-label,结果输入框提示还是英文,辅助技术无法同步播报。
- 每个要翻译的元素至少有1个
data-i18n基础键 - 含
placeholder需额外加data-i18n-placeholder - 同理:用
data-i18n-title、data-i18n-alt、data-i18n-aria-label -
value属性一般不翻译(属用户输入数据),但<label>内的文本必须标记,且for必须与对应id严格匹配
语言包加载必须用fetch+try/catch,不能硬编码或内联JSON
把语言包直接写进JS文件,会导致构建体积膨胀、无法按需加载、热更新困难;用XMLHttpRequest又容易忽略HTTP状态码和MIME类型校验,遇到404或服务器返回text/plain时静默失败。
- 路径统一为
./locales/${lang}.json,例如./locales/zh-HK.json、./locales/en-US.json - 必须用
fetch()加载,外层包try/catch - 内部检查
response.ok和response.headers.get('content-type')?.includes('application/json') - fallback顺序:先试完整BCP 47码(如
zh-HK),再截主语言(zh)
含HTML结构的文案必须用innerHTML替换,且语言包里存可信纯HTML
像“请阅读使用条款”这种带标签的文案,如果只替换textContent,链接会丢失;若直接插入未校验的HTML字符串,又可能触发XSS。
立即学习“前端免费学习笔记(深入)”;
- 语言包中对应字段值必须是可信的纯HTML片段(如
"请阅读<a href='https://www.php.cn/link/07fd2295ead5c4d45892fe3ab22a846a'>使用条款</a>") - 替换时必须用
element.innerHTML = translatedHtml,不能用textContent - 动态插入的DOM(弹窗、AJAX表格行)插入后必须立即调用翻译函数,不会自动监听
真正难的不是写翻译键,而是让每个lang、每个data-i18n-* 、每次innerHTML替换都落在正确位置且不被遗漏——尤其在多人协作或老项目迭代时,漏一处,无障碍体验就断一环。



















