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);
});
}