前言

将 Next.js 的国际化 (i18n) 功能与 react-i18next 结合使用是一个常见的做法。您已经在项目中引入了 i18next 和 react-i18next,并且在 中配置了 Next.js 的 i18n 路由。

以下是如何将它们更好地结合起来的步骤和建议:

  1. 确保 i18next 初始化正确:
    您已经在 文件中初始化了 i18n。这是一个很好的起点。请确保 supportedLngs 与 中的 locales 一致。

     // typescript:src/i18n.ts
    // ... existing code ...
    i18n.use(initReactI18next).init({
      fallbackLng: 'en', // 确保与 next.config.ts 的 defaultLocale 一致或兼容
      supportedLngs: ['en', 'zh'], // 确保与 next.config.ts 的 locales 一致
      // lng: 'en', // 通常不需要在这里硬编码 lng,Next.js 的路由会处理当前语言
      resources: {
        en: {
          translation: {
            welcome: 'Welcome',
            chat: 'Go to Chat',
            // ...更多英文翻译
          },
        },
        zh: {
          translation: {
            welcome: '欢迎',
            chat: '进入聊天',
            // ...更多中文翻译
          },
        },
      },
      // interpolation: { // 如果需要,可以配置插值
      //   escapeValue: false, 
      // },
      // debug: process.env.NODE_ENV === 'development', // 开发模式下开启 debug
    });
    // ... existing code ...
    
  2. 在应用顶层提供 I18nextProvider:
    为了让 react-i18next 的 hooks (如 useTranslation) 能够在您的组件中使用,您需要在应用的根布局 () 中包裹您的 children。

    首先,您可能需要创建一个客户端组件来包裹 I18nextProvider,因为 i18n 实例的传递通常在客户端进行。

    创建一个新的客户端组件,例如 src/components/I18nProviderWrapper.tsx:

    // typescript:src/components/I18nProviderWrapper.tsx
    'use client'
    
    import { I18nextProvider } from 'react-i18next'
    import i18n from '@/i18n' // 确保路径正确
    
    export default function I18nProviderWrapper({ children }: { children: React.ReactNode }) {
      return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
    }
    

    然后,在您的根布局 中使用它:

    tsx:src/app/layout.tsx
    // ... existing code ...
    import I18nProviderWrapper from "@/components/I18nProviderWrapper"; // 导入包装器
    
    // ... existing code ...
    
    export default function RootLayout({
      children,
      params, // Next.js 13+ App Router 会传递 params,其中包含 locale
    }: Readonly<{
      children: React.ReactNode
      params: { locale: string } // 添加 locale 到 params 类型
    }>) {
      // 如果 i18n 实例需要根据 locale 动态改变语言,可以在这里处理
      // useEffect(() => {
      //   if (params.locale && i18n.language !== params.locale) {
      //     i18n.changeLanguage(params.locale);
      //   }
      // }, [params.locale]);
      // 注意:上面的 useEffect 逻辑更适合放在 I18nProviderWrapper 内部,因为它是一个客户端组件
    
      return (
        <html lang={params.locale}> {/* 使用 Next.js 提供的 locale */} 
          <body className={`${geistSans.variable} ${geistMono.variable} antialiased `}>
            <I18nProviderWrapper>
              {children}
            </I18nProviderWrapper>
            <WebVitals />
            <Toaster />
          </body>
        </html>
      )
    }
    

    重要: 在 App Router 中,params.locale 会自动从 URL 中获取 (例如 /en/some-page 或 /zh/some-page)。您应该使用这个 params.locale 来设置 <html> 标签的 lang 属性。
    i18next 也可以配置为自动检测浏览器语言或从 params.locale 更新语言。通常,i18next-http-middleware 或 i18next-browser-languagedetector 可以帮助处理语言检测,但与 Next.js 的内置 i18n 路由结合时,Next.js 自身会处理 URL 中的 locale。

    修改 src/components/I18nProviderWrapper.tsx 以响应 locale 变化:

    // typescript:src/components/I18nProviderWrapper.tsx
    'use client'
    
    import { I18nextProvider } from 'react-i18next'
    import i18n from '@/i18n' // 确保路径正确
    import { useParams } from 'next/navigation' // 用于获取 locale
    import { useEffect } from 'react'
    
    export default function I18nProviderWrapper({ children }: { children: React.ReactNode }) {
      const params = useParams()
      const locale = params.locale as string | undefined
    
      useEffect(() => {
        if (locale && i18n.language !== locale) {
          i18n.changeLanguage(locale);
        }
      }, [locale])
    
      return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
    }
    
  3. 在组件中使用翻译:
    现在您可以在您的客户端组件中使用 useTranslation hook。

    例如,在 (如果它是一个客户端组件) 或者任何其他客户端组件中:

    // tsx:src/components/ChatPanel.tsx
    'use client'
    
    // ... other imports ...
    import { useTranslation } from 'react-i18next';
    
    // ... existing code ...
    
    export default function ChatPanel({ initialConversationId, currentUser }: ChatPanelProps) {
      const { t } = useTranslation();
      // ... existing code ...
    
      return (
        <Card className="flex flex-col h-full w-full max-w-4xl mx-auto bg-card shadow-xl rounded-lg">
          <CardHeader className="border-b p-4">
            {/* 示例:使用 t 函数进行翻译 */}
            <CardTitle>{t('chat')}</CardTitle> 
          </CardHeader>
          {/* ... rest of your component ... */}
          <CardFooter className="p-4 border-t">
            <form onSubmit={handleSubmit} className="flex w-full items-center space-x-2">
              <Input 
                type="text" 
                value={input} 
                onChange={handleInputChange} 
                placeholder={t('typeYourMessage')} // 示例翻译
                disabled={isLoading} 
                className="flex-1"
              />
              <Button type="submit" disabled={isLoading || !input.trim()}>
                {isLoading ? t('sending') : t('send')} {/* 示例翻译 */}
              </Button>
            </form>
          </CardFooter>
        </Card>
      );
    }
    

    确保您在 的 resources 中添加了对应的翻译键,例如 typeYourMessage, sending, send。

  4. 语言切换:
    Next.js 的 i18n 路由允许通过更改 URL 路径来切换语言 (例如,从 /en/about 到 /zh/about)。您可以使用 next/link 或 next/router 来实现语言切换链接或按钮。

    例如,一个简单的语言切换组件:

    tsx:src/components/LanguageSwitcher.tsx
    'use client'
    
    import { usePathname, useRouter } from 'next/navigation'
    import { useTranslation } from 'react-i18next'
    
    export default function LanguageSwitcher() {
      const { i18n } = useTranslation()
      const pathname = usePathname()
      const router = useRouter()
    
      const changeLanguage = (lng: string) => {
        // Next.js i18n routing will handle the locale prefix
        // We need to remove the current locale from pathname if it exists
        const currentLocale = i18n.language;
        let newPath = pathname;
        if (pathname.startsWith(`/${currentLocale}`)) {
          newPath = pathname.replace(`/${currentLocale}`, '');
        }
        if (newPath === '') newPath = '/'; // Handle root path
        
        router.push(`/${lng}${newPath}`);
      }
    
      return (
        <div>
          <button onClick={() => changeLanguage('en')} disabled={i18n.language === 'en'}>
            English
          </button>
          <button onClick={() => changeLanguage('zh')} disabled={i18n.language === 'zh'}>
            中文
          </button>
        </div>
      )
    }
    

    您可以将这个 组件放置在您的导航栏或其他合适的位置。

  5. 服务端组件 (RSC) 中的国际化:
    对于服务端组件,useTranslation hook 不可用。您需要一种不同的策略。一种常见的方法是创建一个接受 locale 作为 prop 的函数,并直接使用 i18next 实例来获取翻译。

    例如,创建一个 src/lib/t.ts (或类似名称):

    // typescript:src/lib/t.ts
    import i18n from '@/i18n'; // 您的 i18n 实例
    
    export const getTranslations = async (locale: string, ns: string | string[] = 'translation') => {
      // 确保 i18n 实例已加载对应语言的资源
      // 这可能需要您在服务器启动时或构建时加载所有语言资源
      // 或者使用 i18next-fs-backend 等插件按需加载
      if (i18n.language !== locale) {
        await i18n.changeLanguage(locale);
      }
      // 如果您有命名空间,确保它们已加载
      // await i18n.loadNamespaces(ns);
      
      return {
        t: i18n.getFixedT(locale, ns),
      };
    };
    

    然后在您的服务端组件中使用它:

    // tsx:src/app/page.tsx // 假设这是一个服务端组件
    import { getTranslations } from '@/lib/t';
    
    export default async function HomePage({ params }: { params: { locale: string } }) {
      const { t } = await getTranslations(params.locale);
    
      return (
        <div>
          <h1>{t('welcome')}</h1>
          <a href={`/${params.locale}/chat`}>{t('chat')}</a>
        </div>
      );
    }
    

    注意:在服务端组件中使用 i18next.changeLanguage 可能会有并发问题,因为它会改变全局实例的状态。更安全的方法是确保 i18next 实例在请求级别是隔离的,或者使用 i18n.getFixedT(locale, namespace),它返回一个绑定到特定语言和命名空间的 t 函数,而不会改变全局实例的语言。
    对于 Next.js App Router,推荐的做法是利用 params.locale 并传递给获取翻译的函数。

通过这些步骤,您可以有效地将 Next.js 的 i18n 路由与 react-i18next 结合起来,实现客户端和(通过一些调整)服务端组件的国际化。

这些是核心的集成步骤。根据您的具体需求,可能还需要进一步的配置,例如处理更复杂的翻译加载策略 (如代码分割命名空间) 或更高级的 i18next 功能。

注意app router 与 page route Next.config.ts 的使用问题:

在这里插入图片描述

在这里插入图片描述

更多推荐