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

Remix+i18next生产环境元数据翻译失效问题求助

Remix + i18next生产环境元数据翻译失效(显示原始键)

问题描述

使用i18next和remix-i18next开发Remix应用时,开发环境下包括元数据在内的翻译均正常,但生产环境中会显示原始键(如"meta.title"、"meta.description")。

版本信息

  • Remix: 2.14.0
  • remix-i18next: 6.4.1
  • i18next: 23.16.5
  • react-i18next: 15.1.1
  • i18next-browser-languagedetector: 8.0.0
  • i18next-fs-backend: 2.3.2
  • i18next-http-backend: 2.6.2

配置代码

app/utils/getLocale.ts

import i18next from "~/i18next.server";
import { getLanguagePreference } from "~/services/session.server";

export async function getLocale(request: Request): Promise<string> {
  const sessionLocale = await getLanguagePreference(request);
  const locale = sessionLocale || (await i18next.getLocale(request));

  if (!locale) {
    return "en";
  }
  
  return locale;
}

app/i18n.ts

export default {
  supportedLngs: ["en", "es", "fr"],
  fallbackLng: "en",
  defaultNS: "common",
  ns: ["common"],  
  react: { useSuspense: false },
  interpolation: {
    escapeValue: false, 
  }
};

app/i18next.server.ts

import Backend from "i18next-fs-backend";
import { resolve } from "node:path";
import { RemixI18Next } from "remix-i18next/server";
import i18n from "~/i18n";

let i18next = new RemixI18Next({
  detection: {
    supportedLanguages: i18n.supportedLngs,
    fallbackLanguage: i18n.fallbackLng,
  },
  i18next: {
    ...i18n,
    backend: {
      loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"),
    },
  },

  plugins: [Backend],
});

export default i18next;

app/routes/_index.tsx

import { json } from "@remix-run/react";
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { LoaderData } from "~types/*";
import i18next from "~/i18next.server";
import { getLocale } from "~/utils/getLocale";

export const handle = { i18n: "home" };

export async function loader({ request }: LoaderFunctionArgs) {
  const locale = await getLocale(request);
  const t = await i18next.getFixedT(locale, "home", { keyPrefix: "meta" });

  const title = t("title")
  const description = t("description")

  console.log({ title, description })

  return json<LoaderData>({
    translations: {
      locale,
      title: t("title"),
      description: t("description"),
      ogTitle: t("ogTitle"),
      ogDescription: t("ogDescription"),
      twitterTitle: t("twitterTitle"),
      twitterDescription: t("twitterDescription"),
    },
  });
}

export const meta: MetaFunction<typeof loader> = ({ data }) => {
  const baseUrl = "https://example.com";

  console.log({Translation: data?.translations });

  if (!data?.translations) {
    console.error("No translations data available");
    return [{ title: "Fallback Title" }];
  }

  return [
    { title: data?.translations.title },
    {
      name: "description",
      content: data?.translations.description,
    },
    { property: "og:title", content: data?.translations.ogTitle },
    {
      property: "og:description",
      content: data?.translations.ogDescription,
    },
    { property: "og:image", content: "/images/banner.jpg" },
    { property: "og:url", content: "https://example.com" },
    { name: "twitter:card", content: "summary_large_image" },
    {
      name: "twitter:title",
      content: data?.translations.twitterTitle,
    },
    {
      name: "twitter:description",
      content: data?.translations.twitterDescription,
    },
    { name: "twitter:image", content: "/banners/banner.jpg" },

    { name: "robots", content: "index, follow, max-image-preview:large" },
    { name: "language", content: data?.translations.locale },
    { name: "revisit-after", content: "1 day" },

    { tagName: "link", rel: "canonical", href: baseUrl },
  ];
};

// the rest of the code

已尝试操作

  • 本地构建后运行正常
  • 生产环境部署后显示原始键

目录结构

public/
  locales/
    en/
      common.json
      home.json
    es/
      ...
    fr/
      ...

示例翻译文件(public/locales/en/common.json)

{
  "meta": {
    "title": "Example Title",
    "description": "Example Description"
    // ... other translations
  }
}

可能原因与解决方案

1. 生产环境翻译文件路径错误

i18next-fs-backend的loadPath在生产环境可能因部署路径变化无法找到文件。本地开发时resolve("./public/locales/...")指向项目根目录的public,但生产环境中Remix构建后的文件结构或部署路径可能不同。

解决:
使用process.cwd()确保路径指向项目根目录:

// app/i18next.server.ts
backend: {
  loadPath: resolve(process.cwd(), "public/locales/{{lng}}/{{ns}}.json"),
},

同时检查生产环境中public/locales目录是否完整部署,文件权限是否正常。

2. 命名空间未正确加载

app/i18n.ts中ns仅配置了["common"],但首页使用了home命名空间(getFixedT(locale, "home")),生产环境可能未预加载该命名空间。

解决:
在app/i18n.ts中添加home到命名空间列表:

export default {
  // ...其他配置
  ns: ["common", "home"],  
};

3. 生产环境i18next初始化异步问题

生产环境服务器启动时可能未完成翻译文件加载,导致首次请求无法获取翻译值,返回原始键。

解决:
在服务器启动时等待i18next初始化完成:

// app/i18next.server.ts
await i18next.initPromise; // 确保初始化完成后再处理请求

4. 语言检测在生产环境失效

生产环境中i18next.getLocale(request)可能无法正确检测语言,导致使用未配置的语言,从而显示原始键。

解决:
在getLocale函数中强制校验语言是否在支持列表中:

// app/utils/getLocale.ts
import i18n from "~/i18n"; // 引入配置

export async function getLocale(request: Request): Promise<string> {
  const sessionLocale = await getLanguagePreference(request);
  let locale = sessionLocale || (await i18next.getLocale(request));
  
  // 确保locale在支持的语言列表内
  if (!i18n.supportedLngs.includes(locale)) {
    locale = "en";
  }
  
  return locale;
}

5. 构建配置问题

Remix生产构建时可能优化资源路径,导致翻译文件访问路径变化。检查remix.config.js中的publicPath是否设置正确,确保public/locales目录被完整复制到构建产物中。

解决:
确认remix.config.js中publicPath为默认值"/",若自定义路径需同步调整i18next的加载路径。同时检查构建命令是否正确复制了public目录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:54:49