SEO国际化解决方案

半兽人 发表于: 2025-07-23   最后更新时间: 2025-08-05 10:06:09  
{{totalSubscript}} 订阅, 123 游览

问题描述

切换语言后,页面显示源码还是默认的英语,这样SEO就有问题了。需要从客户端渲染,改为服务端渲染,以下是改造方式。

  • 服务端渲染: 服务端始终使用默认语言(英语)渲染HTML
  • 客户端切换: 只有客户端JavaScript执行后才显示正确语言
  • SEO影响: 搜索引擎爬虫看到的是英语内容,无法索引其他语言版本

代码改造的核心区别

1. 组件标记

客户端组件

'use client'  // 必须添加这个指令

import { useTranslation } from 'react-i18next'
import { useState } from 'react'

export default function ClientComponent() {
    const { t } = useTranslation()
    const [count, setCount] = useState(0)

    return <div>{t('welcome')}</div>
}

服务端组件

// 不需要 'use client' 指令,默认就是服务端组件

import { useTranslation } from '@/i18n/server'
import { getLocaleOnServer } from '@/i18n/server'

export default async function ServerComponent() {  // 注意:async
    const locale = await getLocaleOnServer()
    const { t } = await useTranslation(locale, 'common')  // 注意:await

    return <div>{t('welcome')}</div>
}

2. 翻译函数导入

客户端翻译

import { useTranslation } from 'react-i18next'

export default function Component() {
    const { t } = useTranslation()  // 直接调用,无需参数
    return <div>{t('app.title')}</div>  // 使用完整键名
}

服务端翻译

import { useTranslation } from '@/i18n/server'
import { getLocaleOnServer } from '@/i18n/server'

export default async function Component() {
    const locale = await getLocaleOnServer()
    const { t } = await useTranslation(locale, 'app')  // 需要locale和namespace
    return <div>{t('title')}</div>  // 不需要namespace前缀
}

3. 函数签名

客户端组件

export default function Component() {  // 普通函数
    // 可以使用所有React hooks
    const [state, setState] = useState()
    const { t } = useTranslation()

    return <div>{t('key')}</div>
}

服务端组件

export default async function Component() {  // 必须是async函数
    // 不能使用useState, useEffect等客户端hooks
    const locale = await getLocaleOnServer()
    const { t } = await useTranslation(locale, 'namespace')

    return <div>{t('key')}</div>
}

4. 具体改造示例

改造前(客户端)

'use client'
import { useTranslation } from 'react-i18next'

export default function ProductPage() {
    const { t } = useTranslation()

    return (
        <div>
            <h1>{t('product.title')}</h1>
            <p>{t('product.description')}</p>
            <button onClick={() => console.log('clicked')}>
                {t('product.buy')}
            </button>
        </div>
    )
}

改造后(服务端)

import { useTranslation } from '@/i18n/server'
import { getLocaleOnServer } from '@/i18n/server'

export default async function ProductPage() {
    const locale = await getLocaleOnServer()
    const { t } = await useTranslation(locale, 'product')

    return (
        <div>
            <h1>{t('title')}</h1>  {/* 去掉 'product.' 前缀 */}
            <p>{t('description')}</p>
            {/* 注意:不能有onClick等事件处理 */}
            <button>{t('buy')}</button>
        </div>
    )
}

5. 混合使用(推荐)

// 主组件 - 服务端渲染SEO内容
import { useTranslation } from '@/i18n/server'
import { getLocaleOnServer } from '@/i18n/server'
import ClientButton from './ClientButton'  // 客户端组件

export default async function ProductPage() {
    const locale = await getLocaleOnServer()
    const { t } = await useTranslation(locale, 'product')

    return (
        <div>
            {/* SEO重要的内容 - 服务端渲染 */}
            <h1>{t('title')}</h1>
            <p>{t('description')}</p>

            {/* 交互功能 - 客户端组件 */}
            <ClientButton />
        </div>
    )
}

// 客户端组件 - 处理交互
'use client'
import { useTranslation } from 'react-i18next'

export default function ClientButton() {
    const { t } = useTranslation()

    return (
        <button onClick={() => console.log('clicked')}>
            {t('product.buy')}  {/* 客户端需要完整键名 */}
        </button>
    )
}

6. 关键改造要点

改造项 客户端 → 服务端
移除 'use client'
添加 async 函数声明
修改导入 react-i18next@/i18n/server
添加语言检测 await getLocaleOnServer()
修改翻译调用 useTranslation()await useTranslation(locale, 'namespace')
简化键名 t('app.title')t('title')
移除交互 删除 onClick, useState

7. 不能改造的情况

// 这些情况必须保持客户端组件
'use client'

// 1. 有事件处理
<button onClick={handleClick}>Click</button>

// 2. 有状态管理
const [count, setCount] = useState(0)

// 3. 有副作用
useEffect(() => {
    // 副作用代码
}, [])

// 4. 使用浏览器API
window.localStorage.getItem('key')

这就是核心的代码改造区别!记住:服务端组件用于SEO重要的内容渲染,客户端组件用于交互功能

更新于 2025-08-05

查看React更多相关的文章或提一个关于React的问题,也可以与我们一起分享文章