lang属性必须显式写在每个含文本的语义标签上,仅改document.documentElement.lang无效;所有含文本的语义化标签(如<h1>、<p>等)须显式设置lang值,特殊元素(如<pre lang="bash">)保留原lang;data-i18n默认只处理textContent,placeholder、alt等需加后缀;动态插入DOM须手动触发翻译;语义化结构可降低i18n维护成本。

lang 属性必须显式写在每个含文本的语义标签上
只改 document.documentElement.lang 不会触发任何文本更新,也不会影响标点、字体回退或屏幕阅读器行为。浏览器和辅助技术是按**每个元素自身的 lang 属性**来决定渲染与朗读方式的,不继承父级。
常见错误:给 <html lang="en"> 切换为 zh-Hans 后,<p> 里的顿号仍按英文间距排版,<pre lang="bash"> 里的代码被中文字体覆盖,<img alt="logo"> 的替代文本仍被读作英文。
- 所有含文本的语义化标签(
<h1>、<p>、<section>、<footer>、<article>、<aside>)都必须显式加lang,值与当前语言包一致(如lang="zh-Hans") - 已有
lang的特殊元素(如<pre lang="bash">、<code lang="sql">)保留原值,这是合法的多语言混排场景 -
<script>和<style>内部不要写lang,它们不参与文本渲染
data-i18n 只处理 textContent,其他属性需显式后缀
data-i18n 是常见翻译标记,但它默认只替换元素的 textContent,对 placeholder、alt、title、aria-label 等完全无效——切换语言后这些字段还是原始值。
典型问题:<input placeholder="Search"> 文字不变;<img alt="user avatar"> 的替代文本没更新;<label for="email">Email</label> 翻译了但 for 属性未同步,点击 label 失效。
立即学习“前端免费学习笔记(深入)”;
- 必须显式加对应后缀:如
data-i18n-placeholder="search_hint"、data-i18n-alt="avatar_desc"、data-i18n-title="tooltip_info" -
value属性一般不翻译(属于用户输入数据),跳过处理;但<button>和<input type="submit">的显示文案建议统一用textContent更新,避免value被意外提交 - 含 HTML 结构的文案(如“请阅读服务条款”)必须用
innerHTML替换,且语言包里对应值要是可信纯 HTML 片段(不能带用户输入、不执行 JS),否则有 XSS 风险
动态插入的 DOM 必须手动触发翻译
AJAX 加载的弹窗、分页表格新行、懒加载模块插入后,里面的 data-i18n 标记不会自动生效。它们只是字符串,不是已翻译内容。
例如:<modal> 点击打开后,内部 <h2 data-i18n="modal_title"> 仍显示键名;分页表格每页请求新 <tr>,新行里的 data-i18n 保持原始键名。
- 插入新节点后,必须立即调用翻译函数遍历并替换,比如
i18n.translate(element)或applyI18n(element) - 不要依赖 MutationObserver 自动监听——它无法识别哪些属性需要翻译,也无法处理
innerHTML中嵌套的data-i18n - 如果使用框架(如 React/Vue),需在组件挂载后主动触发翻译,而非仅靠初始渲染
语义化结构本身降低 i18n 维护成本
用 <nav> 替代 <div class="nav">、用 <main> 区分主体、用 <time datetime="2024-05-20"> 标记时间,这些做法看似和翻译无关,实则大幅减少维护歧义。
比如翻译时发现一个 <div class="header"> 里混着 logo、导航、搜索框、语言切换按钮,很难判断哪些该翻、哪些不该翻;而 <nav> 明确标识导航区域,<header> 下的 <img> 通常不译,逻辑一目了然。
- 语义标签让翻译范围更可控:只有
<nav>里的链接文本、<main>里的段落才需翻译,<code>和<pre>默认跳过 - 屏幕阅读器能按语义结构朗读,避免把“© 2024”读成中文年份却把“Terms of Service”读成英文,这种错位比不翻译更伤体验
- SEO 和可访问性提升间接降低 i18n 测试成本——搜索引擎和辅助工具更容易识别出未翻译区块
lang 当成全局开关来用。这两点导致的问题往往要到上线后才暴露,且难以复现。



















