多语网页
这一篇文章将讨论几种创建多语网页的方法,并给出优缺点。
网站开发, 多语言, Accept-Language, Navigator.language, Alternative language, SEO, HTML, lang, hreflang, 自定义HTML元素, HTML dataset
--by Captdam @ Feb 2, 2026Index
因为我自己是使用英语和中文的,所以我在写博客时也会上传两种语言的版本。
客户端翻译器
最简单的方法就是在客户端运行翻译软件来翻译网页。比如说,可以使用谷歌翻译。下面的截图就展示了谷歌翻译将一个英文页面翻译为中文:
另外,现在大部分浏览器都内置了翻译功能。当一个网页的语言和浏览器中用户设置的偏好语言不同时,浏览器就会提示用户是否使用翻译功能。下面的截图就展示了火狐浏览器内置翻译器将一个英文页面翻译为中文:
优点
这样做的优点是网站开发者不需要考虑多语言支持,内容作者也只需要以自己使用的语言编写内容。
缺点
翻译并不完美。
- 黑话翻译不准确。
- 会错误地把符号、颜文字等翻译。
- 专有名词的翻译被翻译成其它意思。
- 错别字会被错误翻译。
我个人而言不会让翻译器来代替我翻译网页,因为我的网页主要包含技术类内容,涉及大量专有名词。因此,我会自己翻译我的文章(当然,也可以用翻译器作为初稿,但是必须要人工检查复核)。这不仅能确保翻译的准确性,也让我可以再审自己的文章并修改错误或不明的地方。
服务端语言检测
大多数情况下,浏览器在发送HTTP请求时都将包含Accept-Language头,以表示访客偏好的语言。具体可以参考MDN的文档。
让我们用火狐浏览器为例:
例如,我的浏览器将发送Accept-Language: en-US,en;q=0.5,意思是我偏好美式英语的网页。如果美式英语不可用,只要是英语就行(所以,英式英语、澳式英语也行,虽然我不说也不熟悉它们的拼写和俚语,但是能看明白)。
我可以添加中文作为我的第二语言。于是,我的浏览器将发送Accept-Language: en-US,en;q=0.7,zh-CN;q=0.3,也就是说我将最偏好美式英语,其次是英语,再然后是中文。
在服务器端,服务器应该提供其所有的语言中用户最偏好的。如果服务器没有用户接受的炎炎的内容,服务器可以选择一种服务器上已有的语言提供(我的话会选择我编写文章时使用的语言)。然后,访客可以在客户端进行翻译。
比如果,我会手动创建一篇文章的英文与中文两个版本。英语和中文的读者可以直接获得我手动编写的版本之一,其内容将是准确的。对于使用其它语言的读者,例如法语和日语,服务器上没有他们的偏好语言的版本。于是,他们将获得该文章的英文版或日语版。他们将可以使用翻译器来阅读该文章。另外,现在的浏览器已经可以几乎无缝翻译文章了,虽然准确度存疑,但也是没有办法的办法了。
请求也可能不包含Accept-Language头。在HTTP规定中,Accept-Language并不是强制的。所以,有时没有这个头的情况是会发生的,我们可以随便选一个语言。
优点
服务端语言检测可以提供网页的最合适的语言版本给访客。服务器将在已有的一些列审阅过的内容中选取最合适一个语言。如果没有用户想要的语言,就倒退到客户端翻译器。
缺点
服务端语言检测最大的缺点就是非一致性。使用不同的浏览器将可能发送不同的Accept-Language头,会造成一些误解。比如:
- 张三(只说普通话)发了一个链接给李四(说粤语和普通话)。服务向张三提供了普通话的版本,但是向李四提供了粤语的版本。但是,张三本以为李四看到的也是普通话的版本。
- 李四在家里用自己电脑看了一半文章(粤语)。后来他在公司摸鱼时决定用公司电脑(只接受普通话)继续看,结果他找不到在家看到什么地方了,因为网页的语言变了。
我的观点是,访客应该有权选择自己想要看到的内容的语言,例如可以用按键切换语言,而不是让服务器以一种“家长式”的方式决定访客看到的内容。虽然服务器使用的是客户端发送的Accept-Language头来决定语言,但是大部分人都不知道浏览器里面还有这个设置。
使用着陆页
服务端语言检测可以用来创建着陆页。
例如,当访客打开一个URL为link/to/this/article的页面时,服务器将根据Accept-Language头确定访客偏好语言。接下来,根据方才确定的偏好语言来跳转到该页面的英文版本的的link/to/this/article/en或中文的link/to/this/article/zh。
正如URL所示,link/to/this/article/en包含英文内容的网页,link/to/this/article/zh包含中文内容的网页。服务端语言检测应该只存在于link/to/this/article,link/to/this/article/en和link/to/this/article/zh上将不再进行语言检测。这样,当访客分享链接时,或是切换浏览器时,他们将钟时看到相同的页面。
客户端语言检测 - HTML Dataset
假如,现在我们已经有同一篇文章的原版的翻译版,接下来该如何操作?方法之一就是以原版编写HTML网页,并将翻译版的内容放在HTML dataset中。就像是下面这个英文原版中文翻译版的例子所示:
<span class="example_htmldataset" data-lang-en="Hello world" data-lang-zh="你好世界">Hello world</span>
默认情况下,当网页加载完成时,只有原版(例子中的英文)会被显示出来。
可以使用一个按键来让访客切换语言。这个按键将使用HTML dataset中的内容来替换显示内容,参考下面代码:
function example_htmldataset(lang) {
document.querySelectorAll('.example_htmldataset').forEach(element => {
element.textContent = element.dataset['lang'+lang];
});
}
example_htmldataset('Zh');
下面是一个实例:
另外,在页面加载完成后我们可以通过浏览器(user agent)信息来确定偏好语言。和Accept-Language HTTP头类似,客户端执行的JavaScript可以通过navigator.language或navigator.languages这两个值来获取访客偏好语言。
优点
客户端语言检测允许访客在一系列已有的语言中选择偏好的一种。
缺点
客户端语言检测将下载不仅时偏好的语言,而是所有的可选的语言。这对于网络流量带宽的网页加载速度都不友好。
搜索引擎可能只会收录默认的语言,因为爬虫只能爬取文字内容(默认语言)。HTML dataset可以包含任意数据,因此,爬虫将忽略保存在HTML dataset中的其它语言内容。另外,这个方案需要JavaScript的参与,爬虫也不一定会执行或理解JS。
客户端语言检测 - 自定义HTML元素
和dataset相似,我们也可以使用自定义HTML元素来保存文章的其它语言的版本。例如,我们可以使用:
<span class="example_htmlelements">
<lang-zh>你好世界</lang-zh>
<lang-en>Hello world</lang-en>
</span>
当然,我们在使用自定义HTML元素前需要向浏览器注册它们,以便浏览器理解。
(() => {
class LangZhElement extends HTMLElement { constructor() { super(); } }
class LangEnElement extends HTMLElement { constructor() { super(); } }
customElements.define('lang-zh', LangZhElement);
customElements.define('lang-en', LangEnElement);
})();
通过CSS隐藏除我们想要显示的那一种语言之外的其它所有语言的方式来只显示文章的一种语言,如下;(注意我们将需要两个<style>元素,第一个用来隐藏所有语言,第二个用来显示所需的那种语言)
<style>
.example_htmlelements lang-en,.example_htmlelements lang-zh { display: none; }
</style>
<style>
.example_htmlelements lang-en { display: inline; }
</style>
可以使用一个按键来让访客切换语言。这个按键将第二个<style>元素的内容,参考下面代码:
function example_htmlelements(lang) {
document.querySelectorAll('#example_htmlelements style')[1].innerHTML = '.example_htmlelements lang-'+lang+' { display: inline; }'
}
example_htmlelements('zh');
下面是一个实例:
优点
客户端语言检测允许访客在一系列已有的语言中选择偏好的一种。(和HTML dataset方案相同)
缺点
客户端语言检测将下载不仅时偏好的语言,而是所有的可选的语言。这对于网络流量带宽的网页加载速度都不友好。(和HTML dataset方案相同)
别指望爬虫能理解自定义HTML元素。它们可能忽略文章的所有内容,因为它们无法理解自定义HTML元素,最后得到一张空白网页。它们也可能收录一张糅杂了所有语言的版本,因为自定义HTML元素被当成<span>理解了。
一种语言一个URL
这是我倾向于使用的方案。
在这个方案中,我们将为我们的文章的不同语言的版本都各自创建一个网页。
对于中文版,我们创建一个URL为earth/zh的网页,内容如下:
<html><head>
<title>地球</title>
</head><body>
<p>地球是个球。</p>
<a href="/earth/en">see English</a>
</body></html>
对于英文版,我们创建一个URL为earth/en的网页,内容如下:
<html><head>
<title>Earth</title>
</head><body>
<p>Earth is not flat.</p>
<a href="/earth/zh">切换中文</a>
</body></html>
当访客打开该多语言网页中的其中一个时,他们将以特定语言看到该文章。如果访客想要切换到另一种语言且那个语言可用时,访客可以点击该链接来跳转到包含该语言的网页。
示例?这篇文章就是一个实例!参考本页面右上角的“EN”和“中”按钮。
搜索引擎优化
对于这个方案,搜索引擎可以为使用不同语言的用户收录相应的语言的版本。例如,当英文用户搜索“earth”时,搜索引擎就可以返回“earth/en”这个结果,当中文用户搜索“地球”时,搜索引擎就可以返回“earth/zh”这个结果。
现在,搜索引擎将认为这两个网页是独立的、毫不相干的页面。要让搜索引擎知道这两个网页是同一篇文章的不同语言的版本,我们可以添加:
<html lang="zh"><head>
<title>地球</title>
<link rel="alternate" hreflang="en" href="/earth/en" type="text/html" />
<link rel="alternate" hreflang=”zh" href="/earth/zh" type="text/html" />
</head><body>
<p>地球是个球。</p>
<a hreflang="en" href="/earth/en">see English</a>
</body></html>
<html lang="en"><head>
<title>Earth</title>
<link rel="alternate" hreflang="en" href="/earth/en" type="text/html" />
<link rel="alternate" hreflang=”zh" href="/earth/zh" type="text/html" />
</head><body>
<p>Earth is not flat.</p>
<a hreflang="zh" href="/earth/zh">切换中文</a>
</body></html>
<html>中的lang="en"表示了该页面的语言。<link rel="alternate" hreflang="en" href="/earth/en" type="text/html" />元素据告诉搜索引擎该页面的其它语言的版本的地址。<a>中的hreflang="zh"表示该链接目标的语言。
优点
这个方案简单有效。
这个方案将一篇文章的不同语言的版本分别放入不同的网页内,使用不同的URL。在页面层面上,它不再有“多语言”这个概念,而是靠<link rel="alternate" hreflang="en" href="/earth/en" type="text/html" />元素据和<a>链接来创建不同语言的版本的URL之间的联系。这极大简化了网站设计。
不需要借助JavaScript或自定义HTML元素,对爬虫友好。
这个方案确保了一致性,因为不使用服务端或客户端语言检测。当用户打开一个URL,页面内容保证相同,因为同一个URL只可能包含一种语言。
缺点
作为作者需要在不同文件中编写一篇文章的不同语言版本。一些作者(比如我)在编写文章时需要好几个文件来回看,很不方便。