Next.js 多语言 (2) | useTranslations、getTranslations 和 NextIntlClientProvider 的使用

在使用 Next.js 开发多语言网站时,我们通常会遇到一个重要问题:如何实现多语言(i18n) 🌍。
一开始,很多开发者都会选择使用 useTranslations 🤔,这是一个简单直接的工具。但是,这种方式只能在 客户端组件(Client Component) 中使用,而客户端组件对 SEO 不友好 ,这让人非常头疼。

通过查阅官方文档,我发现 next-intl/server 提供的getTranslationsNextIntlClientProvider
它们可以很好地解决这个问题,同时兼顾 国际化SEO 的需求

这篇文章将从这个问题入手,帮助你理解 useTranslationsgetTranslations 的区别,以及如何在实际项目中正确使用它们 💡。


问题:为什么客户端组件的 SEO 表现不好?

1. 什么是客户端组件?

客户端组件是需要加载 JavaScript 并在浏览器端渲染的组件。它们依赖 React 的交互功能,比如 useStateuseEffect。当我们使用 useTranslations 时,就把组件变成了客户端组件。

2. SEO 的问题

客户端组件在初始加载时不会直接生成完整的 HTML,而是生成一个空白的 HTML 框架,然后通过 JavaScript 在浏览器中渲染内容。这会带来以下问题:

  • 搜索引擎爬虫无法获取完整内容:比如页面的标题、描述等内容可能无法被爬取。
  • 初始加载变慢:需要加载额外的 JavaScript,影响用户体验。
3. 为什么 Next.js 推荐服务端组件?

Next.js 的服务端组件(Server Components)会在服务器端生成完整的 HTML,然后将其发送到浏览器。这不仅对 SEO 友好,而且减少了客户端的 JavaScript 负担。


解决方案:服务端的 getTranslations

什么是 getTranslations

getTranslations 是一个用于服务端组件的工具,它允许我们在服务端异步获取翻译内容。这样,我们可以在服务端完成国际化处理,并直接返回完整的 HTML,避免了 SEO 和性能问题。

对比:useTranslations vs getTranslations
特性useTranslationsgetTranslations
适用场景客户端组件服务端组件
SEO 表现较差,内容在客户端渲染优秀,内容在服务端渲染
是否支持异步不支持,依赖上下文支持,直接加载翻译文件
使用复杂度简单,适合小型项目需要服务端处理,但性能更优

具体用法

1. 使用 useTranslations(客户端组件)

当你需要在组件中使用交互功能,比如动态切换语言或表单提交时,可以使用 useTranslations

示例:导航栏的国际化

// 文件:Navbar.tsx
'use client'; // 指定为客户端组件
import {useTranslations} from 'next-intl';

function Navbar() {
  const t = useTranslations('Navbar'); // 读取命名空间 Navbar 的翻译

  return (
    <nav>
    <a href="/">{t('home')}</a>
    <a href="/about">{t('about')}</a>
    </nav>
      );
}

export default Navbar;

假设翻译文件 en.json

{
  "Navbar": {
    "home": "Home",
    "about": "About"
  }
}

效果:

  • 渲染完成后,导航栏会显示翻译内容。
  • 适合需要动态更新内容的场景。

问题:

  • 页面初始加载时,HTML 中的导航内容是空的,这对 SEO 不友好。

2. 使用 getTranslations(服务端组件)

当你的页面内容是静态的,或者对 SEO 友好性要求较高时,应使用 getTranslations

示例:首页的国际化

// 文件:page.tsx
import {getTranslations} from 'next-intl/server';

export default async function HomePage() {
  const t = await getTranslations('HomePage'); // 异步获取翻译

  return (
    <div>
    <h1>{t('title')}</h1>
           <p>{t('description')}</p>
    </div>
  );
}

假设翻译文件 en.json

{
  "HomePage": {
    "title": "Welcome to our website",
    "description": "This is the best place to find amazing content."
  }
}

效果:

  • 页面加载时,服务器直接返回翻译后的 HTML。
  • 搜索引擎爬虫可以直接读取完整内容。
  • 对 SEO 和性能更友好。

问题:客户端国际化的挑战

  1. 非序列化的配置无法直接传递到客户端
    • React 的限制要求从服务端传递到客户端的数据必须是 可序列化的,即 JSON 格式的数据。
    • 配置中如 onError 回调函数或 defaultTranslationValues 等非序列化的数据类型,无法直接通过服务端传递到客户端。
  2. 服务端和客户端的翻译内容可能不一致
    • 如果页面的主内容由服务端渲染,而部分组件需要在客户端动态翻译,服务端和客户端之间的翻译内容可能出现不一致。
    • 这会导致页面内容的显示错误或用户体验下降。
  3. 多个客户端组件需要共享翻译上下文
    • 当页面中有多个客户端组件需要使用相同的翻译配置时,缺乏统一的上下文可能导致重复配置或数据不一致。

什么时候需要使用 NextIntlClientProvider

场景 1:页面主要内容由服务端渲染,但包含客户端动态交互

例如:首页的标题和描述由服务端渲染以保证 SEO,但页面内有一个语言切换器需要客户端交互。

场景 2:需要处理非序列化的配置

例如:需要在客户端组件中实现自定义的错误回调(onError)、占位内容回退(getMessageFallback)或默认富文本格式(defaultTranslationValues)。

场景 3:多个客户端组件需要共享翻译上下文

例如:页面中有多个客户端组件(如动态表单和交互式菜单),需要统一的翻译配置和内容。


场景示例:服务端渲染页面 + 客户端语言切换器

以下是一个常见的场景:

  • 博客首页的标题和描述需要通过服务端渲染,以确保搜索引擎可以抓取完整内容。
  • 同时,页面需要一个动态语言切换器,用户可以在客户端切换语言。

通过结合服务端组件和 NextIntlClientProvider,我们可以实现既对 SEO 友好又具有动态交互功能的多语言支持。


实现步骤

1. 服务端部分:通过 IntlProvider 传递翻译内容

在服务端布局文件中,加载翻译内容并传递到 IntlProvider 中。

// 文件:layout.tsx
import IntlProvider from './IntlProvider';
import {getLocale, getMessages, getTimeZone, getNow} from 'next-intl/server';

export default async function RootLayout({children}) {
  const locale = await getLocale(); // 获取当前语言
  const messages = await getMessages(); // 获取翻译内容
  const timeZone = await getTimeZone(); // 获取时区
  const now = await getNow(); // 获取当前时间

  return (
    <html lang={locale}>
      <body>
    <IntlProvider
    locale={locale}
  messages={messages}
  timeZone={timeZone}
  now={now}
    >
    {children}
    </IntlProvider>
    </body>
    </html>
  );
}
2. 客户端部分:自定义 IntlProvider

在客户端组件中,通过 NextIntlClientProvider 自定义翻译配置和错误回调。

// 文件:IntlProvider.tsx
'use client';
import {NextIntlClientProvider} from 'next-intl';

export default function IntlProvider({locale, messages, now, timeZone}) {
  return (
    <NextIntlClientProvider
      locale={locale} // 当前语言
      messages={messages} // 翻译内容
  now={now} // 当前时间
  timeZone={timeZone} // 当前时区
  defaultTranslationValues={{
    i: (text) => <i>{text}</i>, // 定义富文本格式
  }}
onError={(error) => console.error('Translation error:', error)} // 错误处理回调
getMessageFallback={({namespace, key}) => `${namespace}.${key}`} // 自定义占位内容
/>
);
}
3. 客户端语言切换器

通过一个动态语言切换器实现客户端的交互功能。

// 文件:LanguageSwitcher.tsx
'use client';
import {useState} from 'react';

function LanguageSwitcher() {
  const [language, setLanguage] = useState('en'); // 默认语言

  const handleLanguageChange = (e) => {
    const newLanguage = e.target.value;
    setLanguage(newLanguage);
    console.log(`Switched to ${newLanguage}`);
    // 在实际项目中,可以通过路由跳转实现语言切换
  };

  return (
    <select value={language} onChange={handleLanguageChange}>
    <option value="en">English</option>
    <option value="de">Deutsch</option>
    </select>
  );
}

export default LanguageSwitcher;

效果分析

  1. 服务端渲染的内容
    • 首页的标题和描述直接在服务端生成,并包含在 HTML 中,提升 SEO 表现。
  2. 客户端动态语言切换
    • 语言切换器在客户端动态工作,不会影响页面主内容的 SEO。
  3. 非序列化配置
    • 通过 NextIntlClientProvider,客户端组件可以使用自定义的错误回调和占位内容。

总结 🌟

  1. useTranslations:适用于动态交互的客户端组件,但对 SEO 不友好 ❌。
  2. getTranslations:适用于服务端渲染的静态内容,SEO 表现优秀 ✅。
  3. NextIntlClientProvider:在以下场景非常重要 :
    • 页面主内容由服务端渲染,但包含客户端动态交互 💻。
    • 需要在客户端处理非序列化的配置(如错误回调) ⚙️。
    • 多个客户端组件需要共享翻译上下文 🌐。

通过合理使用 NextIntlClientProvider,可以实现 SEO 友好动态交互数据一致性 的多语言支持 🧩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值