「一旦你在配置里加了日语,框架就会给所有页面生成日语版的hreflang标签——哪怕那个页面根本不存在。」这是BulkPicTools作者的原话。一个浏览器端图片工具站,38个落地页,从"配置完即上线"到"逐页可控发布",中间隔着一次搜索排名危机。
导读:为什么"配置即上线"是陷阱
作者最初的做法很典型:nuxt-i18n里加一行ja配置,机器翻译JSON文件一扔,部署。下午完工。但问题随后浮现——
一半工具还没翻译完,框架却已经告诉Google:日语版全站就绪。用户点进去,404,或者更隐蔽的:显示英文内容,但hreflang标签宣称这是日语页面。这种"静默降级"对搜索排名的伤害,比不做多语言更严重。
核心矛盾:框架的"语言配置"是站点级开关,但内容翻译是页面级进度。两者不同步,SEO埋雷。
方案核心:让数据自己说话
作者没有造一个新系统,而是改造了已有的工具定义JSON结构。每个工具原本就有en、zh键,现在加一个规则:ja键存在=日语版就绪,不存在=未就绪。
没有单独的feature flag文件,没有部署时需要手动翻转的环境变量。数据即状态。
代码层面,工具配置长这样:
{
"slug": "image-compressor",
"category": "compress",
"en": { "name": "Image Compressor", "meta": {...} },
"zh": { "name": "图片压缩", "meta": {...} },
"ja": { "name": "画像圧縮", "meta": {...} } // ← 这行存在,日语版才上线
}
这种设计的妙处:翻译进度天然可视化。产品经理打开JSON,看到哪些工具有ja键,就是哪些已就绪。不需要问开发,不需要查另一个系统。
hreflang的坑:ja不是jp
作者专门提了一个容易踩的细节:ISO 639-1语言码vs ISO 3166-1国家码。
日语的语言码是ja,不是jp。jp是日本的国家码,hreflang标签要的是前者。同理,中文是zh,不是cn。配错会导致搜索引擎理解混乱。
配置文件中这样写:
locales: [
{ code: 'ja', language: 'ja', file: 'ja.json' }, // 正确
// 不是 { code: 'jp', ... }
]
这个错误太常见,作者特意加注释提醒后来者。
过滤hreflang:只声明存在的页面
nuxt-i18n默认的useLocaleHead()会为所有配置语言生成alternate链接。作者需要覆盖这个行为——只生成实际有翻译的hreflang。
实现方式:在页面级别检查当前工具的配置对象,遍历其语言键,动态构建alternate链接数组。没有ja键?就不生成ja的hreflang。
代码逻辑大致:
const availableLocales = Object.keys(toolConfig).filter(k => ['en','zh','ja'].includes(k))
const alternateLinks = availableLocales.map(code => ({
hreflang: code,
href: `/${code}/${toolConfig.slug}`
}))
这保证了Google抓到的每个hreflang链接,背后都有真实对应的本地化内容。
回退策略:未翻译页面怎么处理
用户直接访问/ja/image-compressor,但该工具尚无日语翻译——怎么办?
作者的选择:404。而不是回退到英文并假装这是日语页面。
理由很直接:hreflang已经承诺了"这是日语内容",如果实际显示英文,就是欺骗搜索引擎和用户。404虽然体验硬,但诚实,不损害站点整体信誉。
实现上,在Nuxt的页面中间件或setup中检查:如果路由是/ja/*,但对应工具无ja键,直接抛404错误。
元数据的动态生成
每个工具的SEO元数据(title、description、og:image等)也存储在JSON的语言键内。页面渲染时,根据当前locale读取对应语言的meta对象。
这带来一个好处:翻译和SEO优化可以同步进行。译者写日语描述时,SEO关键词也一并确定,不需要二次交接。
动态meta的实现依赖Nuxt的useHead(),在页面setup中根据toolConfig[locale].meta注入。
扩展性:加韩语只需三步
作者演示了系统如何支持新语言。以韩语为例:
第一步:nuxt.config.ts的locales数组加一行{ code: 'ko', language: 'ko', file: 'ko.json' }。
第二步:翻译文件ko.json放到i18n目录。
第三步:逐个工具添加ko键。加完一个,该工具的韩语版自动上线;没加的,保持不可访问。
没有第四步。不需要改路由逻辑,不需要改hreflang生成代码,不需要改回退策略。所有行为都由"数据是否存在"驱动。
对国内开发者的启示
这个方案的价值不在技术复杂度,而在问题定义准确。
很多团队做多语言时,把"框架配置"当成"内容就绪"的信号。但翻译是渐进过程,框架配置是原子操作。强行同步,要么延迟上线等全部翻译完,要么提前上线牺牲SEO。
作者的做法是解耦:框架配置只管"站点支持哪些语言",JSON数据管"每个页面支持哪些语言"。两者交集,才是实际对外呈现的多语言站点。
这种设计也降低了协作成本。译者直接编辑JSON,添加语言键即触发上线,不需要开发介入部署。开发专注维护框架层面的语言注册,不追踪具体页面的翻译进度。
对于内容型产品、工具型站点、甚至SaaS的落地页体系,这个模式都可借鉴。核心判断标准:你的多语言内容,是不是逐页/逐模块就绪的?如果是,站点级配置+页面级数据的状态分离,比全量开关更安全。
实用指向:怎么迁移现有项目
如果你已经在用nuxt-i18n,且面临类似困境,作者的迁移路径值得参考——
不动现有翻译文件结构,只在工具/页面级别的数据层增加语言键检查。hreflang生成从框架默认行为,改为手动过滤。回退策略从"静默降级"改为"显式404"。
改动范围控制在页面渲染链路,不触及i18n核心配置。风险可控,可逐页验证。
最终检验标准:在Google Search Console里,你的多语言页面报告是否还有"hreflang指向无内容页面"的警告?清零,即成功。
热门跟贴