必须在标签上用bcp 47格式(如zh-cn)声明lang属性,且须在其前;局部多语言内容需显式标注lang,并同步更新所有带lang和data-i18n-*属性的元素,否则屏幕阅读器误读、标点错乱、seo失效。

HTML模板引擎本身不解决国际化问题,它只是把语言数据塞进结构的工具;真正在工程中起决定作用的是语言包组织方式、lang属性同步机制和动态节点处理逻辑。
为什么不能只靠模板引擎做i18n
常见错误现象:用EJS或Pug在服务端渲染时写<h1></h1>,但没配好fallback语言、没处理placeholder、没同步lang属性——结果页面文字变了,屏幕阅读器仍读英文,标点间距错乱,SEO抓取不到多语言内容。
- 模板引擎只管“填空”,不管
lang属性是否合法(必须是zh-CN,不是zh_CN或chinese) - 它不自动处理
data-i18n-placeholder这类带后缀的属性,得手动扩展helper函数 - 静态生成时若语言包缺失某个key,模板可能报错或留空,而客户端JS方案至少能fallback到默认语言
- SSR场景下,若Accept-Language解析不准(比如安卓WebView返回空),模板拿到的
lang值就是错的,整个页面语言基调就崩了
模板引擎配合data-i18n标记的混合方案
在已有静态HTML基础上加轻量JS,比全量重写模板更可控。关键不是选哪个引擎,而是怎么让data-i18n在服务端和客户端行为一致。
- 服务端渲染时,用模板引擎输出
<h1 data-i18n="home.title">首页</h1>,同时设 - 客户端初始化时,优先读
document.documentElement.lang,再加载对应./locales/zh-CN.json,遍历所有data-i18n元素填充 - 对
input、img等需更新属性的节点,必须显式写data-i18n-placeholder、data-i18n-alt,模板里也要同步输出这些属性 - 避免在模板里写
<script>document.querySelector(...).innerText = ...</script>——这破坏了数据驱动原则,也绕过了lang同步逻辑
lang属性必须在之后、html>标签内声明
这是最容易被忽略的底层陷阱:如果<meta charset="UTF-8">写在后面,Chrome可能把lang值本身解码成乱码,导致后续所有依赖lang的行为失效——包括字体回退、标点间距、甚至fetch请求的Accept-Language头生成。
- 正确顺序:
<meta charset="UTF-8">必须在文档前1024字节内,且在开始标签之前 - SSR模板里别用
>直接插值,先确保lang已标准化为BCP 47格式(如zh-Hans而非zh_CN) - 静态站点每个语言版本单独生成HTML时,
要硬编码,不能靠JS注入——否则首屏闪动、SEO丢失 - 检查方法:打开Elements面板,右键
→ “Edit as HTML”,确认lang值是可读的纯ASCII字符串
真正卡住工程进度的,从来不是选哪个模板引擎,而是lang属性有没有在正确位置、用正确格式、同步到每一个该出现的地方。一个lang="zh"写成lang="zh "(末尾空格),就足以让iOS VoiceOver降级为英语朗读。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











