Remix-paraglidejs
使用 ParaglideJS 翻译您的 Remix 应用。
此依赖项包含有助于您设置 Remix 应用以使用 ParaglideJS 的实用程序。
安装
假设您有一个使用 Vite 的 Remix 应用,并且已经设置了 Paraglide,请按照以下步骤操作
- 安装所需的软件包
npm i --save remix-paraglidejs @inlang/paraglide-vite
- 修改 vite.config.ts。
import { vitePlugin as remix } from "@remix-run/dev";
import { installGlobals } from "@remix-run/node";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
/* --- INCLUDE THIS --- */
import { paraglide } from "@inlang/paraglide-vite";
/* ----------------- */
installGlobals();
export default defineConfig({
plugins: [
remix(),
tsconfigPaths(),
/* --- INCLUDE THIS --- */
paraglide({
project: "./project.inlang", //Path to your inlang project
outdir: "./paraglide", //Where you want the generated files to be placed
}),
/* ----------------- */
],
});
- 包含您的消息文件夹,其中包含两个类似于以下的文件
messages/en.js
{
"title": "Remix web example",
"description": "This is a simple example of how to use Remix to create a web app."
}
messages/es.js
{
"title": "Remix web de ejemplo",
"description": "Este es un ejemplo de una web de remix"
}
-
运行
npm run dev
以生成 paraglide 目录。 -
现在我们需要修改 Remix 入口文件 entry.client.tsx 和 entry.server.tsx。如果您没有看到这些文件,请使用
npx remix reveal
。
// entry.client.tsx
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
/* --- INCLUDE THIS --- */
import { hydrateLang } from 'remix-paraglidejs/client';
import { availableLanguageTags, setLanguageTag } from "<YOUR_PARAGLIDE_DIR>/runtime";
/* ----------------- */
startTransition(() => {
/* --- INCLUDE THIS --- */
const lang = hydrateLang('language-tag', availableLanguageTags);
setLanguageTag(lang);
/* ----------------- */
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
</StrictMode>
);
});
// entry.server.tsx
import { PassThrough } from "node:stream";
import type { AppLoadContext, EntryContext } from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";
import {
createReadableStreamFromReadable,
/* --- INCLUDE THIS --- */
createCookie,
} from "@remix-run/node";
import { setLangServerCookie, getContextLang } from 'remix-paraglidejs/server';
import { setLanguageTag, availableLanguageTags } from "<YOUR_PARAGLIDE_DIR>/runtime";
// language-tag value the same as the one in the entry.client.tsx
export const setLangCookie = createCookie("language-tag", {});
/* ----------------- */
const ABORT_DELAY = 5_000;
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
) {
return isbot(request.headers.get("user-agent") || "")
? handleBotRequest(
request,
responseStatusCode,
responseHeaders,
remixContext
)
: handleBrowserRequest(
request,
responseStatusCode,
responseHeaders,
remixContext
);
}
function handleBotRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
{
onAllReady() {
shellRendered = true;
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);
responseHeaders.set("Content-Type", "text/html");
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
})
);
pipe(body);
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
if (shellRendered) {
console.error(error);
}
},
}
);
setTimeout(abort, ABORT_DELAY);
});
}
function handleBrowserRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
return new Promise((resolve, reject) => {
let shellRendered = false;
/* --- INCLUDE THIS --- */
const lang = getContextLang(remixContext, {
defaultValue: availableLanguageTags[0],
availableLanguages: availableLanguageTags,
// The URL parameter to look for when determining the language
// for example ($lang)._index.tsx
urlParam: 'lang',
});
setLanguageTag(lang);
/* ----------------- */
const { pipe, abort } = renderToPipeableStream(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
{
async onShellReady() {
shellRendered = true;
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);
responseHeaders.set("Content-Type", "text/html");
/* --- INCLUDE THIS --- */
await setLangServerCookie(lang, responseHeaders, setLangCookie);
/* ----------------- */
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
})
);
pipe(body);
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
if (shellRendered) {
console.error(error);
}
},
}
);
setTimeout(abort, ABORT_DELAY);
});
}
Remix 路由用法
修改完这些文件后,您可以包含您的路由,例如 _index.tsx
作为 ($lang)._index.tsx
,并使用 Paraglide。
import { Link } from '@remix-run/react';
import * as m from '<YOUR_PARAGLIDE_DIR>/messages';
export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h1>{m.title()}</h1>
<p>{m.description()}</p>
<ul>
<li>
<Link
to="/en"
reloadDocument
>
EN
</Link>
</li>
<li>
<Link
to="/es"
reloadDocument
>
ES
</Link>
</li>
</ul>
</div>
);
}
查看我们的 示例 以了解如何在不使用 reloadDocument 或不使用 ($lang)
的页面中使用链接。
建议:安装 Sherlock 扩展。