Hey! I have sollution for i18next on server on vercel.
if you want more details on how to solve this problem with transactions on the server side, write, I will write a solution that does not cause an error
Hey! I have sollution for i18next on server on vercel.
if you want more details on how to solve this problem with transactions on the server side, write, I will write a solution that does not cause an error
Hey, @mazurro1! I’m sure folks will find this interesting. Feel free to share
All you need to do is fetch the transactions and everything starts working
The rest of the code from the official remix site: remix-i18next | Remix Resources
entry.servet.tsx:
import { PassThrough } from "node:stream";
import {
createReadableStreamFromReadable,
type EntryContext,
} from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import { createInstance } from "i18next";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";
import { I18nextProvider, initReactI18next } from "react-i18next";
import { cookiesKey } from "./constants/cookies";
import { E_Language } from "./constants/languages";
import { getCookieValue } from "./data/cookies.server";
import i18n from "./i18n";
import i18next from "./i18next.server";
const ABORT_DELAY = 5000;
const checkFileExists = async (url: string): Promise<boolean> => {
try {
const response = await fetch(url, { method: "HEAD" });
return response.ok;
} catch (error) {
console.error(`Error checking file existence at ${url}:`, error);
return false;
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const fetchFile = async (url: string): Promise<any> => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to load ${url}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
throw new Error(`Error fetching ${url}: ${error}`);
}
};
export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
) {
const callbackName = isbot(request.headers.get("user-agent"))
? "onAllReady"
: "onShellReady";
const instance = createInstance();
const ns = i18next.getRouteNamespaces(remixContext);
const CustomBackend = {
read: (language: string, namespace: string, callback: Function) => {
const protocol = request.headers.get("x-forwarded-proto") || "http";
const host = request.headers.get("host");
const baseUrl =
process.env.NODE_ENV === "development"
? "https://your-url-prd.com"
: `${protocol}://${host}`;
const url = `${baseUrl}/locales/${language}/${namespace}.json`;
const handleRequest = async () => {
try {
const exists = await checkFileExists(url);
if (!exists) {
callback(new Error(`File not found: ${url}`), false);
return;
}
const data = await fetchFile(url);
callback(null, data);
} catch (error) {
console.error(error);
callback(error, false);
}
};
handleRequest();
},
type: "backend",
};
await instance
.use(initReactI18next)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.use(CustomBackend)
.use(LanguageDetector)
.init({
...i18n,
ns,
});
return new Promise((resolve, reject) => {
let didError = false;
const { abort, pipe } = renderToPipeableStream(
<I18nextProvider i18n={instance}>
<RemixServer context={remixContext} url={request.url} />
</I18nextProvider>,
{
[callbackName]: () => {
const 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);
},
onError(error: unknown) {
didError = true;
console.error(error);
},
onShellError(error: unknown) {
reject(error);
},
},
);
setTimeout(abort, ABORT_DELAY);
});
}