You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Remix i18n子组件用翻译触发Hydration不匹配错误求助

Remix + remix-i18next 子组件加载翻译导致 Hydration 失败问题

我在Remix应用中使用remix-i18next包实现国际化翻译。仅父组件加载特定命名空间的翻译时一切正常,但当子组件也加载指定JSON文件的翻译时,出现**「Hydration failed because the initial UI does not match what was rendered on the server」**错误,移除子组件翻译逻辑后错误消失。

父组件代码

import { Card } from "@shopify/polaris";
import { Spacer } from "../components/globalComponents"
import HaveQuestionsCard from "../components/HaveQuestionsCard";

// i18n imports and configurations
import { useTranslation } from "react-i18next";
export let handle = { i18n: "testHome" };   // 加载"testHome"命名空间的翻译

export default function TestHome() {
  let { t } = useTranslation("testHome");

  return (
    <>
      <Spacer height={"10rem"} />
      <Card>
        <h1>
          {t("title")}
        </h1>
      </Card>

      <Spacer height={"2rem"} />

      <HaveQuestionsCard />
    </>
  )
}

子组件代码

import { Card, Text, InlineStack, Link } from "@shopify/polaris";
import { Spacer } from "./globalComponents";
import SupportImg from "../media/bundle/support.svg";

// i18n imports and configurations
import { useTranslation } from "react-i18next";
export let handle = { i18n: "haveQuestionsCard" };     // 加载"haveQuestionsCard"命名空间的翻译

export default function HaveQuestionsCard() {
   // 获取翻译函数
  let { t } = useTranslation("haveQuestionsCard");

  return (
    <Card>
      <InlineStack gap={"200"}>
        <Text as="h2" variant="headingMd">
          {t("title")}
        </Text>

        <img alt="" width="8%" height="8%"
          style={{
            objectFit: "cover",
            objectPosition: "center",
          }}
          src={SupportImg}
        />
      </InlineStack>

      <Spacer />

      <Text as="p" variant="bodyMd">
        {t("details")}
      </Text>

      {/* <Spacer /> */}

      <Link monochrome url="#">
        <Text as="h2" variant="bodyMd" fontWeight="medium" tone="subdued">
          {t("link")}
        </Text>
      </Link>
    </Card>
  );
}

错误截图

错误截图


解决方案

remix-i18next中,handle.i18n配置仅在路由组件中生效,子组件(非路由组件)不能通过导出handle加载命名空间——这会导致服务端渲染时子组件的翻译未被预加载,客户端hydration阶段才加载内容,最终出现服务端与客户端UI不匹配的问题。

正确处理步骤:

  1. 在父路由组件中预加载所有需要的命名空间
    修改父组件的handle.i18n,将子组件依赖的命名空间也包含进来:
    export let handle = { i18n: ["testHome", "haveQuestionsCard"] };
    
  2. 移除子组件中的handle导出
    删除子组件代码中的export let handle = { i18n: "haveQuestionsCard" };,保留useTranslation("haveQuestionsCard")即可正常使用翻译函数。

原理:remix-i18next会在服务端渲染路由组件时,根据handle.i18n配置预加载所有指定的命名空间翻译内容,确保服务端和客户端渲染的UI完全一致,从根源上避免hydration不匹配错误。

若需要异步加载翻译,也可以结合React的Suspense组件和useTranslation的suspense选项,但预加载命名空间是更贴合Remix服务端渲染模型的最优方案。


内容的提问来源于stack exchange,提问作者Wakil Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 23:30:10