An internationalized HTTP/2 React app that is fast and SEO friendly

Why internationalization?

As a non-native English speaker, I like to think I understand the English language pretty well and it doesn’t bother me to be confronted with a website or product not written in my native language. It is another story when it is not English, nor my native language!

As of 2019, there are over 90 languages that have more than 10 million speakers.

Let’s face it, providing different language support on your web apps is probably a UX best practice and also most certainly increase whatever your conversion metrics are. That’s why your web apps should not be default hardcoded to your native language.

Enter i18next for javascript, and next-i18next for Next.js…

The numeronym i18n represents the word internationalization. It is simply the first and last letter, the 18 stands for the number of letters between the first i and the last n of the word.

based on this internationalization needs was built a framework for javascript called i18next.

I18next is an internationalization-framework written in and for JavaScript. But it’s much more than that. i18next goes beyond just providing the standard i18n features such as (plurals, context, interpolation, format). It provides you with a complete solution to localize your product from web to mobile and desktop.

Among the years, the community created a great ecosystem around it and pretty much built an integration for all major frontend-frameworks.

next-i18next, besides its confusing name, is the integration of i18next for Next.js framework. It is what we are going to use to simply provide internationalization to our Next.js app.

What we are going to build

The goal is to build a small app that goes beyond the basic usage example of next-i18next which is available on their Github repo.

Our app is going to cover those parts :

Use Fastify instead of Express as a faster / smaller server framework that provides HTTP/2 support

Provide auto-detection of user language

Store and use user language preferences with React context API

Provide language change listeners in our component's code, to update special text/lib (using a typed.js component for this example)

It will also use Next.js >9.1.0 that introduces the depreciation of the static folder in preference of the public folder.

Let’s dive into the implementation

Here are the steps we are going to take:

1. Setup the project, and implement a custom server using Fastify

2. Create the frontend app (using react-bootstrap), and add the next-i18next integration

3. Use the React context API to build a user context that store language preferences and provide language update listeners in our components methods

Setup & custom Fastify server

Start by initializing an npm package as usual :

mkdir next-fastify-i18n-example

cd next-fastify-i18n-example

npm init

npm install next @zeit/next-css eventemitter3 react react-dom react-bootstrap react-typed fastify fastify-nextjs fastify-static i18next next-i18next --save

npm install cross-env rimraf --save-dev

You can now create a basic next.config.js file at the root of your project, mainly to set up CSS configuration:

next.config.js

next-i18next provides an Express.js server middleware. But as we are building a mainly static web site, we are going to use Fastify instead of Express. One of the advantages is that it is easily swappable, and Fastify supports Express middleware API, so all middleware available for Express, are also available for Fastify.

Fastify is a web framework highly focused on providing the best developer experience with the least overhead and a powerful plugin architecture. It is inspired by Hapi and Express and as far as we know, it is one of the fastest web frameworks in town.

Create a server.js file at the root of your project. This will be our custom server needed for internationalization. There is three main part of this file:

Fastify server setup

I choose to enable http/2 support, so I need to provide locally generated certificates. To do so I’ve added an npm script that uses OpenSSL in the package.json. Don’t forget to use it once to generate your certificates before starting your server

npm run generate-certs

Setup of Next.js and integration with Fastify & i18next

At this point, code is pretty self-explanatory:

Start Fastify listening

And that’s it, we basically have our running http/2 server:

2. Frontend app with next-i18n

Next-i18next setup

On the custom server configuration, you maybe have spotted a require to an i18n file. This file exports a NextI18Next object that configures i18n. You can create this i18n.js file at the root of your repository and configure it like that :

next-i18next supports an interesting feature: Local subpaths. As they describe it, it’s easiest to explain by example :

myapp.com ---> Homepage in default lang

myapp.com/de ---> Homepage in German

It is not enabled by default, and I think it can be useful in some cases, but as it needs the next.js app to use special Links to support this, we are not going to use it in our app.

Next.js frontend setup

There are two steps here.

> Create an _app.js that use next-i18next appWithTranslation HOC

> Use a second next-i18next’s HOC: withTranslation, in all your pages and components, and specify the translation namespace for your pages

Translation content

Then next-i18next uses JSON files that hold your translations, and expect this kind of folder architecture (be careful, we are using the latest next.js that deprecated the static folder)

.

└── public

└── locales

├── en

| └── common.json

└── fr

└── common.json

Usage in component and page

next-i18next HOC adds a props.t, that is the translation function you can use to reference your JSON documents structure:

It takes a string as the first argument, with first the translation namespace accessible from your component, followed by a colon and the object path in your JSON.

3. Changing and access language preferences with React context API

Changing language will automatically cause a redraw with new data coming from your JSON files. it is done with a call to :

i18n.changeLanguage("fr")

But in most cases, you are going to want more. A frequent approach would be to :

Automatically detect user language from the browser settings,

Provide an icon (or dropdown) that allows the user to change the language

Store your user language settings (if he changes them)

Notify with callbacks, pieces of code that needs to manually update libs or specific stuff when language changes.

Thanks to React context API, we are going to easily be able to implement this in a custom made User Context. A React context comes with a Provider and consumer class. We will, in this case, create our custom context provider that will handle language settings manipulation

React Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Creating the user-context.js file

Let’s create a user-context.js file in your components folder. First, we create our context :

The context needs an object at creation. We will then create a custom Provider component and use its state to define real values :

In the snippet above, we also instantiate an EventEmitter using eventemitter3, that will be used to handle callback triggering when our language changes.

Then we can add our component initialization methods to try to fetch previous user settings from local storage, and if none-detected use the default browser language :

Finally, we implement the component methods we declared in our context to toggle language and register callbacks that will listen to language changes:

Using our context in our navbar component

Let’s create a navbar component that will hold a flag icon that the user can click to toggle language. Using a context in a class component is done like that :

You just need to define a static contextType, and then get your context from this.context. You will then call the toggleLang() method from our user-context provider, which will change its state, trigger i18n.changeLanguage() and re-render with corresponding language file and finally propagate to whoever needs it with the eventemitter. Pretty cool, nah?

Registering a method on language change to update things in code

Let’s create a component that will use the Typed.js lib and will need to update react-typed component props when language changes. we’ll call this component banner.js.

In the same way, this component will use our user-context, but instead of using toggleLang(), it will use our register and unregister listener methods.

First, let’s set up typed with our local state :

Then we will just have to add mount and unmount methods to register our updateTyped() as a listener to our user-context language changes:

And that’s it, our typed component will now reset itself with the corresponding language as soon as the user changes it. It will also avoid memory leaks by un-registering its callback on component unmount.

Conclusion

That’s it, you have now a good base to build a fully internationalized web site or web app, that uses server-side rendering, and can support as many languages as you will need.

A possible next step could be integrating google analytics on your web site to track the origin country of your visitors, create accordingly translation files and delightfully provide them with a website in their native language.

Full code can be found here:

And here is a quick recap of how to init and run this repos :



cd nextjs-fastify-i18n

npm install

npm run generate-certs git clone https://github.com/typedef42/nextjs-fastify-i18n.git cd nextjs-fastify-i18nnpm installnpm run generate-certs

Then use it with production settings :

npm run release

npm run stage

Or in dev :

npm run dev