• Stars
    star
    520
  • Rank 82,101 (Top 2 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 2 years ago
  • Updated 12 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The easiest way to translate your Remix apps

remix-i18next

The easiest way to translate your Remix apps.

Why remix-i18next?

  • Easy to set up, easy to use: setup only takes a few steps, and configuration is simple.
  • No other requirements: remix-i18next simplifies internationalisation for your Remix app without extra dependencies.
  • Production ready: remix-i18next supports passing translations and configuration options into routes from the loader.
  • Take the control: remix-i18next doesn't hide the configuration so you can add any plugin you want or configure as pleased.

Setup

Installation

The first step is to install it in your project with

npm 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.

npm install i18next-http-backend i18next-fs-backend

Configuration

First let's create some translation files

public/locales/en/common.json:

{
  "greeting": "Hello"
}

public/locales/es/common.json:

{
  "greeting": "Hola"
}

Next, set your i18next configuration.

These two files can go somewhere in your app folder.

For this example, we will create app/i18n.ts:

export default {
  // This is the list of languages your application supports
  supportedLngs: ["en", "es"],
  // This is the language you want to use in case
  // if the user language is not in the supportedLngs
  fallbackLng: "en",
  // The default namespace of i18next is "translation", but you can customize it here
  defaultNS: "common",
  // Disabling suspense is recommended
  react: { useSuspense: false },
};

And then create a file named i18next.server.ts with the following code:

import Backend from "i18next-fs-backend";
import { resolve } from "node:path";
import { RemixI18Next } from "remix-i18next";
import i18n from "~/i18n"; // your i18n configuration file

let i18next = new RemixI18Next({
  detection: {
    supportedLanguages: i18n.supportedLngs,
    fallbackLanguage: i18n.fallbackLng,
  },
  // This is the configuration for i18next used
  // when translating messages server-side only
  i18next: {
    ...i18n,
    backend: {
      loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"),
    },
  },
  // The backend you want to use to load the translations
  // Tip: You could pass `resources` to the `i18next` configuration and avoid
  // a backend here
  backend: Backend,
});

export default i18next;

Client-side configuration

Now in your entry.client.tsx replace the default code with this:

import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import i18n from "./i18n";
import i18next from "i18next";
import { I18nextProvider, initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import Backend from "i18next-http-backend";
import { getInitialNamespaces } from "remix-i18next";

async function hydrate() {
  await i18next
    .use(initReactI18next) // Tell i18next to use the react-i18next plugin
    .use(LanguageDetector) // Setup a client-side language detector
    .use(Backend) // Setup your backend
    .init({
      ...i18n, // spread the configuration
      // This function detects the namespaces your routes rendered while SSR use
      ns: getInitialNamespaces(),
      backend: { loadPath: "/locales/{{lng}}/{{ns}}.json" },
      detection: {
        // Here only enable htmlTag detection, we'll detect the language only
        // server-side with remix-i18next, by using the `<html lang>` attribute
        // we can communicate to the client the language detected server-side
        order: ["htmlTag"],
        // Because we only use htmlTag, there's no reason to cache the language
        // on the browser, so we disable it
        caches: [],
      },
    });

  startTransition(() => {
    hydrateRoot(
      document,
      <I18nextProvider i18n={i18next}>
        <StrictMode>
          <RemixBrowser />
        </StrictMode>
      </I18nextProvider>
    );
  });
}

if (window.requestIdleCallback) {
  window.requestIdleCallback(hydrate);
} else {
  // Safari doesn't support requestIdleCallback
  // https://caniuse.com/requestidlecallback
  window.setTimeout(hydrate, 1);
}

Server-side configuration

And in your entry.server.tsx replace the code with this:

import { PassThrough } from "stream";
import type { EntryContext } from "@remix-run/node";
import { Response } from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import isbot from "isbot";
import { renderToPipeableStream } from "react-dom/server";
import { createInstance } from "i18next";
import i18next from "./i18next.server";
import { I18nextProvider, initReactI18next } from "react-i18next";
import Backend from "i18next-fs-backend";
import i18n from "./i18n"; // your i18n configuration file
import { resolve } from "node:path";

const ABORT_DELAY = 5000;

export default async function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  let callbackName = isbot(request.headers.get("user-agent"))
    ? "onAllReady"
    : "onShellReady";

  let instance = createInstance();
  let lng = await i18next.getLocale(request);
  let ns = i18next.getRouteNamespaces(remixContext);

  await instance
    .use(initReactI18next) // Tell our instance to use react-i18next
    .use(Backend) // Setup our backend
    .init({
      ...i18n, // spread the configuration
      lng, // The locale we detected above
      ns, // The namespaces the routes about to render wants to use
      backend: { loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json") },
    });

  return new Promise((resolve, reject) => {
    let didError = false;

    let { pipe, abort } = renderToPipeableStream(
      <I18nextProvider i18n={instance}>
        <RemixServer context={remixContext} url={request.url} />
      </I18nextProvider>,
      {
        [callbackName]: () => {
          let body = new PassThrough();

          responseHeaders.set("Content-Type", "text/html");

          resolve(
            new Response(body, {
              headers: responseHeaders,
              status: didError ? 500 : responseStatusCode,
            })
          );

          pipe(body);
        },
        onShellError(error: unknown) {
          reject(error);
        },
        onError(error: unknown) {
          didError = true;

          console.error(error);
        },
      }
    );

    setTimeout(abort, ABORT_DELAY);
  });
}

Usage

Now, in your app/root.tsx or app/root.jsx file create a loader if you don't have one with the following code.

import { useChangeLanguage } from "remix-i18next";
import { useTranslation } from "react-i18next";
import i18next from "~/i18next.server";

export async function loader({ request }: LoaderArgs) {
  let locale = await i18next.getLocale(request);
  return json({ locale });
}

export let handle = {
  // In the handle export, we can add a i18n key with namespaces our route
  // will need to load. This key can be a single string or an array of strings.
  // TIP: In most cases, you should set this to your defaultNS from your i18n config
  // or if you did not set one, set it to the i18next default namespace "translation"
  i18n: "common",
};

export default function Root() {
  // Get the locale from the loader
  let { locale } = useLoaderData<typeof loader>();

  let { i18n } = useTranslation();

  // This hook will change the i18n instance language to the current locale
  // detected by the loader, this way, when we do something to change the
  // language, this locale will change and i18next will load the correct
  // translation files
  useChangeLanguage(locale);

  return (
    <html lang={locale} dir={i18n.dir()}>
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

Warning In latest versions you may find an error with useChangeLanguage hook, (see #107), to solve it, copy the code of useChangeLanguage to your own app and use it instead of the one provided by remix-i18next.

export function useChangeLanguage(locale: string) {
  let { i18n } = useTranslation();
  useEffect(() => {
    i18n.changeLanguage(locale);
  }, [locale, i18n]);
}

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.

import { useTranslation } from "react-i18next";

export default function Component() {
  let { t } = useTranslation();
  return <h1>{t("greeting")}</h1>;
}

If you wish to split up your translation files, you create new translation files like:

public/locales/en/home.json

{
  "title": "remix-i18n is awesome"
}

public/locales/es/home.json

{
  "title": "remix-i18n es incre铆ble"
}

And use them in your routes:

import { useTranslation } from "react-i18next";

// This tells remix to load the "home" namespace
export let handle = { i18n: "home" };

export default function Component() {
  let { t } = useTranslation("home");
  return <h1>{t("title")}</h1>;
}

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.

Translating text inside loaders or actions

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.

export async function loader({ request }: LoaderArgs) {
  let t = await i18n.getFixedT(request);
  let title = t("My page title");
  return json({ title });
}

export let meta: MetaFunction = ({ data }) => {
  return { title: data.title };
};

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.

export let i18n = new RemixI18Next({
  detection: { supportedLanguages: ["es", "en"], fallbackLanguage: "en" },
  // The config here will be used for getFixedT
  i18next: {
    backend: { loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json") },
  },
  // This backend will be used by getFixedT
  backend: Backend,
});

This options will be overwritten by the options provided to getFixedT.

More Repositories

1

remix-utils

A set of utility functions and types to use with Remix.run
TypeScript
1,934
star
2

remix-auth

Simple Authentication for Remix
TypeScript
1,782
star
3

impresionante-javascript

Recopilaci贸n de links en espa帽ol sobre JavaScript
416
star
4

flagged

Feature Flags for React made easy with hooks, HOC and Render Props
TypeScript
386
star
5

remix-hono

Hono middlewares for Remix
TypeScript
321
star
6

next-ga

Next.js HOC to integrate Google Analytics on every page change
JavaScript
232
star
7

grial

A Node.js framework for creating GraphQL API servers easily and without a lot of boilerplate.
JavaScript
189
star
8

next-nprogress

Next.js HOC to integrate NProgress inside your app
JavaScript
149
star
9

remix-auth-oauth2

A OAuth2Strategy for Remix Auth
TypeScript
129
star
10

use-mutation

馃К Run side-effects safely in React
TypeScript
119
star
11

swr-sync-storage

Synchronize SWR cache with localStorage or sessionStorage to get offline cache
TypeScript
109
star
12

react-lazy-image

Component to render images and lazyload them if are in the viewport (or near to them).
JavaScript
101
star
13

personal-site

Personal website
TypeScript
95
star
14

now-storage

Use Now static deployments to upload and store files.
JavaScript
90
star
15

remix-auth-form

A Remix Auth strategy for working with forms.
TypeScript
79
star
16

sergiodxa.com

The code behind sergiodxa.com
TypeScript
71
star
17

remix-auth-github

A GitHubStrategy for Remix Auth, based on the OAuth2Strategy
TypeScript
66
star
18

redux-in-spanish

Traducci贸n al espa帽ol de la documentaci贸n de Redux.
64
star
19

web-oidc

An OpenID Connect client built using only Web APIs
TypeScript
49
star
20

remix-auth-strategy-template

A template for creating a new Remix Auth strategy.
TypeScript
47
star
21

remix-socket.io

A Remix app using Express and Socket.io
TypeScript
45
star
22

next-socket.io

An example app with Next.js and socket.io
JavaScript
38
star
23

remix-inspector

Remix devtools to inspect your route data.
TypeScript
38
star
24

react-wordpress

Example of a React+Next.js+WordPress application.
JavaScript
36
star
25

react-lazy-memo

CRA with Suspense, lazy and memo usage demo app
JavaScript
36
star
26

swr-mutate-many

Little function to call mutate against multiple cached keys of SWR
TypeScript
26
star
27

remix-demo-infinite-scroll

A collection of infinite scroll pagination demos built with Remix
TypeScript
26
star
28

remix-on-bun

TypeScript
25
star
29

now-ab

A simple proxy server to handle Now.sh deployments AB tests
JavaScript
25
star
30

es6

Material para la clase BONUS en Comunidad Platzi sobre ECMAScript 6.
HTML
23
star
31

react-course-project

Proyecto para el curso de React.js
JavaScript
22
star
32

micro-next

Integrations between Micro and Next.js
JavaScript
20
star
33

collected-notes

A TypeScript client for the Collected Notes API
17
star
34

now-parcel

A Now v2 Parcel builder.
JavaScript
16
star
35

api-client

A strongly typed API client using Zod
TypeScript
15
star
36

remix-demo-file-upload

A simple demo on how to add a file upload that shows the image being uploaded and later replace it with the actual one
TypeScript
15
star
37

remix-vite-i18next

An example Remix + Vite app with remix-i18next setup
TypeScript
14
star
38

collected-remix

A Collected Notes client in Remix
TypeScript
12
star
39

micro-platzi-profile

Ejemplo de microservicio usando https://github.com/zeit/micro para scrappear un perf铆l de Platzi
JavaScript
12
star
40

markdown-it-mentions

markdown-it plugin to support Twitter like mentions
JavaScript
11
star
41

react-use-permissions

React hook for Permissions API
JavaScript
11
star
42

use-safe-callback

Wrap a function to ensure you never call it if a component is unmounted, useful to avoid cases where an async code could finish after a component has unmounted and it tries to update a state
TypeScript
11
star
43

redux-duck

Helper function to create Redux modules using the ducks-modular-redux proposal
TypeScript
10
star
44

use-log

Log a state or prop every time it changes
TypeScript
10
star
45

cf-bootcamp-react-router-lesson

TypeScript
9
star
46

react-simple-modal

React modal make it simple.
JavaScript
8
star
47

remix-mdn

A MDN clone of Remix
TypeScript
8
star
48

remix-auth-token

Token based authentication for Remix Auth
TypeScript
8
star
49

next-analytics

Next.js HOC to integrate Google Analytics and FB Pixel
JavaScript
8
star
50

collected-notes-website

A Next.js based clon of the Collected Notes website
TypeScript
8
star
51

personal-slides

The slides of my talks
JavaScript
7
star
52

collected-notes-next-blog

Blog example using Collected Notes as CMS
TypeScript
7
star
53

react-use-quicklink

Quicklink hook for React
JavaScript
6
star
54

use-validate-image-url

TypeScript
6
star
55

email-value

TypeScript
6
star
56

isomorphic-react-todo

Aplicaci贸n web de TODOs isom贸rfica hecha con React.js
JavaScript
6
star
57

use-consistent-value

Keep a consistent reference to an object or array based on their values.
TypeScript
6
star
58

markdown-it-codesandbox

markdown-it plugin to embed CodeSandbox editor.
JavaScript
5
star
59

remix-demo-prefetch-fetcher

Demo Remix app to prefetch the fetcher data
TypeScript
5
star
60

next-custom-query

Example Next.js app using a custom server with queries in the URLs.
JavaScript
5
star
61

yifi-search

Backbone.js WebApp for search torrents with the YIFI's API.
JavaScript
5
star
62

navBarAdaptable-mejorUX

Barra de navegaci贸n adaptable con Media Queries y JS
CSS
5
star
63

next-babel-minify

Next.js plugin to replace UglifyJS for BabelMinify
JavaScript
5
star
64

gulp-tasks-boilerplate

Plantilla de tareas de Gulp.js para distintos usos, con un package.json con todos los m贸dulos requeridos.
JavaScript
5
star
65

remix-auth-webauthn

A strategy to support WebAuthn
TypeScript
4
star
66

remix-auth-austin-demo

Remix Auth demo for the Remix Austin meetup
TypeScript
4
star
67

MarvelDB

Webapp to find characters in the Marvel database.
JavaScript
4
star
68

remix-markdoc-example

TypeScript
4
star
69

swr-sync-session

This is an example Next.js + SWR application using SWR revalidation on focus feature to sync session between tabs
JavaScript
4
star
70

dataset

A free to use API to get a lists of various common datasets such as languages, countries, etc.
JavaScript
3
star
71

cf-custom-hooks

Ejercicios de Custom Hooks para C贸digo Facilito
TypeScript
3
star
72

next-credentials

Next.js example using Credential Management API
JavaScript
3
star
73

personal-cli

A CLI tool with commands for personal usage.
JavaScript
3
star
74

platzimusic

Proyecto para el diplomado de React en Platzi
JavaScript
3
star
75

personal-api

An API created for my personal site usage.
JavaScript
2
star
76

cf-bootcamp-deploy

TypeScript
2
star
77

techtalks-remix-demo

Demo of Remix.run for Tech Talks.pe
TypeScript
2
star
78

react-render-service

React Render as a Service
JavaScript
1
star
79

personal-shortening

A personal URL shortening service
JavaScript
1
star
80

virtual-event-starter-kit

TypeScript
1
star
81

remix-with-sentry-sdk

A reproduction of the issue https://github.com/getsentry/sentry-javascript/issues/6294
TypeScript
1
star
82

archive-it

API to get the messages of a Slack channel
TypeScript
1
star
83

dice-roller

A tiny library to calculate roll dices using the classic D&D sintax: 1d20+4.
JavaScript
1
star
84

ngCreate

CLI to create AngularJS files
JavaScript
1
star
85

remix-define-routes

A DSL to define Remix routes with code
TypeScript
1
star
86

react-chat-app

JavaScript
1
star
87

pokedex-swr

Pokedex Application using Next.js and SWR with Tailwind for styling
TypeScript
1
star
88

swr-devtools

TypeScript
1
star
89

react-i18n

Ejemplo de internacionalizaci贸n con React.js y Format.js
JavaScript
1
star
90

es-query

An utilitarian library for DOM manipulation using ECMAScript 7 Function Bind Syntax
JavaScript
1
star
91

device-info

A little service to check a device information.
HTML
1
star
92

deno-md-to-html

Simple MD to HTML parser
TypeScript
1
star
93

Check-userAgent

New method for JS window object that checks the userAgent and returns true if a mobile.
1
star
94

package

A template to create new packages.
TypeScript
1
star
95

Elecalc

Webapp hecha con AngularJS para realizar distintos c谩lculos de electricidad de una manera f谩cil y r谩pida
HTML
1
star
96

tpb-app

WebApp in NodeJS to search torrents in The Pirate Bay
JavaScript
1
star