Remix实现多语言

JS 中不同框架都有自己的多语言库,在 Remix 使用多语言,需要安装 remix-i18next 这个库。这个包是基于 i18next 开发的,使用方式可以到官网查看。 Remix-i18next 安装步骤如下:

安装依赖

npm install remix-i18next i18next react-i18next i18next-browser-languagedetector i18next-http-backend i18next-fs-backend

创建语言文件

创建英文和中文的语言文件。

public/locales/en/common.json

{
	"greeting": "Hello"
}

public/locales/zh/common.json

{
	"greeting": "你好"
}

i18n 配置文件

app/i18n.ts

export default {
	// 这是您的应用程序支持的语言列表
	supportedLngs: ["en", "zh"],
	// 如果用户的语言不在 supportedLngs 中,
	// 您希望使用的默认语言
	fallbackLng: "zh",
	// i18next 的默认命名空间是 "translation",
	// 但您可以在这里自定义
	defaultNS: "common",
};

app/i18next.server.ts

import Backend from "i18next-fs-backend";  // 导入 i18next 文件系统后端
import { resolve } from "node:path";       // 导入 node 的 path 解析函数
import { RemixI18Next } from "remix-i18next/server";  // 导入 Remix 的 i18next 服务器端模块
import i18n from "~/i18n"; // 导入您的 i18n 配置文件

let i18next = new RemixI18Next({
    detection: {
        supportedLanguages: i18n.supportedLngs,  // 支持的语言列表
        fallbackLanguage: i18n.fallbackLng,      // 默认回退语言
    },
    // 以下是仅在服务器端翻译消息时使用的 i18next 配置
    i18next: {
        ...i18n,
        backend: {
            loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"),  // 指定翻译文件加载路径
        },
    },
    // 您希望 RemixI18next 在 loaders 和 actions 中通过 `i18n.getFixedT` 使用的 i18next 插件。
    // 例如:文件系统后端插件用于从文件系统加载翻译
    // 提示:您可以将 `resources` 传递给 `i18next` 配置并在此处避免使用后端
    plugins: [Backend],
});

export default i18next;  // 导出 i18next 配置

Remix Client

app/entry.client.tsx

import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import i18n from "./i18n";
import i18next from "i18next";
import { I18nextProvider, initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import Backend from "i18next-http-backend";
import { getInitialNamespaces } from "remix-i18next/client";

async function hydrate() {
    await i18next
        .use(initReactI18next) // 告诉 i18next 使用 react-i18next 插件
        .use(LanguageDetector) // 设置客户端语言检测
        .use(Backend) // 配置您的后端
        .init({
            ...i18n, // 展开配置
            // 此功能检测您的路由在服务端渲染时使用的命名空间
            ns: getInitialNamespaces(),
            backend: { loadPath: "/locales/{{lng}}/{{ns}}.json" },
            detection: {
                // 这里仅启用 htmlTag 检测, 我们将仅在服务器端使用 remix-i18next 检测语言
                // 通过使用 `<html lang>` 属性,我们可以通知客户端服务器端检测到的语言
                order: ["htmlTag"],
                // 因为我们只使用 htmlTag,没有理由在浏览器上缓存语言,所以我们禁用它
                caches: [],
            },
        });

    startTransition(() => {
        hydrateRoot(
            document,
            <I18nextProvider i18n={i18next}>
                <StrictMode>
                    <RemixBrowser />
                </StrictMode>
            </I18nextProvider>,
        );
    });
}

if (window.requestIdleCallback) {
    window.requestIdleCallback(hydrate);
} else {
    // Safari 不支持 requestIdleCallback
    // https://caniuse.com/requestidlecallback
    window.setTimeout(hydrate, 1);
}

Remix Server

app/entry.server.tsx

import { PassThrough } from "stream";
import {
	createReadableStreamFromReadable,
	type EntryContext,
} from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";
import { createInstance } from "i18next";
import i18next from "./i18next.server";
import { I18nextProvider, initReactI18next } from "react-i18next";
import Backend from "i18next-fs-backend";
import i18n from "./i18n"; // 你的 i18n 配置文件
import { resolve } from "node:path";

const ABORT_DELAY = 5000;  // 终止延迟时间

export default async function handleRequest(
	request: Request,
	responseStatusCode: number,
	responseHeaders: Headers,
	remixContext: EntryContext,
) {
	let callbackName = isbot(request.headers.get("user-agent"))
		? "onAllReady"  // 当所有内容准备就绪时
		: "onShellReady";  // 当外壳准备就绪时

	let instance = createInstance();
	let lng = await i18next.getLocale(request);  // 获取请求的语言环境
	let ns = i18next.getRouteNamespaces(remixContext);  // 获取即将渲染的路由的命名空间

	await instance
		.use(initReactI18next) // 告诉我们的实例使用 react-i18next
		.use(Backend) // 设置我们的后端
		.init({
			...i18n, // 展开配置
			lng, // 我们上面检测到的语言环境
			ns, // 路由想要使用的命名空间
			backend: { loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json") },  // 加载路径
		});

	return new Promise((resolve, reject) => {
		let didError = false;

		let { pipe, abort } = renderToPipeableStream(
			<I18nextProvider i18n={instance}>
				<RemixServer context={remixContext} url={request.url} />
			</I18nextProvider>,
			{
				[callbackName]: () => {
					let body = new PassThrough();
					const stream = createReadableStreamFromReadable(body);
					responseHeaders.set("Content-Type", "text/html");  // 设置内容类型

					resolve(
						new Response(stream, {
							headers: responseHeaders,
							status: didError ? 500 : responseStatusCode,
						}),
					);

					pipe(body);
				},
				onShellError(error: unknown) {  // 外壳错误处理
					reject(error);
				},
				onError(error: unknown) {  // 错误处理
					didError = true;

					console.error(error);  // 打印错误信息
				},
			},
		);

		setTimeout(abort, ABORT_DELAY);  // 设置终止时间
	});
}

修改入口文件

修改 root.tsx 文件,添加 i18n相关配置。

import { useChangeLanguage } from "remix-i18next/react";
import { useTranslation } from "react-i18next";
import i18next from "~/i18next.server";

export async function loader({ request }: LoaderArgs) {
	let locale = await i18next.getLocale(request);
	return json({ locale });
}

export let handle = {
	// In the handle export, we can add a i18n key with namespaces our route
	// will need to load. This key can be a single string or an array of strings.
	// TIP: In most cases, you should set this to your defaultNS from your i18n config
	// or if you did not set one, set it to the i18next default namespace "translation"
	i18n: "common",
};

export default function Root() {
	// Get the locale from the loader
	let { locale } = useLoaderData<typeof loader>();

	let { i18n } = useTranslation();

	// This hook will change the i18n instance language to the current locale
	// detected by the loader, this way, when we do something to change the
	// language, this locale will change and i18next will load the correct
	// translation files
	useChangeLanguage(locale);

	return (
		<html lang={locale} dir={i18n.dir()}>
			<head>
				<Meta />
				<Links />
			</head>
			<body>
				<Outlet />
				<ScrollRestoration />
				<Scripts />
				<LiveReload />
			</body>
		</html>
	);
}

把 t 方法引进来直接使用即可。

let { i18n, t} = useTranslation();

配置生效

在这里插入图片描述
更多的用法可以参考官网,例如在语言文件中添加参数。

#配置文件
en: {
      translation: {
        greeting: "Hello, {{name}}! Welcome to our website."
      }
    }
# 参数
t 将参数传入
t('greeting', { name: userName });    

相关推荐

  1. 在 10 分钟内在 Remix (React) 中实现单点登录

    2024-05-09 20:12:02       38 阅读
  2. c语言线程队列实现

    2024-05-09 20:12:02       40 阅读
  3. Go语言中如何实现

    2024-05-09 20:12:02       13 阅读
  4. Remix路由详解

    2024-05-09 20:12:02       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-09 20:12:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-09 20:12:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-09 20:12:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-09 20:12:02       20 阅读

热门阅读

  1. Github 2024-05-06 开源项目日报 Top10

    2024-05-09 20:12:02       11 阅读
  2. Springboot整合Minio,2024版教程

    2024-05-09 20:12:02       11 阅读
  3. 【android systrace学习】

    2024-05-09 20:12:02       11 阅读
  4. MySQL中SELECT语句的执行流程详解

    2024-05-09 20:12:02       10 阅读
  5. BL120协议Modbus RTU和Modbus TCP互转

    2024-05-09 20:12:02       11 阅读
  6. mybatis 批量添加数据

    2024-05-09 20:12:02       8 阅读
  7. Utreexod:支持Utreexo累加器的比特币全节点

    2024-05-09 20:12:02       12 阅读
  8. 解决报错HTTPError: HTTP Error 403: Forbidden

    2024-05-09 20:12:02       11 阅读
  9. TypeScript基础:类型系统介绍

    2024-05-09 20:12:02       11 阅读