多语言官网可以复用组件和产品事实,同时按市场调整内容模块。前端需要让这些差异落到独立页面与可用的切换路径上,而不只是给导航加一个语言下拉框。
这里用静态页面演示同一产品的三个英语版本。它是虚构教学站点,没有真实销售、询盘或排名数据。美国版突出工程资料、英国版突出渠道资料,只是人为配置的两种业务任务,不代表国家偏好。
先确定"同一页",再设计语言切换
切换语言时跳回首页,通常意味着系统只有全站语言列表,没有当前内容的版本映射。产品页应先确定内容编号,再找这个内容实际存在的语言或地区版本。
一篇市场活动文章没有另一个版本时,不应为了凑全语言菜单链接到一个无关页面。界面可以说明该内容暂未提供其他语言,并另给站点导航;不能让用户以为已经切到了同一内容。
因此,页面列表应该存储 locale、URL 和 contentId。语言与市场可以另有业务映射,URL 结构则保持稳定。
用一个小脚本生成可直接打开的页面
以下代码保存为 build.mjs,用 node build.mjs 运行。不需要框架或构建依赖;在 dist 目录通过静态服务器访问。HTML 中 example.com 是示意域名,部署时必须换成实际站点 origin。
js
import fs from 'node:fs/promises';
import path from 'node:path';
const origin = 'https://example.com';
const product = {id: 'sensor-01', model: 'S-01', voltage: '24 V DC'};
const variants = [
{locale: 'en', segment: 'en', proof: 'Confirm service scope'},
{locale: 'en-US', segment: 'en-us', proof: 'Request integration notes'},
{locale: 'en-GB', segment: 'en-gb', proof: 'Request distribution documents'}
].map(v => ({...v, contentId: product.id,
url: `/${v.segment}/products/${product.id}/`}));
// 输入为本脚本固定教学数据;接 CMS 后须增加校验与 HTML 转义。
for (const v of variants) {
const peers = variants.filter(x => x.contentId === v.contentId);
const alternate = peers.map(x =>
`<link rel="alternate" hreflang="${x.locale}" href="${origin+x.url}">`
).join('\n');
const nav = peers.map(x =>
`<a href="${x.url}" lang="${x.locale}">${x.locale}</a>`
).join(' | ');
const html = `<!doctype html><html lang="${v.locale}">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Industrial sensor | ${v.locale}</title>
<link rel="canonical" href="${origin+v.url}">${alternate}</head>
<body><nav aria-label="Language and market">${nav}</nav>
<h1>Industrial sensor</h1>
<p>Model: ${product.model} / Supply: ${product.voltage}</p>
<p>${v.proof}</p><p>Fictional demonstration only.</p></body></html>`;
const file = path.join('dist', v.url, 'index.html');
await fs.mkdir(path.dirname(file), {recursive: true});
await fs.writeFile(file, html);
}
console.log('Built 3 locale pages');
每个页面保留相同型号和参数,市场提示由配置决定。普通 a 链接既能被用户使用,也不依赖脚本才能找到对应版本。独立静态页面还便于逐个核验正文,而不是只检查浏览器内当前状态。
先运行,再检查输出
脚本已在本地运行并生成三个 index.html;正文中的型号一致,市场提示随版本变化。通过静态服务器访问时,站点根目录应是 dist。不要直接用 file:// 判断以 / 开头的切换链接是否正确。
sh
node build.mjs
python3 -m http.server 8080 --directory dist
# 浏览器打开 http://localhost:8080/en-us/products/sensor-01/

图|配套教学站点实际运行截图,增加了阅读样式与联系说明;不是上述最简模板的逐像素结果,业务数据均为虚构。
同一种语言,为什么出现三个 URL?
这里的 en 是不限定地区的通用英语说明;en-US 和 en-GB 展示两个经过配置的市场差异。如果业务并没有这些差别,就可以只保留 en,不必复制地区页面。
不要根据访客 IP 强制把他送到某个版本。英语用户未必正在了解美国业务,出差中的采购人员也可能需要查看其他市场信息。可以提示推荐版本,但应保留用户选择和可直接访问的地址。
语言选择器里的国旗也不是语言本身。用语言和地区名称说明选项,通常比用一个国家象征覆盖所有语言使用者更清楚。
hreflang 只连接真正对应的版本
Google 的等效语言版本文档说明 hreflang 的对应关系应包含自身及返回链接;语言不能只写国家代码。这里使用 en-GB,不能把它写成 UK。
代码按同一 contentId 生成 alternate 集合,每页都包含自己和其他版本。真实项目应该只纳入已发布、可访问且内容等效的页面。
如果一个地区不提供这项产品,不能仍然生成一个空模板,然后放进所有页面的版本集合。应当先处理业务可用性,再生成网站导航和搜索标注。
Google 的多地区站点说明也建议保留可访问的版本 URL 与用户切换选择。本文没有采用 IP 强制跳转,也没有把浏览器语言当成供货市场。
示例采用各页自指向 canonical,适用于这里已经安排了不同市场说明的教学页面。真实站点若存在同语言完全重复的 URL,需要按重复内容情况确定规范页,不能照搬模板就算验收完成。
用浏览器验收,而不是只看生成成功
至少检查直接打开每个 URL 是否有正确正文,切换是否仍停留在同一产品,手机宽度下菜单能否使用,以及不存在的版本是否从导航和标注中消失。
配套演示已实际检查从 en-US 切到 en-GB 后仍停留 sensor-01 产品页、共同型号和参数保持一致,而市场提示改变;在 390 像素宽度下切换到通用 en 页无整页横向溢出。未生成的 de-DE 路径在本地测试路由中返回 404,未进入页面导航。
这类检查能发现"头部标注正确,但用户总回首页""内容跟语言菜单不一致"等问题。真实网站还需结合服务端状态码、索引配置和上线后的搜索工具继续验证。
多语言前端可以共用模板,但不应因此把不同客户的业务环境压成同一份文字。版本映射和内容配置一起设计,才能让统一架构承载真实差异。