data-i18n 属性需显式添加到每个可翻译节点,不继承不冒泡;lang 属性须手动绑定至语义化标签,不依赖继承;动态插入节点需主动触发翻译;语言包加载须校验 mime 类型并设 fallback。

data-i18n 属性必须显式打在每个可翻译节点上
HTML 组件本身不带语言上下文,data-i18n 不会自动冒泡或继承。哪怕你把整个 <my-card></my-card> 组件标记了 data-i18n="card_title",它内部的 <h2></h2>、<p></p>、<input placeholder> 依然得各自加对应属性,否则不会被翻译函数识别。
常见错误现象:
-
<my-form></my-form>标了data-i18n="form",但里面<label></label>和<input placeholder>没单独标 → 提示文字仍是英文 -
<nav-bar></nav-bar>切换语言后菜单项翻了,但右上角用户头像的alt还是 “User avatar” → 因为漏了data-i18n-alt="user_avatar"
实操建议:
- 每个含文本的子节点(
<h2></h2>、<span></span>、<option></option>)都需独立加data-i18n,值为语言包键名 - 可翻译属性要逐个覆盖:
data-i18n-placeholder、data-i18n-title、data-i18n-alt、data-i18n-aria-label -
value属性通常不翻译(属用户输入态),但textContent和innerHTML必须处理;含 HTML 结构的文案(如“请阅读服务条款”)需确保语言包值是可信 HTML 片段
Web Components 里 lang 属性不能靠继承
Custom Element 如 <product-card></product-card> 渲染后生成 Shadow DOM,其内部元素默认不继承 document.documentElement.lang。浏览器和屏幕阅读器只看元素自身的 lang,不是父级或根节点的。
这意味着:即使你执行了 document.documentElement.lang = 'ja',组件内未显式设 lang 的 <p></p> 仍按默认语言渲染标点、字体回退、连字规则——顿号宽度错、代码块字体被中文字体覆盖、<title></title> 被读成中文音。
实操建议:
- 组件模板中所有含文本的语义化标签(
<h3></h3>、<p></p>、<section></section>)都应绑定lang属性,值来自组件接收的langprop 或全局语言状态 - 已有明确语言意图的子元素(如
<pre class="brush:php;toolbar:false;" lang="bash"></pre>、<code lang="sql">)保留原lang,不随组件语言切换而覆盖 - 不要给
<script></script>或<style></style>加lang—— 它们不参与文本渲染,无效
动态插入的组件必须手动触发翻译
Web Components 实例化、customElements.define() 注册、或通过 JS 动态 appendChild() 插入的节点,不会被初始翻译函数扫描到。DOM 插入完成 ≠ 文本已本地化。
典型场景:
- 点击按钮弹出
<modal-dialog></modal-dialog>,内部<h2 data-i18n="modal_title"></h2>显示原始键名而非翻译文本 - 分页表格用
template.content.cloneNode(true)批量生成新行,新<tr> 里的 <code>data-i18n属性没生效实操建议:
- 组件挂载完成后(如
connectedCallback中),调用统一翻译函数(如i18n.translate(this.shadowRoot || this)) - 若使用第三方 i18n 库(如 i18next),确保其
init后注册了 DOM 变更监听,或主动调用translate()方法传入新节点范围 - 避免在
render()中直接拼接翻译结果字符串——破坏 SSR 兼容性,且无法响应语言切换
模块化工程中语言包加载与 fallback 必须可控
把语言包硬编码进 JS bundle 或写死在 HTML 中,会导致构建体积膨胀、无法按需加载、热更新失效。而用
fetch('./locales/zh-CN.json')却不校验响应类型,遇到服务器返回text/plainMIME 或 404 时,response.json()会静默失败或抛错,页面留白。实操建议:
- 语言包路径严格按 BCP 47 规范:优先请求
./locales/zh-HK.json,失败后降级为./locales/zh.json,最终 fallback 到内置最小英文对象(非空字符串,避免undefined) - fetch 必须包裹
try/catch,检查response.ok和response.headers.get('content-type')?.includes('application/json') - 模块打包时,避免将全部语言包打进主 bundle;可用
import(`./locales/${lang}.json`)动态导入,Webpack 会自动 code split - 服务端返回 JSON 时,响应头必须含
Content-Type: application/json,否则前端解析可能失败
复杂点在于:组件化 + 模块化 + 国际化三者交叠时,lang 状态管理、翻译时机、DOM 生命周期、资源加载链路必须对齐。漏掉任意一环,就会出现局部文本没翻、标点错位、辅助技术播报异常——这些都不是“整体切语言”能兜住的。
- 组件挂载完成后(如
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











