(edited)
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, ! 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: https://remix.run/resources/remix-i18next
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-anyconst 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); });}