How toPersist the user locale using cookies with Remix & i18next

If you're using remix-i18next this has a getLocale method that you can use to know the user locale.

The way it works is by checking in multiple places if it can find the locale, being the last one the Accept-Language header that browsers send to the server.

But it can also read from a cookie if configured to do so. First let's create a cookie where we'll save the locale.

app/cookies.server.ts
import { createCookie } from "@remix-run/node"; export const localeCookie = createCookie("locale", { httpOnly: true, secure: process.env.NODE_ENV === "production", path: "/", maxAge: 365 * 24 * 60 * 60, });

Now, let's go to the file where we configure and instantiate RemixI18Next, the diff below supposes you're using the i18next.server.ts from remix-i18next docs.

app/i18next.server.ts
import Backend from "i18next-fs-backend"; import { resolve } from "node:path"; import { RemixI18Next } from "remix-i18next"; import i18n from "~/i18n"; // your i18n configuration file +import { localeCookie } from "~/cookies.server"; let i18next = new RemixI18Next({ detection: { supportedLanguages: i18n.supportedLngs, fallbackLanguage: i18n.fallbackLng, + cookie: localeCookie, }, // This is the configuration for i18next used // when translating messages server-side only i18next: { ...i18n, backend: { loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"), }, }, // The i18next plugins you want RemixI18next to use for `i18n.getFixedT` inside loaders and actions. // E.g. The Backend plugin for loading translations from the file system // Tip: You could pass `resources` to the `i18next` configuration and avoid a backend here plugins: [Backend], }); export default i18next;

Now, when you call i18next.getLocale(request) it will check if the localeCookie has a value and if the value is one of the supported languages, if it's it will return that as the user locale, if it's not it will continue checking until it finds the Accept-Language header or use the fallback language.

app/root.tsx
export async function loader({ request }: LoaderFunctionArgs) { let locale = await i18next.getLocale(request) return json({ locale }); }

Now you can set the cookie in an action to let the user persist the language it wants to use.