Gathering detailed insights and metrics for remix-i18next-cp
Gathering detailed insights and metrics for remix-i18next-cp
Gathering detailed insights and metrics for remix-i18next-cp
Gathering detailed insights and metrics for remix-i18next-cp
npm install remix-i18next-cp
Typescript
Module System
Node Version
NPM Version
TypeScript (95.8%)
JavaScript (4.2%)
Total Downloads
139
Last Day
1
Last Week
4
Last Month
9
Last Year
61
MIT License
115 Commits
1 Branches
1 Contributors
Updated on Jan 15, 2024
Minified
Minified + Gzipped
Latest Version
5.5.0
Package Id
remix-i18next-cp@5.5.0
Unpacked Size
52.54 kB
Size
11.68 kB
File Count
32
NPM Version
9.8.1
Node Version
18.18.2
Published on
Jan 15, 2024
Cumulative downloads
Total Downloads
Last Day
0%
1
Compared to previous day
Last Week
33.3%
4
Compared to previous week
Last Month
28.6%
9
Compared to previous month
Last Year
-21.8%
61
Compared to previous year
5
26
The easiest way to translate your Remix apps.
remix-i18next
simplifies internationalisation for your Remix app without extra dependencies.remix-i18next
supports passing translations and configuration options into routes from the loader.remix-i18next
doesn't hide the configuration so you can add any plugin you want or configure as pleased.The first step is to install it in your project with
1npm install remix-i18next i18next react-i18next i18next-browser-languagedetector
You will need to configure an i18next backend and language detector, in that case you can install them too, for the rest of the setup guide we'll use the http and fs backends.
1npm install i18next-http-backend i18next-fs-backend
First let's create some translation files
public/locales/en/common.json
:
1{ 2 "greeting": "Hello" 3}
public/locales/es/common.json
:
1{ 2 "greeting": "Hola" 3}
Next, set your i18next configuration.
These two files can go somewhere in your app folder.
For this example, we will create app/i18n.ts
:
1export default { 2 // This is the list of languages your application supports 3 supportedLngs: ["en", "es"], 4 // This is the language you want to use in case 5 // if the user language is not in the supportedLngs 6 fallbackLng: "en", 7 // The default namespace of i18next is "translation", but you can customize it here 8 defaultNS: "common", 9 // Disabling suspense is recommended 10 react: { useSuspense: false }, 11};
And then create a file named i18next.server.ts
with the following code:
1import Backend from "i18next-fs-backend";
2import { resolve } from "node:path";
3import { RemixI18Next } from "remix-i18next";
4import i18n from "~/i18n"; // your i18n configuration file
5
6let i18next = new RemixI18Next({
7 detection: {
8 supportedLanguages: i18n.supportedLngs,
9 fallbackLanguage: i18n.fallbackLng,
10 },
11 // This is the configuration for i18next used
12 // when translating messages server-side only
13 i18next: {
14 ...i18n,
15 backend: {
16 loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"),
17 },
18 },
19 // The i18next plugins you want RemixI18next to use for `i18n.getFixedT` inside loaders and actions.
20 // E.g. The Backend plugin for loading translations from the file system
21 // Tip: You could pass `resources` to the `i18next` configuration and avoid a backend here
22 plugins: [Backend],
23});
24
25export default i18next;
Now in your entry.client.tsx
replace the default code with this:
1import { RemixBrowser } from "@remix-run/react"; 2import { startTransition, StrictMode } from "react"; 3import { hydrateRoot } from "react-dom/client"; 4import i18n from "./i18n"; 5import i18next from "i18next"; 6import { I18nextProvider, initReactI18next } from "react-i18next"; 7import LanguageDetector from "i18next-browser-languagedetector"; 8import Backend from "i18next-http-backend"; 9import { getInitialNamespaces } from "remix-i18next"; 10 11async function hydrate() { 12 await i18next 13 .use(initReactI18next) // Tell i18next to use the react-i18next plugin 14 .use(LanguageDetector) // Setup a client-side language detector 15 .use(Backend) // Setup your backend 16 .init({ 17 ...i18n, // spread the configuration 18 // This function detects the namespaces your routes rendered while SSR use 19 ns: getInitialNamespaces(), 20 backend: { loadPath: "/locales/{{lng}}/{{ns}}.json" }, 21 detection: { 22 // Here only enable htmlTag detection, we'll detect the language only 23 // server-side with remix-i18next, by using the `<html lang>` attribute 24 // we can communicate to the client the language detected server-side 25 order: ["htmlTag"], 26 // Because we only use htmlTag, there's no reason to cache the language 27 // on the browser, so we disable it 28 caches: [], 29 }, 30 }); 31 32 startTransition(() => { 33 hydrateRoot( 34 document, 35 <I18nextProvider i18n={i18next}> 36 <StrictMode> 37 <RemixBrowser /> 38 </StrictMode> 39 </I18nextProvider> 40 ); 41 }); 42} 43 44if (window.requestIdleCallback) { 45 window.requestIdleCallback(hydrate); 46} else { 47 // Safari doesn't support requestIdleCallback 48 // https://caniuse.com/requestidlecallback 49 window.setTimeout(hydrate, 1); 50}
And in your entry.server.tsx
replace the code with this:
1import { PassThrough } from "stream"; 2import type { EntryContext } from "@remix-run/node"; 3import { Response } from "@remix-run/node"; 4import { RemixServer } from "@remix-run/react"; 5import isbot from "isbot"; 6import { renderToPipeableStream } from "react-dom/server"; 7import { createInstance } from "i18next"; 8import i18next from "./i18next.server"; 9import { I18nextProvider, initReactI18next } from "react-i18next"; 10import Backend from "i18next-fs-backend"; 11import i18n from "./i18n"; // your i18n configuration file 12import { resolve } from "node:path"; 13 14const ABORT_DELAY = 5000; 15 16export default async function handleRequest( 17 request: Request, 18 responseStatusCode: number, 19 responseHeaders: Headers, 20 remixContext: EntryContext 21) { 22 let callbackName = isbot(request.headers.get("user-agent")) 23 ? "onAllReady" 24 : "onShellReady"; 25 26 let instance = createInstance(); 27 let lng = await i18next.getLocale(request); 28 let ns = i18next.getRouteNamespaces(remixContext); 29 30 await instance 31 .use(initReactI18next) // Tell our instance to use react-i18next 32 .use(Backend) // Setup our backend 33 .init({ 34 ...i18n, // spread the configuration 35 lng, // The locale we detected above 36 ns, // The namespaces the routes about to render wants to use 37 backend: { loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json") }, 38 }); 39 40 return new Promise((resolve, reject) => { 41 let didError = false; 42 43 let { pipe, abort } = renderToPipeableStream( 44 <I18nextProvider i18n={instance}> 45 <RemixServer context={remixContext} url={request.url} /> 46 </I18nextProvider>, 47 { 48 [callbackName]: () => { 49 let body = new PassThrough(); 50 51 responseHeaders.set("Content-Type", "text/html"); 52 53 resolve( 54 new Response(body, { 55 headers: responseHeaders, 56 status: didError ? 500 : responseStatusCode, 57 }) 58 ); 59 60 pipe(body); 61 }, 62 onShellError(error: unknown) { 63 reject(error); 64 }, 65 onError(error: unknown) { 66 didError = true; 67 68 console.error(error); 69 }, 70 } 71 ); 72 73 setTimeout(abort, ABORT_DELAY); 74 }); 75}
Now, in your app/root.tsx
or app/root.jsx
file create a loader if you don't have one with the following code.
1import { useChangeLanguage } from "remix-i18next"; 2import { useTranslation } from "react-i18next"; 3import i18next from "~/i18next.server"; 4 5export async function loader({ request }: LoaderArgs) { 6 let locale = await i18next.getLocale(request); 7 return json({ locale }); 8} 9 10export let handle = { 11 // In the handle export, we can add a i18n key with namespaces our route 12 // will need to load. This key can be a single string or an array of strings. 13 // TIP: In most cases, you should set this to your defaultNS from your i18n config 14 // or if you did not set one, set it to the i18next default namespace "translation" 15 i18n: "common", 16}; 17 18export default function Root() { 19 // Get the locale from the loader 20 let { locale } = useLoaderData<typeof loader>(); 21 22 let { i18n } = useTranslation(); 23 24 // This hook will change the i18n instance language to the current locale 25 // detected by the loader, this way, when we do something to change the 26 // language, this locale will change and i18next will load the correct 27 // translation files 28 useChangeLanguage(locale); 29 30 return ( 31 <html lang={locale} dir={i18n.dir()}> 32 <head> 33 <Meta /> 34 <Links /> 35 </head> 36 <body> 37 <Outlet /> 38 <ScrollRestoration /> 39 <Scripts /> 40 <LiveReload /> 41 </body> 42 </html> 43 ); 44}
Warning In latest versions you may find an error with
useChangeLanguage
hook, (see #107), to solve it, copy the code ofuseChangeLanguage
to your own app and use it instead of the one provided byremix-i18next
.
1export function useChangeLanguage(locale: string) { 2 let { i18n } = useTranslation(); 3 useEffect(() => { 4 i18n.changeLanguage(locale); 5 }, [locale, i18n]); 6}
Finally, in any route you want to translate, you can use the t()
function, as per the i18next documentation and use translations from the default namespace.
1import { useTranslation } from "react-i18next"; 2 3export default function Component() { 4 let { t } = useTranslation(); 5 return <h1>{t("greeting")}</h1>; 6}
If you wish to split up your translation files, you create new translation files like:
public/locales/en/home.json
1{ 2 "title": "remix-i18n is awesome" 3}
public/locales/es/home.json
1{ 2 "title": "remix-i18n es increíble" 3}
And use them in your routes:
1import { useTranslation } from "react-i18next"; 2 3// This tells remix to load the "home" namespace 4export let handle = { i18n: "home" }; 5 6export default function Component() { 7 let { t } = useTranslation("home"); 8 return <h1>{t("title")}</h1>; 9}
And that's it, repeat the last step for each route you want to translate, remix-i18next will automatically let i18next what namespaces and language to use and this one will load the correct translation files using your configured backend.
If you need to get translated texts inside a loader or action function, for example to translate the page title used later in a MetaFunction, you can use the i18n.getFixedT
method to get a t
function.
1export async function loader({ request }: LoaderArgs) { 2 let t = await i18n.getFixedT(request); 3 let title = t("My page title"); 4 return json({ title }); 5} 6 7export let meta: MetaFunction = ({ data }) => { 8 return { title: data.title }; 9};
The getFixedT
function can be called using a combination of parameters:
getFixedT(request)
: will use the request to get the locale and the defaultNS
set in the config or translation
(the i18next default namespace)getFixedT("es")
: will use the specified es
locale and the defaultNS
set in config, or translation
(the i18next default namespace)getFixedT(request, "common")
will use the request to get the locale and the specified common
namespace to get the translations.getFixedT("es", "common")
will use the specified es
locale and the specified common
namespace to get the translations.getFixedT(request, "common", { keySeparator: false })
will use the request to get the locale and the common
namespace to get the translations, also use the options of the third argument to initialize the i18next instance.getFixedT("es", "common", { keySeparator: false })
will use the specified es
locale and the common
namespace to get the translations, also use the options of the third argument to initialize the i18next instance.If you always need to set the same i18next options, you can pass them to RemixI18Next when creating the new instance.
1export let i18n = new RemixI18Next({
2 detection: { supportedLanguages: ["es", "en"], fallbackLanguage: "en" },
3 // The config here will be used for getFixedT
4 i18next: {
5 backend: { loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json") },
6 },
7 // This backend will be used by getFixedT
8 backend: Backend,
9});
This options will be overwritten by the options provided to getFixedT
.
No vulnerabilities found.
No security vulnerabilities found.