前言
做工具站,如果只做中文市场,不仅竞争激烈(卷),而且广告单价(RPM)相对较低。想要获得更高的被动收入,出海是必经之路。
想象一下,你的工具站支持英文后,就可以提交到 Product Hunt、Reddit 等平台,获取全球流量。
在 Nuxt 4 中,实现多语言并不是简单的“翻译文本”,它涉及到路由前缀(/en/tool)、SEO 标签(hreflang)、自动语言检测以及按需加载。
今天这篇,我们就利用 @nuxtjs/i18n 模块,为 SonicToolLab 装上“翻译官”。
🌍 1. 安装与基础配置
Nuxt 官方推荐的国际化模块是 @nuxtjs/i18n(目前 v8/v9 版本适配 Nuxt 3/4,这里pnpm和i18n的版本不要太新)。
安装依赖
pnpm add @nuxtjs/i18n
配置文件 nuxt.config.ts
这是最关键的一步。我们需要定义支持的语言、默认语言以及路由策略。
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n'],
i18n: {
// 策略:'prefix_except_default' 意味着默认语言(中文)不带前缀,其他语言带前缀
// 中文:/tools/json
// 英文:/en/tools/json
strategy: 'prefix_except_default',
defaultLocale: 'zh', // 默认语言
// 语言配置列表
locales: [
{
code: 'en',
name: 'English',
file: 'en.json' // 翻译文件路径
},
{
code: 'zh',
name: '简体中文',
file: 'zh.json'
}
],
// 懒加载:只有切换到英文时,才加载 en.json,减小首屏体积
lazy: true,
langDir: 'locales', // 翻译文件存放目录
// 自动检测浏览器语言并重定向
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_redirected',
redirectOn: 'root', // 仅在访问根目录时检测
}
}
})
📝 2. 管理翻译文件
在项目根目录下创建 locales 文件夹。
DX 建议: 强烈推荐在 VS Code 中安装 i18n Ally 插件,它能让你在代码里直接看到翻译后的文案,不用来回切文件。
locales/zh.json:
{
"nav": {
"home": "首页",
"tools": "工具列表",
"about": "关于我们"
},
"hero": {
"title": "开发者的百宝箱",
"subtitle": "免费、快速、隐私安全的在线工具"
}
}
locales/en.json:
{
"nav": {
"home": "Home",
"tools": "Tools",
"about": "About"
},
"hero": {
"title": "Developer's Toolkit",
"subtitle": "Free, Fast, and Privacy-Focused Online Tools"
}
}
🗣️ 3. 组件中的使用
文本替换
将原本写死的中文换成 $t() 函数。
<template>
<h1>{{ $t('hero.title') }}</h1>
<p>{{ $t('hero.subtitle') }}</p>
</template>
链接跳转 (关键!)
千万不要直接写 to="/tools"。
如果在英文版页面点击这个链接,会跳回中文版。必须使用 useLocalePath。
<script setup>
const localePath = useLocalePath()
</script>
<template>
<NuxtLink :to="localePath('tools')">
{{ $t('nav.tools') }}
</NuxtLink>
</template>
🔄 4. 制作语言切换器
我们需要一个下拉菜单或按钮来切换语言。这里用 Nuxt UI 的 UDropdown 或 USelectMenu 实现。
<script setup>
const { locale, locales, setLocale } = useI18n()
// 计算当前语言的名称
const currentLocale = computed(() => {
return locales.value.find(i => i.code === locale.value)
})
// 切换语言函数
const onLocaleChanged = (code) => {
setLocale(code)
}
</script>
<template>
<div class="flex items-center gap-2">
<UButton
color="gray"
variant="ghost"
@click="onLocaleChanged(locale === 'zh' ? 'en' : 'zh')"
>
{{ locale === 'zh' ? 'En' : '中' }}
</UButton>
</div>
</template>
🕷️ 5. SEO 与 Hreflang (重中之重)
做了多语言,如果 Google 认为你的中文页和英文页是“重复内容”,那就麻烦了。
好消息是,@nuxtjs/i18n 模块会自动为每个页面生成 <link rel="alternate" hreflang="..." /> 标签。
检查方法:
打开网页源代码,查看 <head> 部分,你应该能看到类似的代码:
<link rel="alternate" href="[https://sonictoollab.dpdns.org/](https://sonictoollab.dpdns.org/)" hreflang="zh" />
<link rel="alternate" href="[https://sonictoollab.dpdns.org/en](https://sonictoollab.dpdns.org/en)" hreflang="en" />
<link rel="alternate" href="[https://sonictoollab.dpdns.org/](https://sonictoollab.dpdns.org/)" hreflang="x-default" />
这告诉 Google:“如果用户说中文,给他看这个;如果用户说英文,给他看那个。”这是多语言 SEO 排名的基石。
注意: 务必在 nuxt.config.ts 中配置 site.url (配合 @nuxtjs/seo) 或 i18n.baseUrl,否则生成的链接可能不带域名。
⚠️ 6. 避坑指南:Hydration Mismatch
在服务端渲染(SSR)中,如果不小心处理,很容易出现“服务端渲染了中文,客户端检测到浏览器是英文,强行变成了英文”,导致页面闪烁或控制台报错。
最佳实践:
- 依赖路由前缀: 尽量让 URL 决定语言(/en),而不是单纯依赖 Cookie。
- Date/Number 格式化: 如果你在页面上显示日期,不要用 JS 原生的
new Date().toLocaleDateString(),因为它依赖运行环境。请使用 i18n 提供的$d()函数,确保服务端和客户端输出一致。
<p>{{ $d(new Date(), 'short') }}</p>
总结
通过配置 i18n,SonicToolLab 成功具备了接待全球用户的能力。
虽然前期的翻译工作比较繁琐(特别是工具的说明文案),但这是一次性的投入。一旦部署完成,你的工具站市场规模将从 14 亿人瞬间扩展到 80 亿人。
至此,我们的 Nuxt 4 技术实战系列专栏就告一段落了。
从环境搭建、部署运维、功能开发、性能优化、SEO、UI细节到今天的国际化,我们完整走完了一个独立开发者打造产品的全过程。
希望这个系列能成为你开发路上的一盏路灯。如果你也做出了自己的产品,别忘了发给我看看!